actual-css 0.9.2 → 0.11.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 +169 -1
- package/README.md +65 -44
- package/components.json +35 -14
- package/dist/actual.full.css +877 -583
- 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/enhancement-contract.md +16 -7
- package/docs/design-notes/forced-colors.md +12 -1
- package/docs/design-notes/naming.md +1 -1
- package/docs/design-notes/platform-alignment.md +8 -4
- package/docs/design-notes/rich-text.md +61 -0
- package/docs/design-notes/steps.md +2 -2
- package/docs/design-notes/surface-reparenting.md +8 -9
- package/docs/navigation.json +4 -0
- package/docs/pages/components/accordion.md +55 -1
- package/docs/pages/components/alert.md +9 -15
- package/docs/pages/components/aura.md +12 -13
- package/docs/pages/components/badge.md +16 -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/chat.md +5 -0
- package/docs/pages/components/close.md +48 -0
- package/docs/pages/components/data-list.md +26 -1
- package/docs/pages/components/dialog.md +21 -18
- package/docs/pages/components/drawer.md +9 -9
- package/docs/pages/components/navbar.md +5 -5
- package/docs/pages/components/pagination.md +15 -15
- package/docs/pages/components/shimmer.md +57 -0
- package/docs/pages/components/skeleton.md +2 -1
- package/docs/pages/components/spinner.md +14 -9
- package/docs/pages/components/table.md +13 -0
- package/docs/pages/enhancements/flyout.md +2 -0
- package/docs/pages/enhancements/overview.md +5 -0
- package/docs/pages/enhancements/tabs.md +4 -0
- package/docs/pages/enhancements/tooltip.md +3 -1
- package/docs/pages/examples/overview.md +2 -1
- package/docs/pages/forms/choice-cards.md +116 -4
- package/docs/pages/forms/combobox.md +39 -12
- package/docs/pages/forms/joined-controls.md +2 -5
- package/docs/pages/forms/overview.md +10 -1
- package/docs/pages/forms/range.md +110 -5
- package/docs/pages/forms/rich-text.md +110 -0
- package/docs/pages/forms/validation.md +5 -1
- package/docs/pages/foundations/theming.md +110 -18
- package/docs/pages/foundations/tokens.md +41 -13
- package/docs/pages/guides/bootstrap.md +2 -1
- package/docs/pages/guides/introduction.md +15 -23
- package/docs/pages/guides/modular-import.md +54 -7
- package/docs/pages/guides/progressive-enhancement.md +19 -17
- package/docs/pages/guides/tailwind.md +21 -7
- 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 +25 -4
- package/package.json +10 -6
- package/reserved-classes.json +6 -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/avatar.css +1 -1
- 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 +5 -1
- 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/meter.css +1 -1
- package/src/css/components/modal.css +10 -57
- package/src/css/components/navbar.css +27 -0
- package/src/css/components/pagination.css +3 -2
- package/src/css/components/rating.css +1 -1
- package/src/css/components/status-bar.css +2 -2
- package/src/css/components/steps.css +3 -3
- package/src/css/components/tab.css +3 -3
- package/src/css/components/table.css +6 -0
- package/src/css/components/tooltip.css +1 -1
- package/src/css/core/focus.css +19 -3
- package/src/css/core/theme.css +33 -43
- package/src/css/core/tokens.css +9 -5
- package/src/css/core/variants.css +5 -34
- package/src/css/effects/aura.css +51 -33
- package/src/css/effects/index.css +1 -0
- package/src/css/effects/shimmer.css +57 -0
- package/src/css/forms/choice-card.css +142 -24
- package/src/css/forms/choice.css +2 -3
- package/src/css/forms/control.css +17 -10
- package/src/css/forms/custom-select.css +4 -4
- package/src/css/forms/native.css +4 -4
- package/src/css/forms/otp.css +9 -1
- package/src/css/forms/range.css +72 -14
- 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/command.js +61 -32
- package/src/js/dialog.js +103 -47
- package/src/js/dismiss.js +1 -2
- package/src/js/enhance.js +91 -126
- 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/full.js +1 -0
- package/src/js/keys.js +14 -0
- package/src/js/menu.js +2 -1
- package/src/js/password.js +1 -8
- package/src/js/range.js +151 -0
- package/src/js/scrollspy.js +4 -5
- package/src/js/status.js +21 -10
- package/src/js/surface.js +12 -8
- package/src/js/tab.js +2 -1
- package/src/js/tooltip.js +29 -2
- package/src/js/validation.js +8 -15
- package/dist/actual.css +0 -876
- package/dist/actual.js +0 -1
- package/dist/actual.min.css +0 -1
|
@@ -5,20 +5,29 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Each .choice-card is a <label> wrapping a native radio or checkbox. When
|
|
7
7
|
* :has() is supported the input is visually hidden and the card mirrors its
|
|
8
|
-
* state:
|
|
9
|
-
*
|
|
10
|
-
* native input stays visible
|
|
8
|
+
* state: an indicator on the leading edge (a ring for radio, a box for
|
|
9
|
+
* checkbox) that fills with a check, checked border and tint, focus ring,
|
|
10
|
+
* disabled dimming. Without :has() the native input stays visible in the
|
|
11
|
+
* indicator's slot, so selection state never disappears.
|
|
11
12
|
*
|
|
12
|
-
* The
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* The card is a two-column grid: the indicator in the first column, level
|
|
14
|
+
* with the first row, and each child element of the label on its own row in
|
|
15
|
+
* the second. Wrap loose text in an element. A corner badge for tiles is a
|
|
16
|
+
* documented recipe on these same layers, not a variant.
|
|
17
|
+
*
|
|
18
|
+
* .chip is the compact pill form (filter chips, selectable tags): control
|
|
19
|
+
* height, and a leading dot that turns into the check in a fixed slot, so
|
|
20
|
+
* checking never moves the label.
|
|
15
21
|
*
|
|
16
22
|
* Public hooks:
|
|
17
23
|
* --choice-card-pad
|
|
24
|
+
* --choice-card-gap
|
|
18
25
|
* --choice-card-radius
|
|
19
26
|
* --choice-card-border
|
|
20
27
|
* --choice-card-bg
|
|
21
28
|
* --choice-card-check-size
|
|
29
|
+
* --choice-chip-dot (read-only, on .chip ::before: the check-slot centre as a
|
|
30
|
+
* circle() position, for a theme that grows the dot into a fill)
|
|
22
31
|
*/
|
|
23
32
|
:where(.choice-card) {
|
|
24
33
|
--intent: initial;
|
|
@@ -27,19 +36,25 @@
|
|
|
27
36
|
|
|
28
37
|
.choice-card {
|
|
29
38
|
--choice-card-pad: var(--density-space, var(--space-40));
|
|
39
|
+
--choice-card-gap: var(--space-30);
|
|
30
40
|
--choice-card-radius: var(--radius);
|
|
31
41
|
--choice-card-border: var(--border);
|
|
32
42
|
--choice-card-bg: var(--surface);
|
|
33
|
-
--choice-card-check-size: 1.
|
|
43
|
+
--choice-card-check-size: 1.25em;
|
|
34
44
|
|
|
35
45
|
position: relative;
|
|
36
|
-
display:
|
|
46
|
+
display: grid;
|
|
47
|
+
grid-template-columns: var(--choice-card-check-size) minmax(0, 1fr);
|
|
48
|
+
/* why: a card stretched by a taller grid neighbour keeps its rows at the
|
|
49
|
+
top instead of spreading them. */
|
|
50
|
+
align-content: start;
|
|
51
|
+
column-gap: var(--choice-card-gap);
|
|
37
52
|
padding: var(--choice-card-pad);
|
|
38
53
|
border: var(--border-width) solid var(--form-invalid-border, var(--choice-card-border));
|
|
39
54
|
border-radius: var(--choice-card-radius);
|
|
40
55
|
background: var(--choice-card-bg);
|
|
41
56
|
color: var(--text);
|
|
42
|
-
/* Consumed so .sm/.lg reach the card; the
|
|
57
|
+
/* Consumed so .sm/.lg reach the card; the indicator's em sizing follows. */
|
|
43
58
|
font-size: var(--control-font-size);
|
|
44
59
|
cursor: pointer;
|
|
45
60
|
transition:
|
|
@@ -48,6 +63,18 @@
|
|
|
48
63
|
box-shadow var(--duration);
|
|
49
64
|
}
|
|
50
65
|
|
|
66
|
+
.choice-card > * {
|
|
67
|
+
grid-column: 2;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* The input owns the indicator's cell: visible there without :has(), hidden
|
|
71
|
+
in it with :has(). */
|
|
72
|
+
.choice-card > :is(input[type="radio"], input[type="checkbox"]) {
|
|
73
|
+
grid-area: 1 / 1;
|
|
74
|
+
place-self: center;
|
|
75
|
+
margin: 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
51
78
|
/* The native input is visually hidden but stays focusable and participates in
|
|
52
79
|
form submission and keyboard navigation (sr-only pattern). Guarded: without
|
|
53
80
|
:has() the card cannot mirror the input's state, so the visible native
|
|
@@ -68,8 +95,8 @@
|
|
|
68
95
|
|
|
69
96
|
/* Focus ring on the label follows the hidden input's focus. */
|
|
70
97
|
.choice-card:has(:focus-visible) {
|
|
71
|
-
outline:
|
|
72
|
-
|
|
98
|
+
outline: var(--focus-ring-width) solid var(--focus);
|
|
99
|
+
outline-offset: var(--focus-outline-offset);
|
|
73
100
|
}
|
|
74
101
|
|
|
75
102
|
.choice-card:has(:disabled) {
|
|
@@ -93,32 +120,41 @@
|
|
|
93
120
|
}
|
|
94
121
|
}
|
|
95
122
|
|
|
96
|
-
/*
|
|
97
|
-
the
|
|
123
|
+
/* Indicator: ::before is the ring or box, ::after the check. Both share the
|
|
124
|
+
input's cell, centred on the first line — not the first row, which a long
|
|
125
|
+
label wraps — and stay hidden until :has() can mirror the input. The em
|
|
126
|
+
margin is the lh-less fallback at the default 1.5 line-height. */
|
|
98
127
|
.choice-card::before,
|
|
99
128
|
.choice-card::after {
|
|
100
129
|
content: "";
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
130
|
+
grid-area: 1 / 1;
|
|
131
|
+
align-self: start;
|
|
132
|
+
margin-block-start: calc((1.5em - var(--choice-card-check-size)) / 2);
|
|
133
|
+
margin-block-start: calc((1lh - var(--choice-card-check-size)) / 2);
|
|
104
134
|
inline-size: var(--choice-card-check-size);
|
|
105
135
|
block-size: var(--choice-card-check-size);
|
|
106
136
|
transform: scale(0);
|
|
107
|
-
transition:
|
|
137
|
+
transition:
|
|
138
|
+
transform var(--duration),
|
|
139
|
+
background-color var(--duration),
|
|
140
|
+
border-color var(--duration);
|
|
108
141
|
}
|
|
109
142
|
|
|
110
|
-
/*
|
|
143
|
+
/* A border, not an inset shadow: forced colors drop shadows, and the resting
|
|
144
|
+
ring is the only sign of an unchecked option. The invalid border reaches it
|
|
145
|
+
through the same hook as the card's. */
|
|
111
146
|
.choice-card::before {
|
|
112
|
-
|
|
113
|
-
box-shadow: 0 0 0 2px var(--surface);
|
|
147
|
+
border: max(var(--border-width), 1.5px) solid var(--form-invalid-border, var(--text-muted));
|
|
114
148
|
z-index: 1;
|
|
115
149
|
}
|
|
116
150
|
|
|
117
|
-
|
|
151
|
+
/* :where() keeps the shape at single-class weight, so .chip can reshape the
|
|
152
|
+
layer without out-arguing the input-type test. */
|
|
153
|
+
.choice-card:where(:has(input[type="radio"]))::before {
|
|
118
154
|
border-radius: var(--radius-full);
|
|
119
155
|
}
|
|
120
156
|
|
|
121
|
-
.choice-card:has(input[type="checkbox"])::before {
|
|
157
|
+
.choice-card:where(:has(input[type="checkbox"]))::before {
|
|
122
158
|
border-radius: var(--radius-sm);
|
|
123
159
|
}
|
|
124
160
|
|
|
@@ -130,18 +166,86 @@
|
|
|
130
166
|
z-index: 2;
|
|
131
167
|
}
|
|
132
168
|
|
|
133
|
-
|
|
169
|
+
/* Guarded with the input's hiding above: a resting ring beside a visible
|
|
170
|
+
native input would draw the indicator twice. */
|
|
171
|
+
@supports selector(:has(*)) {
|
|
172
|
+
.choice-card::before {
|
|
173
|
+
transform: none;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.choice-card:has(:checked)::before {
|
|
178
|
+
border-color: var(--intent, var(--state-selected));
|
|
179
|
+
background: var(--intent, var(--state-selected));
|
|
180
|
+
}
|
|
181
|
+
|
|
134
182
|
.choice-card:has(:checked)::after {
|
|
135
183
|
transform: scale(1);
|
|
136
184
|
}
|
|
137
185
|
|
|
186
|
+
/* Chip — the rules above stay in force; this section only relocates the
|
|
187
|
+
indicator. Must follow them: several ties are won by source order. */
|
|
188
|
+
.choice-card.chip {
|
|
189
|
+
--choice-card-pad: 0 var(--control-pad-x);
|
|
190
|
+
--choice-card-radius: var(--radius-full);
|
|
191
|
+
--choice-card-check-size: 1em;
|
|
192
|
+
|
|
193
|
+
display: inline-flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
min-block-size: var(--control-size);
|
|
196
|
+
line-height: 1.2;
|
|
197
|
+
/* The dot layer sits under the label at z-index -1. */
|
|
198
|
+
isolation: isolate;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* ::before is a full-bleed layer clipped to a dot centred on the check slot,
|
|
202
|
+
so a theme can grow the same clip into a selected fill. The chip itself
|
|
203
|
+
must not clip (its focus ring is an outline), hence the inherited radius. */
|
|
204
|
+
.choice-card.chip::before {
|
|
205
|
+
--choice-chip-dot: calc(var(--control-pad-x) + var(--choice-card-check-size) / 2) 50%;
|
|
206
|
+
|
|
207
|
+
position: absolute;
|
|
208
|
+
inset: 0;
|
|
209
|
+
/* why: an absolute box honours self-alignment — the card's start would
|
|
210
|
+
shrink this layer to 0 height and the dot with it. */
|
|
211
|
+
align-self: auto;
|
|
212
|
+
margin: 0;
|
|
213
|
+
inline-size: auto;
|
|
214
|
+
block-size: auto;
|
|
215
|
+
border: 0;
|
|
216
|
+
border-radius: inherit;
|
|
217
|
+
background: var(--text-subtle);
|
|
218
|
+
transform: none;
|
|
219
|
+
clip-path: circle(0.1875em at var(--choice-chip-dot));
|
|
220
|
+
z-index: -1;
|
|
221
|
+
transition: clip-path var(--duration);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.choice-card.chip:has(:checked)::before {
|
|
225
|
+
clip-path: circle(0 at var(--choice-chip-dot));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* The check keeps the card's scale-in, in flow ahead of the label. */
|
|
229
|
+
.choice-card.chip::after {
|
|
230
|
+
order: -1;
|
|
231
|
+
flex: none;
|
|
232
|
+
align-self: center;
|
|
233
|
+
margin-block: 0;
|
|
234
|
+
margin-inline: 0 0.375em;
|
|
235
|
+
background: var(--intent, var(--state-selected));
|
|
236
|
+
-webkit-mask-size: contain;
|
|
237
|
+
mask-size: contain;
|
|
238
|
+
}
|
|
239
|
+
|
|
138
240
|
/* The native input is hidden and the checked marker is custom-drawn: without
|
|
139
241
|
this repair, forced colors pushes disc and check to the same system color
|
|
140
242
|
and the state disappears. Highlight/Canvas/HighlightText are preserved as-is,
|
|
141
243
|
so no forced-color-adjust is needed. The pseudo is not focusable — the
|
|
142
|
-
outline
|
|
244
|
+
outline separates the disc from whatever it overlaps (the card's border,
|
|
245
|
+
for a corner-badge recipe), not a focus channel. */
|
|
143
246
|
@media (forced-colors: active) {
|
|
144
247
|
.choice-card:has(:checked)::before {
|
|
248
|
+
border-color: Highlight;
|
|
145
249
|
background: Highlight;
|
|
146
250
|
outline: 2px solid Canvas;
|
|
147
251
|
}
|
|
@@ -149,4 +253,18 @@
|
|
|
149
253
|
.choice-card:has(:checked)::after {
|
|
150
254
|
background: HighlightText;
|
|
151
255
|
}
|
|
256
|
+
|
|
257
|
+
/* A chip has no disc behind its check: the check carries the state alone,
|
|
258
|
+
and dropping the checked layer also drops any theme fill. */
|
|
259
|
+
.choice-card.chip::before {
|
|
260
|
+
background: CanvasText;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.choice-card.chip:has(:checked)::before {
|
|
264
|
+
content: none;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.choice-card.chip:has(:checked)::after {
|
|
268
|
+
background: CanvasText;
|
|
269
|
+
}
|
|
152
270
|
}
|
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,
|
|
@@ -51,8 +53,8 @@
|
|
|
51
53
|
cursor: var(--control-cursor);
|
|
52
54
|
/* No outline: none here — the core :focus fallback must survive for browsers
|
|
53
55
|
without :focus-visible. Pointer focus is hidden by core's
|
|
54
|
-
:focus:not(:focus-visible); keyboard focus
|
|
55
|
-
|
|
56
|
+
:focus:not(:focus-visible); keyboard focus draws the inset outline below
|
|
57
|
+
(invalid fields take --form-invalid-border, the documented exception). */
|
|
56
58
|
transition:
|
|
57
59
|
border-color var(--duration),
|
|
58
60
|
box-shadow var(--duration),
|
|
@@ -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/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/otp.css
CHANGED
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
padding-block: 0;
|
|
65
65
|
padding-inline: calc((var(--otp-cell-size) - 1ch) / 2) 0;
|
|
66
66
|
border: 0;
|
|
67
|
-
outline:
|
|
67
|
+
outline: none;
|
|
68
68
|
box-shadow: none;
|
|
69
69
|
background: transparent;
|
|
70
70
|
color: var(--text);
|
|
@@ -89,6 +89,14 @@
|
|
|
89
89
|
character after it: the state rules select the cells as following siblings
|
|
90
90
|
(input:focus ~ span, input:disabled ~ span, …), so that order is part of the
|
|
91
91
|
contract. */
|
|
92
|
+
/* The documented markup puts .input on the native input, so the field ring
|
|
93
|
+
(.input:focus-visible:not(:disabled), control.css) would outrank the reset
|
|
94
|
+
above and draw that group ring; this selector must stay one step above it.
|
|
95
|
+
No transparent outline either: forced colors would paint it as a ring. */
|
|
96
|
+
.otp > input:focus-visible:not(:disabled) {
|
|
97
|
+
outline: none;
|
|
98
|
+
}
|
|
99
|
+
|
|
92
100
|
.otp > input:focus ~ span {
|
|
93
101
|
--otp-cell-border: var(--focus);
|
|
94
102
|
--otp-cell-border-width: calc(var(--border-width) * 2);
|
package/src/css/forms/range.css
CHANGED
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
* --range-track-height
|
|
14
14
|
* --range-thumb-bg
|
|
15
15
|
* --range-track-bg
|
|
16
|
+
* --range-fill-bg
|
|
17
|
+
*
|
|
18
|
+
* Runtime-written (by actual-css/js/range, never set by hand):
|
|
19
|
+
* --range-progress
|
|
16
20
|
*/
|
|
17
21
|
|
|
18
22
|
/* Intent boundary — inherited intents do not tint this component.
|
|
@@ -22,10 +26,17 @@
|
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
.range {
|
|
25
|
-
--range-thumb-size: 1.
|
|
26
|
-
--range-track-height: 0.
|
|
29
|
+
--range-thumb-size: 1.125rem;
|
|
30
|
+
--range-track-height: 0.625rem;
|
|
27
31
|
--range-thumb-bg: var(--intent, var(--primary));
|
|
28
32
|
--range-track-bg: var(--border);
|
|
33
|
+
--range-fill-bg: var(--intent, var(--primary));
|
|
34
|
+
/* Fill share, written by actual-css/js/range. 0% with no runtime means a
|
|
35
|
+
plain track — the control stays fully usable without JavaScript. */
|
|
36
|
+
--range-progress: 0%;
|
|
37
|
+
/* Logical fill direction: the gradient below cannot use flow-relative
|
|
38
|
+
keywords, so RTL flips the angle through this hook instead. */
|
|
39
|
+
--range-fill-from: to right;
|
|
29
40
|
|
|
30
41
|
appearance: none;
|
|
31
42
|
-webkit-appearance: none;
|
|
@@ -35,16 +46,29 @@
|
|
|
35
46
|
cursor: pointer;
|
|
36
47
|
}
|
|
37
48
|
|
|
49
|
+
:where([dir="rtl"]) .range,
|
|
50
|
+
.range:dir(rtl) {
|
|
51
|
+
--range-fill-from: to left;
|
|
52
|
+
}
|
|
53
|
+
|
|
38
54
|
.range::-webkit-slider-runnable-track {
|
|
39
55
|
block-size: var(--range-track-height);
|
|
40
56
|
border-radius: var(--radius-full);
|
|
41
|
-
background:
|
|
57
|
+
background: linear-gradient(
|
|
58
|
+
var(--range-fill-from),
|
|
59
|
+
var(--range-fill-bg) var(--range-progress),
|
|
60
|
+
var(--range-track-bg) var(--range-progress)
|
|
61
|
+
);
|
|
42
62
|
}
|
|
43
63
|
|
|
44
64
|
.range::-moz-range-track {
|
|
45
65
|
block-size: var(--range-track-height);
|
|
46
66
|
border-radius: var(--radius-full);
|
|
47
|
-
background:
|
|
67
|
+
background: linear-gradient(
|
|
68
|
+
var(--range-fill-from),
|
|
69
|
+
var(--range-fill-bg) var(--range-progress),
|
|
70
|
+
var(--range-track-bg) var(--range-progress)
|
|
71
|
+
);
|
|
48
72
|
}
|
|
49
73
|
|
|
50
74
|
.range::-webkit-slider-thumb {
|
|
@@ -57,6 +81,10 @@
|
|
|
57
81
|
background: var(--range-thumb-bg);
|
|
58
82
|
margin-block-start: calc((var(--range-track-height) - var(--range-thumb-size)) / 2);
|
|
59
83
|
cursor: inherit;
|
|
84
|
+
/* Permanent surface separator: the fill defaults to the thumb color, so
|
|
85
|
+
without this the thumb would melt into the filled part of the track.
|
|
86
|
+
Focus-visible keeps this ring and adds the --focus ring outside it. */
|
|
87
|
+
box-shadow: 0 0 0 2px var(--surface-opaque);
|
|
60
88
|
transition: box-shadow var(--duration);
|
|
61
89
|
}
|
|
62
90
|
|
|
@@ -67,6 +95,7 @@
|
|
|
67
95
|
border: 0;
|
|
68
96
|
background: var(--range-thumb-bg);
|
|
69
97
|
cursor: inherit;
|
|
98
|
+
box-shadow: 0 0 0 2px var(--surface-opaque);
|
|
70
99
|
transition: box-shadow var(--duration);
|
|
71
100
|
}
|
|
72
101
|
|
|
@@ -75,20 +104,23 @@
|
|
|
75
104
|
}
|
|
76
105
|
|
|
77
106
|
/*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
107
|
+
* A thumb pseudo-element takes no reliable outline, so its ring is a solid
|
|
108
|
+
* --focus shadow (the transparent outline above covers forced colors). The
|
|
109
|
+
* surface-coloured separator keeps the ring visually distinct from the
|
|
110
|
+
* coloured thumb — unlike a full-size input where the same gap would
|
|
111
|
+
* read as an unwanted void. It doubles as the resting separator between
|
|
112
|
+
* the thumb and the same-coloured fill.
|
|
81
113
|
*/
|
|
82
114
|
.range:focus-visible::-webkit-slider-thumb {
|
|
83
115
|
box-shadow:
|
|
84
|
-
0 0 0
|
|
85
|
-
0 0 0 calc(
|
|
116
|
+
0 0 0 2px var(--surface-opaque),
|
|
117
|
+
0 0 0 calc(2px + var(--focus-ring-width)) var(--focus);
|
|
86
118
|
}
|
|
87
119
|
|
|
88
120
|
.range:focus-visible::-moz-range-thumb {
|
|
89
121
|
box-shadow:
|
|
90
|
-
0 0 0
|
|
91
|
-
0 0 0 calc(
|
|
122
|
+
0 0 0 2px var(--surface-opaque),
|
|
123
|
+
0 0 0 calc(2px + var(--focus-ring-width)) var(--focus);
|
|
92
124
|
}
|
|
93
125
|
|
|
94
126
|
.range:disabled {
|
|
@@ -96,22 +128,48 @@
|
|
|
96
128
|
cursor: not-allowed;
|
|
97
129
|
opacity: var(--disabled-opacity);
|
|
98
130
|
}
|
|
99
|
-
|
|
100
131
|
/* Forced colors: the track must stay a visible line against the page (Canvas
|
|
101
132
|
would merge with it) and the thumb needs a real boundary to remain
|
|
102
|
-
distinguishable from the track.
|
|
103
|
-
|
|
133
|
+
distinguishable from the track. The fill keeps its two-tone split with
|
|
134
|
+
system colors so the value stays readable. Disabled keeps GrayText via
|
|
135
|
+
:disabled. Focus stays on the transparent outline of :focus-visible. Track
|
|
136
|
+
and thumb fills are written on the properties, not only the hooks: an
|
|
137
|
+
application hook override at the same specificity loads after this file
|
|
138
|
+
and would win the variable, leaving a Canvas-remapped fill.
|
|
139
|
+
The split lives in a background-image, which forced-color-adjust: auto
|
|
140
|
+
drops — so the tracks opt out locally. The colors stay system keywords,
|
|
141
|
+
keeping native contrast adaptation; only the host keeps adjusting. */
|
|
104
142
|
@media (forced-colors: active) {
|
|
105
143
|
.range {
|
|
106
144
|
--range-track-bg: CanvasText;
|
|
107
145
|
--range-thumb-bg: ButtonFace;
|
|
146
|
+
--range-fill-bg: Highlight;
|
|
108
147
|
}
|
|
109
148
|
|
|
149
|
+
.range::-webkit-slider-runnable-track {
|
|
150
|
+
forced-color-adjust: none;
|
|
151
|
+
background: linear-gradient(
|
|
152
|
+
var(--range-fill-from),
|
|
153
|
+
Highlight var(--range-progress),
|
|
154
|
+
CanvasText var(--range-progress)
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.range::-moz-range-track {
|
|
159
|
+
forced-color-adjust: none;
|
|
160
|
+
background: linear-gradient(
|
|
161
|
+
var(--range-fill-from),
|
|
162
|
+
Highlight var(--range-progress),
|
|
163
|
+
CanvasText var(--range-progress)
|
|
164
|
+
);
|
|
165
|
+
}
|
|
110
166
|
.range::-webkit-slider-thumb {
|
|
167
|
+
background: ButtonFace;
|
|
111
168
|
border: var(--border-width) solid ButtonText;
|
|
112
169
|
}
|
|
113
170
|
|
|
114
171
|
.range::-moz-range-thumb {
|
|
172
|
+
background: ButtonFace;
|
|
115
173
|
border: var(--border-width) solid ButtonText;
|
|
116
174
|
}
|
|
117
175
|
}
|
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 {
|