actual-css 0.1.0 → 0.3.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.
- package/CHANGELOG.md +90 -1
- package/README.md +25 -8
- package/dist/actual-themes.min.css +1 -0
- package/dist/actual.css +1492 -931
- package/dist/actual.js +1 -3
- package/dist/actual.min.css +1 -1
- package/dist/optional.css +1048 -0
- package/dist/optional.min.css +1 -0
- package/docs/design-notes/browser-support.md +95 -6
- package/docs/design-notes/cascade-layer.md +1 -1
- package/docs/design-notes/enhancement-contract.md +101 -0
- package/docs/design-notes/forced-colors.md +105 -0
- package/docs/design-notes/naming.md +3 -1
- package/docs/design-notes/platform-alignment.md +492 -0
- package/docs/design-notes/references.md +8 -0
- package/docs/design-notes/widget-primitives.md +119 -0
- package/docs/navigation.json +135 -0
- package/docs/pages/components/accordion.md +80 -0
- package/docs/pages/components/alert.md +203 -0
- package/docs/pages/components/aura.md +35 -0
- package/docs/pages/components/avatar.md +95 -0
- package/docs/pages/components/badge.md +102 -0
- package/docs/pages/components/breadcrumb.md +19 -0
- package/docs/pages/components/button.md +205 -0
- package/docs/pages/components/card.md +183 -0
- package/docs/pages/components/chat.md +50 -0
- package/docs/pages/components/dialog.md +367 -0
- package/docs/pages/components/drawer.md +113 -0
- package/docs/pages/components/fab.md +118 -0
- package/docs/pages/components/key.md +33 -0
- package/docs/pages/components/meter.md +23 -0
- package/docs/pages/components/navbar.md +92 -0
- package/docs/pages/components/pagination.md +55 -0
- package/docs/pages/components/progress.md +29 -0
- package/docs/pages/components/skeleton.md +38 -0
- package/docs/pages/components/spinner.md +93 -0
- package/docs/pages/components/table.md +102 -0
- package/docs/pages/enhancements/flyout.md +391 -0
- package/docs/pages/enhancements/overview.md +36 -0
- package/docs/pages/enhancements/scrollspy.md +71 -0
- package/docs/pages/enhancements/status-bar.md +72 -0
- package/docs/pages/enhancements/tabs.md +80 -0
- package/docs/pages/enhancements/tooltip.md +130 -0
- package/docs/pages/examples/overview.md +41 -0
- package/docs/pages/forms/choice-cards.md +102 -0
- package/docs/pages/forms/controls.md +32 -0
- package/docs/pages/forms/detached-actions.md +59 -0
- package/docs/pages/forms/floating-field.md +88 -0
- package/docs/pages/forms/form-actions.md +56 -0
- package/docs/pages/forms/input-policy.md +91 -0
- package/docs/pages/forms/joined-controls.md +37 -0
- package/docs/pages/forms/labels.md +42 -0
- package/docs/pages/forms/otp.md +50 -0
- package/docs/pages/forms/overview.md +188 -0
- package/docs/pages/forms/password.md +40 -0
- package/docs/pages/forms/range.md +17 -0
- package/docs/pages/forms/states-and-sizes.md +321 -0
- package/docs/pages/forms/sticky-actions.md +187 -0
- package/docs/pages/forms/switch.md +31 -0
- package/docs/pages/forms/validation.md +241 -0
- package/docs/{mental-model.md → pages/foundations/philosophy.md} +33 -18
- package/docs/pages/foundations/theming.md +92 -0
- package/docs/pages/foundations/tokens.md +421 -0
- package/docs/{typography.md → pages/foundations/typography.md} +27 -9
- package/docs/pages/guides/bootstrap.md +438 -0
- package/docs/pages/guides/introduction.md +157 -0
- package/docs/pages/guides/optional-components.md +33 -0
- package/docs/pages/guides/progressive-enhancement.md +686 -0
- package/docs/pages/guides/tailwind.md +455 -0
- package/docs/pages/layout/app-shell.md +25 -0
- package/docs/pages/layout/center.md +29 -0
- package/docs/pages/layout/cluster.md +57 -0
- package/docs/pages/layout/frame.md +30 -0
- package/docs/pages/layout/grid.md +142 -0
- package/docs/pages/layout/list.md +41 -0
- package/docs/pages/layout/media.md +21 -0
- package/docs/pages/layout/overview.md +37 -0
- package/docs/pages/layout/scroll-snap.md +54 -0
- package/docs/pages/layout/sidebar-layout.md +27 -0
- package/docs/pages/layout/stack.md +18 -0
- package/docs/pages/layout/switcher.md +36 -0
- package/docs/pages/layout/topbar.md +23 -0
- package/docs/pages/patterns/actions.md +52 -0
- package/docs/pages/patterns/code-block.md +36 -0
- package/docs/pages/patterns/exclusive-accordion.md +16 -0
- package/docs/pages/patterns/hierarchical-nav.md +26 -0
- package/docs/pages/patterns/icon-input.md +25 -0
- package/docs/pages/patterns/intent-nesting.md +3 -0
- package/docs/pages/patterns/navigation-list.md +235 -0
- package/docs/pages/patterns/overview.md +58 -0
- package/docs/pages/patterns/structured-lists.md +47 -0
- package/docs/pages/utilities/accessibility.md +34 -0
- package/docs/pages/utilities/flex.md +105 -0
- package/docs/pages/utilities/links.md +18 -0
- package/docs/pages/utilities/list.md +31 -0
- package/docs/pages/utilities/overflow.md +43 -0
- package/docs/pages/utilities/overview.md +18 -0
- package/docs/pages/utilities/shape.md +25 -0
- package/docs/pages/utilities/sizing-wrapping.md +115 -0
- package/docs/pages/utilities/spacing.md +79 -0
- package/docs/pages/utilities/text.md +43 -0
- package/llms.txt +10 -4
- package/package.json +30 -16
- package/src/css/actual.css +12 -1
- package/src/css/base.css +3 -9
- package/src/css/components/accordion.css +18 -30
- package/src/css/components/actions.css +4 -1
- package/src/css/components/alert.css +162 -49
- package/src/css/components/avatar.css +16 -10
- package/src/css/components/badge.css +82 -39
- package/src/css/components/breadcrumb.css +7 -15
- package/src/css/components/busy.css +7 -0
- package/src/css/components/button.css +82 -38
- package/src/css/components/card.css +17 -37
- package/src/css/components/drawer.css +30 -13
- package/src/css/components/flyout.css +39 -110
- package/src/css/components/index.css +1 -0
- package/src/css/components/join.css +14 -3
- package/src/css/components/key.css +2 -0
- package/src/css/components/menu.css +125 -0
- package/src/css/components/meter.css +0 -23
- package/src/css/components/modal.css +162 -7
- package/src/css/components/navbar.css +17 -18
- package/src/css/components/overline.css +0 -7
- package/src/css/components/pagination.css +1 -1
- package/src/css/components/progress.css +8 -26
- package/src/css/components/scrollspy.css +2 -2
- package/src/css/components/skeleton.css +7 -0
- package/src/css/components/spinner.css +10 -11
- package/src/css/components/status-bar.css +18 -28
- package/src/css/components/surface.css +2 -8
- package/src/css/components/tab.css +20 -48
- package/src/css/components/table.css +7 -5
- package/src/css/components/tooltip.css +14 -7
- package/src/css/focus.css +16 -3
- package/src/css/forms/choice-card.css +13 -15
- package/src/css/forms/choice.css +21 -28
- package/src/css/forms/control.css +16 -26
- package/src/css/forms/custom-select.css +9 -11
- package/src/css/forms/form-actions.css +14 -3
- package/src/css/forms/form.css +7 -4
- package/src/css/forms/index.css +5 -4
- package/src/css/forms/input-icon.css +30 -12
- package/src/css/forms/native.css +48 -33
- package/src/css/forms/range.css +103 -0
- package/src/css/forms/select.css +8 -8
- package/src/css/forms/switch.css +14 -13
- package/src/css/forms/validation.css +32 -5
- package/src/css/grid.css +19 -18
- package/src/css/intents.css +2 -2
- package/src/css/layout.css +29 -1
- package/src/css/optional/aura.css +93 -0
- package/src/css/optional/chat.css +105 -0
- package/src/css/optional/fab.css +104 -0
- package/src/css/optional/floating-field.css +87 -0
- package/src/css/optional/index.css +5 -0
- package/src/css/optional/layout-extra.css +7 -4
- package/src/css/optional/otp.css +115 -0
- package/src/css/optional/scroll-snap.css +6 -0
- package/src/css/optional/scroller.css +19 -7
- package/src/css/optional/typography-fluid.css +5 -0
- package/src/css/optional/utilities-extra.css +17 -2
- package/src/css/prose.css +18 -8
- package/src/css/reset.css +0 -10
- package/src/css/theme.css +29 -13
- package/src/css/themes/cyberpunk.css +1 -1
- package/src/css/themes/dim.css +3 -3
- package/src/css/themes/edge.css +11 -37
- package/src/css/themes/index.css +3 -3
- package/src/css/themes/{dark.css → indigo.css} +5 -4
- package/src/css/themes/neon.css +5 -5
- package/src/css/themes/square.css +0 -3
- package/src/css/tokens.css +81 -21
- package/src/css/utilities.css +8 -10
- package/src/css/variants.css +37 -65
- package/src/js/command.js +1 -3
- package/src/js/context-menu.js +72 -53
- package/src/js/dialog.js +68 -79
- package/src/js/dismiss.js +15 -0
- package/src/js/enhance.js +76 -19
- package/src/js/enhancement-loader.js +301 -0
- package/src/js/events.js +3 -0
- package/src/js/filter.js +3 -3
- package/src/js/floating.js +101 -131
- package/src/js/flyout.js +106 -96
- package/src/js/focus-group.js +116 -0
- package/src/js/focus.js +6 -1
- package/src/js/index.js +4 -2
- package/src/js/input.js +1 -1
- package/src/js/keys.js +27 -0
- package/src/js/mask.js +7 -6
- package/src/js/menu.js +89 -35
- package/src/js/parse-config.js +47 -0
- package/src/js/password.js +9 -1
- package/src/js/scrollspy.js +133 -62
- package/src/js/selectors.js +14 -31
- package/src/js/status.js +16 -14
- package/src/js/surface.js +219 -125
- package/src/js/tab.js +46 -66
- package/src/js/tooltip.js +53 -31
- package/src/js/validation.js +45 -51
- package/dist/actual.js.map +0 -31
- package/docs/README.md +0 -26
- package/docs/components.md +0 -860
- package/docs/design-notes/previous-work.md +0 -5
- package/docs/forms.md +0 -1265
- package/docs/javascript.md +0 -426
- package/docs/layout.md +0 -480
- package/docs/patterns.md +0 -415
- package/docs/reserved.md +0 -7
- package/docs/tokens.md +0 -455
- package/docs/ui.md +0 -937
- package/docs/utilities.md +0 -384
- package/src/css/dialog-fallback.css +0 -56
- package/src/js/dialog-fallback.js +0 -163
package/docs/tokens.md
DELETED
|
@@ -1,455 +0,0 @@
|
|
|
1
|
-
# Tokens
|
|
2
|
-
|
|
3
|
-
Tokens are the public theming surface. They should make the library feel easy to restyle without requiring users to understand every component implementation detail.
|
|
4
|
-
|
|
5
|
-
The surface stays small by keeping most tokens semantic, not component-specific.
|
|
6
|
-
|
|
7
|
-
- Global tokens are public API.
|
|
8
|
-
- Component-local tokens are implementation details unless documented by that component.
|
|
9
|
-
- Components should map global tokens to local variables internally.
|
|
10
|
-
- Themes override tokens, not selectors.
|
|
11
|
-
- Defaults must work without modern color features or build tooling.
|
|
12
|
-
- Modern enhancements can derive better values with `color-mix()`, `light-dark()`, or container queries.
|
|
13
|
-
|
|
14
|
-
## Public Surface
|
|
15
|
-
|
|
16
|
-
> Tokens exposed as the public theming API, kept small and semantic.
|
|
17
|
-
|
|
18
|
-
### Color
|
|
19
|
-
|
|
20
|
-
Intent tokens are used by `.primary`, `.secondary`, `.success`, `.warning`, `.danger`, and `.neutral`.
|
|
21
|
-
They are curated color/foreground pairs. Actual CSS does not compute foregrounds automatically in the core theme contract.
|
|
22
|
-
|
|
23
|
-
```css
|
|
24
|
-
:root {
|
|
25
|
-
--primary: hsl(270 24% 32%);
|
|
26
|
-
--primary-fg: hsl(0 0% 98%);
|
|
27
|
-
--secondary: hsl(18 24% 36%);
|
|
28
|
-
--secondary-fg: hsl(0 0% 98%);
|
|
29
|
-
--success: hsl(145 55% 32%);
|
|
30
|
-
--success-fg: hsl(0 0% 98%);
|
|
31
|
-
--warning: hsl(38 76% 34%);
|
|
32
|
-
--warning-fg: hsl(0 0% 98%);
|
|
33
|
-
--danger: hsl(2 62% 45%);
|
|
34
|
-
--danger-fg: hsl(0 0% 98%);
|
|
35
|
-
--neutral: hsl(220 8% 42%);
|
|
36
|
-
--neutral-fg: hsl(0 0% 98%);
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Surface tokens describe the canvas and elevation model. Components should prefer these over hard-coded colors.
|
|
41
|
-
|
|
42
|
-
```css
|
|
43
|
-
:root {
|
|
44
|
-
--surface: hsl(0 0% 100%);
|
|
45
|
-
--surface-raised: hsl(0 0% 100%);
|
|
46
|
-
--surface-subtle: hsl(220 14% 96%);
|
|
47
|
-
--surface-solid: hsl(222 18% 12%);
|
|
48
|
-
|
|
49
|
-
--text: hsl(222 18% 12%);
|
|
50
|
-
--text-muted: hsl(220 8% 42%);
|
|
51
|
-
--text-subtle: hsl(220 7% 58%);
|
|
52
|
-
|
|
53
|
-
--border: hsl(220 12% 88%);
|
|
54
|
-
--focus: var(--primary);
|
|
55
|
-
--focus-outline: 2px solid var(--focus);
|
|
56
|
-
--focus-outline-offset: 2px;
|
|
57
|
-
--focus-ring: hsl(258 21% 32% / 0.22);
|
|
58
|
-
--hover-overlay: hsl(220 20% 10% / 0.04);
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
The surface levels are: `surface` (canvas), `surface-raised` (cards, raised surfaces), `surface-subtle` (the working subtle level — for hover overlays, form fields, table headers), and `surface-solid` (the dark inverse surface, used for accent areas).
|
|
63
|
-
|
|
64
|
-
Use `*-fg` pairs only for solid backgrounds where the component controls both foreground and background. Do not require a foreground token for every surface token.
|
|
65
|
-
|
|
66
|
-
Focus styling is outline-first. A global focus baseline uses `--focus-outline`
|
|
67
|
-
and `--focus-outline-offset` for the guaranteed visible affordance: older
|
|
68
|
-
browsers show it on `:focus`, while modern browsers limit it to
|
|
69
|
-
`:focus-visible`. Components add `--focus-ring-shadow` only where a softer
|
|
70
|
-
halo helps.
|
|
71
|
-
`forced-colors: active` disables the shadow and maps the outline to
|
|
72
|
-
`Highlight`.
|
|
73
|
-
|
|
74
|
-
### Default theme philosophy
|
|
75
|
-
|
|
76
|
-
The default theme is intentionally calm. `primary` is a near-neutral dark grey rather than a saturated brand color, `secondary` is a neutral surface rather than a colored intent, and the semantic states are desaturated. Optional themes can re-introduce vivid intent palettes on top of this foundation; the default is meant to be the adult, editorial baseline.
|
|
77
|
-
|
|
78
|
-
Soft surfaces (`.btn.soft`, `.badge.soft`, and the default `.alert`) are generated from intent colors through `color-mix()`. Themes can tune the mix globally instead of rewriting component selectors.
|
|
79
|
-
|
|
80
|
-
```css
|
|
81
|
-
:root {
|
|
82
|
-
--soft-bg-mix: 88%;
|
|
83
|
-
--soft-border-mix: 65%;
|
|
84
|
-
--soft-hover-alpha: 12%;
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### Shape
|
|
89
|
-
|
|
90
|
-
Shape is intentionally small. Components can derive local radii from these.
|
|
91
|
-
|
|
92
|
-
```css
|
|
93
|
-
:root {
|
|
94
|
-
--radius-sm: 0.25rem;
|
|
95
|
-
--radius: 0.5rem;
|
|
96
|
-
--radius-lg: 0.75rem;
|
|
97
|
-
--border-width: 1px;
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Do not create shape APIs like `.rounded`, `.pill`, or `.square` for every component. Prefer theme-level changes, for example a square theme can set all radius tokens to `0`.
|
|
102
|
-
|
|
103
|
-
### Spacing
|
|
104
|
-
|
|
105
|
-
Spacing should be useful without becoming a design-token encyclopedia.
|
|
106
|
-
|
|
107
|
-
```css
|
|
108
|
-
:root {
|
|
109
|
-
--gap: 0.75rem;
|
|
110
|
-
--space-1: 0.25rem;
|
|
111
|
-
--space-2: 0.5rem;
|
|
112
|
-
--space-3: 0.75rem;
|
|
113
|
-
--space-4: 1rem;
|
|
114
|
-
--space-5: 1.5rem;
|
|
115
|
-
--space-6: 2rem;
|
|
116
|
-
}
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Use `em` for spacing that should scale with the component text size, such as button gaps and inline padding. Use `rem` for global rhythm, layouts, and fixed density.
|
|
120
|
-
|
|
121
|
-
### Typography
|
|
122
|
-
|
|
123
|
-
Typography tokens should cover the document baseline and common component needs.
|
|
124
|
-
|
|
125
|
-
```css
|
|
126
|
-
:root {
|
|
127
|
-
--font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
128
|
-
--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
129
|
-
|
|
130
|
-
--line-height: 1.5;
|
|
131
|
-
--font-weight: 400;
|
|
132
|
-
--font-weight-strong: 650;
|
|
133
|
-
|
|
134
|
-
/* Type scale */
|
|
135
|
-
--font-size-xs: 0.8125rem;
|
|
136
|
-
--font-size-sm: 0.875rem;
|
|
137
|
-
--font-size-md: 1rem; /* base */
|
|
138
|
-
--font-size-lg: 1.125rem;
|
|
139
|
-
}
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Components can use local font variables for their own mapping, but should inherit by default. `--font-size-md` is the baseline — going smaller (`.sm`/`--font-size-sm`/`-xs`) is the easy direction; going larger adjusts line-heights and surrounding paddings, so prefer keeping base body text at `--font-size-md`.
|
|
143
|
-
|
|
144
|
-
### Controls
|
|
145
|
-
|
|
146
|
-
Controls share sizing so buttons, inputs, selects, and compact app UI always align. `.sm`/`.lg` set shared density tokens for controls, content components, and compact labels.
|
|
147
|
-
|
|
148
|
-
```css
|
|
149
|
-
:root {
|
|
150
|
-
--control-size-sm: 2rem; /* 32px → .sm */
|
|
151
|
-
--control-size-md: 2.375rem; /* 38px → default */
|
|
152
|
-
--control-size-lg: 2.75rem; /* 44px → .lg */
|
|
153
|
-
--control-size: var(--control-size-md);
|
|
154
|
-
--control-pad-x: 1em;
|
|
155
|
-
--control-font-size: var(--font-size-md);
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
- `.lg` — 44px control height, `--font-size-lg` (18px)
|
|
160
|
-
- default — 38px control height, `--font-size-md` (16px, the baseline)
|
|
161
|
-
- `.sm` — 32px control height, `--font-size-sm` (14px)
|
|
162
|
-
|
|
163
|
-
Size modifiers swap the shared control tokens:
|
|
164
|
-
|
|
165
|
-
```css
|
|
166
|
-
.sm {
|
|
167
|
-
--control-size: var(--control-size-sm);
|
|
168
|
-
--control-font-size: var(--font-size-sm);
|
|
169
|
-
--variant-font-size: var(--font-size-sm);
|
|
170
|
-
--variant-icon-size: 1rem;
|
|
171
|
-
--variant-pad-block: var(--space-3);
|
|
172
|
-
--variant-compact-size: 1.25rem;
|
|
173
|
-
--variant-compact-font-size: 0.75rem;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
.lg {
|
|
177
|
-
--control-size: var(--control-size-lg);
|
|
178
|
-
--control-font-size: var(--font-size-lg);
|
|
179
|
-
--variant-font-size: var(--font-size-lg);
|
|
180
|
-
--variant-icon-size: 1.5rem;
|
|
181
|
-
--variant-pad-block: var(--space-5);
|
|
182
|
-
--variant-compact-size: 1.875rem;
|
|
183
|
-
--variant-compact-font-size: var(--font-size-sm);
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
Content components can consume `--variant-*` for local density. Compact labels such as `.badge` consume `--variant-compact-*`; display elements with special geometry, such as avatar and spinner, keep their own size scales.
|
|
188
|
-
|
|
189
|
-
### Elevation
|
|
190
|
-
|
|
191
|
-
Use a tiny elevation surface. Components should not invent their own shadow scales.
|
|
192
|
-
|
|
193
|
-
```css
|
|
194
|
-
:root {
|
|
195
|
-
--shadow: 0 0.5rem 1.5rem hsl(220 30% 10% / 0.08);
|
|
196
|
-
--shadow-popout: 0 0.25rem 0.75rem hsl(220 30% 10% / 0.15);
|
|
197
|
-
}
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### Motion
|
|
201
|
-
|
|
202
|
-
Motion tokens keep transitions consistent and easy to disable.
|
|
203
|
-
|
|
204
|
-
```css
|
|
205
|
-
:root {
|
|
206
|
-
--duration: 150ms; /* component transitions (hover/focus) */
|
|
207
|
-
--duration-slow: 200ms; /* one-shot open/close (.drawer animation) */
|
|
208
|
-
--duration-spin: 0.75s; /* continuous spin (.spinner) */
|
|
209
|
-
--duration-shimmer: 1.2s; /* continuous shimmer (.skeleton, indeterminate .progress) */
|
|
210
|
-
--ease: ease;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
@media (prefers-reduced-motion: reduce) {
|
|
214
|
-
:root {
|
|
215
|
-
--duration: 0.01ms;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
`reset.css` clamps raw animation and transition durations to `0.01ms`, resets
|
|
221
|
-
their delays, and restores `html` scroll behavior under
|
|
222
|
-
`prefers-reduced-motion: reduce` in addition to the `--duration` token above.
|
|
223
|
-
Decorative loops effectively complete immediately without blanket-disabling
|
|
224
|
-
animation support.
|
|
225
|
-
|
|
226
|
-
### Icons
|
|
227
|
-
|
|
228
|
-
Icons used by CSS-only controls are public tokens because users may need to replace them.
|
|
229
|
-
|
|
230
|
-
```css
|
|
231
|
-
:root {
|
|
232
|
-
--icon-chevron-mask: url("data:image/svg+xml,...stroke='black'...");
|
|
233
|
-
--icon-check-mask: url("data:image/svg+xml,...stroke='black'...");
|
|
234
|
-
--icon-chevron-image: url("data:image/svg+xml,...");
|
|
235
|
-
}
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
Prefer `*-mask` tokens for CSS icons so components can color them with `background-color: currentColor` or a semantic token. Use image tokens only where the platform requires `background-image` (native select chevrons cannot use `currentColor`). Only add an icon token when CSS needs the icon. Markup icons should stay in markup.
|
|
239
|
-
|
|
240
|
-
### Composition tokens
|
|
241
|
-
|
|
242
|
-
A few cross-component helpers:
|
|
243
|
-
|
|
244
|
-
- `--indicator-offset` / `--indicator-ring` — positioning and contrast ring for a status dot attached to `.avatar > .badge:empty`.
|
|
245
|
-
- `--backdrop-color` / `--backdrop-opacity` / `--backdrop-fill` — scrim color and opacity shared by `dialog.modal::backdrop`, `dialog.drawer::backdrop`, and `.surface-backdrop`. Override `--backdrop-opacity` per theme for a denser or lighter scrim.
|
|
246
|
-
|
|
247
|
-
```css
|
|
248
|
-
:root {
|
|
249
|
-
--indicator-offset: 14.65%; /* circle-aware inset */
|
|
250
|
-
--indicator-ring: var(--surface);
|
|
251
|
-
--backdrop-color: var(--surface-solid);
|
|
252
|
-
--backdrop-opacity: 0.45;
|
|
253
|
-
}
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
## Internal Tokens
|
|
257
|
-
|
|
258
|
-
> Component-local tokens allowed when they reduce duplication or make component code clearer.
|
|
259
|
-
|
|
260
|
-
Internal tokens are allowed when they reduce duplication or make component code clearer.
|
|
261
|
-
|
|
262
|
-
### Variant Plumbing
|
|
263
|
-
|
|
264
|
-
The shared visual variants are:
|
|
265
|
-
|
|
266
|
-
- `.solid`: full background, paired foreground. This is the default for buttons and badges.
|
|
267
|
-
- `.soft`: lightly tinted background, colored text. Good for alerts and secondary badges.
|
|
268
|
-
- `.outline`: transparent background, colored border and text.
|
|
269
|
-
|
|
270
|
-
Buttons also support two button-only variants:
|
|
271
|
-
|
|
272
|
-
- `.ghost`: transparent button with hover affordance.
|
|
273
|
-
- `.link`: button semantics with link presentation.
|
|
274
|
-
|
|
275
|
-
Do not force `.ghost` or `.link` onto components where the interaction model does not fit. Shared API should follow shared use.
|
|
276
|
-
|
|
277
|
-
```css
|
|
278
|
-
.btn {
|
|
279
|
-
--btn-bg: var(--ui-bg, var(--btn-default-bg, var(--intent, var(--neutral))));
|
|
280
|
-
--btn-fg: var(--ui-fg, var(--btn-default-fg, var(--intent-fg, var(--neutral-fg))));
|
|
281
|
-
--btn-border: var(--ui-border, var(--btn-default-border, transparent));
|
|
282
|
-
|
|
283
|
-
border: var(--border-width) solid var(--btn-border-color, var(--btn-border));
|
|
284
|
-
border-block-end-color: var(
|
|
285
|
-
--btn-border-block-end-color,
|
|
286
|
-
var(--btn-border-color, var(--btn-border))
|
|
287
|
-
);
|
|
288
|
-
border-block-end-width: var(--btn-border-block-end-width, var(--border-width));
|
|
289
|
-
}
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
Button-local defaults are intentionally inherited extension points for themes
|
|
293
|
-
that change button grammar, such as turning the default solid button into a
|
|
294
|
-
surface button with an intent-colored lower edge. Keep them local until the
|
|
295
|
-
same need appears across multiple components.
|
|
296
|
-
|
|
297
|
-
Text controls (`.input`/`.textarea`/`.select`, forms/control.css) expose the
|
|
298
|
-
same lower-edge extension point: `--control-border-block-end-color` and
|
|
299
|
-
`--control-border-block-end-width` are unset by default and fall back to the
|
|
300
|
-
regular border, so a theme can give inputs an underline-style lower edge the
|
|
301
|
-
same way `edge.css` does for buttons, without fighting the hover/focus/disabled
|
|
302
|
-
state rules that still own `--control-border` itself.
|
|
303
|
-
|
|
304
|
-
Alerts (components/alert.css) expose the same idea on the leading edge instead
|
|
305
|
-
of the lower one: `--alert-border-inline-start-color` and
|
|
306
|
-
`--alert-border-inline-start-width` are unset by default and fall back to the
|
|
307
|
-
regular `--ui-border`, so a theme can turn an alert into a callout with a
|
|
308
|
-
colored flag on the inline-start side, matching the classic notice pattern,
|
|
309
|
-
without a per-variant override.
|
|
310
|
-
|
|
311
|
-
A theme is not limited to the tokens above; it can restate any component-local
|
|
312
|
-
token under a state selector to change the interaction recipe itself, not just
|
|
313
|
-
the palette. `edge.css` does this for focus: the base input recipe
|
|
314
|
-
(forms/control.css) flips `--control-border` to `--focus` on top of the shared
|
|
315
|
-
outline + ring, which triples up with the lower-edge accent above. Edge drops
|
|
316
|
-
the border-color flip under `:focus-visible` (thickening the accent edge
|
|
317
|
-
instead) and also thins `--focus-outline` and tightens `--focus-outline-offset`
|
|
318
|
-
so the outline hugs the control rather than floating a second ring around it.
|
|
319
|
-
|
|
320
|
-
Buttons don't consume `--focus-outline` — button.css computes its own outline
|
|
321
|
-
inline so the color can follow `--btn-focus-color` (intent-aware, unlike the
|
|
322
|
-
plain `--focus` the shared token is hardcoded to), so it exposes a matching
|
|
323
|
-
local token, `--btn-focus-outline-width`, defaulting to the same
|
|
324
|
-
`calc(var(--border-width) * 2)` the old hardcoded value used. `edge.css`
|
|
325
|
-
overrides it the same way it overrides `--focus-outline` for inputs.
|
|
326
|
-
|
|
327
|
-
The outline overrides are gated in `@media not (forced-colors: active)`. Under
|
|
328
|
-
forced-colors, `theme.css` repoints `--focus-outline` to a Highlight-colored
|
|
329
|
-
system outline via a `:root, [data-theme="light"], [data-theme="dark"]` rule —
|
|
330
|
-
`:root` always matches the root element regardless of its `data-theme`
|
|
331
|
-
attribute, so that fallback reaches every theme, edge included. But it is a
|
|
332
|
-
lower-specificity, inherited value: an unguarded same-property override
|
|
333
|
-
declared directly on `.input` would out-specificity it in every color mode,
|
|
334
|
-
including forced-colors, because direct beats inherited regardless of the
|
|
335
|
-
media condition either rule was written under. Any theme that restyles
|
|
336
|
-
`--focus-outline`, `--btn-focus-outline-width`, or `--focus-ring-shadow` needs
|
|
337
|
-
the same guard.
|
|
338
|
-
|
|
339
|
-
Rules:
|
|
340
|
-
|
|
341
|
-
- Prefix internal component tokens with the component name (`--btn-*`, `--alert-*`, `--card-*`).
|
|
342
|
-
- Prefix shared variant plumbing with `--ui-*`.
|
|
343
|
-
- Keep component-specific variants in component code when they do not generalize.
|
|
344
|
-
- Do not require users to override internal tokens for ordinary theming.
|
|
345
|
-
- Promote an internal token to public only when there is a repeated, reasonable customization need.
|
|
346
|
-
|
|
347
|
-
## Theme Contract
|
|
348
|
-
|
|
349
|
-
> What a theme must override to restyle the library without touching components.
|
|
350
|
-
|
|
351
|
-
Themes override tokens, not selectors. The themes in `src/css/themes/` are demo examples, not shipped by the default stylesheet; they exist to show valid ways to use this contract.
|
|
352
|
-
|
|
353
|
-
A minimal recolor theme overrides the intent pairs, surfaces, text colors, border, focus, focus ring, and hover overlay.
|
|
354
|
-
|
|
355
|
-
Shape, shadow, motion, typography, and soft-variant mix tokens are optional knobs. Override them only when the theme actually changes that part of the system.
|
|
356
|
-
|
|
357
|
-
A full theme can override:
|
|
358
|
-
|
|
359
|
-
- intent colors and foreground pairs
|
|
360
|
-
- surface, text, border, focus, and overlay colors
|
|
361
|
-
- radius tokens
|
|
362
|
-
- optional shadow, motion, typography, and soft-variant mix tokens
|
|
363
|
-
|
|
364
|
-
```css
|
|
365
|
-
[data-theme="brand"] {
|
|
366
|
-
color-scheme: light;
|
|
367
|
-
|
|
368
|
-
--primary: hsl(260 70% 54%);
|
|
369
|
-
--primary-fg: white;
|
|
370
|
-
--secondary: hsl(190 70% 38%);
|
|
371
|
-
--secondary-fg: white;
|
|
372
|
-
--success: hsl(150 58% 34%);
|
|
373
|
-
--success-fg: white;
|
|
374
|
-
--warning: hsl(42 92% 52%);
|
|
375
|
-
--warning-fg: hsl(35 35% 10%);
|
|
376
|
-
--danger: hsl(355 72% 52%);
|
|
377
|
-
--danger-fg: white;
|
|
378
|
-
--neutral: hsl(250 8% 42%);
|
|
379
|
-
--neutral-fg: white;
|
|
380
|
-
|
|
381
|
-
--surface: hsl(260 45% 98%);
|
|
382
|
-
--surface-raised: white;
|
|
383
|
-
--surface-subtle: hsl(260 35% 94%);
|
|
384
|
-
--surface-solid: hsl(260 28% 16%);
|
|
385
|
-
|
|
386
|
-
--text: hsl(260 24% 14%);
|
|
387
|
-
--text-muted: hsl(260 10% 42%);
|
|
388
|
-
--text-subtle: hsl(260 8% 56%);
|
|
389
|
-
--border: hsl(260 24% 84%);
|
|
390
|
-
--focus: var(--primary);
|
|
391
|
-
--focus-ring: hsl(260 70% 54% / 0.22);
|
|
392
|
-
--hover-overlay: hsl(260 30% 10% / 0.04);
|
|
393
|
-
--soft-bg-mix: 84%;
|
|
394
|
-
--soft-border-mix: 58%;
|
|
395
|
-
|
|
396
|
-
--radius: 0.625rem;
|
|
397
|
-
--radius-sm: 0.3125rem;
|
|
398
|
-
--radius-lg: 0.875rem;
|
|
399
|
-
}
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
Without `data-theme`, the default theme advertises `color-scheme: light dark`
|
|
403
|
-
and follows the user's OS preference in browsers that support `light-dark()`.
|
|
404
|
-
Dark themes should set `color-scheme: dark`. Light themes should set
|
|
405
|
-
`color-scheme: light`. Browsers without `light-dark()` receive the light
|
|
406
|
-
fallback.
|
|
407
|
-
|
|
408
|
-
Because the native select chevron is an SVG background image and cannot inherit
|
|
409
|
-
`currentColor`, the select hands the arrow back to the browser under
|
|
410
|
-
`prefers-contrast: more` (same treatment as `forced-colors: active`) instead of
|
|
411
|
-
shipping a recolored chevron per theme — one native fallback covers every
|
|
412
|
-
theme without a high-contrast icon variant to maintain.
|
|
413
|
-
|
|
414
|
-
In `forced-colors: active`, the default theme maps the public color tokens to
|
|
415
|
-
system colors (`Canvas`, `CanvasText`, `ButtonText`, `Highlight`, and
|
|
416
|
-
`HighlightText`) and removes decorative shadows. Components should inherit
|
|
417
|
-
that token mode by default; keep local forced-color rules only for custom
|
|
418
|
-
control geometry, native bar parts, disabled states, or selected states that
|
|
419
|
-
would otherwise be communicated only by background color.
|
|
420
|
-
|
|
421
|
-
## Component Mapping
|
|
422
|
-
|
|
423
|
-
> How components map global tokens to local aliases for a clear theme and variant contract.
|
|
424
|
-
|
|
425
|
-
Components should map global tokens once at the top of the component and use local tokens afterwards.
|
|
426
|
-
|
|
427
|
-
```css
|
|
428
|
-
.alert {
|
|
429
|
-
--alert-bg: var(--ui-bg, var(--surface-subtle));
|
|
430
|
-
--alert-fg: var(--ui-fg, var(--text));
|
|
431
|
-
--alert-border: var(--ui-border, var(--border));
|
|
432
|
-
--alert-radius: var(--radius-lg);
|
|
433
|
-
|
|
434
|
-
border: var(--border-width) solid var(--alert-border);
|
|
435
|
-
border-radius: var(--alert-radius);
|
|
436
|
-
background: var(--alert-bg);
|
|
437
|
-
color: var(--alert-fg);
|
|
438
|
-
}
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
This makes the contract obvious:
|
|
442
|
-
|
|
443
|
-
- theme authors adjust global tokens
|
|
444
|
-
- variant code adjusts `--ui-*`
|
|
445
|
-
- component code consumes local aliases
|
|
446
|
-
|
|
447
|
-
## Non-Goals
|
|
448
|
-
|
|
449
|
-
> Decisions deliberately excluded from the token surface to keep it small.
|
|
450
|
-
|
|
451
|
-
- Do not expose a separate token for every selector.
|
|
452
|
-
- Do not mirror Pico-style component-specific variables unless there is a clear need.
|
|
453
|
-
- Do not add Tailwind-like color scales as public API.
|
|
454
|
-
- Do not rely on Sass, PostCSS, or generated token files.
|
|
455
|
-
- Do not make dark mode required for old browsers. Light mode is the fallback.
|