@neuravision/construct 2.1.1 → 2.1.3

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 (66) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +13 -0
  3. package/behaviors/combobox.js +43 -4
  4. package/behaviors/dropdown.js +16 -2
  5. package/behaviors/index.d.ts +2 -0
  6. package/behaviors/internal/dialog-controller.js +79 -9
  7. package/behaviors/internal/dom.js +38 -7
  8. package/behaviors/internal/overlay.js +4 -4
  9. package/behaviors/internal/popup-layer.js +2 -0
  10. package/behaviors/popover.js +21 -12
  11. package/behaviors/select-menu.js +20 -2
  12. package/behaviors/tabs.js +13 -2
  13. package/behaviors/toggle-group.js +37 -5
  14. package/behaviors/tooltip.js +26 -2
  15. package/components/README.md +40 -0
  16. package/components/_shared.css +11 -0
  17. package/components/accordion.css +8 -0
  18. package/components/app-shell-v2.css +7 -0
  19. package/components/app-shell.css +7 -0
  20. package/components/banner.css +32 -1
  21. package/components/card.css +23 -9
  22. package/components/chart.css +26 -0
  23. package/components/checkbox.css +32 -0
  24. package/components/chip.css +41 -1
  25. package/components/combobox.css +21 -0
  26. package/components/datepicker.css +55 -8
  27. package/components/datum.css +84 -11
  28. package/components/dropdown.css +23 -3
  29. package/components/field.css +6 -4
  30. package/components/file-upload.css +1 -1
  31. package/components/list.css +28 -2
  32. package/components/navbar.css +14 -1
  33. package/components/pagination.css +18 -0
  34. package/components/progress-bar.css +22 -1
  35. package/components/select-menu.css +8 -0
  36. package/components/select.css +8 -0
  37. package/components/sidebar.css +15 -2
  38. package/components/skip-link.css +6 -2
  39. package/components/slider.css +5 -0
  40. package/components/switch.css +62 -2
  41. package/components/table.css +53 -4
  42. package/components/tabs.css +55 -3
  43. package/components/toast.css +10 -1
  44. package/components/toggle-group.css +16 -5
  45. package/components/toolbar.css +17 -1
  46. package/components/tree.css +30 -0
  47. package/docs/behaviors.md +26 -5
  48. package/docs/guidelines.md +30 -2
  49. package/foundations.css +6 -0
  50. package/package.json +7 -3
  51. package/schemas/components.schema.json +16 -1
  52. package/schemas/semantic.schema.json +2 -1
  53. package/schemas/theme.schema.json +1 -1
  54. package/scripts/build-tokens.mjs +87 -5
  55. package/themes/README.md +37 -0
  56. package/themes/walnut.theme.json +106 -0
  57. package/tokens/README.md +10 -2
  58. package/tokens/components.json +10 -0
  59. package/tokens/semantic.dark.json +4 -3
  60. package/tokens/semantic.high-contrast-dark.json +94 -0
  61. package/tokens/semantic.high-contrast.json +3 -2
  62. package/tokens/semantic.light.json +2 -1
  63. package/tokens/tokens.css +431 -33
  64. package/tokens/tokens.js +432 -133
  65. package/tokens/tokens.json +432 -133
  66. package/tokens/tokens.ts +432 -133
