@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.
- package/CHANGELOG.md +55 -0
- package/README.md +13 -0
- package/behaviors/combobox.js +43 -4
- package/behaviors/dropdown.js +16 -2
- package/behaviors/index.d.ts +2 -0
- package/behaviors/internal/dialog-controller.js +79 -9
- package/behaviors/internal/dom.js +38 -7
- package/behaviors/internal/overlay.js +4 -4
- package/behaviors/internal/popup-layer.js +2 -0
- package/behaviors/popover.js +21 -12
- package/behaviors/select-menu.js +20 -2
- package/behaviors/tabs.js +13 -2
- package/behaviors/toggle-group.js +37 -5
- package/behaviors/tooltip.js +26 -2
- package/components/README.md +40 -0
- package/components/_shared.css +11 -0
- package/components/accordion.css +8 -0
- package/components/app-shell-v2.css +7 -0
- package/components/app-shell.css +7 -0
- package/components/banner.css +32 -1
- package/components/card.css +23 -9
- package/components/chart.css +26 -0
- package/components/checkbox.css +32 -0
- package/components/chip.css +41 -1
- package/components/combobox.css +21 -0
- package/components/datepicker.css +55 -8
- package/components/datum.css +84 -11
- package/components/dropdown.css +23 -3
- package/components/field.css +6 -4
- package/components/file-upload.css +1 -1
- package/components/list.css +28 -2
- package/components/navbar.css +14 -1
- package/components/pagination.css +18 -0
- package/components/progress-bar.css +22 -1
- package/components/select-menu.css +8 -0
- package/components/select.css +8 -0
- package/components/sidebar.css +15 -2
- package/components/skip-link.css +6 -2
- package/components/slider.css +5 -0
- package/components/switch.css +62 -2
- package/components/table.css +53 -4
- package/components/tabs.css +55 -3
- package/components/toast.css +10 -1
- package/components/toggle-group.css +16 -5
- package/components/toolbar.css +17 -1
- package/components/tree.css +30 -0
- package/docs/behaviors.md +26 -5
- package/docs/guidelines.md +30 -2
- package/foundations.css +6 -0
- package/package.json +7 -3
- package/schemas/components.schema.json +16 -1
- package/schemas/semantic.schema.json +2 -1
- package/schemas/theme.schema.json +1 -1
- package/scripts/build-tokens.mjs +87 -5
- package/themes/README.md +37 -0
- package/themes/walnut.theme.json +106 -0
- package/tokens/README.md +10 -2
- package/tokens/components.json +10 -0
- package/tokens/semantic.dark.json +4 -3
- package/tokens/semantic.high-contrast-dark.json +94 -0
- package/tokens/semantic.high-contrast.json +3 -2
- package/tokens/semantic.light.json +2 -1
- package/tokens/tokens.css +431 -33
- package/tokens/tokens.js +432 -133
- package/tokens/tokens.json +432 -133
- package/tokens/tokens.ts +432 -133
package/themes/README.md
ADDED
|
@@ -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
|
|
package/tokens/components.json
CHANGED
|
@@ -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.
|
|
27
|
+
"subtle": "{color.stone.600}",
|
|
27
28
|
"default": "{color.stone.400}",
|
|
28
|
-
"strong": "{color.stone.
|
|
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.
|
|
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.
|
|
24
|
+
"muted": "{color.stone.200}"
|
|
24
25
|
},
|
|
25
26
|
"border": {
|
|
26
|
-
"subtle": "{color.stone.
|
|
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.
|
|
30
|
+
"strong": "{color.stone.600}",
|
|
30
31
|
"muted": "{color.border.subtle}"
|
|
31
32
|
},
|
|
32
33
|
"state": {
|