@xeonr/platform-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/index.d.ts +18 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +24 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/kit/Badge.d.ts +19 -0
  6. package/dist/kit/Badge.d.ts.map +1 -0
  7. package/dist/kit/Badge.js +61 -0
  8. package/dist/kit/Badge.js.map +1 -0
  9. package/dist/kit/Button.d.ts +28 -0
  10. package/dist/kit/Button.d.ts.map +1 -0
  11. package/dist/kit/Button.js +73 -0
  12. package/dist/kit/Button.js.map +1 -0
  13. package/dist/kit/DataTable.d.ts +23 -0
  14. package/dist/kit/DataTable.d.ts.map +1 -0
  15. package/dist/kit/DataTable.js +59 -0
  16. package/dist/kit/DataTable.js.map +1 -0
  17. package/dist/kit/EmptyState.d.ts +10 -0
  18. package/dist/kit/EmptyState.d.ts.map +1 -0
  19. package/dist/kit/EmptyState.js +42 -0
  20. package/dist/kit/EmptyState.js.map +1 -0
  21. package/dist/kit/EntityHeader.d.ts +17 -0
  22. package/dist/kit/EntityHeader.d.ts.map +1 -0
  23. package/dist/kit/EntityHeader.js +61 -0
  24. package/dist/kit/EntityHeader.js.map +1 -0
  25. package/dist/kit/Form.d.ts +78 -0
  26. package/dist/kit/Form.d.ts.map +1 -0
  27. package/dist/kit/Form.js +210 -0
  28. package/dist/kit/Form.js.map +1 -0
  29. package/dist/kit/Icon.d.ts +102 -0
  30. package/dist/kit/Icon.d.ts.map +1 -0
  31. package/dist/kit/Icon.js +48 -0
  32. package/dist/kit/Icon.js.map +1 -0
  33. package/dist/kit/KV.d.ts +9 -0
  34. package/dist/kit/KV.d.ts.map +1 -0
  35. package/dist/kit/KV.js +23 -0
  36. package/dist/kit/KV.js.map +1 -0
  37. package/dist/kit/LogView.d.ts +21 -0
  38. package/dist/kit/LogView.d.ts.map +1 -0
  39. package/dist/kit/LogView.js +35 -0
  40. package/dist/kit/LogView.js.map +1 -0
  41. package/dist/kit/Modal.d.ts +50 -0
  42. package/dist/kit/Modal.d.ts.map +1 -0
  43. package/dist/kit/Modal.js +112 -0
  44. package/dist/kit/Modal.js.map +1 -0
  45. package/dist/kit/PageHeader.d.ts +30 -0
  46. package/dist/kit/PageHeader.d.ts.map +1 -0
  47. package/dist/kit/PageHeader.js +67 -0
  48. package/dist/kit/PageHeader.js.map +1 -0
  49. package/dist/kit/Panel.d.ts +26 -0
  50. package/dist/kit/Panel.d.ts.map +1 -0
  51. package/dist/kit/Panel.js +49 -0
  52. package/dist/kit/Panel.js.map +1 -0
  53. package/dist/kit/Secret.d.ts +9 -0
  54. package/dist/kit/Secret.d.ts.map +1 -0
  55. package/dist/kit/Secret.js +39 -0
  56. package/dist/kit/Secret.js.map +1 -0
  57. package/dist/kit/Skeleton.d.ts +12 -0
  58. package/dist/kit/Skeleton.d.ts.map +1 -0
  59. package/dist/kit/Skeleton.js +21 -0
  60. package/dist/kit/Skeleton.js.map +1 -0
  61. package/dist/kit/Toast.d.ts +17 -0
  62. package/dist/kit/Toast.d.ts.map +1 -0
  63. package/dist/kit/Toast.js +64 -0
  64. package/dist/kit/Toast.js.map +1 -0
  65. package/dist/theme/ThemeProvider.d.ts +16 -0
  66. package/dist/theme/ThemeProvider.d.ts.map +1 -0
  67. package/dist/theme/ThemeProvider.js +105 -0
  68. package/dist/theme/ThemeProvider.js.map +1 -0
  69. package/dist/theme/tokens.d.ts +62 -0
  70. package/dist/theme/tokens.d.ts.map +1 -0
  71. package/dist/theme/tokens.js +171 -0
  72. package/dist/theme/tokens.js.map +1 -0
  73. package/package.json +49 -0
  74. package/scripts/check-colour-literals.mjs +221 -0
