actual-css 0.9.2 → 0.10.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 +96 -0
- package/README.md +65 -44
- package/components.json +33 -13
- package/dist/actual.full.css +585 -499
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/docs/design-notes/app-ui.md +1 -1
- package/docs/design-notes/column-layout.md +103 -520
- package/docs/design-notes/forced-colors.md +12 -1
- package/docs/design-notes/naming.md +1 -1
- package/docs/design-notes/surface-reparenting.md +8 -9
- package/docs/navigation.json +2 -0
- package/docs/pages/components/accordion.md +55 -1
- package/docs/pages/components/alert.md +9 -15
- package/docs/pages/components/badge.md +6 -7
- package/docs/pages/components/bleed.md +42 -0
- package/docs/pages/components/button.md +30 -4
- package/docs/pages/components/card.md +21 -86
- package/docs/pages/components/close.md +48 -0
- package/docs/pages/components/data-list.md +26 -1
- package/docs/pages/components/dialog.md +16 -16
- package/docs/pages/components/drawer.md +9 -9
- package/docs/pages/components/navbar.md +5 -5
- package/docs/pages/components/spinner.md +3 -3
- package/docs/pages/enhancements/overview.md +5 -0
- package/docs/pages/enhancements/tooltip.md +3 -1
- package/docs/pages/examples/overview.md +2 -1
- package/docs/pages/forms/choice-cards.md +39 -0
- package/docs/pages/forms/combobox.md +23 -11
- package/docs/pages/forms/joined-controls.md +2 -5
- package/docs/pages/forms/overview.md +6 -0
- package/docs/pages/foundations/theming.md +64 -18
- package/docs/pages/foundations/tokens.md +28 -10
- package/docs/pages/guides/introduction.md +15 -23
- package/docs/pages/guides/modular-import.md +53 -6
- package/docs/pages/guides/progressive-enhancement.md +6 -3
- package/docs/pages/guides/tailwind.md +17 -5
- package/docs/pages/layout/column-layout.md +92 -54
- package/docs/pages/layout/media.md +1 -1
- package/docs/pages/layout/overview.md +1 -1
- package/docs/pages/utilities/overview.md +1 -1
- package/docs/pages/utilities/sizing-wrapping.md +5 -3
- package/llms.txt +24 -4
- package/package.json +6 -6
- package/reserved-classes.json +5 -30
- package/skills/actual-css/PROJECT.md.example +65 -0
- package/skills/actual-css/README.md +64 -0
- package/skills/actual-css/SKILL.md +234 -0
- package/skills/actual-css/references/accessibility.md +44 -0
- package/skills/actual-css/references/api-and-vocabulary.md +94 -0
- package/skills/actual-css/references/enhancements.md +59 -0
- package/skills/actual-css/references/forms.md +49 -0
- package/skills/actual-css/references/layout-and-theming.md +82 -0
- package/skills/actual-css/references/review-checklist.md +51 -0
- package/skills/actual-css/scripts/inspect-actual-css.mjs +171 -0
- package/src/css/components/accordion.css +44 -7
- package/src/css/components/alert.css +23 -43
- package/src/css/components/app-nav.css +9 -0
- package/src/css/components/badge.css +11 -39
- package/src/css/components/bleed.css +79 -0
- package/src/css/components/button.css +6 -17
- package/src/css/components/card.css +7 -40
- package/src/css/components/close.css +91 -0
- package/src/css/components/data-list.css +18 -0
- package/src/css/components/drawer.css +15 -56
- package/src/css/components/fab.css +10 -0
- package/src/css/components/flyout.css +1 -0
- package/src/css/components/index.css +2 -0
- package/src/css/components/join.css +36 -20
- package/src/css/components/menu.css +12 -0
- package/src/css/components/modal.css +10 -57
- package/src/css/components/navbar.css +27 -0
- package/src/css/core/focus.css +9 -3
- package/src/css/core/theme.css +25 -43
- package/src/css/core/tokens.css +1 -1
- package/src/css/core/variants.css +3 -33
- package/src/css/forms/choice-card.css +74 -4
- package/src/css/forms/choice.css +2 -3
- package/src/css/forms/control.css +15 -8
- package/src/css/forms/custom-select.css +4 -4
- package/src/css/forms/native.css +4 -4
- package/src/css/forms/range.css +23 -8
- package/src/css/forms/switch.css +2 -3
- package/src/css/layout/app-layout.css +1 -1
- package/src/css/layout/column-layout.css +26 -111
- package/src/css/utilities/extra.css +7 -6
- package/src/js/dialog.js +95 -24
- package/src/js/enhance.js +7 -3
- package/src/js/escape.js +1 -0
- package/src/js/flyout.js +10 -1
- package/src/js/focus-group.js +2 -1
- package/src/js/keys.js +14 -0
- package/src/js/menu.js +2 -1
- package/src/js/status.js +19 -0
- package/src/js/surface.js +12 -8
- package/src/js/tab.js +2 -1
- package/src/js/tooltip.js +18 -1
- package/src/js/validation.js +5 -6
- package/dist/actual.css +0 -876
- package/dist/actual.js +0 -1
- package/dist/actual.min.css +0 -1
package/dist/actual.css
DELETED
|
@@ -1,876 +0,0 @@
|
|
|
1
|
-
/* Core — the minimal Actual language. Every module depends on this order. */
|
|
2
|
-
/* Mild reset: useful defaults without classless magic. */
|
|
3
|
-
*,
|
|
4
|
-
*::before,
|
|
5
|
-
*::after {
|
|
6
|
-
box-sizing: border-box;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
html {
|
|
10
|
-
-webkit-text-size-adjust: 100%;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
body {
|
|
14
|
-
margin: 0;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
button,
|
|
18
|
-
input,
|
|
19
|
-
textarea,
|
|
20
|
-
select {
|
|
21
|
-
font: inherit;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
[hidden]:not([hidden="until-found"]) {
|
|
25
|
-
display: none !important;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
:where(img, svg, video) {
|
|
29
|
-
max-inline-size: 100%;
|
|
30
|
-
block-size: auto;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
@media (prefers-reduced-motion: reduce) {
|
|
34
|
-
html {
|
|
35
|
-
scroll-behavior: auto !important;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
*,
|
|
39
|
-
*::before,
|
|
40
|
-
*::after {
|
|
41
|
-
animation-duration: 0.01ms !important;
|
|
42
|
-
animation-delay: 0s !important;
|
|
43
|
-
animation-iteration-count: 1 !important;
|
|
44
|
-
transition-duration: 0.01ms !important;
|
|
45
|
-
transition-delay: 0s !important;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
:root {
|
|
50
|
-
/*
|
|
51
|
-
* Global framework tokens.
|
|
52
|
-
*
|
|
53
|
-
* Public: properties in the named groups below are public API (typography,
|
|
54
|
-
* spacing, shape, motion, focus, controls, viewport, z-index, icons) unless
|
|
55
|
-
* a group is explicitly marked Internal or Derived.
|
|
56
|
-
*
|
|
57
|
-
* Derived: "Theme-derived aliases" and "State tokens" reference intent and
|
|
58
|
-
* surface tokens and follow the theme; do not hard-code values into them.
|
|
59
|
-
* Internal: indicator offsets, --disabled-opacity and the like are
|
|
60
|
-
* consumed by components, not authored.
|
|
61
|
-
*
|
|
62
|
-
* Component-local hooks and internal state relays live with their
|
|
63
|
-
* components. See docs/pages/foundations/tokens.md for the public/internal
|
|
64
|
-
* token contract.
|
|
65
|
-
*/
|
|
66
|
-
|
|
67
|
-
/* Typography */
|
|
68
|
-
--font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
69
|
-
--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
70
|
-
--font-width: 100%;
|
|
71
|
-
--font-width-dense: 95%;
|
|
72
|
-
--line-height: 1.5;
|
|
73
|
-
--line-height-tight: 1.25;
|
|
74
|
-
--line-height-relaxed: 1.75;
|
|
75
|
-
--font-weight-normal: 400;
|
|
76
|
-
--font-weight-medium: 500;
|
|
77
|
-
--font-weight-semibold: 600;
|
|
78
|
-
--font-weight-bold: 700;
|
|
79
|
-
|
|
80
|
-
/* Typography scale */
|
|
81
|
-
--font-size-xs: 0.8125rem;
|
|
82
|
-
--font-size-sm: 0.875rem;
|
|
83
|
-
--font-size-md: 1rem;
|
|
84
|
-
--font-size-lg: 1.125rem;
|
|
85
|
-
|
|
86
|
-
/* Spacing — official steps are named in tens so the scale stays open.
|
|
87
|
-
Actual CSS will never define a --space-* that is not a multiple of 10:
|
|
88
|
-
the intermediate names (--space-15, --space-45…) are reserved for
|
|
89
|
-
applications, and no future framework step can collide with them. */
|
|
90
|
-
--gap: 0.75rem;
|
|
91
|
-
--density-space: var(--space-40);
|
|
92
|
-
--space-10: 0.25rem;
|
|
93
|
-
--space-20: 0.5rem;
|
|
94
|
-
--space-30: 0.75rem;
|
|
95
|
-
--space-40: 1rem;
|
|
96
|
-
--space-50: 1.5rem;
|
|
97
|
-
--space-60: 2rem;
|
|
98
|
-
|
|
99
|
-
/* Shape */
|
|
100
|
-
--border-width: 1px;
|
|
101
|
-
--radius-full: 9999px;
|
|
102
|
-
--shadow: 0 1px 3px rgb(0 0 0 / 0.05), 0 4px 12px rgb(0 0 0 / 0.04);
|
|
103
|
-
--shadow-popout: 0 0.25rem 0.75rem rgb(0 0 0 / 0.15);
|
|
104
|
-
|
|
105
|
-
/* Motion — see reset.css for the prefers-reduced-motion hard reset.
|
|
106
|
-
--duration scales component transitions (hover/focus); the dedicated
|
|
107
|
-
animation rates below tune one-shot open/close and decorative loops.
|
|
108
|
-
Generic state/interaction motion uses the CSS default `ease` directly
|
|
109
|
-
(registered nowhere). --ease-enter/--ease-exit express the character of
|
|
110
|
-
one-shot presence changes, owned by the open/closed state rules that
|
|
111
|
-
separate them (status-bar, drawer, modal). The pair follows the standard
|
|
112
|
-
M3 rhythm; the more aggressive emphasized-decelerate entrance is
|
|
113
|
-
deliberately not offered because it only pays off on 250–400ms motions,
|
|
114
|
-
beyond this vocabulary. Toggles that share one transition list at
|
|
115
|
-
--duration-fast (flyout, tooltip) and pure-opacity backdrops stay on
|
|
116
|
-
`ease`: at 100ms the curve has almost no resolving time, and a scrim is
|
|
117
|
-
not traveling anywhere. Animation policy: reset.css is the global safety
|
|
118
|
-
net; every meaningful @keyframes is explicitly gated behind
|
|
119
|
-
prefers-reduced-motion: no-preference; ordinary transitions rely on the
|
|
120
|
-
reset alone. */
|
|
121
|
-
--duration: 150ms;
|
|
122
|
-
--duration-fast: 100ms;
|
|
123
|
-
--duration-slow: 200ms;
|
|
124
|
-
--duration-spin: 0.75s;
|
|
125
|
-
--duration-shimmer: 1.2s;
|
|
126
|
-
--ease-enter: cubic-bezier(0.2, 0, 0, 1);
|
|
127
|
-
--ease-exit: cubic-bezier(0.4, 0, 1, 1);
|
|
128
|
-
|
|
129
|
-
/* Focus ring — shared by every interactive component's :focus-visible */
|
|
130
|
-
--focus-outline-color: currentColor;
|
|
131
|
-
--focus-outline: calc(var(--border-width) * 2) solid var(--focus-outline-color);
|
|
132
|
-
--focus-outline-offset: 2px;
|
|
133
|
-
--focus-ring-width: 3px;
|
|
134
|
-
|
|
135
|
-
/* Status dot positioning — used by .avatar > .badge:empty. The offset is
|
|
136
|
-
the circle-aware inset (1 - 1/√2) / 2 ≈ 14.65%; the ring surrounds the
|
|
137
|
-
dot with the parent surface color. */
|
|
138
|
-
--indicator-offset: 14.65%;
|
|
139
|
-
|
|
140
|
-
/* Shared backdrop — consumed by modal, drawer, and mobile flyout sheets. */
|
|
141
|
-
--backdrop-color: rgb(0 0 0);
|
|
142
|
-
--backdrop-opacity: 0.45;
|
|
143
|
-
--backdrop-fill: rgb(0 0 0 / var(--backdrop-opacity));
|
|
144
|
-
|
|
145
|
-
/* Bar thickness shared by .progress and .meter — two conceptual variants of
|
|
146
|
-
the same geometry. Public: set it on the element or on an application
|
|
147
|
-
class rather than reaching for a size variant. */
|
|
148
|
-
--bar-height: 0.65rem;
|
|
149
|
-
|
|
150
|
-
/* Controls — inputs and buttons share --control-size so heights always match.
|
|
151
|
-
.compact/.spacious (variants.css) switch geometry without changing
|
|
152
|
-
--control-font-size. Local .sm/.lg component sizes switch all three.
|
|
153
|
-
|
|
154
|
-
Inline choices (checkbox, radio, switch) are typographic, not field-sized:
|
|
155
|
-
they sit on a line of text, so they follow --control-font-size through
|
|
156
|
-
--choice-control-size instead of --control-size. Being an em value in
|
|
157
|
-
:root is deliberate — an unregistered custom property resolves its units
|
|
158
|
-
at the element that consumes it, so one declaration tracks whatever font
|
|
159
|
-
size the choice actually renders at.
|
|
160
|
-
|
|
161
|
-
--control-pad-x is the opposite kind of token: a resolved geometry length,
|
|
162
|
-
not an em ratio. Every part of one control must see the same horizontal
|
|
163
|
-
distance, so it is stated in the typographic tokens rather than an em
|
|
164
|
-
whose value would change on a descendant with its own font size (the
|
|
165
|
-
input-icon glyph, made larger by --input-icon-size). It mirrors
|
|
166
|
-
--control-font-size and is rebound beside it at the .sm/.lg boundaries
|
|
167
|
-
(variants.css); declaring it only in :root would freeze the md value,
|
|
168
|
-
because a var() inside a custom property resolves where it is declared. */
|
|
169
|
-
--control-size-sm: 2rem;
|
|
170
|
-
--control-size-md: 2.375rem;
|
|
171
|
-
--control-size-lg: 2.75rem;
|
|
172
|
-
--control-size: var(--control-size-md);
|
|
173
|
-
--control-pad-x: var(--font-size-md);
|
|
174
|
-
--control-font-size: var(--font-size-md);
|
|
175
|
-
--choice-control-size: 1.125em;
|
|
176
|
-
--disabled-opacity: 0.65;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/* Theme-derived aliases. These reference intent/surface tokens, so they must
|
|
180
|
-
be re-declared on every theme boundary: a custom property resolves its
|
|
181
|
-
var() references at computed-value time on the element that declares it,
|
|
182
|
-
so an alias declared only on :root is inherited as an already-resolved
|
|
183
|
-
value and would not follow a [data-theme] island's overridden tokens.
|
|
184
|
-
Themes that want a distinct alias override it explicitly afterwards. */
|
|
185
|
-
:root,
|
|
186
|
-
[data-theme] {
|
|
187
|
-
/* State tokens — semantic colors for selected/pressed/current and disabled.
|
|
188
|
-
Components consume these instead of hard-wiring --primary/--text-subtle
|
|
189
|
-
so forced-colors can override them in one place. */
|
|
190
|
-
--state-selected: var(--primary);
|
|
191
|
-
--state-selected-fg: var(--primary-fg);
|
|
192
|
-
--state-disabled: var(--text-subtle);
|
|
193
|
-
|
|
194
|
-
/* Status dot ring — follows the island's surface. */
|
|
195
|
-
--indicator-ring: var(--surface);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
:root {
|
|
199
|
-
/* Variant tuning — themes may adjust generated soft surfaces without
|
|
200
|
-
replacing the component rules. */
|
|
201
|
-
--soft-bg-mix: 88%;
|
|
202
|
-
--soft-border-mix: 65%;
|
|
203
|
-
--soft-hover-alpha: 12%;
|
|
204
|
-
/* Soft foreground: the share of raw intent in soft text. 100% keeps the
|
|
205
|
-
pure intent color; lower values rebate it toward --text, which vivid or
|
|
206
|
-
light intent palettes need to stay legible on their own soft surface.
|
|
207
|
-
The default palette ships per-role --*-soft-fg hooks calibrated against
|
|
208
|
-
the hovered soft fill instead (theme.css); this mix is the fallback a
|
|
209
|
-
theme without hooks falls back to. */
|
|
210
|
-
--soft-fg-mix: 100%;
|
|
211
|
-
|
|
212
|
-
/* Shared icon assets. Override these URLs when a strict CSP disallows data:. */
|
|
213
|
-
--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%238a8594' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
|
|
214
|
-
--icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
|
|
215
|
-
--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3C/svg%3E");
|
|
216
|
-
--icon-plus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='12' y1='5' x2='12' y2='19'/%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3C/svg%3E");
|
|
217
|
-
|
|
218
|
-
/* Viewport size tokens — fallback vh/vw with dvh/vi enhancement.
|
|
219
|
-
Consumed by full-viewport components (app-shell, drawer, modal, flyout).
|
|
220
|
-
Custom properties keep the fallback explicit while allowing modern units
|
|
221
|
-
to enhance the same public tokens. */
|
|
222
|
-
--viewport-block: 100vh;
|
|
223
|
-
--viewport-inline: 100vw;
|
|
224
|
-
|
|
225
|
-
/* Z-index scale — orders document-layer components only. Native top-layer
|
|
226
|
-
elements, dialogs and popovers alike, do not participate in this ordering:
|
|
227
|
-
they always paint above the document, and their order among themselves is
|
|
228
|
-
their top-layer promotion order. No z-index value here can reach them.
|
|
229
|
-
Actual CSS reserves multiples of 10 for framework layers; applications may
|
|
230
|
-
use intermediate values for local layering. */
|
|
231
|
-
--z-sticky: 10;
|
|
232
|
-
--z-menu: 20;
|
|
233
|
-
--z-status: 60;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
@media (forced-colors: active) {
|
|
237
|
-
:root,
|
|
238
|
-
[data-theme] {
|
|
239
|
-
--state-disabled: GrayText;
|
|
240
|
-
--disabled-opacity: 1;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
@supports (block-size: 100dvh) {
|
|
245
|
-
:root {
|
|
246
|
-
--viewport-block: 100dvh;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
@supports (inline-size: 100vi) {
|
|
251
|
-
:root {
|
|
252
|
-
--viewport-inline: 100vi;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
@supports (color: color-mix(in oklch, red, white)) {
|
|
257
|
-
:root,
|
|
258
|
-
[data-theme] {
|
|
259
|
-
/* Shadows derive from --shadow-color (theme.css) — re-declared on every
|
|
260
|
-
theme boundary so islands follow their own shadow-color. */
|
|
261
|
-
--shadow:
|
|
262
|
-
0 1px 3px color-mix(in oklch, var(--shadow-color) 5%, transparent),
|
|
263
|
-
0 4px 12px color-mix(in oklch, var(--shadow-color) 4%, transparent);
|
|
264
|
-
--shadow-popout: 0 0.25rem 0.75rem color-mix(in oklch, var(--shadow-color) 15%, transparent);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/* Theme-derived like the shadows above, so it is declared on the same
|
|
268
|
-
boundaries: an alias resolved only on :root is inherited already composed,
|
|
269
|
-
and a [data-theme] island overriding --backdrop-color or
|
|
270
|
-
--backdrop-opacity would not change the scrim of a dialog inside it. */
|
|
271
|
-
:root,
|
|
272
|
-
[data-theme] {
|
|
273
|
-
--backdrop-fill: color-mix(
|
|
274
|
-
in oklch,
|
|
275
|
-
var(--backdrop-color) calc(var(--backdrop-opacity) * 100%),
|
|
276
|
-
transparent
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/*
|
|
282
|
-
* Theme tokens — the palette, surfaces, text, borders, radius and shadow
|
|
283
|
-
* color. Named themes (themes/*.css) override this file; authors can set the
|
|
284
|
-
* same tokens on :root or on [data-theme="…"] islands.
|
|
285
|
-
*
|
|
286
|
-
* Public: Intent colors, Surfaces, Text, Lines and focus, Radius scale,
|
|
287
|
-
* Shadow color, Focus ring derivation.
|
|
288
|
-
* Derived: "Theme-derived aliases", Tone subsystem, Backdrop fill — reference
|
|
289
|
-
* other tokens; hard-coded overrides on a custom theme will break the intent.
|
|
290
|
-
* Internal: --hgroup-gap.
|
|
291
|
-
*/
|
|
292
|
-
:root {
|
|
293
|
-
color-scheme: light dark;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
[data-theme="light"] {
|
|
297
|
-
color-scheme: light;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
[data-theme="dark"] {
|
|
301
|
-
color-scheme: dark;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/* Dark mode is progressive enhancement via light-dark(). Older browsers get
|
|
305
|
-
the light fallback rather than a partially dark native color scheme over
|
|
306
|
-
light tokens. */
|
|
307
|
-
@supports not (color: light-dark(red, blue)) {
|
|
308
|
-
:root,
|
|
309
|
-
[data-theme="light"],
|
|
310
|
-
[data-theme="dark"] {
|
|
311
|
-
color-scheme: light;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/* Default palette — "ink & terra": aubergine primary, terracotta secondary,
|
|
316
|
-
pigment-toned statuses (sage, ochre, brick) tuned to equal perceived weight.
|
|
317
|
-
The neutral ramp (text, borders, subtle surfaces) is tinted toward the
|
|
318
|
-
primary hue (~260) so the whole page carries the identity, quietly. */
|
|
319
|
-
|
|
320
|
-
/* Light fallback — also the default on browsers without light-dark() support. */
|
|
321
|
-
:root,
|
|
322
|
-
[data-theme="light"] {
|
|
323
|
-
/* Intent colors */
|
|
324
|
-
--primary: hsl(268 32% 33%);
|
|
325
|
-
--primary-fg: hsl(0 0% 98%);
|
|
326
|
-
|
|
327
|
-
--secondary: hsl(16 38% 38%);
|
|
328
|
-
--secondary-fg: hsl(0 0% 98%);
|
|
329
|
-
|
|
330
|
-
--success: hsl(150 40% 31%);
|
|
331
|
-
--success-fg: hsl(0 0% 98%);
|
|
332
|
-
|
|
333
|
-
--warning: hsl(36 68% 34%);
|
|
334
|
-
--warning-fg: hsl(0 0% 98%);
|
|
335
|
-
|
|
336
|
-
--danger: hsl(5 58% 43%);
|
|
337
|
-
--danger-fg: hsl(0 0% 98%);
|
|
338
|
-
|
|
339
|
-
--neutral: hsl(258 7% 43%);
|
|
340
|
-
--neutral-fg: hsl(0 0% 98%);
|
|
341
|
-
|
|
342
|
-
/* Surfaces */
|
|
343
|
-
--surface: hsl(0 0% 100%);
|
|
344
|
-
--surface-raised: hsl(0 0% 100%);
|
|
345
|
-
--surface-subtle: hsl(262 24% 96%);
|
|
346
|
-
--surface-solid: hsl(261 16% 13%);
|
|
347
|
-
|
|
348
|
-
/* Text */
|
|
349
|
-
--text: hsl(261 20% 13%);
|
|
350
|
-
--text-muted: hsl(259 9% 42%);
|
|
351
|
-
--text-subtle: hsl(258 7% 57%);
|
|
352
|
-
|
|
353
|
-
/* Lines and focus */
|
|
354
|
-
--border: hsl(260 15% 88%);
|
|
355
|
-
--focus: var(--neutral);
|
|
356
|
-
/* Pre-color-mix fallback only — superseded below by a derivation from
|
|
357
|
-
--focus so overriding --neutral/--focus never leaves a stale ring. */
|
|
358
|
-
--focus-ring: hsl(258 7% 43% / 0.3);
|
|
359
|
-
--hover-overlay: hsl(260 20% 10% / 0.04);
|
|
360
|
-
|
|
361
|
-
/* Radius scale — themes may override the whole scale (e.g. [data-theme="square"]) */
|
|
362
|
-
--radius: 0.5rem;
|
|
363
|
-
--radius-sm: 0.25rem;
|
|
364
|
-
--radius-lg: 0.75rem;
|
|
365
|
-
|
|
366
|
-
/* Shadow color — consumed by --shadow / --shadow-popout in tokens.css */
|
|
367
|
-
--shadow-color: hsl(260 25% 10%);
|
|
368
|
-
|
|
369
|
-
/* hgroup internal gap */
|
|
370
|
-
--hgroup-gap: 0.25em;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/* Theme-derived aliases — re-declared on every theme boundary (see tokens.css
|
|
374
|
-
for the custom-property resolution rationale): islands recompute these from
|
|
375
|
-
their own tokens instead of inheriting :root's resolved values. */
|
|
376
|
-
:root,
|
|
377
|
-
[data-theme] {
|
|
378
|
-
--heading: var(--text);
|
|
379
|
-
--selection-bg: var(--primary);
|
|
380
|
-
--selection-fg: var(--primary-fg);
|
|
381
|
-
--focus-ring-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/* Soft foreground hooks — the soft ink is the intent rebated toward the local
|
|
385
|
-
text so it keeps 4.5:1 against the soft hover fill (the resting fill carries
|
|
386
|
-
the extra margin; variants.css soft recipe). Deliberately scoped to the
|
|
387
|
-
default theme: a custom property bakes its var() references where it is
|
|
388
|
-
declared, so these resolve against root/light/dark tokens on every boundary.
|
|
389
|
-
Presets and custom islands keep their --soft-fg-mix / raw-intent ink unless
|
|
390
|
-
they declare hooks themselves — :where() keeps this reset from outranking a
|
|
391
|
-
theme's own declaration (e.g. brutalist's 45% soft-fg-mix). */
|
|
392
|
-
:where([data-theme]:not([data-theme="light"]):not([data-theme="dark"])) {
|
|
393
|
-
--primary-soft-fg: initial;
|
|
394
|
-
--secondary-soft-fg: initial;
|
|
395
|
-
--success-soft-fg: initial;
|
|
396
|
-
--warning-soft-fg: initial;
|
|
397
|
-
--danger-soft-fg: initial;
|
|
398
|
-
--neutral-soft-fg: initial;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
:root,
|
|
402
|
-
[data-theme="light"],
|
|
403
|
-
[data-theme="dark"] {
|
|
404
|
-
--secondary-soft-fg: color-mix(in oklab, var(--text) 10%, var(--secondary) 90%);
|
|
405
|
-
--success-soft-fg: color-mix(in oklab, var(--text) 15%, var(--success) 85%);
|
|
406
|
-
--warning-soft-fg: color-mix(in oklab, var(--text) 25%, var(--warning) 75%);
|
|
407
|
-
--danger-soft-fg: color-mix(in oklab, var(--text) 15%, var(--danger) 85%);
|
|
408
|
-
--neutral-soft-fg: color-mix(in oklab, var(--text) 20%, var(--neutral) 80%);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/* Native platform controls (checkbox, radio, range, progress) that are not
|
|
412
|
-
restyled by a component class still follow the theme accent. Declared on
|
|
413
|
-
every island: accent-color inherits as a computed value, so islands must
|
|
414
|
-
recompute it from their own --primary (same resolution rule as above).
|
|
415
|
-
Unsupported browsers ignore it; forced-colors keeps browser control. */
|
|
416
|
-
:root,
|
|
417
|
-
[data-theme] {
|
|
418
|
-
accent-color: var(--primary);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/* Tone subsystem — fixed-backdrop foreground for components placed over media
|
|
422
|
-
or other surfaces outside the theme token stack. Use data-theme islands for
|
|
423
|
-
full dark/light subtree inversion. */
|
|
424
|
-
[data-tone="dark"] {
|
|
425
|
-
--tone-fg: white;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
[data-tone="light"] {
|
|
429
|
-
--tone-fg: var(--text);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/* Focus ring derivation — color-mix tier. Runs whether or not light-dark()
|
|
433
|
-
is also supported, so it covers the color-mix-without-light-dark browser
|
|
434
|
-
tier on its own; the light-dark block below refines it further with the
|
|
435
|
-
light/dark alpha split once both features are available. Declared on every
|
|
436
|
-
[data-theme] island (not just the built-in light/dark pair) so custom
|
|
437
|
-
islands recolor the ring for free; a preset that re-declares --focus-ring
|
|
438
|
-
later in source order intentionally pins its own ring instead. */
|
|
439
|
-
@supports (color: color-mix(in oklch, red, white)) {
|
|
440
|
-
:root,
|
|
441
|
-
[data-theme] {
|
|
442
|
-
--focus-ring: color-mix(in oklch, var(--focus) 30%, transparent);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/* Auto dark mode via light-dark(). Resolves based on the element's color-scheme:
|
|
447
|
-
:root (light dark) follows the OS, [data-theme="light"] forces light,
|
|
448
|
-
[data-theme="dark"] forces dark by overriding color-scheme.
|
|
449
|
-
Dark pastels sit a little lower and more chromatic (L 70-76, sat 38-45)
|
|
450
|
-
than usual so the palette stays warm rather than washed gray. */
|
|
451
|
-
@supports (color: light-dark(red, blue)) {
|
|
452
|
-
:root,
|
|
453
|
-
[data-theme="light"] {
|
|
454
|
-
--primary: light-dark(hsl(268 32% 33%), hsl(268 42% 76%));
|
|
455
|
-
--primary-fg: light-dark(hsl(0 0% 98%), hsl(268 30% 12%));
|
|
456
|
-
|
|
457
|
-
--secondary: light-dark(hsl(16 38% 38%), hsl(18 45% 72%));
|
|
458
|
-
--secondary-fg: light-dark(hsl(0 0% 98%), hsl(18 32% 11%));
|
|
459
|
-
|
|
460
|
-
--success: light-dark(hsl(150 40% 31%), hsl(150 38% 62%));
|
|
461
|
-
--success-fg: light-dark(hsl(0 0% 98%), hsl(150 35% 8%));
|
|
462
|
-
|
|
463
|
-
--warning: light-dark(hsl(36 68% 34%), hsl(40 62% 64%));
|
|
464
|
-
--warning-fg: light-dark(hsl(0 0% 98%), hsl(38 35% 10%));
|
|
465
|
-
|
|
466
|
-
--danger: light-dark(hsl(5 58% 43%), hsl(4 60% 70%));
|
|
467
|
-
--danger-fg: light-dark(hsl(0 0% 98%), hsl(4 35% 10%));
|
|
468
|
-
|
|
469
|
-
--neutral: light-dark(hsl(258 7% 43%), hsl(258 10% 72%));
|
|
470
|
-
--neutral-fg: light-dark(hsl(0 0% 98%), hsl(260 18% 10%));
|
|
471
|
-
|
|
472
|
-
--surface: light-dark(hsl(0 0% 100%), hsl(263 17% 9%));
|
|
473
|
-
--surface-raised: light-dark(hsl(0 0% 100%), hsl(263 15% 12%));
|
|
474
|
-
--surface-subtle: light-dark(hsl(262 24% 96%), hsl(263 13% 16%));
|
|
475
|
-
--surface-solid: light-dark(hsl(261 16% 13%), hsl(0 0% 96%));
|
|
476
|
-
|
|
477
|
-
--text: light-dark(hsl(261 20% 13%), hsl(260 12% 95%));
|
|
478
|
-
--text-muted: light-dark(hsl(259 9% 42%), hsl(259 9% 67%));
|
|
479
|
-
--text-subtle: light-dark(hsl(258 7% 57%), hsl(258 7% 54%));
|
|
480
|
-
|
|
481
|
-
--border: light-dark(hsl(260 15% 88%), hsl(262 12% 24%));
|
|
482
|
-
--focus-ring: light-dark(hsl(258 7% 43% / 0.3), hsl(258 10% 72% / 0.28));
|
|
483
|
-
--hover-overlay: light-dark(hsl(260 20% 10% / 0.04), hsl(0 0% 100% / 0.06));
|
|
484
|
-
--shadow-color: light-dark(hsl(260 25% 10%), hsl(262 40% 2%));
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
@supports (color: color-mix(in oklch, red, white)) and (color: light-dark(red, blue)) {
|
|
489
|
-
:root,
|
|
490
|
-
[data-theme] {
|
|
491
|
-
--focus-ring: light-dark(
|
|
492
|
-
color-mix(in oklch, var(--focus) 30%, transparent),
|
|
493
|
-
color-mix(in oklch, var(--focus) 28%, transparent)
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
@media (prefers-contrast: more) {
|
|
499
|
-
:root,
|
|
500
|
-
/* The repeated attribute intentionally outranks single-attribute preset
|
|
501
|
-
palettes, including a preset stylesheet loaded after the core. */
|
|
502
|
-
[data-theme][data-theme] {
|
|
503
|
-
--border-width: 2px;
|
|
504
|
-
--focus-outline-offset: 3px;
|
|
505
|
-
--focus-ring-width: 4px;
|
|
506
|
-
--text-muted: var(--text);
|
|
507
|
-
--text-subtle: var(--text);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/*
|
|
512
|
-
* Base element styles for native HTML semantics.
|
|
513
|
-
*
|
|
514
|
-
* Sits between reset (browser defaults) and prose (rich content scope).
|
|
515
|
-
* These styles apply everywhere — cards, dialogs, heroes — not only in prose.
|
|
516
|
-
*
|
|
517
|
-
* Public hooks:
|
|
518
|
-
* --link
|
|
519
|
-
* --hr-space
|
|
520
|
-
* --font-width / --font-width-dense (see variants.css for the dense application)
|
|
521
|
-
*/
|
|
522
|
-
|
|
523
|
-
body {
|
|
524
|
-
background: var(--surface);
|
|
525
|
-
color: var(--text);
|
|
526
|
-
font-family: var(--font-sans);
|
|
527
|
-
font-stretch: var(--font-width);
|
|
528
|
-
line-height: var(--line-height);
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
/* Bare links inherit their ink by default, so nothing changes unless a theme
|
|
532
|
-
opts in by setting --link. Because this is an element selector, any component
|
|
533
|
-
rule that colors its own anchors still wins. */
|
|
534
|
-
a {
|
|
535
|
-
color: var(--link, inherit);
|
|
536
|
-
text-underline-offset: 0.18em;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
a:hover {
|
|
540
|
-
text-decoration-thickness: 0.12em;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
/* Global heading chrome: color (--heading or inherit), a tight line-height,
|
|
544
|
-
and progressive text-wrap. No font-size or font-weight is set globally —
|
|
545
|
-
components and .prose own the per-context scale. */
|
|
546
|
-
:where(h1, h2, h3, h4, h5, h6) {
|
|
547
|
-
color: var(--heading, inherit);
|
|
548
|
-
line-height: var(--line-height-tight);
|
|
549
|
-
text-wrap: balance;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
/* Compact heading groups. Structural only: display, gap, internal margin reset.
|
|
553
|
-
No font-size, color, or weight is applied here. */
|
|
554
|
-
hgroup {
|
|
555
|
-
display: grid;
|
|
556
|
-
gap: var(--hgroup-gap);
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
hgroup > * {
|
|
560
|
-
margin-block: 0;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
/* Section break.
|
|
564
|
-
|
|
565
|
-
margin-inline: 0 overrides the UA's `margin: 0.5em auto`. In block flow the
|
|
566
|
-
auto margins resolve to zero and nothing changes, but as a flex or grid item
|
|
567
|
-
an inline auto margin replaces the stretch — an <hr> in a .stack computed to
|
|
568
|
-
width 0 and vanished. .stack deliberately leaves the inline axis free, so
|
|
569
|
-
the reset belongs here.
|
|
570
|
-
|
|
571
|
-
--hr-space tunes the block rhythm on the instance: a rule closing a page
|
|
572
|
-
section and a rule separating two rows in a panel are the same element at
|
|
573
|
-
very different scales. It applies to an hr in document flow; inside .stack
|
|
574
|
-
or .prose the parent owns the spacing between siblings and wins. */
|
|
575
|
-
hr {
|
|
576
|
-
border: 0;
|
|
577
|
-
border-block-start: var(--border-width) solid var(--border);
|
|
578
|
-
margin-block: var(--hr-space, var(--space-60));
|
|
579
|
-
margin-inline: 0;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
code,
|
|
583
|
-
kbd,
|
|
584
|
-
samp,
|
|
585
|
-
pre {
|
|
586
|
-
font-family: var(--font-mono);
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
/* Overflow containment only. One unbreakable line — a URL, a minified token, a
|
|
590
|
-
wide ASCII table — otherwise widens the whole page, and an author cannot see
|
|
591
|
-
it coming from the markup. Background, padding and radius stay out: those
|
|
592
|
-
belong to .prose or to an application code-block component. */
|
|
593
|
-
pre {
|
|
594
|
-
overflow-x: auto;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
small {
|
|
598
|
-
font-size: 0.875em;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
::selection {
|
|
602
|
-
background: var(--selection-bg, var(--primary));
|
|
603
|
-
color: var(--selection-fg, var(--primary-fg));
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
/*
|
|
607
|
-
* Intent classes do not style elements directly.
|
|
608
|
-
* They only expose --intent, --intent-fg, and the optional per-intent soft
|
|
609
|
-
* foreground --intent-soft-fg, relayed from the per-role --*-soft-fg hooks.
|
|
610
|
-
* The default theme declares those hooks for its soft ink (see theme.css);
|
|
611
|
-
* a theme without them lets the relay resolve to an invalid value, so the soft
|
|
612
|
-
* recipe falls back to its --soft-fg-mix derivation and a theme opts in one
|
|
613
|
-
* role at a time (docs/pages/foundations/tokens.md). Components and variants
|
|
614
|
-
* decide how to consume them. Each component that consumes --intent,
|
|
615
|
-
* --intent-soft-fg or --ui-* opens a local intent boundary in a :where() block
|
|
616
|
-
* that resets exactly the tokens it consumes to initial. Parent intents do not
|
|
617
|
-
* leak into nested components. Local intent classes (.primary, .danger, etc.)
|
|
618
|
-
* still apply because they have higher specificity than :where().
|
|
619
|
-
*/
|
|
620
|
-
.primary {
|
|
621
|
-
--intent: var(--primary);
|
|
622
|
-
--intent-fg: var(--primary-fg);
|
|
623
|
-
--intent-soft-fg: var(--primary-soft-fg);
|
|
624
|
-
}
|
|
625
|
-
.secondary {
|
|
626
|
-
--intent: var(--secondary);
|
|
627
|
-
--intent-fg: var(--secondary-fg);
|
|
628
|
-
--intent-soft-fg: var(--secondary-soft-fg);
|
|
629
|
-
}
|
|
630
|
-
.success {
|
|
631
|
-
--intent: var(--success);
|
|
632
|
-
--intent-fg: var(--success-fg);
|
|
633
|
-
--intent-soft-fg: var(--success-soft-fg);
|
|
634
|
-
}
|
|
635
|
-
.warning {
|
|
636
|
-
--intent: var(--warning);
|
|
637
|
-
--intent-fg: var(--warning-fg);
|
|
638
|
-
--intent-soft-fg: var(--warning-soft-fg);
|
|
639
|
-
}
|
|
640
|
-
.danger {
|
|
641
|
-
--intent: var(--danger);
|
|
642
|
-
--intent-fg: var(--danger-fg);
|
|
643
|
-
--intent-soft-fg: var(--danger-soft-fg);
|
|
644
|
-
}
|
|
645
|
-
.neutral {
|
|
646
|
-
--intent: var(--neutral);
|
|
647
|
-
--intent-fg: var(--neutral-fg);
|
|
648
|
-
--intent-soft-fg: var(--neutral-soft-fg);
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
/* Paint an inherited or local intent on a simple foreground element without
|
|
652
|
-
turning the intent classes into general-purpose color utilities. */
|
|
653
|
-
.intent-color {
|
|
654
|
-
color: var(--intent);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
/*
|
|
658
|
-
* Cross-cutting variant token mapping.
|
|
659
|
-
*
|
|
660
|
-
* Variant scope: .solid/.soft/.outline/.surface set --ui-* for components that
|
|
661
|
-
* opt into shared variants. .inverted is a surface modifier, not a variant: it paints
|
|
662
|
-
* the element directly (background/color/border-color) so it works on any bare
|
|
663
|
-
* block, and remaps the shared --ui-* tokens so participating components
|
|
664
|
-
* (.card, .navbar, .alert, .badge) invert through their own rules. Loaded
|
|
665
|
-
* before components, so a component with its own explicit surface (e.g.
|
|
666
|
-
* .accordion) naturally keeps that surface — .inverted never forces a
|
|
667
|
-
* non-participant.
|
|
668
|
-
* Component-specific defaults (.btn solid-by-default, .badge
|
|
669
|
-
* soft-by-default, .alert soft-by-default) live with their components:
|
|
670
|
-
* components/button.css — .btn default solid + .btn.ghost + .btn.link
|
|
671
|
-
* components/badge.css — .badge default soft behind explicit --ui-*
|
|
672
|
-
* components/alert.css — .alert default soft behind explicit --ui-*
|
|
673
|
-
*
|
|
674
|
-
* .compact/.spacious set inherited density tokens: spacing rhythm and geometry
|
|
675
|
-
* only, plus a slight typographic width bonus. Density never changes font size
|
|
676
|
-
* or icon size. Controls consume --control-size; non-control components may
|
|
677
|
-
* consume --density-* when the context should affect their local rhythm.
|
|
678
|
-
* .compact applies --font-width-dense through font-stretch (a variable wdth
|
|
679
|
-
* axis when available, otherwise normal width-face matching); layout must
|
|
680
|
-
* never depend on the narrowing being available. .spacious restores
|
|
681
|
-
* --font-width, so a spacious island inside a compact context returns to the
|
|
682
|
-
* normal width exactly as it restores its own gap, spacing, and control size.
|
|
683
|
-
* Themes whose typeface has no useful width axis opt out with
|
|
684
|
-
* --font-width-dense: 100%.
|
|
685
|
-
*
|
|
686
|
-
* .sm/.lg are local size roles, not shared measurements. Shared control
|
|
687
|
-
* participants below map both typography and geometry; components with
|
|
688
|
-
* bespoke geometry (badge, spinner, avatar, rating, key, prose) own those
|
|
689
|
-
* mappings locally. Components without an explicit mapping do not participate.
|
|
690
|
-
*
|
|
691
|
-
* Intent list source of truth: intents.css. Components consume --intent* tokens
|
|
692
|
-
* instead of re-listing intent classes.
|
|
693
|
-
*/
|
|
694
|
-
.solid {
|
|
695
|
-
--ui-bg: var(--intent, var(--neutral));
|
|
696
|
-
--ui-fg: var(--intent-fg, var(--neutral-fg));
|
|
697
|
-
--ui-border: transparent;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
.soft {
|
|
701
|
-
--ui-bg: var(--surface-subtle);
|
|
702
|
-
--ui-border: var(--border);
|
|
703
|
-
--ui-fg: var(--intent, var(--text));
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
.outline {
|
|
707
|
-
--ui-bg: transparent;
|
|
708
|
-
--ui-fg: var(--intent, currentColor);
|
|
709
|
-
--ui-border: var(--intent, currentColor);
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
/* Neutral chrome, intent only in the ink: the page surface with the theme
|
|
713
|
-
border. .soft is the intent-tinted treatment (its baseline fallback uses
|
|
714
|
-
--surface-subtle) and .outline has no fill at all, so neither can express
|
|
715
|
-
it. Deliberately literal — no color-mix enhancement — so this stays the one
|
|
716
|
-
variant that reads exactly as the theme's own surface. */
|
|
717
|
-
.surface {
|
|
718
|
-
--ui-bg: var(--surface);
|
|
719
|
-
--ui-fg: var(--intent, var(--text));
|
|
720
|
-
--ui-border: var(--border);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
/* Inverse surface treatment. Paints the element directly (so it works on any
|
|
724
|
-
bare block) and remaps the shared --ui-* tokens so opt-in components
|
|
725
|
-
(.card, .navbar) invert through their own rules. Because this loads before
|
|
726
|
-
component base surfaces, components with their own explicit surface
|
|
727
|
-
(e.g. .accordion) keep it — participation is opt-in, never forced. --heading
|
|
728
|
-
is set so global headings (var(--heading, inherit)) do not inherit an outside
|
|
729
|
-
heading color onto the dark surface; surface-owning descendants derive
|
|
730
|
-
their contextual aliases from their own local surface. */
|
|
731
|
-
.inverted {
|
|
732
|
-
--ui-bg: var(--surface-solid);
|
|
733
|
-
--ui-fg: var(--surface);
|
|
734
|
-
--ui-border: transparent;
|
|
735
|
-
--ui-hover-bg: var(--surface-solid);
|
|
736
|
-
|
|
737
|
-
--heading: var(--surface);
|
|
738
|
-
--busy-overlay-bg: var(--surface-solid);
|
|
739
|
-
|
|
740
|
-
background: var(--ui-bg);
|
|
741
|
-
color: var(--ui-fg);
|
|
742
|
-
border-color: var(--ui-border);
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
.compact {
|
|
746
|
-
--gap: var(--space-20);
|
|
747
|
-
--density-space: var(--space-20);
|
|
748
|
-
--control-size: var(--control-size-sm);
|
|
749
|
-
font-stretch: var(--font-width-dense);
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
.spacious {
|
|
753
|
-
--gap: var(--space-50);
|
|
754
|
-
--density-space: var(--space-50);
|
|
755
|
-
--control-size: var(--control-size-lg);
|
|
756
|
-
font-stretch: var(--font-width);
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
/* Shared control scale. Wrappers are listed when their descendants form one
|
|
760
|
-
component (field, choice-card, floating-field, input-icon, join, pagination,
|
|
761
|
-
tabs, flyout). A local size declaration beats inherited density because it
|
|
762
|
-
is declared on the sized component itself. */
|
|
763
|
-
:where(
|
|
764
|
-
.btn,
|
|
765
|
-
.input,
|
|
766
|
-
.textarea,
|
|
767
|
-
.select,
|
|
768
|
-
.color,
|
|
769
|
-
.file,
|
|
770
|
-
.field,
|
|
771
|
-
.choice,
|
|
772
|
-
.choice-card,
|
|
773
|
-
.input-icon,
|
|
774
|
-
.floating-field,
|
|
775
|
-
.otp,
|
|
776
|
-
.join,
|
|
777
|
-
.pagination,
|
|
778
|
-
.tabs,
|
|
779
|
-
.tab,
|
|
780
|
-
.flyout
|
|
781
|
-
).sm {
|
|
782
|
-
--control-size: var(--control-size-sm);
|
|
783
|
-
--control-font-size: var(--font-size-sm);
|
|
784
|
-
--control-pad-x: var(--font-size-sm);
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
:where(
|
|
788
|
-
.btn,
|
|
789
|
-
.input,
|
|
790
|
-
.textarea,
|
|
791
|
-
.select,
|
|
792
|
-
.color,
|
|
793
|
-
.file,
|
|
794
|
-
.field,
|
|
795
|
-
.choice,
|
|
796
|
-
.choice-card,
|
|
797
|
-
.input-icon,
|
|
798
|
-
.floating-field,
|
|
799
|
-
.otp,
|
|
800
|
-
.join,
|
|
801
|
-
.pagination,
|
|
802
|
-
.tabs,
|
|
803
|
-
.tab,
|
|
804
|
-
.flyout
|
|
805
|
-
).lg {
|
|
806
|
-
--control-size: var(--control-size-lg);
|
|
807
|
-
--control-font-size: var(--font-size-lg);
|
|
808
|
-
--control-pad-x: var(--font-size-lg);
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
/* Modern color enhancement. Baseline variant values above remain usable
|
|
812
|
-
everywhere. Interactive states live in the components that consume them. */
|
|
813
|
-
@supports (color: color-mix(in oklch, red, white)) {
|
|
814
|
-
.soft {
|
|
815
|
-
/* @sync soft-recipe */
|
|
816
|
-
--ui-bg: color-mix(in oklab, var(--surface) var(--soft-bg-mix), var(--intent, var(--neutral)));
|
|
817
|
-
--ui-border: color-mix(
|
|
818
|
-
in oklab,
|
|
819
|
-
var(--surface) var(--soft-border-mix),
|
|
820
|
-
var(--intent, var(--neutral))
|
|
821
|
-
);
|
|
822
|
-
--ui-fg: var(
|
|
823
|
-
--intent-soft-fg,
|
|
824
|
-
color-mix(in oklab, var(--text), var(--intent, var(--text)) var(--soft-fg-mix))
|
|
825
|
-
);
|
|
826
|
-
/* @sync soft-recipe */
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
.inverted {
|
|
830
|
-
--ui-hover-bg: color-mix(in oklch, var(--surface-solid) 88%, black);
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
/* Interactive hover recipes live in the components that consume them:
|
|
835
|
-
components/button.css and components/badge.css. */
|
|
836
|
-
|
|
837
|
-
/* Focus baseline — a visible outline is the guaranteed focus affordance. The
|
|
838
|
-
:focus outline covers legacy browsers; modern browsers hide it for
|
|
839
|
-
pointer/mouse focus and interactive components layer their ring on top.
|
|
840
|
-
|
|
841
|
-
Invariant: an interactive component must never rely on box-shadow alone
|
|
842
|
-
for its keyboard focus indicator. Components that remove the native
|
|
843
|
-
outline on :focus-visible replace it with a transparent outline — invisible
|
|
844
|
-
normally, forced to the system focus color under forced-colors. */
|
|
845
|
-
:focus {
|
|
846
|
-
outline: var(--focus-outline);
|
|
847
|
-
outline-offset: var(--focus-outline-offset);
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
@supports selector(:focus-visible) {
|
|
851
|
-
:focus:not(:focus-visible) {
|
|
852
|
-
outline: none;
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
@media print {
|
|
857
|
-
:root,
|
|
858
|
-
/* The doubled attribute intentionally outranks single-attribute preset
|
|
859
|
-
islands (data-theme="dark", paired themes) loaded after the core. */
|
|
860
|
-
[data-theme][data-theme],
|
|
861
|
-
body {
|
|
862
|
-
/* Save some ink in dark mode */
|
|
863
|
-
color-scheme: light;
|
|
864
|
-
background: #fff;
|
|
865
|
-
color: #000;
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
h1,
|
|
869
|
-
h2,
|
|
870
|
-
h3,
|
|
871
|
-
h4,
|
|
872
|
-
h5,
|
|
873
|
-
h6 {
|
|
874
|
-
break-after: avoid;
|
|
875
|
-
}
|
|
876
|
-
}
|