@@ -0,0 +1,37 @@
1
+ # Preset themes
2
+
3
+ Preset themes are validated, ready-to-use theme inputs shipped with the package. Unlike the
4
+ built-in themes (`light`, `dark`, `high-contrast`), presets are **not** compiled into
5
+ `tokens/tokens.css` — they are brand skins rather than modes, have no system-preference
6
+ mapping, and add no CSS payload for consumers that do not use them. A product opts in by
7
+ compiling the preset through the `construct-theme` CLI:
8
+
9
+ ```bash
10
+ npx construct-theme \
11
+ --theme node_modules/@neuravision/construct/themes/walnut.theme.json \
12
+ --out-dir ./src/generated/construct
13
+ ```
14
+
15
+ Then activate it like any theme scope:
16
+
17
+ ```html
18
+ <html data-theme="walnut"> … </html>
19
+ ```
20
+
21
+ Products may also copy a preset as the starting point for their own theme and adjust
22
+ individual tokens; the CLI re-validates every override, including the contrast gates.
23
+
24
+ ## Available presets
25
+
26
+ | Preset | Extends | Character |
27
+ |--------|---------|-----------|
28
+ | `walnut` | `light` | Warm ivory surfaces, walnut-brown brand color, wood-toned accents. Body text meets WCAG AAA (≥ 7:1) on all surfaces, enforced by `contrast` gates in the theme file. |
29
+
30
+ ## Bar for adding a preset
31
+
32
+ - Must validate against `schemas/theme.schema.json` and pass every default contrast pair
33
+ (`npm run themes:validate` wires this into `npm run check`).
34
+ - Must declare its own `contrast` entries for any token the preset repurposes
35
+ (e.g. brand color used as text) and for stricter targets it claims (e.g. AAA body text).
36
+ - Palette, radius, and shadow decisions only — no font-family or type-scale overrides, so
37
+ presets compose with any product typography.
@@ -0,0 +1,106 @@
1
+ {
2
+ "$schema": "../schemas/theme.schema.json",
3
+ "name": "walnut",
4
+ "extends": "light",
5
+ "semantic": {
6
+ "color": {
7
+ "brand": {
8
+ "primary": "#6D4E33",
9
+ "primaryHover": "#5C4128",
10
+ "primaryActive": "#4C351F",
11
+ "accent": "#B0835E",
12
+ "accentHover": "#C2996F",
13
+ "accentActive": "#CBA57E",
14
+ "accentStrong": "#9F5210",
15
+ "onAccent": "#2A2014"
16
+ },
17
+ "text": {
18
+ "primary": "#2A2014",
19
+ "secondary": "#55432C",
20
+ "muted": "#6B573C",
21
+ "inverse": "#FFFFFF",
22
+ "onPrimary": "#FFFFFF"
23
+ },
24
+ "background": {
25
+ "canvas": "#FAF6ED",
26
+ "surface": "#F2EBDC",
27
+ "elevated": "#FFFFFF",
28
+ "muted": "#E9E0CC",
29
+ "subtle": "#F2EBDC"
30
+ },
31
+ "border": {
32
+ "subtle": "#E2D8C2",
33
+ "default": "#8A7256",
34
+ "strong": "#6D4E33",
35
+ "muted": "#E2D8C2"
36
+ },
37
+ "overlay": {
38
+ "scrim": "rgba(42, 32, 20, 0.55)"
39
+ },
40
+ "focus": {
41
+ "ring": "#9F5210"
42
+ }
43
+ },
44
+ "radius": {
45
+ "control": "7px",
46
+ "card": "10px",
47
+ "modal": "12px",
48
+ "pill": "999px"
49
+ },
50
+ "shadow": {
51
+ "card": "0 1px 2px rgba(58, 45, 26, 0.05), 0 2px 6px rgba(58, 45, 26, 0.06)",
52
+ "dropdown": "0 2px 6px rgba(58, 45, 26, 0.08), 0 8px 20px rgba(58, 45, 26, 0.12)",
53
+ "modal": "0 6px 16px rgba(58, 45, 26, 0.1), 0 24px 48px rgba(58, 45, 26, 0.18)"
54
+ }
55
+ },
56
+ "contrast": [
57
+ {
58
+ "name": "AAA: body text on canvas",
59
+ "foreground": "semantic.color.text.primary",
60
+ "background": "semantic.color.background.canvas",
61
+ "minRatio": 7
62
+ },
63
+ {
64
+ "name": "AAA: body text on surface",
65
+ "foreground": "semantic.color.text.primary",
66
+ "background": "semantic.color.background.surface",
67
+ "minRatio": 7
68
+ },
69
+ {
70
+ "name": "AAA: body text on elevated",
71
+ "foreground": "semantic.color.text.primary",
72
+ "background": "semantic.color.background.elevated",
73
+ "minRatio": 7
74
+ },
75
+ {
76
+ "name": "AAA: secondary text on canvas",
77
+ "foreground": "semantic.color.text.secondary",
78
+ "background": "semantic.color.background.canvas",
79
+ "minRatio": 7
80
+ },
81
+ {
82
+ "name": "brand primary used as text on canvas",
83
+ "foreground": "semantic.color.brand.primary",
84
+ "background": "semantic.color.background.canvas",
85
+ "minRatio": 4.5
86
+ },
87
+ {
88
+ "name": "brand primary used as text on surface",
89
+ "foreground": "semantic.color.brand.primary",
90
+ "background": "semantic.color.background.surface",
91
+ "minRatio": 4.5
92
+ },
93
+ {
94
+ "name": "brand primary used as text on elevated",
95
+ "foreground": "semantic.color.brand.primary",
96
+ "background": "semantic.color.background.elevated",
97
+ "minRatio": 4.5
98
+ },
99
+ {
100
+ "name": "accent used as boundary on canvas",
101
+ "foreground": "semantic.color.brand.accent",
102
+ "background": "semantic.color.background.canvas",
103
+ "minRatio": 3
104
+ }
105
+ ]
106
+ }
package/tokens/README.md CHANGED
@@ -95,7 +95,7 @@ console.log(tokens.metadata.themes);
95
95
 