@@ -0,0 +1,105 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
3
+ import { createGlobalStyle } from "styled-components";
4
+ import { cssVariables, v } from "./tokens.js";
5
+ // One preference for every console: the key is shared so a theme choice on
6
+ // one product host is picked up by the next (same-origin storage only — the
7
+ // cross-host copy rides the console context, see @xeonr/console-shell).
8
+ const DEFAULT_STORAGE_KEY = "xnr.theme";
9
+ const Ctx = createContext(null);
10
+ function systemScheme() {
11
+ return window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
12
+ }
13
+ export function AppThemeProvider({ children, storageKey = DEFAULT_STORAGE_KEY }) {
14
+ const STORAGE_KEY = storageKey;
15
+ const [pref, setPrefState] = useState(() => {
16
+ const s = localStorage.getItem(STORAGE_KEY);
17
+ return s === "light" || s === "dark" ? s : "system";
18
+ });
19
+ const [sys, setSys] = useState(() => systemScheme());
20
+ useEffect(() => {
21
+ const mq = window.matchMedia("(prefers-color-scheme: light)");
22
+ const on = () => setSys(systemScheme());
23
+ mq.addEventListener("change", on);
24
+ return () => mq.removeEventListener("change", on);
25
+ }, []);
26
+ const resolved = pref === "system" ? sys : pref;
27
+ useEffect(() => {
28
+ const el = document.documentElement;
29
+ if (pref === "system")
30
+ el.removeAttribute("data-theme");
31
+ else
32
+ el.setAttribute("data-theme", pref);
33
+ }, [pref]);
34
+ // Cross-tab sync: another tab changing the preference updates us live.
35
+ useEffect(() => {
36
+ const on = (e) => {
37
+ if (e.key !== STORAGE_KEY)
38
+ return;
39
+ const s = e.newValue;
40
+ setPrefState(s === "light" || s === "dark" ? s : "system");
41
+ };
42
+ window.addEventListener("storage", on);
43
+ return () => window.removeEventListener("storage", on);
44
+ }, [STORAGE_KEY]);
45
+ const setPref = useCallback((p) => {
46
+ setPrefState(p);
47
+ if (p === "system")
48
+ localStorage.removeItem(STORAGE_KEY);
49
+ else
50
+ localStorage.setItem(STORAGE_KEY, p);
51
+ }, [STORAGE_KEY]);
52
+ const cycle = useCallback(() => {
53
+ setPref(pref === "system" ? (resolved === "dark" ? "light" : "dark") : pref === "dark" ? "light" : "system");
54
+ }, [pref, resolved, setPref]);
55
+ const ctx = useMemo(() => ({ pref, resolved, setPref, cycle }), [pref, resolved, setPref, cycle]);
56
+ return (_jsxs(Ctx.Provider, { value: ctx, children: [_jsx(GlobalStyle, {}), children] }));
57
+ }
58
+ export function useTheme() {
59
+ const c = useContext(Ctx);
60
+ if (!c)
61
+ throw new Error("useTheme outside AppThemeProvider");
62
+ return c;
63
+ }
64
+ const GlobalStyle = createGlobalStyle `
65
+ ${cssVariables()}
66
+
67
+ *, *::before, *::after { box-sizing: border-box; }
68
+ html, body, #root { height: 100%; }
69
+ body {
70
+ margin: 0;
71
+ background: ${v.bg};
72
+ background-attachment: fixed;
73
+ color: ${v.text};
74
+ font-family: ${v.font};
75
+ font-size: 13px;
76
+ line-height: 1.55;
77
+ -webkit-font-smoothing: antialiased;
78
+ }
79
+ a { color: inherit; }
80
+ button { font: inherit; color: inherit; background: none; border: none; padding: 0; cursor: pointer; }
81
+ input, select, textarea { font: inherit; color: inherit; }
82
+ code, pre, kbd { font-family: ${v.mono}; }
83
+
84
+ /* Native <select> popups: macOS paints them with the system UI, but Windows
85
+ (and Linux) Chromium/Firefox render <option>s with page styles — unstyled,
86
+ that's a white UA list under our light-on-dark text. Solid surface tokens
87
+ keep the popup readable everywhere; macOS simply ignores these. */
88
+ select option, select optgroup {
89
+ background: ${v.surface};
90
+ color: ${v.text};
91
+ }
92
+
93
+ :focus-visible { outline: none; box-shadow: ${v.ring}; border-radius: 4px; }
94
+ ::selection { background: ${v.brand22}; }
95
+
96
+ * { scrollbar-width: thin; scrollbar-color: ${v.line2} transparent; }
97
+ *::-webkit-scrollbar { width: 10px; height: 10px; }
98
+ *::-webkit-scrollbar-thumb { background: ${v.line2}; border-radius: 8px; border: 3px solid transparent; background-clip: content-box; }
99
+ *::-webkit-scrollbar-track { background: transparent; }
100
+
101
+ @media (prefers-reduced-motion: reduce) {
102
+ *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
103
+ }
104
+ `;
105
+ //# sourceMappingURL=ThemeProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThemeProvider.js","sourceRoot":"","sources":["../../src/theme/ThemeProvider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AAC7G,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,aAAa,CAAC;AAO9C,2EAA2E;AAC3E,4EAA4E;AAC5E,wEAAwE;AACxE,MAAM,mBAAmB,GAAG,WAAW,CAAC;AAUxC,MAAM,GAAG,GAAG,aAAa,CAAkB,IAAI,CAAC,CAAC;AAEjD,SAAS,YAAY;IACnB,OAAO,MAAM,CAAC,UAAU,CAAC,+BAA+B,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAE,QAAQ,EAAE,UAAU,GAAG,mBAAmB,EAA4D;IACvI,MAAM,WAAW,GAAG,UAAU,CAAC;IAC/B,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAY,GAAG,EAAE;QACpD,MAAM,CAAC,GAAG,YAAY,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAC5C,OAAO,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IACtD,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAmB,GAAG,EAAE,CAAC,YAAY,EAAE,CAAC,CAAC;IAEvE,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,+BAA+B,CAAC,CAAC;QAC9D,MAAM,EAAE,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;QACxC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAClC,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACpD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,QAAQ,GAAG,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAEhD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC;QACpC,IAAI,IAAI,KAAK,QAAQ;YAAE,EAAE,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;;YACnD,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IAC3C,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,uEAAuE;IACvE,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,EAAE,GAAG,CAAC,CAAe,EAAE,EAAE;YAC7B,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW;gBAAE,OAAO;YAClC,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;YACrB,YAAY,CAAC,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC7D,CAAC,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACvC,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IACzD,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAElB,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAY,EAAE,EAAE;QAC3C,YAAY,CAAC,CAAC,CAAC,CAAC;QAChB,IAAI,CAAC,KAAK,QAAQ;YAAE,YAAY,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;;YACpD,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAC5C,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAElB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC/G,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IAE9B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IAElG,OAAO,CACL,MAAC,GAAG,CAAC,QAAQ,IAAC,KAAK,EAAE,GAAG,aACtB,KAAC,WAAW,KAAG,EACd,QAAQ,IACI,CAChB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAC7D,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,WAAW,GAAG,iBAAiB,CAAA;IACjC,YAAY,EAAE;;;;;;kBAMA,CAAC,CAAC,EAAE;;aAET,CAAC,CAAC,IAAI;mBACA,CAAC,CAAC,IAAI;;;;;;;;kCAQS,CAAC,CAAC,IAAI;;;;;;;kBAOtB,CAAC,CAAC,OAAO;aACd,CAAC,CAAC,IAAI;;;gDAG6B,CAAC,CAAC,IAAI;8BACxB,CAAC,CAAC,OAAO;;gDAES,CAAC,CAAC,KAAK;;6CAEV,CAAC,CAAC,KAAK;;;;;;CAMnD,CAAC"}
@@ -0,0 +1,62 @@
1
+ export declare const BRAND: {
2
+ primary: string;
3
+ g1: string;
4
+ g2: string;
5
+ g3: string;
6
+ g4: string;
7
+ };
8
+ export declare const GRAD: string;
9
+ export declare const GRAD_90: string;
10
+ export declare function cssVariables(): string;
11
+ export declare const v: {
12
+ brand: string;
13
+ grad: string;
14
+ grad90: string;
15
+ onBrand: string;
16
+ brand08: string;
17
+ brand12: string;
18
+ brand16: string;
19
+ brand22: string;
20
+ brand55: string;
21
+ font: string;
22
+ mono: string;
23
+ rCtl: string;
24
+ rCard: string;
25
+ rPanel: string;
26
+ rModal: string;
27
+ ring: string;
28
+ canvas: string;
29
+ bg: string;
30
+ sidebar: string;
31
+ surface: string;
32
+ surface2: string;
33
+ text: string;
34
+ muted: string;
35
+ faint: string;
36
+ line: string;
37
+ line2: string;
38
+ field: string;
39
+ fieldLine: string;
40
+ hover: string;
41
+ elev: string;
42
+ scrim: string;
43
+ danger: string;
44
+ dangerBg: string;
45
+ success: string;
46
+ successBg: string;
47
+ warning: string;
48
+ warningBg: string;
49
+ info: string;
50
+ infoBg: string;
51
+ logBg: string;
52
+ logText: string;
53
+ logDim: string;
54
+ termBg: string;
55
+ zNav: string;
56
+ zPop: string;
57
+ zScrim: string;
58
+ zModal: string;
59
+ zPalette: string;
60
+ zToast: string;
61
+ };
62
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/theme/tokens.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,KAAK;;;;;;CAMjB,CAAC;AAEF,eAAO,MAAM,IAAI,QAAwF,CAAC;AAC1G,eAAO,MAAM,OAAO,QAAuF,CAAC;AAG5G,wBAAgB,YAAY,IAAI,MAAM,CAgIrC;AAyBD,eAAO,MAAM,CAAC,EAAkB;IAC9B,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAC7D,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IACpF,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IACtG,MAAM,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAC/E,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IACxE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAC7E,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IACrE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IACjE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAC/D,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;CAC9F,CAAC"}
@@ -0,0 +1,171 @@
1
+ // Design tokens for the Functions console — the "Xeonr Platform Suite" design
2
+ // language shared with container-platform/ui: the auth/ui --x-* custom-property
3
+ // system plus the ops-density additions ported from the xmb kit (log surfaces,
4
+ // severity marks, mono accents).
5
+ //
6
+ // Everything user-visible derives from these variables; components never hardcode
7
+ // colour literals (scripts/check-colour-literals.mjs enforces it). Light and dark
8
+ // are both first-class: the ThemeProvider stamps data-theme on <html> and these
9
+ // blocks re-map the same variable names.
10
+ export const BRAND = {
11
+ primary: "#A354A2",
12
+ g1: "#9C4699",
13
+ g2: "#A354A2",
14
+ g3: "#9B3EE4",
15
+ g4: "#5726BE",
16
+ };
17
+ export const GRAD = `linear-gradient(135deg, ${BRAND.g1}, ${BRAND.g2} 34%, ${BRAND.g3} 68%, ${BRAND.g4})`;
18
+ export const GRAD_90 = `linear-gradient(90deg, ${BRAND.g1}, ${BRAND.g2} 34%, ${BRAND.g3} 68%, ${BRAND.g4})`;
19
+ // css() returns the full variable sheet injected by GlobalStyle.
20
+ export function cssVariables() {
21
+ return `
22
+ :root {
23
+ --x-brand: ${BRAND.primary};
24
+ --x-g1: ${BRAND.g1}; --x-g2: ${BRAND.g2}; --x-g3: ${BRAND.g3}; --x-g4: ${BRAND.g4};
25
+ --x-grad: ${GRAD};
26
+ --x-grad-90: ${GRAD_90};
27
+ --x-on-brand: #ffffff;
28
+ --x-brand-08: rgba(163, 84, 162, 0.08);
29
+ --x-brand-12: rgba(163, 84, 162, 0.12);
30
+ --x-brand-16: rgba(163, 84, 162, 0.16);
31
+ --x-brand-22: rgba(163, 84, 162, 0.22);
32
+ --x-brand-55: rgba(163, 84, 162, 0.55);
33
+ --x-violet-glow: rgba(155, 62, 228, 0.16);
34
+ --x-font: "Poppins", ui-sans-serif, -apple-system, "Segoe UI", Roboto, sans-serif;
35
+ --x-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
36
+ --x-r-ctl: 8px; /* buttons, inputs, nav items */
37
+ --x-r-card: 12px; /* cards, tiles */
38
+ --x-r-panel: 14px;/* panels, tables, empty states */
39
+ --x-r-modal: 16px;/* modals, palette */
40
+ --x-ring: 0 0 0 3px var(--x-brand-55);
41
+ --x-z-nav: 20; --x-z-pop: 30; --x-z-scrim: 40; --x-z-modal: 41; --x-z-palette: 50; --x-z-toast: 60;
42
+ }
43
+
44
+ :root, :root[data-theme="dark"] {
45
+ color-scheme: dark;
46
+ --x-canvas: #0c0a10;
47
+ --x-bg:
48
+ radial-gradient(560px 300px at 82% -80px, var(--x-violet-glow), transparent 70%),
49
+ radial-gradient(640px 340px at 12% -120px, var(--x-brand-12), transparent 70%),
50
+ #0c0a10;
51
+ --x-sidebar: #131019;
52
+ --x-surface: #17131f;
53
+ --x-surface-2: #1d1828;
54
+ --x-text: #f1eef6;
55
+ --x-muted: #ada6ba;
56
+ --x-faint: #8a8298;
57
+ --x-line: rgba(255, 255, 255, 0.07);
58
+ --x-line-2: rgba(255, 255, 255, 0.12);
59
+ --x-field: rgba(255, 255, 255, 0.04);
60
+ --x-field-line: rgba(255, 255, 255, 0.10);
61
+ --x-hover: rgba(255, 255, 255, 0.045);
62
+ --x-elev: 0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 28px 70px -40px rgba(0, 0, 0, 0.8);
63
+ --x-scrim: rgba(6, 4, 12, 0.62);
64
+ --x-danger: #ff8f8f;
65
+ --x-danger-bg: rgba(255, 107, 107, 0.13);
66
+ --x-success: #3ad29f;
67
+ --x-success-bg: rgba(58, 210, 159, 0.13);
68
+ --x-warning: #ffc46b;
69
+ --x-warning-bg: rgba(255, 196, 107, 0.13);
70
+ --x-info: #d8a3d7;
71
+ --x-info-bg: rgba(163, 84, 162, 0.16);
72
+ --x-log-bg: #100d17;
73
+ --x-log-text: #c6bad8;
74
+ --x-log-dim: #6f6585;
75
+ --x-term-bg: #100d17;
76
+ }
77
+
78
+ @media (prefers-color-scheme: light) {
79
+ :root:not([data-theme="dark"]) {
80
+ color-scheme: light;
81
+ --x-canvas: #f6f7f9;
82
+ --x-bg:
83
+ radial-gradient(560px 300px at 82% -100px, rgba(155, 62, 228, 0.07), transparent 70%),
84
+ radial-gradient(640px 340px at 12% -140px, rgba(163, 84, 162, 0.06), transparent 70%),
85
+ #f6f7f9;
86
+ --x-sidebar: #fdfdfe;
87
+ --x-surface: #ffffff;
88
+ --x-surface-2: #fafafb;
89
+ --x-text: #1a1b22;
90
+ --x-muted: #565a68;
91
+ --x-faint: #6b7080;
92
+ --x-line: #e7e8ee;
93
+ --x-line-2: #ddd8e6;
94
+ --x-field: #ffffff;
95
+ --x-field-line: #e2dceb;
96
+ --x-hover: rgba(28, 20, 48, 0.035);
97
+ --x-elev: 0 1px 2px rgba(28, 20, 48, 0.05), 0 18px 44px -24px rgba(87, 38, 190, 0.18);
98
+ --x-scrim: rgba(24, 18, 40, 0.38);
99
+ --x-danger: #c0362f;
100
+ --x-danger-bg: rgba(192, 54, 47, 0.08);
101
+ --x-success: #1f9d6b;
102
+ --x-success-bg: rgba(31, 157, 107, 0.10);
103
+ --x-warning: #b8740c;
104
+ --x-warning-bg: rgba(184, 116, 12, 0.10);
105
+ --x-info: #7a2bb8;
106
+ --x-info-bg: rgba(155, 62, 228, 0.10);
107
+ --x-log-bg: #100d17;
108
+ --x-log-text: #c6bad8;
109
+ --x-log-dim: #6f6585;
110
+ --x-term-bg: #100d17;
111
+ }
112
+ }
113
+
114
+ :root[data-theme="light"] {
115
+ color-scheme: light;
116
+ --x-canvas: #f6f7f9;
117
+ --x-bg:
118
+ radial-gradient(560px 300px at 82% -100px, rgba(155, 62, 228, 0.07), transparent 70%),
119
+ radial-gradient(640px 340px at 12% -140px, rgba(163, 84, 162, 0.06), transparent 70%),
120
+ #f6f7f9;
121
+ --x-sidebar: #fdfdfe;
122
+ --x-surface: #ffffff;
123
+ --x-surface-2: #fafafb;
124
+ --x-text: #1a1b22;
125
+ --x-muted: #565a68;
126
+ --x-faint: #6b7080;
127
+ --x-line: #e7e8ee;
128
+ --x-line-2: #ddd8e6;
129
+ --x-field: #ffffff;
130
+ --x-field-line: #e2dceb;
131
+ --x-hover: rgba(28, 20, 48, 0.035);
132
+ --x-elev: 0 1px 2px rgba(28, 20, 48, 0.05), 0 18px 44px -24px rgba(87, 38, 190, 0.18);
133
+ --x-scrim: rgba(24, 18, 40, 0.38);
134
+ --x-danger: #c0362f;
135
+ --x-danger-bg: rgba(192, 54, 47, 0.08);
136
+ --x-success: #1f9d6b;
137
+ --x-success-bg: rgba(31, 157, 107, 0.10);
138
+ --x-warning: #b8740c;
139
+ --x-warning-bg: rgba(184, 116, 12, 0.10);
140
+ --x-info: #7a2bb8;
141
+ --x-info-bg: rgba(155, 62, 228, 0.10);
142
+ --x-log-bg: #100d17;
143
+ --x-log-text: #c6bad8;
144
+ --x-log-dim: #6f6585;
145
+ --x-term-bg: #100d17;
146
+ }
147
+ `;
148
+ }
149
+ // v is a tiny typed helper so component styles read `${v.text}` instead of the
150
+ // raw var() incantation — a misspelt token is a build error, not a silent default.
151
+ const names = [
152
+ "brand", "grad", "grad-90", "on-brand",
153
+ "brand-08", "brand-12", "brand-16", "brand-22", "brand-55",
154
+ "font", "mono", "r-ctl", "r-card", "r-panel", "r-modal", "ring",
155
+ "canvas", "bg", "sidebar", "surface", "surface-2", "text", "muted", "faint",
156
+ "line", "line-2", "field", "field-line", "hover", "elev", "scrim",
157
+ "danger", "danger-bg", "success", "success-bg", "warning", "warning-bg", "info", "info-bg",
158
+ "log-bg", "log-text", "log-dim", "term-bg",
159
+ "z-nav", "z-pop", "z-scrim", "z-modal", "z-palette", "z-toast",
160
+ ];
161
+ function buildVars() {
162
+ const out = {};
163
+ for (const n of names) {
164
+ // camelCase alias: surface-2 -> surface2, r-ctl -> rCtl, grad-90 -> grad90
165
+ const camel = n.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
166
+ out[camel] = `var(--x-${n})`;
167
+ }
168
+ return out;
169
+ }
170
+ export const v = buildVars();
171
+ //# sourceMappingURL=tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/theme/tokens.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,gFAAgF;AAChF,+EAA+E;AAC/E,iCAAiC;AACjC,EAAE;AACF,kFAAkF;AAClF,kFAAkF;AAClF,gFAAgF;AAChF,yCAAyC;AAEzC,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,OAAO,EAAE,SAAS;IAClB,EAAE,EAAE,SAAS;IACb,EAAE,EAAE,SAAS;IACb,EAAE,EAAE,SAAS;IACb,EAAE,EAAE,SAAS;CACd,CAAC;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,2BAA2B,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,SAAS,KAAK,CAAC,EAAE,SAAS,KAAK,CAAC,EAAE,GAAG,CAAC;AAC1G,MAAM,CAAC,MAAM,OAAO,GAAG,0BAA0B,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,SAAS,KAAK,CAAC,EAAE,SAAS,KAAK,CAAC,EAAE,GAAG,CAAC;AAE5G,iEAAiE;AACjE,MAAM,UAAU,YAAY;IAC1B,OAAO;;eAEM,KAAK,CAAC,OAAO;YAChB,KAAK,CAAC,EAAE,aAAa,KAAK,CAAC,EAAE,aAAa,KAAK,CAAC,EAAE,aAAa,KAAK,CAAC,EAAE;cACrE,IAAI;iBACD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyHvB,CAAC;AACF,CAAC;AAED,+EAA+E;AAC/E,mFAAmF;AACnF,MAAM,KAAK,GAAG;IACZ,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU;IACtC,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAC1D,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM;IAC/D,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAC3E,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO;IACjE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS;IAC1F,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS;IAC1C,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS;CACtD,CAAC;AAEX,SAAS,SAAS;IAChB,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,2EAA2E;QAC3E,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAC3E,GAAG,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC;IAC/B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,MAAM,CAAC,GAAG,SAAS,EAWzB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@xeonr/platform-ui",
3
+ "version": "0.1.0",
4
+ "description": "Xeonr Platform Suite design language: the --x-* token sheet, theme provider and kit shared by every Xeonr console.",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./tokens": {
14
+ "types": "./dist/theme/tokens.d.ts",
15
+ "import": "./dist/theme/tokens.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "scripts"
21
+ ],
22
+ "bin": {
23
+ "xeonr-check-colour-literals": "./scripts/check-colour-literals.mjs"
24
+ },
25
+ "peerDependencies": {
26
+ "react": "^19.0.0",
27
+ "react-dom": "^19.0.0",
28
+ "react-router-dom": "^7.0.0",
29
+ "styled-components": "^6.0.0"
30
+ },
31
+ "dependencies": {
32
+ "@fortawesome/fontawesome-svg-core": "^7.0.0",
33
+ "@fortawesome/free-solid-svg-icons": "^7.0.0",
34
+ "@fortawesome/react-fontawesome": "^3.0.2"
35
+ },
36
+ "devDependencies": {
37
+ "@types/react": "^19.1.0",
38
+ "@types/react-dom": "^19.1.0",
39
+ "react": "^19.1.0",
40
+ "react-dom": "^19.1.0",
41
+ "react-router-dom": "^7.8.0",
42
+ "styled-components": "^6.1.19",
43
+ "typescript": "^5.9.2"
44
+ },
45
+ "scripts": {
46
+ "build": "tsc -p tsconfig.build.json",
47
+ "lint": "tsc -p tsconfig.json --noEmit && node scripts/check-colour-literals.mjs"
48
+ }
49
+ }
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env node
2
+ // Colour-literal ratchet: raw colours belong in src/theme/tokens.ts — screens
3
+ // and kit components must reference tokens, never literals. Scans
4
+ // src/**/*.{ts,tsx} for hex colours (#abc / #aabbcc / #aabbccdd …) and
5
+ // rgb(/rgba(/hsl(/hsla( calls appearing inside string or template-literal
6
+ // contexts (plain code and comments don't count), then fails with a
7
+ // file:line listing for any hit outside the allowlisted files.
8
+ //
9
+ // Allowlist rationale: tokens.ts is where literals are supposed to live;
10
+ // ConsoleTerminal.tsx hands xterm.js a theme object that requires concrete
11
+ // colour strings; monacoSetup.ts may need the same for monaco theme plumbing.
12
+ //
13
+ // Plain Node ESM, zero dependencies.
14
+
15
+ import { readdirSync, readFileSync } from "node:fs";
16
+ import { join, relative } from "node:path";
17
+ import process from "node:process";
18
+
19
+ const ROOT = process.cwd();
20
+ const SRC = join(ROOT, "src");
21
+
22
+ // Consumers run this as `xeonr-check-colour-literals [extra allowlisted paths…]`
23
+ // from their own package root; the kit's tokens.ts is the only default.
24
+ const ALLOWLIST = new Set(["src/theme/tokens.ts", ...process.argv.slice(2)]);
25
+
26
+ /** Hex runs; length checked afterwards so 12+-char content hashes don't match. */
27
+ const HEX = /#[0-9a-fA-F]+/g;
28
+ const COLOUR_FN = /\b(?:rgba?|hsla?)\(/g;
29
+
30
+ /** Recursively collect .ts/.tsx files under dir, sorted for stable output. */
31
+ function collect(dir) {
32
+ const out = [];
33
+ for (const ent of readdirSync(dir, { withFileTypes: true })) {
34
+ const p = join(dir, ent.name);
35
+ if (ent.isDirectory()) out.push(...collect(p));
36
+ else if (/\.tsx?$/.test(ent.name)) out.push(p);
37
+ }
38
+ return out.sort();
39
+ }
40
+
41
+ // Keywords that put a following `/` in regex (not division) position.
42
+ const REGEX_KEYWORDS = new Set([
43
+ "return", "typeof", "instanceof", "in", "of", "new", "do", "else", "case",
44
+ "void", "delete", "yield", "await",
45
+ ]);
46
+ // Significant chars after which `/` starts a regex literal. Deliberately
47
+ // excludes `<` `>` `}` so JSX (`</Tag>`, `{expr} />`) reads as plain code.
48
+ const REGEX_TRIGGERS = "(,=:[!&|?;+-*%^~{";
49
+ const WORD = /[A-Za-z0-9_$]/;
50
+
51
+ /**
52
+ * Returns `source` with everything that is NOT string/template character
53
+ * content replaced by spaces (newlines kept), so line numbers and columns
54
+ * survive into the pattern scan. A tiny state machine tracks line/block
55
+ * comments, '…'/"…" strings (terminated at newline, as in JS), `…` templates
56
+ * with nested ${…} interpolation, and — heuristically — regex literals, all
57
+ * masked out as "not string content".
58
+ */
59
+ function stringContentMask(src) {
60
+ const out = new Array(src.length).fill(" ");
61
+ let mode = "code";
62
+ const interp = []; // brace-depth stack for nested `${ … }` template holes
63
+ let lastSig = ""; // last significant code char (regex-vs-division heuristic)
64
+ let prevSig = "";
65
+ let lastWord = "";
66
+
67
+ const setSig = (c) => {
68
+ prevSig = lastSig;
69
+ lastSig = c;
70
+ };
71
+
72
+ for (let i = 0; i < src.length; i++) {
73
+ const c = src[i];
74
+ const n = src[i + 1];
75
+
76
+ if (c === "\n") {
77
+ out[i] = "\n";
78
+ // Line comments end; unterminated '…'/"…" cannot span lines — bail out
79
+ // so a stray apostrophe (JSX text) only poisons its own line.
80
+ if (mode === "line" || mode === "single" || mode === "double") mode = "code";
81
+ continue;
82
+ }
83
+
84
+ switch (mode) {
85
+ case "line":
86
+ break;
87
+
88
+ case "block":
89
+ if (c === "*" && n === "/") {
90
+ mode = "code";
91
+ i += 1;
92
+ }
93
+ break;
94
+
95
+ case "single":
96
+ case "double": {
97
+ const quote = mode === "single" ? "'" : '"';
98
+ if (c === "\\" && n !== undefined) {
99
+ out[i] = c;
100
+ out[i + 1] = n;
101
+ i += 1;
102
+ } else if (c === quote) {
103
+ mode = "code";
104
+ setSig(quote);
105
+ } else {
106
+ out[i] = c;
107
+ }
108
+ break;
109
+ }
110
+
111
+ case "template":
112
+ if (c === "\\" && n !== undefined) {
113
+ out[i] = c;
114
+ out[i + 1] = n;
115
+ i += 1;
116
+ } else if (c === "`") {
117
+ mode = "code";
118
+ setSig("`");
119
+ } else if (c === "$" && n === "{") {
120
+ interp.push(0);
121
+ mode = "code";
122
+ setSig("{");
123
+ i += 1;
124
+ } else {
125
+ out[i] = c;
126
+ }
127
+ break;
128
+
129
+ case "regex":
130
+ if (c === "\\") i += 1;
131
+ else if (c === "[") mode = "regexClass";
132
+ else if (c === "/") {
133
+ mode = "code";
134
+ setSig("/");
135
+ }
136
+ break;
137
+
138
+ case "regexClass":
139
+ if (c === "\\") i += 1;
140
+ else if (c === "]") mode = "regex";
141
+ break;
142
+
143
+ case "code": {
144
+ if (c === "/" && n === "/") {
145
+ mode = "line";
146
+ i += 1;
147
+ } else if (c === "/" && n === "*") {
148
+ mode = "block";
149
+ i += 1;
150
+ } else if (c === "'") {
151
+ mode = "single";
152
+ } else if (c === '"') {
153
+ mode = "double";
154
+ } else if (c === "`") {
155
+ mode = "template";
156
+ } else if (c === "{" && interp.length > 0) {
157
+ interp[interp.length - 1] += 1;
158
+ setSig("{");
159
+ } else if (c === "}" && interp.length > 0) {
160
+ if (interp[interp.length - 1] === 0) {
161
+ interp.pop();
162
+ mode = "template";
163
+ } else {
164
+ interp[interp.length - 1] -= 1;
165
+ setSig("}");
166
+ }
167
+ } else if (c === "/") {
168
+ const afterArrow = lastSig === ">" && prevSig === "=";
169
+ const afterKeyword = WORD.test(lastSig) && REGEX_KEYWORDS.has(lastWord);
170
+ if (lastSig === "" || afterArrow || afterKeyword || REGEX_TRIGGERS.includes(lastSig)) {
171
+ mode = "regex";
172
+ } else {
173
+ setSig("/"); // division
174
+ }
175
+ } else if (WORD.test(c)) {
176
+ lastWord = WORD.test(lastSig) ? lastWord + c : c;
177
+ setSig(c);
178
+ } else if (!/\s/.test(c)) {
179
+ setSig(c);
180
+ }
181
+ break;
182
+ }
183
+ }
184
+ }
185
+
186
+ return out.join("");
187
+ }
188
+
189
+ function scanFile(abs) {
190
+ const rel = relative(ROOT, abs).split("\\").join("/");
191
+ const mask = stringContentMask(readFileSync(abs, "utf8"));
192
+ const hits = [];
193
+ mask.split("\n").forEach((line, idx) => {
194
+ for (const m of line.matchAll(HEX)) {
195
+ const digits = m[0].length - 1;
196
+ if (digits >= 3 && digits <= 8) hits.push({ line: idx + 1, match: m[0] });
197
+ }
198
+ for (const m of line.matchAll(COLOUR_FN)) {
199
+ hits.push({ line: idx + 1, match: m[0] });
200
+ }
201
+ });
202
+ return { rel, hits };
203
+ }
204
+
205
+ const files = collect(SRC);
206
+ const offenders = [];
207
+ for (const abs of files) {
208
+ const { rel, hits } = scanFile(abs);
209
+ if (hits.length === 0 || ALLOWLIST.has(rel)) continue;
210
+ for (const h of hits) offenders.push(`${rel}:${h.line} ${h.match}`);
211
+ }
212
+
213
+ if (offenders.length > 0) {
214
+ console.error(
215
+ `colour-literals: ${offenders.length} raw colour literal(s) outside the theme allowlist — use src/theme/tokens.ts instead:`,
216
+ );
217
+ for (const o of offenders) console.error(` ${o}`);
218
+ process.exit(1);
219
+ }
220
+
221
+ console.log(`colour-literals: clean (${files.length} files scanned)`);