actual-css 0.7.0 → 0.9.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 +217 -0
- package/README.md +13 -11
- package/components.json +668 -0
- package/dist/actual.css +166 -37
- package/dist/actual.full.css +1087 -643
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/dist/actual.js +1 -1
- package/dist/actual.min.css +1 -1
- package/docs/design-notes/accessibility-decisions.md +33 -0
- package/docs/design-notes/browser-support.md +62 -30
- package/docs/design-notes/column-layout.md +3 -3
- package/docs/design-notes/css-anchor.md +75 -21
- package/docs/design-notes/enhancement-contract.md +14 -6
- package/docs/design-notes/grid.md +4 -4
- package/docs/design-notes/naming.md +6 -1
- package/docs/design-notes/platform-alignment.md +53 -7
- package/docs/design-notes/popover-manual-poc.md +105 -25
- package/docs/design-notes/surface-reparenting.md +56 -47
- package/docs/design-notes/tooltip-coordinate-space.md +132 -0
- package/docs/design-notes/widget-primitives.md +7 -4
- package/docs/pages/components/accordion.md +41 -2
- package/docs/pages/components/alert.md +85 -30
- package/docs/pages/components/app-navigation.md +6 -0
- package/docs/pages/components/avatar.md +6 -6
- package/docs/pages/components/badge.md +101 -16
- package/docs/pages/components/button.md +16 -8
- package/docs/pages/components/card.md +10 -10
- package/docs/pages/components/dialog.md +21 -6
- package/docs/pages/components/drawer.md +13 -0
- package/docs/pages/components/fab.md +4 -3
- package/docs/pages/components/list.md +42 -4
- package/docs/pages/components/navbar.md +17 -1
- package/docs/pages/components/pagination.md +2 -2
- package/docs/pages/components/skeleton.md +3 -1
- package/docs/pages/components/steps.md +19 -0
- package/docs/pages/components/table.md +3 -2
- package/docs/pages/enhancements/flyout.md +82 -38
- package/docs/pages/enhancements/overview.md +25 -0
- package/docs/pages/enhancements/status-bar.md +1 -1
- package/docs/pages/enhancements/tabs.md +45 -37
- package/docs/pages/enhancements/tooltip.md +36 -10
- package/docs/pages/examples/overview.md +38 -5
- package/docs/pages/forms/choice-cards.md +11 -6
- package/docs/pages/forms/combobox.md +1 -1
- package/docs/pages/forms/floating-field.md +4 -3
- package/docs/pages/forms/joined-controls.md +22 -0
- package/docs/pages/forms/otp.md +2 -1
- package/docs/pages/forms/overview.md +1 -1
- package/docs/pages/forms/states-and-sizes.md +16 -18
- package/docs/pages/forms/sticky-actions.md +2 -2
- package/docs/pages/forms/switch.md +1 -1
- package/docs/pages/foundations/philosophy.md +57 -26
- package/docs/pages/foundations/theming.md +2 -2
- package/docs/pages/foundations/tokens.md +118 -21
- package/docs/pages/foundations/typography.md +35 -14
- package/docs/pages/guides/introduction.md +11 -9
- package/docs/pages/guides/progressive-enhancement.md +115 -30
- package/docs/pages/guides/tailwind.md +3 -2
- package/docs/pages/layout/media.md +1 -1
- package/docs/pages/layout/overview.md +11 -1
- package/docs/pages/layout/scroll-snap.md +3 -3
- package/docs/pages/layout/stack.md +17 -9
- package/docs/pages/patterns/icon-input.md +4 -2
- package/docs/pages/patterns/icon-slot.md +2 -0
- package/docs/pages/patterns/intent-nesting.md +1 -1
- package/docs/pages/utilities/overview.md +23 -2
- package/docs/pages/utilities/sizing-wrapping.md +1 -1
- package/docs/pages/utilities/spacing.md +10 -8
- package/llms.txt +2 -1
- package/package.json +159 -149
- package/{scripts/reserved-classes.json → reserved-classes.json} +4 -6
- package/src/css/components/accordion.css +42 -3
- package/src/css/components/alert.css +25 -13
- package/src/css/components/app-nav.css +18 -8
- package/src/css/components/avatar.css +13 -1
- package/src/css/components/badge.css +78 -12
- package/src/css/components/breadcrumb.css +2 -3
- package/src/css/components/button.css +9 -16
- package/src/css/components/card.css +9 -2
- package/src/css/components/chat.css +1 -0
- package/src/css/components/data-list.css +1 -1
- package/src/css/components/drawer.css +101 -32
- package/src/css/components/fab.css +0 -6
- package/src/css/components/flyout.css +35 -98
- package/src/css/components/index.css +0 -1
- package/src/css/components/indicator.css +21 -0
- package/src/css/components/key.css +10 -0
- package/src/css/components/list.css +40 -4
- package/src/css/components/menu.css +2 -1
- package/src/css/components/modal.css +48 -33
- package/src/css/components/navbar.css +37 -4
- package/src/css/components/pagination.css +1 -0
- package/src/css/components/skeleton.css +15 -4
- package/src/css/components/spinner.css +6 -6
- package/src/css/components/status-bar.css +0 -5
- package/src/css/components/steps.css +45 -59
- package/src/css/components/tab.css +29 -9
- package/src/css/components/table.css +13 -5
- package/src/css/components/tooltip.css +36 -40
- package/src/css/core/base.css +23 -1
- package/src/css/core/intents.css +17 -6
- package/src/css/core/theme.css +27 -0
- package/src/css/core/tokens.css +35 -15
- package/src/css/core/variants.css +64 -15
- package/src/css/forms/choice-card.css +4 -4
- package/src/css/forms/choice.css +76 -29
- package/src/css/forms/control.css +2 -1
- package/src/css/forms/floating-field.css +3 -3
- package/src/css/forms/form.css +4 -3
- package/src/css/forms/input-icon.css +20 -14
- package/src/css/forms/native.css +6 -1
- package/src/css/forms/otp.css +9 -1
- package/src/css/forms/range.css +2 -0
- package/src/css/forms/select.css +29 -9
- package/src/css/forms/switch.css +28 -8
- package/src/css/layout/app-layout.css +1 -1
- package/src/css/layout/grid.css +50 -76
- package/src/css/layout/scroll-snap.css +11 -7
- package/src/css/layout/scroller.css +3 -4
- package/src/css/layout/stack.css +2 -2
- package/src/css/typography/fluid.css +4 -3
- package/src/css/typography/prose.css +6 -5
- package/src/css/utilities/base.css +2 -1
- package/src/css/utilities/extra.css +20 -17
- package/src/js/context-menu.js +27 -26
- package/src/js/dialog.js +2 -1
- package/src/js/dismiss.js +5 -0
- package/src/js/enhance.js +34 -2
- package/src/js/enhancement-loader.js +36 -43
- package/src/js/events.js +0 -3
- package/src/js/filter.js +0 -1
- package/src/js/floating-space.js +42 -0
- package/src/js/flyout.js +9 -44
- package/src/js/focus-group.js +7 -0
- package/src/js/focus.js +6 -0
- package/src/js/menu.js +10 -1
- package/src/js/selectors.js +5 -9
- package/src/js/status.js +6 -5
- package/src/js/surface.js +97 -139
- package/src/js/tab.js +6 -1
- package/src/js/tooltip.js +150 -51
- package/src/js/validation.js +1 -1
- package/src/css/components/surface.css +0 -49
package/src/css/forms/choice.css
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* geometry and states. switch.css is imported after this file.
|
|
11
11
|
*
|
|
12
12
|
* Public hooks:
|
|
13
|
-
* --choice-control-
|
|
13
|
+
* --choice-control-nudge
|
|
14
14
|
* --choice-border
|
|
15
15
|
* --choice-bg
|
|
16
16
|
* --choice-mark
|
|
@@ -42,45 +42,89 @@
|
|
|
42
42
|
cursor: pointer;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
/* Optical alignment:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
45
|
+
/* Optical alignment: the control reads level with the first line's cap height,
|
|
46
|
+
not with the top of its line box. Every inline choice runs the same formula
|
|
47
|
+
over its own block size, so a checkbox and a taller switch stay level with
|
|
48
|
+
each other under any line-height or --choice-control-size — something a
|
|
49
|
+
per-control constant cannot do.
|
|
50
|
+
|
|
51
|
+
Three tiers, each the best the engine can express:
|
|
52
|
+
|
|
53
|
+
- `cap` unit: exact, and the only font-independent answer. Where the cap
|
|
54
|
+
center sits inside a line box depends on the font's ascent/descent/cap
|
|
55
|
+
ratios, so no constant can serve both Segoe UI and Roboto — the same rule
|
|
56
|
+
was a pixel high on Windows and two pixels low on Android. Baseline
|
|
57
|
+
alignment puts the control's border-box bottom on the text baseline, and
|
|
58
|
+
one inset drops it by half the difference between its own height and the
|
|
59
|
+
cap height. Both classed controls synthesize that baseline from their
|
|
60
|
+
border box because their mark and knob are out of flow.
|
|
61
|
+
- `lh` only: center the control on the line box, which is within a pixel of
|
|
62
|
+
the cap center for the usual UI faces.
|
|
63
|
+
- neither: the constants that formula yields at the default 1.5
|
|
64
|
+
line-height, 0.1875em for a checkbox and 0.125em for the switch track.
|
|
65
|
+
|
|
66
|
+
--choice-control-nudge adds to whichever tier applies, rather than replacing
|
|
67
|
+
it: the derived value is exact now, so a design has a correction to make,
|
|
68
|
+
not a formula to restate — and one number cannot stand in for three
|
|
69
|
+
different references anyway. It stays fallback-only rather than declared
|
|
70
|
+
here, so an override placed anywhere up the tree — :root included — reaches
|
|
71
|
+
the control instead of losing to a component-level declaration. */
|
|
62
72
|
.choice > :is(input[type="checkbox"]:not([role="switch"]), input[type="radio"]) {
|
|
63
73
|
flex: 0 0 auto;
|
|
64
|
-
margin-block-start: var(--choice-control-
|
|
74
|
+
margin-block-start: calc(0.1875em + var(--choice-control-nudge, 0px));
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
.choice > .switch {
|
|
68
78
|
flex: 0 0 auto;
|
|
69
|
-
margin-block-start: var(--choice-control-
|
|
79
|
+
margin-block-start: calc(0.125em + var(--choice-control-nudge, 0px));
|
|
70
80
|
}
|
|
71
81
|
|
|
72
82
|
@supports (margin-block-start: 1lh) {
|
|
73
83
|
.choice > :is(input[type="checkbox"]:not([role="switch"]), input[type="radio"]) {
|
|
74
|
-
margin-block-start:
|
|
75
|
-
--choice-control-
|
|
76
|
-
|
|
84
|
+
margin-block-start: calc(
|
|
85
|
+
(1lh - var(--choice-control-size)) /
|
|
86
|
+
2 +
|
|
87
|
+
var(--choice-control-nudge, 0px)
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.choice > .switch {
|
|
92
|
+
margin-block-start: calc(
|
|
93
|
+
(1lh - var(--switch-block-size)) /
|
|
94
|
+
2 +
|
|
95
|
+
var(--choice-control-nudge, 0px)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@supports (margin-block-start: 1cap) {
|
|
101
|
+
.choice {
|
|
102
|
+
align-items: baseline;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Baseline alignment owns the block position now; the tier above would only
|
|
106
|
+
grow the row. An unclassed native input keeps the UA's own baseline, which
|
|
107
|
+
is already tuned for text — the formula below describes the framework's
|
|
108
|
+
geometry and would not fit it. */
|
|
109
|
+
.choice > :is(input[type="checkbox"], input[type="radio"]) {
|
|
110
|
+
margin-block-start: 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* inset-block-start rides the `position: relative` these controls already
|
|
114
|
+
carry for their mark and knob. */
|
|
115
|
+
.choice > :is(.check, .radio) {
|
|
116
|
+
inset-block-start: calc(
|
|
117
|
+
(var(--choice-control-size) - 1cap) /
|
|
118
|
+
2 +
|
|
119
|
+
var(--choice-control-nudge, 0px)
|
|
77
120
|
);
|
|
78
121
|
}
|
|
79
122
|
|
|
80
123
|
.choice > .switch {
|
|
81
|
-
|
|
82
|
-
--
|
|
83
|
-
|
|
124
|
+
inset-block-start: calc(
|
|
125
|
+
(var(--switch-block-size) - 1cap) /
|
|
126
|
+
2 +
|
|
127
|
+
var(--choice-control-nudge, 0px)
|
|
84
128
|
);
|
|
85
129
|
}
|
|
86
130
|
}
|
|
@@ -115,6 +159,7 @@
|
|
|
115
159
|
background: var(--choice-bg);
|
|
116
160
|
color: var(--choice-mark);
|
|
117
161
|
appearance: none;
|
|
162
|
+
-webkit-appearance: none;
|
|
118
163
|
cursor: pointer;
|
|
119
164
|
}
|
|
120
165
|
|
|
@@ -153,9 +198,9 @@
|
|
|
153
198
|
|
|
154
199
|
.check:is(:checked, :indeterminate),
|
|
155
200
|
.radio:checked {
|
|
156
|
-
--choice-border: var(--intent, var(--
|
|
157
|
-
--choice-bg: var(--intent, var(--
|
|
158
|
-
--choice-mark: var(--intent-fg, var(--
|
|
201
|
+
--choice-border: var(--intent, var(--state-selected));
|
|
202
|
+
--choice-bg: var(--intent, var(--state-selected));
|
|
203
|
+
--choice-mark: var(--intent-fg, var(--state-selected-fg));
|
|
159
204
|
}
|
|
160
205
|
|
|
161
206
|
.check:checked::before {
|
|
@@ -174,6 +219,8 @@
|
|
|
174
219
|
transform: scale(1);
|
|
175
220
|
}
|
|
176
221
|
|
|
222
|
+
/* Progressive: without :not() selector lists, the normal unchecked border
|
|
223
|
+
remains visible and the control stays usable. */
|
|
177
224
|
.check:hover:not(:checked, :indeterminate, :disabled),
|
|
178
225
|
.radio:hover:not(:checked, :indeterminate, :disabled) {
|
|
179
226
|
--choice-border: var(--text-subtle);
|
|
@@ -32,9 +32,10 @@
|
|
|
32
32
|
--control-cursor: auto;
|
|
33
33
|
|
|
34
34
|
appearance: none;
|
|
35
|
+
-webkit-appearance: none;
|
|
35
36
|
inline-size: 100%;
|
|
36
37
|
min-block-size: var(--control-size);
|
|
37
|
-
padding-inline: var(--
|
|
38
|
+
padding-inline: var(--control-pad-x);
|
|
38
39
|
border: var(--border-width) solid var(--form-invalid-border, var(--control-border));
|
|
39
40
|
border-block-end-color: var(
|
|
40
41
|
--form-invalid-border,
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
* placeholder=" " to expose a reliable empty state — without it the label
|
|
15
15
|
* only reacts to focus.
|
|
16
16
|
*
|
|
17
|
-
* Density flows through --control-size: .
|
|
18
|
-
* and the reserved headroom with it, leaving typography untouched.
|
|
17
|
+
* Density flows through --control-size: .compact/.spacious change the control
|
|
18
|
+
* geometry and the reserved headroom with it, leaving typography untouched.
|
|
19
19
|
*
|
|
20
20
|
* Public hooks:
|
|
21
21
|
* --floating-label-top
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
inset-inline-start: calc(var(--space-30) + var(--border-width));
|
|
47
47
|
color: var(--text-muted);
|
|
48
48
|
font-size: var(--control-font-size);
|
|
49
|
-
font-weight: var(--font-weight);
|
|
49
|
+
font-weight: var(--font-weight-normal);
|
|
50
50
|
opacity: var(--floating-label-opacity);
|
|
51
51
|
pointer-events: none;
|
|
52
52
|
transition:
|
package/src/css/forms/form.css
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
display: grid;
|
|
19
19
|
gap: var(--space-10);
|
|
20
20
|
color: var(--text);
|
|
21
|
+
font-size: var(--control-font-size);
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
.field-group {
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
.field-label,
|
|
32
33
|
.field-group > legend {
|
|
33
34
|
color: var(--text);
|
|
34
|
-
font-weight: var(--font-weight-
|
|
35
|
+
font-weight: var(--font-weight-semibold);
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
.field-group > legend {
|
|
@@ -42,13 +43,13 @@
|
|
|
42
43
|
|
|
43
44
|
.field-help {
|
|
44
45
|
color: var(--text-muted);
|
|
45
|
-
font-size:
|
|
46
|
+
font-size: 0.875em;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
.field-error {
|
|
49
50
|
display: none;
|
|
50
51
|
color: var(--danger);
|
|
51
|
-
font-size:
|
|
52
|
+
font-size: 0.875em;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
.required-mark {
|
|
@@ -2,18 +2,19 @@
|
|
|
2
2
|
* Input Icon — decorative icon inside a relative wrapper, leading or trailing.
|
|
3
3
|
*
|
|
4
4
|
* Icon-library agnostic: detects position by DOM order. First child that is
|
|
5
|
-
* not .input = leading icon; icon after .input = trailing. The icon
|
|
6
|
-
*
|
|
7
|
-
* the pictogram.
|
|
5
|
+
* not .input = leading icon; icon after .input = trailing. The icon follows
|
|
6
|
+
* local typography; density contexts shrink the control geometry around it
|
|
7
|
+
* without changing the pictogram.
|
|
8
8
|
*
|
|
9
9
|
* Public hooks:
|
|
10
10
|
* --input-icon-size
|
|
11
11
|
*/
|
|
12
12
|
.input-icon {
|
|
13
|
-
--input-icon-size: 1.
|
|
13
|
+
--input-icon-size: 1.25em;
|
|
14
14
|
|
|
15
15
|
display: block;
|
|
16
16
|
position: relative;
|
|
17
|
+
font-size: var(--control-font-size);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/* Icon — shared by leading and trailing */
|
|
@@ -28,27 +29,32 @@
|
|
|
28
29
|
|
|
29
30
|
/* Leading: icon is the first child */
|
|
30
31
|
.input-icon > :first-child:where(:not(.input)) {
|
|
31
|
-
inset-inline-start: var(--
|
|
32
|
+
inset-inline-start: var(--control-pad-x);
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
/* Trailing:
|
|
35
|
-
.input-icon > .input
|
|
36
|
-
inset-inline-end: var(--
|
|
35
|
+
/* Trailing: an icon follows the input, with or without a leading icon. */
|
|
36
|
+
.input-icon > .input ~ :where(:not(.input)) {
|
|
37
|
+
inset-inline-end: var(--control-pad-x);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
/* font-size only sizes text-metric icons (webfont <i>, <span>). An inline
|
|
40
41
|
<svg> with just a viewBox ignores it, so give it explicit dimensions. */
|
|
41
42
|
.input-icon > svg:where(:not(.input)) {
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
/* The shared rule already maps --input-icon-size onto font-size. One em is
|
|
44
|
+
therefore the requested optical size; reusing the em-valued hook here
|
|
45
|
+
would apply its factor a second time. */
|
|
46
|
+
inline-size: 1em;
|
|
47
|
+
block-size: 1em;
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
/* Leading: input gets start padding to clear the icon */
|
|
47
51
|
.input-icon > :first-child:where(:not(.input)) ~ .input {
|
|
48
|
-
padding-inline-start: calc(var(--
|
|
52
|
+
padding-inline-start: calc(var(--control-pad-x) * 2 + var(--input-icon-size));
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
/* Trailing: input
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
/* Trailing: the input has a following sibling → end padding. Not tied to the
|
|
56
|
+
input being first child, so a leading icon does not disable the trailing
|
|
57
|
+
icon's reserved space. */
|
|
58
|
+
.input-icon > .input:where(:not(:last-child)) {
|
|
59
|
+
padding-inline-end: calc(var(--control-pad-x) * 2 + var(--input-icon-size));
|
|
54
60
|
}
|
package/src/css/forms/native.css
CHANGED
|
@@ -8,18 +8,23 @@
|
|
|
8
8
|
*/
|
|
9
9
|
.file {
|
|
10
10
|
color: var(--text-muted);
|
|
11
|
+
font-size: var(--control-font-size);
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
.file::file-selector-button {
|
|
14
15
|
margin-inline-end: var(--space-30);
|
|
16
|
+
/* Consume the shared control scale like .btn, so .sm/.lg and density resize
|
|
17
|
+
the whole control, not just the filename text. */
|
|
18
|
+
min-block-size: var(--control-size);
|
|
15
19
|
padding-block: var(--space-10);
|
|
20
|
+
padding-block: max(var(--space-10), calc((var(--control-size) - 1lh) / 2));
|
|
16
21
|
padding-inline: var(--space-20);
|
|
17
22
|
border: var(--border-width) solid var(--border);
|
|
18
23
|
border-radius: var(--radius-sm);
|
|
19
24
|
background: var(--surface-subtle);
|
|
20
25
|
color: var(--text);
|
|
21
26
|
font: inherit;
|
|
22
|
-
font-size: var(--font-size
|
|
27
|
+
font-size: var(--control-font-size);
|
|
23
28
|
font-weight: var(--font-weight-medium);
|
|
24
29
|
cursor: pointer;
|
|
25
30
|
transition:
|
package/src/css/forms/otp.css
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
.otp {
|
|
19
19
|
--otp-cell-size: var(--control-size);
|
|
20
20
|
--otp-gap: var(--space-10);
|
|
21
|
-
--otp-font-size: 1.
|
|
21
|
+
--otp-font-size: 1.5em;
|
|
22
22
|
--otp-cell-border: var(--border);
|
|
23
23
|
--otp-cell-bg: var(--surface);
|
|
24
24
|
--otp-caret-space: calc((var(--otp-cell-size) - 1ch) / 2 + var(--otp-gap) + 1px);
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
max-inline-size: 100%;
|
|
32
32
|
gap: var(--otp-gap);
|
|
33
33
|
border-radius: var(--radius);
|
|
34
|
+
font-size: var(--control-font-size);
|
|
34
35
|
/* Unlike hidden, clip cannot become a scroll container when the caret moves
|
|
35
36
|
into the input extension. */
|
|
36
37
|
overflow: clip;
|
|
@@ -72,6 +73,7 @@
|
|
|
72
73
|
letter-spacing: calc(var(--otp-cell-size) + var(--otp-gap) - 1ch);
|
|
73
74
|
text-align: start;
|
|
74
75
|
appearance: none;
|
|
76
|
+
-webkit-appearance: none;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
/* Deliberately no group ring: a box around all the cells reads as a second
|
|
@@ -106,6 +108,12 @@
|
|
|
106
108
|
cursor: not-allowed;
|
|
107
109
|
}
|
|
108
110
|
|
|
111
|
+
/* biome-ignore lint/style/noDescendingSpecificity: disabled must come last of
|
|
112
|
+
the cell states, and the reorder the rule asks for would invert that. A
|
|
113
|
+
disabled input is barred from constraint validation, so `:invalid` and
|
|
114
|
+
`:focus` cannot reach it at all; the only overlap left is an authored
|
|
115
|
+
aria-invalid, where equal specificity and source order are what make
|
|
116
|
+
disabled — not danger — the colour a dead field reads as. */
|
|
109
117
|
.otp > input:disabled ~ span {
|
|
110
118
|
--otp-cell-bg: var(--surface-subtle);
|
|
111
119
|
--otp-cell-border: var(--state-disabled);
|
package/src/css/forms/range.css
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
--range-track-bg: var(--border);
|
|
27
27
|
|
|
28
28
|
appearance: none;
|
|
29
|
+
-webkit-appearance: none;
|
|
29
30
|
inline-size: 100%;
|
|
30
31
|
block-size: var(--range-thumb-size);
|
|
31
32
|
background: transparent;
|
|
@@ -46,6 +47,7 @@
|
|
|
46
47
|
|
|
47
48
|
.range::-webkit-slider-thumb {
|
|
48
49
|
appearance: none;
|
|
50
|
+
-webkit-appearance: none;
|
|
49
51
|
inline-size: var(--range-thumb-size);
|
|
50
52
|
block-size: var(--range-thumb-size);
|
|
51
53
|
border-radius: var(--radius-full);
|
package/src/css/forms/select.css
CHANGED
|
@@ -7,20 +7,40 @@
|
|
|
7
7
|
* the CSS chevron, so the picker (appearance: base-select) enhancement in
|
|
8
8
|
* custom-select.css can be opted out independently. Add .native to a specific
|
|
9
9
|
* select to opt out of the enhancement.
|
|
10
|
+
*
|
|
11
|
+
* Public hooks:
|
|
12
|
+
* --select-icon-size
|
|
10
13
|
*/
|
|
11
|
-
|
|
14
|
+
/* --select-icon-size is typographic (em), so the reserved slot and the
|
|
15
|
+
pictogram scale with the control's font size, not a fixed rem sliver. */
|
|
16
|
+
.select:not(:is([multiple], [size]:not([size="1"]))) {
|
|
17
|
+
--select-icon-size: 1em;
|
|
18
|
+
|
|
12
19
|
appearance: none;
|
|
13
|
-
|
|
20
|
+
-webkit-appearance: none;
|
|
21
|
+
padding-inline-end: calc(var(--control-pad-x) * 2 + var(--select-icon-size));
|
|
14
22
|
background-image: var(--icon-chevron);
|
|
15
|
-
background-position: center right var(--
|
|
16
|
-
background-size:
|
|
23
|
+
background-position: center right var(--control-pad-x);
|
|
24
|
+
background-size: var(--select-icon-size) auto;
|
|
17
25
|
background-repeat: no-repeat;
|
|
18
26
|
}
|
|
19
27
|
|
|
20
28
|
/* The chevron sits on the inline-end side; flip it in RTL so it follows
|
|
21
|
-
the padding-inline-end space reserved for it.
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
the padding-inline-end space reserved for it. Safari before 16.4 has no
|
|
30
|
+
:dir(); the ancestor-attribute path is the Degraded-tier fallback and the
|
|
31
|
+
:dir() rules are dropped on their own there. :dir() follows inherited
|
|
32
|
+
directionality, so the explicit ltr rule neutralizes an outer RTL fallback
|
|
33
|
+
for a nested direction change. */
|
|
34
|
+
[dir="rtl"] .select:not(:is([multiple], [size]:not([size="1"]))) {
|
|
35
|
+
background-position: center left var(--control-pad-x);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.select:not(:is([multiple], [size]:not([size="1"]))):dir(ltr) {
|
|
39
|
+
background-position: center right var(--control-pad-x);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.select:not(:is([multiple], [size]:not([size="1"]))):dir(rtl) {
|
|
43
|
+
background-position: center left var(--control-pad-x);
|
|
24
44
|
}
|
|
25
45
|
|
|
26
46
|
/* Both forced-colors and prefers-contrast hand the arrow back to the
|
|
@@ -28,9 +48,9 @@
|
|
|
28
48
|
simpler than maintaining contrast variants, and native rendering already
|
|
29
49
|
matches whatever contrast mode the user picked. */
|
|
30
50
|
@media (forced-colors: active), (prefers-contrast: more) {
|
|
31
|
-
.select:not([multiple], [size]:not([size="1"])) {
|
|
51
|
+
.select:not(:is([multiple], [size]:not([size="1"]))) {
|
|
32
52
|
appearance: auto;
|
|
33
53
|
background-image: none;
|
|
34
|
-
padding-inline-end: var(--
|
|
54
|
+
padding-inline-end: var(--control-pad-x);
|
|
35
55
|
}
|
|
36
56
|
}
|
package/src/css/forms/switch.css
CHANGED
|
@@ -56,8 +56,6 @@
|
|
|
56
56
|
|
|
57
57
|
position: relative;
|
|
58
58
|
display: inline-grid;
|
|
59
|
-
align-items: center;
|
|
60
|
-
justify-content: start;
|
|
61
59
|
inline-size: var(--switch-width);
|
|
62
60
|
block-size: var(--switch-block-size);
|
|
63
61
|
margin: 0;
|
|
@@ -65,6 +63,7 @@
|
|
|
65
63
|
border-radius: var(--radius-full);
|
|
66
64
|
background: var(--switch-bg);
|
|
67
65
|
appearance: none;
|
|
66
|
+
-webkit-appearance: none;
|
|
68
67
|
cursor: pointer;
|
|
69
68
|
}
|
|
70
69
|
|
|
@@ -75,9 +74,17 @@
|
|
|
75
74
|
against the track is what makes the knob legible. */
|
|
76
75
|
.switch::before {
|
|
77
76
|
content: "";
|
|
77
|
+
/* Out of flow like the checkbox mark, so the track carries no in-flow item
|
|
78
|
+
and the switch synthesizes its baseline from its own border box. A knob
|
|
79
|
+
laid out as a grid item makes the baseline the knob's bottom edge instead,
|
|
80
|
+
which is what .choice measures its cap-height offset from. The inset is
|
|
81
|
+
the same --switch-knob-margin on both axes: it resolves against the
|
|
82
|
+
padding box, so the border is already out of the way. */
|
|
83
|
+
position: absolute;
|
|
84
|
+
inset-block-start: var(--switch-knob-margin);
|
|
85
|
+
inset-inline-start: var(--switch-knob-margin);
|
|
78
86
|
inline-size: var(--switch-knob-size);
|
|
79
87
|
block-size: var(--switch-knob-size);
|
|
80
|
-
margin-inline: var(--switch-knob-margin);
|
|
81
88
|
border-radius: var(--radius-full);
|
|
82
89
|
background: var(--switch-knob);
|
|
83
90
|
transition:
|
|
@@ -85,14 +92,17 @@
|
|
|
85
92
|
background-color var(--duration);
|
|
86
93
|
}
|
|
87
94
|
|
|
88
|
-
|
|
95
|
+
/* Hover is not selected: an unchecked switch keeps the brand fallback on hover,
|
|
96
|
+
so a theme that separates --primary from --state-selected never tints an
|
|
97
|
+
OFF control with the selection accent before it is checked. */
|
|
98
|
+
.switch:not(:checked):hover:not(:disabled) {
|
|
89
99
|
--switch-border: var(--intent, var(--primary));
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
.switch:checked {
|
|
93
|
-
--switch-bg: var(--intent, var(--
|
|
94
|
-
--switch-border: var(--intent, var(--
|
|
95
|
-
--switch-knob: var(--intent-fg, var(--
|
|
103
|
+
--switch-bg: var(--intent, var(--state-selected));
|
|
104
|
+
--switch-border: var(--intent, var(--state-selected));
|
|
105
|
+
--switch-knob: var(--intent-fg, var(--state-selected-fg));
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
.switch:checked::before {
|
|
@@ -100,7 +110,17 @@
|
|
|
100
110
|
}
|
|
101
111
|
|
|
102
112
|
/* The knob travels toward inline-end; in RTL that is leftward, so the
|
|
103
|
-
translateX sign flips. translateX is physical, not logical.
|
|
113
|
+
translateX sign flips. translateX is physical, not logical. Safari before
|
|
114
|
+
16.4 has no :dir(); the ancestor-attribute path is the Degraded-tier
|
|
115
|
+
fallback and the :dir() rules are dropped on their own there. */
|
|
116
|
+
[dir="rtl"] .switch:checked::before {
|
|
117
|
+
transform: translateX(calc(var(--switch-travel) * -1));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.switch:checked:dir(ltr)::before {
|
|
121
|
+
transform: translateX(var(--switch-travel));
|
|
122
|
+
}
|
|
123
|
+
|
|
104
124
|
.switch:checked:dir(rtl)::before {
|
|
105
125
|
transform: translateX(calc(var(--switch-travel) * -1));
|
|
106
126
|
}
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
text-align: start;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
/*
|
|
86
|
+
/* In the side layout the FAB owns the viewport's bottom safe-area inset. */
|
|
87
87
|
.app-layout > .fab {
|
|
88
88
|
margin-block-end: max(var(--fab-offset), env(safe-area-inset-bottom));
|
|
89
89
|
}
|