96
96
  ## Built-in themes
97
97
 
98
- Construct ships `light`, `dark`, and `high-contrast` scopes:
98
+ Construct ships `light`, `dark`, `high-contrast`, and `high-contrast-dark` scopes:
99
99
 
100
100
  ```html
101
101
  <html data-theme="dark">…</html>
@@ -103,7 +103,15 @@ Construct ships `light`, `dark`, and `high-contrast` scopes:
103
103
 
104
104
  Themes can also be nested on containers. Every explicit theme scope declares the complete semantic and component contract, so light inside dark, high-contrast inside light, and custom themes inside any parent do not leak inherited values.
105
105
 
106
- With no explicit `data-theme`, root-level `prefers-color-scheme: dark` and `prefers-contrast: more` preferences apply automatically.
106
+ With no explicit `data-theme`, root-level `prefers-color-scheme: dark` and `prefers-contrast: more` preferences apply automatically — including the combination: dark-scheme users who request more contrast get `high-contrast-dark`, not a light UI. Note that pinning any `data-theme` on the root opts the page out of these automatic preference hookups; products that pin a theme should offer their own contrast/scheme switch (for example by pinning `high-contrast`/`high-contrast-dark` when the user asks for it).
107
+
108
+ ### Type scale units
109
+
110
+ Font-size and line-height tokens are emitted in `rem` (authored in px, divided by 16) so user browser font-size preferences scale the entire system. Control heights, spacing, and the 44px coarse-pointer targets intentionally stay in px.
111
+
112
+ ### Accent roles
113
+
114
+ `--color-brand-accent` is the decorative datum orange for edges and rules on canvas/surface. `--color-brand-accent-strong` is the validated fill orange — gated at ≥3:1 against `bg.canvas`, `bg.surface`, and `bg.muted` in every theme — for progress fills, meters, and any indicator drawn against the muted track color. Orange is never a text color; text uses the neutral `--color-text-*` roles.
107
115
 
108
116
  ## Custom product themes
109
117
 
@@ -50,5 +50,15 @@
50
50
  "height": "{space.3}",
51
51
  "radius": "{radius.pill}",
52
52
  "track": "{color.background.muted}"
53
+ },
54
+ "datum": {
55
+ "edgeWidth": "{border.width.thick}",
56
+ "tickWidth": "24px",
57
+ "tickHeight": "3px",
58
+ "originLength": "48px",
59
+ "markSize": "18px",
60
+ "scaleHeight": "9px",
61
+ "scaleMinorStep": "12px",
62
+ "scaleMajorStep": "60px"
53
63
  }
54
64
  }
@@ -7,6 +7,7 @@
7
7
  "accent": "{color.orange.400}",
8
8
  "accentHover": "{color.orange.300}",
9
9
  "accentActive": "{color.orange.500}",
10
+ "accentStrong": "{color.orange.400}",
10
11
  "onAccent": "{color.stone.950}"
11
12
  },
12
13
  "text": {
@@ -23,9 +24,9 @@
23
24
  "muted": "{color.stone.900}"
24
25
  },
25
26
  "border": {
26
- "subtle": "{color.stone.700}",
27
+ "subtle": "{color.stone.600}",
27
28
  "default": "{color.stone.400}",
28
- "strong": "{color.stone.500}"
29
+ "strong": "{color.stone.300}"
29
30
  },
30
31
  "state": {
31
32
  "info": "{color.ocean.300}",
@@ -50,7 +51,7 @@
50
51
  "info": "{color.ocean.200}",
51
52
  "success": "{color.green.200}",
52
53
  "warning": "{color.amber.200}",
53
- "danger": "{color.red.200}"
54
+ "danger": "{color.red.100}"
54
55
  },
55
56
  "overlay": {
56
57
  "scrim": "rgba(8, 6, 4, 0.72)"
@@ -0,0 +1,94 @@
1
+ {
2
+ "color": {
3
+ "brand": {
4
+ "primary": "{color.stone.0}",
5
+ "primaryHover": "{color.stone.100}",
6
+ "primaryActive": "{color.stone.200}",
7
+ "accent": "{color.orange.300}",
8
+ "accentHover": "{color.orange.200}",
9
+ "accentActive": "{color.orange.400}",
10
+ "accentStrong": "{color.orange.300}",
11
+ "onAccent": "{color.stone.950}"
12
+ },
13
+ "text": {
14
+ "primary": "{color.stone.0}",
15
+ "secondary": "{color.stone.100}",
16
+ "muted": "{color.stone.200}",
17
+ "inverse": "{color.stone.950}",
18
+ "onPrimary": "{color.stone.950}"
19
+ },
20
+ "background": {
21
+ "canvas": "{color.stone.950}",
22
+ "surface": "{color.stone.950}",
23
+ "elevated": "{color.stone.900}",
24
+ "muted": "{color.stone.800}"
25
+ },
26
+ "border": {
27
+ "subtle": "{color.stone.500}",
28
+ "default": "{color.stone.300}",
29
+ "strong": "{color.stone.0}"
30
+ },
31
+ "state": {
32
+ "info": "{color.ocean.200}",
33
+ "success": "{color.green.200}",
34
+ "warning": "{color.amber.200}",
35
+ "danger": "{color.red.200}",
36
+ "onSolid": "{color.stone.950}"
37
+ },
38
+ "stateSurface": {
39
+ "info": "{color.ocean.900}",
40
+ "success": "{color.green.900}",
41
+ "warning": "{color.amber.900}",
42
+ "danger": "{color.red.900}"
43
+ },
44
+ "stateBorder": {
45
+ "info": "{color.ocean.300}",
46
+ "success": "{color.green.300}",
47
+ "warning": "{color.amber.300}",
48
+ "danger": "{color.red.300}"
49
+ },
50
+ "stateText": {
51
+ "info": "{color.ocean.100}",
52
+ "success": "{color.green.100}",
53
+ "warning": "{color.amber.100}",
54
+ "danger": "{color.red.100}"
55
+ },
56
+ "overlay": {
57
+ "scrim": "rgba(0, 0, 0, 0.82)"
58
+ },
59
+ "focus": {
60
+ "ring": "{color.orange.300}"
61
+ },
62
+ "avatarSeed": {
63
+ "1Bg": "{color.ocean.900}",
64
+ "1Fg": "{color.ocean.100}",
65
+ "2Bg": "{color.teal.900}",
66
+ "2Fg": "{color.teal.100}",
67
+ "3Bg": "{color.amber.900}",
68
+ "3Fg": "{color.amber.100}",
69
+ "4Bg": "{color.red.900}",
70
+ "4Fg": "{color.red.100}",
71
+ "5Bg": "{color.green.900}",
72
+ "5Fg": "{color.green.100}",
73
+ "6Bg": "{color.purple.900}",
74
+ "6Fg": "{color.purple.100}",
75
+ "7Bg": "{color.pink.900}",
76
+ "7Fg": "{color.pink.100}",
77
+ "8Bg": "{color.stone.800}",
78
+ "8Fg": "{color.stone.100}"
79
+ },
80
+ "chartSeries": {
81
+ "1": "{color.ocean.200}",
82
+ "2": "{color.amber.200}",
83
+ "3": "{color.green.200}",
84
+ "4": "{color.purple.200}",
85
+ "5": "{color.pink.200}",
86
+ "6": "{color.teal.200}",
87
+ "7": "{color.red.200}",
88
+ "8": "{color.stone.200}"
89
+ }
90
+ },
91
+ "theme": {
92
+ "colorScheme": "dark"
93
+ }
94
+ }
@@ -7,6 +7,7 @@
7
7
  "accent": "{color.orange.700}",
8
8
  "accentHover": "{color.orange.800}",
9
9
  "accentActive": "{color.orange.900}",
10
+ "accentStrong": "{color.orange.800}",
10
11
  "onAccent": "{color.stone.0}"
11
12
  },
12
13
  "text": {
@@ -20,10 +21,10 @@
20
21
  "canvas": "{color.stone.0}",
21
22
  "surface": "{color.stone.0}",
22
23
  "elevated": "{color.stone.0}",
23
- "muted": "{color.stone.50}"
24
+ "muted": "{color.stone.200}"
24
25
  },
25
26
  "border": {
26
- "subtle": "{color.stone.400}",
27
+ "subtle": "{color.stone.500}",
27
28
  "default": "{color.stone.700}",
28
29
  "strong": "{color.stone.950}"
29
30
  },
@@ -7,6 +7,7 @@
7
7
  "accent": "{color.orange.500}",
8
8
  "accentHover": "{color.orange.400}",
9
9
  "accentActive": "{color.orange.300}",
10
+ "accentStrong": "{color.orange.600}",
10
11
  "onAccent": "{color.stone.950}"
11
12
  },
12
13
  "text": {
@@ -26,7 +27,7 @@
26
27
  "border": {
27
28
  "subtle": "{color.stone.200}",
28
29
  "default": "{color.stone.500}",
29
- "strong": "{color.stone.500}",
30
+ "strong": "{color.stone.600}",
30
31
  "muted": "{color.border.subtle}"
31
32
  },
32
33
  "state": {