actual-css 0.9.1 → 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 +116 -0
- package/README.md +67 -45
- package/components.json +33 -13
- package/dist/actual.full.css +607 -502
- 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/components/table.md +3 -2
- package/docs/pages/enhancements/overview.md +5 -0
- package/docs/pages/enhancements/tooltip.md +3 -1
- package/docs/pages/examples/overview.md +3 -1
- package/docs/pages/forms/choice-cards.md +39 -0
- package/docs/pages/forms/combobox.md +23 -11
- package/docs/pages/forms/input-policy.md +59 -0
- package/docs/pages/forms/joined-controls.md +2 -5
- package/docs/pages/forms/overview.md +6 -0
- package/docs/pages/forms/validation.md +37 -1
- package/docs/pages/foundations/theming.md +64 -18
- package/docs/pages/foundations/tokens.md +50 -13
- package/docs/pages/foundations/typography.md +10 -0
- package/docs/pages/guides/introduction.md +17 -24
- package/docs/pages/guides/modular-import.md +53 -6
- package/docs/pages/guides/progressive-enhancement.md +9 -5
- 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 +7 -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/base.css +2 -0
- package/src/css/core/focus.css +9 -3
- package/src/css/core/theme.css +25 -43
- package/src/css/core/tokens.css +3 -1
- package/src/css/core/variants.css +15 -36
- 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/form.css +6 -0
- 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 +36 -6
- package/dist/actual.css +0 -863
- package/dist/actual.js +0 -1
- package/dist/actual.min.css +0 -1
package/src/css/core/theme.css
CHANGED
|
@@ -72,10 +72,10 @@
|
|
|
72
72
|
|
|
73
73
|
/* Lines and focus */
|
|
74
74
|
--border: hsl(260 15% 88%);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
--focus
|
|
75
|
+
/* A primary-hued mid-tone on purpose, one value for both schemes: focus
|
|
76
|
+
lines must hold 3:1 against --surface and --surface-solid alike (page,
|
|
77
|
+
fields, a solid band), which rules out the primary itself. */
|
|
78
|
+
--focus: hsl(268 30% 55%);
|
|
79
79
|
--hover-overlay: hsl(260 20% 10% / 0.04);
|
|
80
80
|
|
|
81
81
|
/* Radius scale — themes may override the whole scale (e.g. [data-theme="square"]) */
|
|
@@ -98,7 +98,6 @@
|
|
|
98
98
|
--heading: var(--text);
|
|
99
99
|
--selection-bg: var(--primary);
|
|
100
100
|
--selection-fg: var(--primary-fg);
|
|
101
|
-
--focus-ring-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring);
|
|
102
101
|
}
|
|
103
102
|
|
|
104
103
|
/* Soft foreground hooks — the soft ink is the intent rebated toward the local
|
|
@@ -107,18 +106,17 @@
|
|
|
107
106
|
default theme: a custom property bakes its var() references where it is
|
|
108
107
|
declared, so these resolve against root/light/dark tokens on every boundary.
|
|
109
108
|
Presets and custom islands keep their --soft-fg-mix / raw-intent ink unless
|
|
110
|
-
they declare hooks themselves
|
|
111
|
-
|
|
112
|
-
:where(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
:root,
|
|
109
|
+
they declare hooks themselves.
|
|
110
|
+
|
|
111
|
+
Why :where(:root) and why the reset comes second: both rules match when a
|
|
112
|
+
custom theme sits on <html> itself. A bare :root is (0,1,0) and would
|
|
113
|
+
outrank the zero-specificity reset, so the same theme resolved a calibrated
|
|
114
|
+
Ink & Terra ink on <html data-theme="brand"> and the raw --soft-fg-mix ink
|
|
115
|
+
on <div data-theme="brand"> — one attribute, two soft palettes. Levelling
|
|
116
|
+
both rules to zero specificity makes source order the tiebreaker, so the
|
|
117
|
+
reset must stay below. Zero specificity is also what lets a theme declare
|
|
118
|
+
its own hooks (e.g. brutalist's 45% soft-fg-mix) and win at (0,1,0). */
|
|
119
|
+
:where(:root),
|
|
122
120
|
[data-theme="light"],
|
|
123
121
|
[data-theme="dark"] {
|
|
124
122
|
--secondary-soft-fg: color-mix(in oklab, var(--text) 10%, var(--secondary) 90%);
|
|
@@ -128,6 +126,15 @@
|
|
|
128
126
|
--neutral-soft-fg: color-mix(in oklab, var(--text) 20%, var(--neutral) 80%);
|
|
129
127
|
}
|
|
130
128
|
|
|
129
|
+
:where([data-theme]:not([data-theme="light"]):not([data-theme="dark"])) {
|
|
130
|
+
--primary-soft-fg: initial;
|
|
131
|
+
--secondary-soft-fg: initial;
|
|
132
|
+
--success-soft-fg: initial;
|
|
133
|
+
--warning-soft-fg: initial;
|
|
134
|
+
--danger-soft-fg: initial;
|
|
135
|
+
--neutral-soft-fg: initial;
|
|
136
|
+
}
|
|
137
|
+
|
|
131
138
|
/* Native platform controls (checkbox, radio, range, progress) that are not
|
|
132
139
|
restyled by a component class still follow the theme accent. Declared on
|
|
133
140
|
every island: accent-color inherits as a computed value, so islands must
|
|
@@ -149,20 +156,6 @@
|
|
|
149
156
|
--tone-fg: var(--text);
|
|
150
157
|
}
|
|
151
158
|
|
|
152
|
-
/* Focus ring derivation — color-mix tier. Runs whether or not light-dark()
|
|
153
|
-
is also supported, so it covers the color-mix-without-light-dark browser
|
|
154
|
-
tier on its own; the light-dark block below refines it further with the
|
|
155
|
-
light/dark alpha split once both features are available. Declared on every
|
|
156
|
-
[data-theme] island (not just the built-in light/dark pair) so custom
|
|
157
|
-
islands recolor the ring for free; a preset that re-declares --focus-ring
|
|
158
|
-
later in source order intentionally pins its own ring instead. */
|
|
159
|
-
@supports (color: color-mix(in oklch, red, white)) {
|
|
160
|
-
:root,
|
|
161
|
-
[data-theme] {
|
|
162
|
-
--focus-ring: color-mix(in oklch, var(--focus) 30%, transparent);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
159
|
/* Auto dark mode via light-dark(). Resolves based on the element's color-scheme:
|
|
167
160
|
:root (light dark) follows the OS, [data-theme="light"] forces light,
|
|
168
161
|
[data-theme="dark"] forces dark by overriding color-scheme.
|
|
@@ -199,22 +192,11 @@
|
|
|
199
192
|
--text-subtle: light-dark(hsl(258 7% 57%), hsl(258 7% 54%));
|
|
200
193
|
|
|
201
194
|
--border: light-dark(hsl(260 15% 88%), hsl(262 12% 24%));
|
|
202
|
-
--focus-ring: light-dark(hsl(258 7% 43% / 0.3), hsl(258 10% 72% / 0.28));
|
|
203
195
|
--hover-overlay: light-dark(hsl(260 20% 10% / 0.04), hsl(0 0% 100% / 0.06));
|
|
204
196
|
--shadow-color: light-dark(hsl(260 25% 10%), hsl(262 40% 2%));
|
|
205
197
|
}
|
|
206
198
|
}
|
|
207
199
|
|
|
208
|
-
@supports (color: color-mix(in oklch, red, white)) and (color: light-dark(red, blue)) {
|
|
209
|
-
:root,
|
|
210
|
-
[data-theme] {
|
|
211
|
-
--focus-ring: light-dark(
|
|
212
|
-
color-mix(in oklch, var(--focus) 30%, transparent),
|
|
213
|
-
color-mix(in oklch, var(--focus) 28%, transparent)
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
200
|
@media (prefers-contrast: more) {
|
|
219
201
|
:root,
|
|
220
202
|
/* The repeated attribute intentionally outranks single-attribute preset
|
|
@@ -222,7 +204,7 @@
|
|
|
222
204
|
[data-theme][data-theme] {
|
|
223
205
|
--border-width: 2px;
|
|
224
206
|
--focus-outline-offset: 3px;
|
|
225
|
-
--focus-ring-width:
|
|
207
|
+
--focus-ring-width: 3px;
|
|
226
208
|
--text-muted: var(--text);
|
|
227
209
|
--text-subtle: var(--text);
|
|
228
210
|
}
|
package/src/css/core/tokens.css
CHANGED
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
/* Typography */
|
|
20
20
|
--font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
21
21
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
22
|
+
--font-width: 100%;
|
|
23
|
+
--font-width-dense: 95%;
|
|
22
24
|
--line-height: 1.5;
|
|
23
25
|
--line-height-tight: 1.25;
|
|
24
26
|
--line-height-relaxed: 1.75;
|
|
@@ -80,7 +82,7 @@
|
|
|
80
82
|
--focus-outline-color: currentColor;
|
|
81
83
|
--focus-outline: calc(var(--border-width) * 2) solid var(--focus-outline-color);
|
|
82
84
|
--focus-outline-offset: 2px;
|
|
83
|
-
--focus-ring-width:
|
|
85
|
+
--focus-ring-width: 2px;
|
|
84
86
|
|
|
85
87
|
/* Status dot positioning — used by .avatar > .badge:empty. The offset is
|
|
86
88
|
the circle-aware inset (1 - 1/√2) / 2 ≈ 14.65%; the ring surrounds the
|
|
@@ -2,13 +2,9 @@
|
|
|
2
2
|
* Cross-cutting variant token mapping.
|
|
3
3
|
*
|
|
4
4
|
* Variant scope: .solid/.soft/.outline/.surface set --ui-* for components that
|
|
5
|
-
* opt into shared variants.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* (.card, .navbar, .alert, .badge) invert through their own rules. Loaded
|
|
9
|
-
* before components, so a component with its own explicit surface (e.g.
|
|
10
|
-
* .accordion) naturally keeps that surface — .inverted never forces a
|
|
11
|
-
* non-participant.
|
|
5
|
+
* opt into shared variants. A subtree that needs another palette or scheme is
|
|
6
|
+
* a data-theme island, never a variant: only an island recomputes every token
|
|
7
|
+
* (fields, states, intents), where a remap of a few --ui-* tokens cannot.
|
|
12
8
|
* Component-specific defaults (.btn solid-by-default, .badge
|
|
13
9
|
* soft-by-default, .alert soft-by-default) live with their components:
|
|
14
10
|
* components/button.css — .btn default solid + .btn.ghost + .btn.link
|
|
@@ -16,9 +12,16 @@
|
|
|
16
12
|
* components/alert.css — .alert default soft behind explicit --ui-*
|
|
17
13
|
*
|
|
18
14
|
* .compact/.spacious set inherited density tokens: spacing rhythm and geometry
|
|
19
|
-
* only. Density never changes
|
|
20
|
-
* --control-size; non-control components may
|
|
21
|
-
* context should affect their local rhythm.
|
|
15
|
+
* only, plus a slight typographic width bonus. Density never changes font size
|
|
16
|
+
* or icon size. Controls consume --control-size; non-control components may
|
|
17
|
+
* consume --density-* when the context should affect their local rhythm.
|
|
18
|
+
* .compact applies --font-width-dense through font-stretch (a variable wdth
|
|
19
|
+
* axis when available, otherwise normal width-face matching); layout must
|
|
20
|
+
* never depend on the narrowing being available. .spacious restores
|
|
21
|
+
* --font-width, so a spacious island inside a compact context returns to the
|
|
22
|
+
* normal width exactly as it restores its own gap, spacing, and control size.
|
|
23
|
+
* Themes whose typeface has no useful width axis opt out with
|
|
24
|
+
* --font-width-dense: 100%.
|
|
22
25
|
*
|
|
23
26
|
* .sm/.lg are local size roles, not shared measurements. Shared control
|
|
24
27
|
* participants below map both typography and geometry; components with
|
|
@@ -57,38 +60,18 @@
|
|
|
57
60
|
--ui-border: var(--border);
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
/* Inverse surface treatment. Paints the element directly (so it works on any
|
|
61
|
-
bare block) and remaps the shared --ui-* tokens so opt-in components
|
|
62
|
-
(.card, .navbar) invert through their own rules. Because this loads before
|
|
63
|
-
component base surfaces, components with their own explicit surface
|
|
64
|
-
(e.g. .accordion) keep it — participation is opt-in, never forced. --heading
|
|
65
|
-
is set so global headings (var(--heading, inherit)) do not inherit an outside
|
|
66
|
-
heading color onto the dark surface; surface-owning descendants derive
|
|
67
|
-
their contextual aliases from their own local surface. */
|
|
68
|
-
.inverted {
|
|
69
|
-
--ui-bg: var(--surface-solid);
|
|
70
|
-
--ui-fg: var(--surface);
|
|
71
|
-
--ui-border: transparent;
|
|
72
|
-
--ui-hover-bg: var(--surface-solid);
|
|
73
|
-
|
|
74
|
-
--heading: var(--surface);
|
|
75
|
-
--busy-overlay-bg: var(--surface-solid);
|
|
76
|
-
|
|
77
|
-
background: var(--ui-bg);
|
|
78
|
-
color: var(--ui-fg);
|
|
79
|
-
border-color: var(--ui-border);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
63
|
.compact {
|
|
83
64
|
--gap: var(--space-20);
|
|
84
65
|
--density-space: var(--space-20);
|
|
85
66
|
--control-size: var(--control-size-sm);
|
|
67
|
+
font-stretch: var(--font-width-dense);
|
|
86
68
|
}
|
|
87
69
|
|
|
88
70
|
.spacious {
|
|
89
71
|
--gap: var(--space-50);
|
|
90
72
|
--density-space: var(--space-50);
|
|
91
73
|
--control-size: var(--control-size-lg);
|
|
74
|
+
font-stretch: var(--font-width);
|
|
92
75
|
}
|
|
93
76
|
|
|
94
77
|
/* Shared control scale. Wrappers are listed when their descendants form one
|
|
@@ -160,10 +143,6 @@
|
|
|
160
143
|
);
|
|
161
144
|
/* @sync soft-recipe */
|
|
162
145
|
}
|
|
163
|
-
|
|
164
|
-
.inverted {
|
|
165
|
-
--ui-hover-bg: color-mix(in oklch, var(--surface-solid) 88%, black);
|
|
166
|
-
}
|
|
167
146
|
}
|
|
168
147
|
|
|
169
148
|
/* Interactive hover recipes live in the components that consume them:
|
|
@@ -13,12 +13,18 @@
|
|
|
13
13
|
* overflow: hidden will clip it. Pair with grid or flex utilities for group
|
|
14
14
|
* layout.
|
|
15
15
|
*
|
|
16
|
+
* .chip is the compact pill form (filter chips, selectable tags): control
|
|
17
|
+
* height, and a leading dot that turns into the check in a fixed slot, so
|
|
18
|
+
* checking never moves the label.
|
|
19
|
+
*
|
|
16
20
|
* Public hooks:
|
|
17
21
|
* --choice-card-pad
|
|
18
22
|
* --choice-card-radius
|
|
19
23
|
* --choice-card-border
|
|
20
24
|
* --choice-card-bg
|
|
21
25
|
* --choice-card-check-size
|
|
26
|
+
* --choice-chip-dot (read-only, on .chip ::before: the check-slot centre as a
|
|
27
|
+
* circle() position, for a theme that grows the dot into a fill)
|
|
22
28
|
*/
|
|
23
29
|
:where(.choice-card) {
|
|
24
30
|
--intent: initial;
|
|
@@ -68,8 +74,8 @@
|
|
|
68
74
|
|
|
69
75
|
/* Focus ring on the label follows the hidden input's focus. */
|
|
70
76
|
.choice-card:has(:focus-visible) {
|
|
71
|
-
outline:
|
|
72
|
-
|
|
77
|
+
outline: var(--focus-ring-width) solid var(--focus);
|
|
78
|
+
outline-offset: var(--focus-outline-offset);
|
|
73
79
|
}
|
|
74
80
|
|
|
75
81
|
.choice-card:has(:disabled) {
|
|
@@ -114,11 +120,13 @@
|
|
|
114
120
|
z-index: 1;
|
|
115
121
|
}
|
|
116
122
|
|
|
117
|
-
|
|
123
|
+
/* :where() keeps the shape at single-class weight, so .chip can reshape the
|
|
124
|
+
layer without out-arguing the input-type test. */
|
|
125
|
+
.choice-card:where(:has(input[type="radio"]))::before {
|
|
118
126
|
border-radius: var(--radius-full);
|
|
119
127
|
}
|
|
120
128
|
|
|
121
|
-
.choice-card:has(input[type="checkbox"])::before {
|
|
129
|
+
.choice-card:where(:has(input[type="checkbox"]))::before {
|
|
122
130
|
border-radius: var(--radius-sm);
|
|
123
131
|
}
|
|
124
132
|
|
|
@@ -135,6 +143,54 @@
|
|
|
135
143
|
transform: scale(1);
|
|
136
144
|
}
|
|
137
145
|
|
|
146
|
+
/* Chip — the rules above stay in force; this section only relocates the
|
|
147
|
+
indicator. Must follow them: several ties are won by source order. */
|
|
148
|
+
.choice-card.chip {
|
|
149
|
+
--choice-card-pad: 0 var(--control-pad-x);
|
|
150
|
+
--choice-card-radius: var(--radius-full);
|
|
151
|
+
--choice-card-check-size: 1em;
|
|
152
|
+
|
|
153
|
+
display: inline-flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
min-block-size: var(--control-size);
|
|
156
|
+
line-height: 1.2;
|
|
157
|
+
/* The dot layer sits under the label at z-index -1. */
|
|
158
|
+
isolation: isolate;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* ::before is a full-bleed layer clipped to a dot centred on the check slot,
|
|
162
|
+
so a theme can grow the same clip into a selected fill. The chip itself
|
|
163
|
+
must not clip (its focus ring is an outline), hence the inherited radius. */
|
|
164
|
+
.choice-card.chip::before {
|
|
165
|
+
--choice-chip-dot: calc(var(--control-pad-x) + var(--choice-card-check-size) / 2) 50%;
|
|
166
|
+
|
|
167
|
+
inset: 0;
|
|
168
|
+
inline-size: auto;
|
|
169
|
+
block-size: auto;
|
|
170
|
+
border-radius: inherit;
|
|
171
|
+
box-shadow: none;
|
|
172
|
+
background: var(--text-subtle);
|
|
173
|
+
transform: none;
|
|
174
|
+
clip-path: circle(0.1875em at var(--choice-chip-dot));
|
|
175
|
+
z-index: -1;
|
|
176
|
+
transition: clip-path var(--duration);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.choice-card.chip:has(:checked)::before {
|
|
180
|
+
clip-path: circle(0 at var(--choice-chip-dot));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* The check keeps the card's scale-in, in flow ahead of the label. */
|
|
184
|
+
.choice-card.chip::after {
|
|
185
|
+
position: static;
|
|
186
|
+
order: -1;
|
|
187
|
+
flex: none;
|
|
188
|
+
margin-inline-end: 0.375em;
|
|
189
|
+
background: var(--intent, var(--state-selected));
|
|
190
|
+
-webkit-mask-size: contain;
|
|
191
|
+
mask-size: contain;
|
|
192
|
+
}
|
|
193
|
+
|
|
138
194
|
/* The native input is hidden and the checked marker is custom-drawn: without
|
|
139
195
|
this repair, forced colors pushes disc and check to the same system color
|
|
140
196
|
and the state disappears. Highlight/Canvas/HighlightText are preserved as-is,
|
|
@@ -149,4 +205,18 @@
|
|
|
149
205
|
.choice-card:has(:checked)::after {
|
|
150
206
|
background: HighlightText;
|
|
151
207
|
}
|
|
208
|
+
|
|
209
|
+
/* A chip has no disc behind its check: the check carries the state alone,
|
|
210
|
+
and dropping the checked layer also drops any theme fill. */
|
|
211
|
+
.choice-card.chip::before {
|
|
212
|
+
background: CanvasText;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.choice-card.chip:has(:checked)::before {
|
|
216
|
+
content: none;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.choice-card.chip:has(:checked)::after {
|
|
220
|
+
background: CanvasText;
|
|
221
|
+
}
|
|
152
222
|
}
|
package/src/css/forms/choice.css
CHANGED
|
@@ -228,9 +228,8 @@
|
|
|
228
228
|
|
|
229
229
|
.check:focus-visible,
|
|
230
230
|
.radio:focus-visible {
|
|
231
|
-
--
|
|
232
|
-
outline:
|
|
233
|
-
box-shadow: var(--focus-ring-shadow);
|
|
231
|
+
outline: var(--focus-ring-width) solid var(--focus);
|
|
232
|
+
outline-offset: var(--focus-outline-offset);
|
|
234
233
|
}
|
|
235
234
|
|
|
236
235
|
.check:disabled,
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
* --control-cursor
|
|
25
25
|
* --control-border-block-end-color
|
|
26
26
|
* --control-border-block-end-width
|
|
27
|
+
* --control-hover-border (input and textarea)
|
|
28
|
+
* --control-hover-bg (select)
|
|
27
29
|
*/
|
|
28
30
|
.input,
|
|
29
31
|
.textarea,
|
|
@@ -68,19 +70,24 @@
|
|
|
68
70
|
|
|
69
71
|
.input:hover:not(:disabled),
|
|
70
72
|
.textarea:hover:not(:disabled) {
|
|
71
|
-
--control-border: var(--text-subtle);
|
|
73
|
+
--control-border: var(--control-hover-border, var(--text-subtle));
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
.select:hover:not(:disabled) {
|
|
75
|
-
--control-bg: var(--surface-subtle);
|
|
77
|
+
--control-bg: var(--control-hover-bg, var(--surface-subtle));
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
/* Field focus is an inset outline: offset = -width keeps every pixel inside
|
|
81
|
+
the control's border box (any smaller offset bleeds outward), so it covers
|
|
82
|
+
the border, never touches a .join neighbour or a scrollport edge, and needs
|
|
83
|
+
no group treatment. Width follows --focus-ring-width, including its
|
|
84
|
+
prefers-contrast step. Border = shape and validation, outline = focus. */
|
|
85
|
+
.input:focus-visible:not(:disabled),
|
|
86
|
+
.textarea:focus-visible:not(:disabled),
|
|
87
|
+
.select:focus-visible:not(:disabled) {
|
|
88
|
+
outline: var(--focus-ring-width) solid var(--form-invalid-border, var(--focus));
|
|
89
|
+
outline-offset: calc(var(--focus-ring-width) * -1);
|
|
90
|
+
box-shadow: none;
|
|
84
91
|
}
|
|
85
92
|
|
|
86
93
|
.input[readonly],
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
* Arbitrary menu and popover behavior belongs elsewhere.
|
|
10
10
|
*
|
|
11
11
|
* Internal:
|
|
12
|
-
* --control-border
|
|
13
12
|
* --control-bg
|
|
14
13
|
*/
|
|
15
14
|
/* This enhancement is excluded from forced-colors, where the native/shared
|
|
@@ -34,11 +33,12 @@
|
|
|
34
33
|
box-shadow: var(--shadow-popout);
|
|
35
34
|
}
|
|
36
35
|
|
|
36
|
+
/* Same inset outline as a focused field (control.css). */
|
|
37
37
|
&:open {
|
|
38
|
-
--control-border: var(--border);
|
|
39
38
|
--control-bg: var(--surface-raised);
|
|
40
|
-
outline:
|
|
41
|
-
|
|
39
|
+
outline: var(--focus-ring-width) solid var(--form-invalid-border, var(--focus));
|
|
40
|
+
outline-offset: calc(var(--focus-ring-width) * -1);
|
|
41
|
+
box-shadow: none;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
&::picker-icon {
|
package/src/css/forms/form.css
CHANGED
|
@@ -18,6 +18,12 @@
|
|
|
18
18
|
*/
|
|
19
19
|
.field {
|
|
20
20
|
display: grid;
|
|
21
|
+
|
|
22
|
+
/* why: a field stretched by a taller row sibling — the classic case is a
|
|
23
|
+
neighbour revealing its .field-error — would otherwise hand the extra
|
|
24
|
+
block size to its auto rows and inflate the control. Rows keep their
|
|
25
|
+
natural height; the slack stays at the end. */
|
|
26
|
+
align-content: start;
|
|
21
27
|
gap: var(--space-10);
|
|
22
28
|
color: var(--text);
|
|
23
29
|
font-size: var(--control-font-size);
|
package/src/css/forms/native.css
CHANGED
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
.file:focus-visible {
|
|
47
|
-
outline:
|
|
48
|
-
|
|
47
|
+
outline: var(--focus-ring-width) solid var(--focus);
|
|
48
|
+
outline-offset: var(--focus-outline-offset);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/* Native color picker — keep the browser's dialog and selected-color UI. The
|
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
.color:focus-visible {
|
|
84
|
-
outline:
|
|
85
|
-
|
|
84
|
+
outline: var(--focus-ring-width) solid var(--focus);
|
|
85
|
+
outline-offset: var(--focus-outline-offset);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
@media (forced-colors: active) {
|
package/src/css/forms/range.css
CHANGED
|
@@ -75,20 +75,22 @@
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
/*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
78
|
+
* A thumb pseudo-element takes no reliable outline, so its ring is a solid
|
|
79
|
+
* --focus shadow (the transparent outline above covers forced colors). The
|
|
80
|
+
* 1px surface-coloured separator keeps the ring visually distinct from the
|
|
81
|
+
* small, coloured thumb — unlike a full-size input where the same gap would
|
|
82
|
+
* read as an unwanted void.
|
|
81
83
|
*/
|
|
82
84
|
.range:focus-visible::-webkit-slider-thumb {
|
|
83
85
|
box-shadow:
|
|
84
86
|
0 0 0 1px var(--surface),
|
|
85
|
-
0 0 0 calc(1px + var(--focus-ring-width)) var(--focus
|
|
87
|
+
0 0 0 calc(1px + var(--focus-ring-width)) var(--focus);
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
.range:focus-visible::-moz-range-thumb {
|
|
89
91
|
box-shadow:
|
|
90
92
|
0 0 0 1px var(--surface),
|
|
91
|
-
0 0 0 calc(1px + var(--focus-ring-width)) var(--focus
|
|
93
|
+
0 0 0 calc(1px + var(--focus-ring-width)) var(--focus);
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
.range:disabled {
|
|
@@ -98,20 +100,33 @@
|
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
/* Forced colors: the track must stay a visible line against the page (Canvas
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
would merge with it) and the thumb needs a real boundary to remain
|
|
104
|
+
distinguishable from the track. Disabled keeps GrayText via :disabled.
|
|
105
|
+
Focus stays on the transparent outline of :focus-visible. Track and thumb
|
|
106
|
+
fills are written on the properties, not only the hooks: an application
|
|
107
|
+
hook override at the same specificity loads after this file and would win
|
|
108
|
+
the variable, leaving a Canvas-remapped fill. */
|
|
104
109
|
@media (forced-colors: active) {
|
|
105
110
|
.range {
|
|
106
111
|
--range-track-bg: CanvasText;
|
|
107
112
|
--range-thumb-bg: ButtonFace;
|
|
108
113
|
}
|
|
109
114
|
|
|
115
|
+
.range::-webkit-slider-runnable-track {
|
|
116
|
+
background: CanvasText;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.range::-moz-range-track {
|
|
120
|
+
background: CanvasText;
|
|
121
|
+
}
|
|
122
|
+
|
|
110
123
|
.range::-webkit-slider-thumb {
|
|
124
|
+
background: ButtonFace;
|
|
111
125
|
border: var(--border-width) solid ButtonText;
|
|
112
126
|
}
|
|
113
127
|
|
|
114
128
|
.range::-moz-range-thumb {
|
|
129
|
+
background: ButtonFace;
|
|
115
130
|
border: var(--border-width) solid ButtonText;
|
|
116
131
|
}
|
|
117
132
|
}
|
package/src/css/forms/switch.css
CHANGED
|
@@ -128,9 +128,8 @@
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
.switch:focus-visible {
|
|
131
|
-
--
|
|
132
|
-
outline:
|
|
133
|
-
box-shadow: var(--focus-ring-shadow);
|
|
131
|
+
outline: var(--focus-ring-width) solid var(--focus);
|
|
132
|
+
outline-offset: var(--focus-outline-offset);
|
|
134
133
|
}
|
|
135
134
|
|
|
136
135
|
.switch:disabled {
|