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/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
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
--focus-outline-color: currentColor;
|
|
83
83
|
--focus-outline: calc(var(--border-width) * 2) solid var(--focus-outline-color);
|
|
84
84
|
--focus-outline-offset: 2px;
|
|
85
|
-
--focus-ring-width:
|
|
85
|
+
--focus-ring-width: 2px;
|
|
86
86
|
|
|
87
87
|
/* Status dot positioning — used by .avatar > .badge:empty. The offset is
|
|
88
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
|
|
@@ -64,28 +60,6 @@
|
|
|
64
60
|
--ui-border: var(--border);
|
|
65
61
|
}
|
|
66
62
|
|
|
67
|
-
/* Inverse surface treatment. Paints the element directly (so it works on any
|
|
68
|
-
bare block) and remaps the shared --ui-* tokens so opt-in components
|
|
69
|
-
(.card, .navbar) invert through their own rules. Because this loads before
|
|
70
|
-
component base surfaces, components with their own explicit surface
|
|
71
|
-
(e.g. .accordion) keep it — participation is opt-in, never forced. --heading
|
|
72
|
-
is set so global headings (var(--heading, inherit)) do not inherit an outside
|
|
73
|
-
heading color onto the dark surface; surface-owning descendants derive
|
|
74
|
-
their contextual aliases from their own local surface. */
|
|
75
|
-
.inverted {
|
|
76
|
-
--ui-bg: var(--surface-solid);
|
|
77
|
-
--ui-fg: var(--surface);
|
|
78
|
-
--ui-border: transparent;
|
|
79
|
-
--ui-hover-bg: var(--surface-solid);
|
|
80
|
-
|
|
81
|
-
--heading: var(--surface);
|
|
82
|
-
--busy-overlay-bg: var(--surface-solid);
|
|
83
|
-
|
|
84
|
-
background: var(--ui-bg);
|
|
85
|
-
color: var(--ui-fg);
|
|
86
|
-
border-color: var(--ui-border);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
63
|
.compact {
|
|
90
64
|
--gap: var(--space-20);
|
|
91
65
|
--density-space: var(--space-20);
|
|
@@ -169,10 +143,6 @@
|
|
|
169
143
|
);
|
|
170
144
|
/* @sync soft-recipe */
|
|
171
145
|
}
|
|
172
|
-
|
|
173
|
-
.inverted {
|
|
174
|
-
--ui-hover-bg: color-mix(in oklch, var(--surface-solid) 88%, black);
|
|
175
|
-
}
|
|
176
146
|
}
|
|
177
147
|
|
|
178
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/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 {
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
* Docs: docs/pages/layout/column-layout.md
|
|
5
5
|
*
|
|
6
6
|
* Neither space-driven like .grid nor count-driven like .grid-N: this is a
|
|
7
|
-
* coordinate system.
|
|
8
|
-
*
|
|
9
|
-
* deliberately no automatic collapse — no threshold,
|
|
10
|
-
* .container-query behavior, no breakpoint variants. A single
|
|
11
|
-
* threshold cannot be right for arbitrary per-region compositions,
|
|
12
|
-
* hidden default would have to be undone rather than extended.
|
|
7
|
+
* coordinate system. Each direct child places itself with two numbers,
|
|
8
|
+
* --column-start and --column-span, and the author owns narrow-container
|
|
9
|
+
* recomposition. There is deliberately no automatic collapse — no threshold,
|
|
10
|
+
* no hidden .container-query behavior, no breakpoint variants. A single
|
|
11
|
+
* framework threshold cannot be right for arbitrary per-region compositions,
|
|
12
|
+
* and a hidden default would have to be undone rather than extended.
|
|
13
13
|
*
|
|
14
14
|
* See docs/design-notes/column-layout.md.
|
|
15
15
|
*
|
|
@@ -21,11 +21,12 @@
|
|
|
21
21
|
* media + flexible content -> .media
|
|
22
22
|
* exact custom track templates -> --grid-columns on .grid
|
|
23
23
|
*
|
|
24
|
-
* Child contract: each direct child is placed on the canvas via its own
|
|
25
|
-
* .column-span-N / .column-start-N.
|
|
26
|
-
*
|
|
27
24
|
* Unlike .grid-N, the track template does not read --gap, so the gap
|
|
28
25
|
* utilities are exact here at every width.
|
|
26
|
+
*
|
|
27
|
+
* Public hooks:
|
|
28
|
+
* --column-start (the start line, 1-12; unset = auto-placed)
|
|
29
|
+
* --column-span (the width in canvas units, 1-12; unset = to the canvas end)
|
|
29
30
|
*/
|
|
30
31
|
.column-layout {
|
|
31
32
|
display: grid;
|
|
@@ -33,114 +34,28 @@
|
|
|
33
34
|
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
/*
|
|
37
|
-
*
|
|
37
|
+
/* One rule covers the four placements:
|
|
38
|
+
* neither auto-placed, spanning the whole canvas
|
|
39
|
+
* span only auto-placed over N units, so 8 + 4 flows onto one row
|
|
40
|
+
* start only from line S to the canvas end (span 13 - S)
|
|
41
|
+
* start and span explicit; keep S + N - 1 <= 12
|
|
38
42
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* 3. .column-span-* the end only, so it refines either of the above
|
|
43
|
+
* The start stays `auto` unless set: a definite start on every child would
|
|
44
|
+
* take all of them out of auto-placement, and an 8 + 4 pair would stack.
|
|
42
45
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* Zero specificity is the point: an application's own container-query rules
|
|
49
|
-
* override placement additively, with a single-class selector and without
|
|
50
|
-
* having to undo framework specificity first.
|
|
51
|
-
*/
|
|
52
|
-
|
|
53
|
-
/* Tier 1. A child with no placement class stays a readable full-width region
|
|
54
|
-
* instead of collapsing to one twelfth. Expressed as a span, not as
|
|
55
|
-
* `1 / -1`: a definite start line would give every child a definite column
|
|
56
|
-
* position and disable auto-placement, so `.column-span-8` next to
|
|
57
|
-
* `.column-span-4` would stack instead of composing 8 + 4.
|
|
46
|
+
* The reset makes the two hooks inputs of this grid item rather than an
|
|
47
|
+
* inherited context: a nested .column-layout keeps the coordinates it was
|
|
48
|
+
* given for its own placement, and its children start from nothing. Zero
|
|
49
|
+
* specificity, so any author rule on the child wins.
|
|
58
50
|
*
|
|
59
51
|
* min-inline-size: 0 keeps the item box inside its track so the canvas never
|
|
60
52
|
* grows past its container. It does not stop content from painting outside a
|
|
61
53
|
* narrow track — that distinction is the primitive's contract, not a bug. */
|
|
62
54
|
:where(.column-layout) > * {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/* Tier 2. A definite start line, running to the end of the canvas unless a
|
|
68
|
-
* .column-span-* refines the end. `column-start-9` alone is therefore the
|
|
69
|
-
* last four columns rather than an implicit 21-line overflow. */
|
|
70
|
-
:where(.column-start-1) {
|
|
71
|
-
grid-column: 1 / -1;
|
|
72
|
-
}
|
|
73
|
-
:where(.column-start-2) {
|
|
74
|
-
grid-column: 2 / -1;
|
|
75
|
-
}
|
|
76
|
-
:where(.column-start-3) {
|
|
77
|
-
grid-column: 3 / -1;
|
|
78
|
-
}
|
|
79
|
-
:where(.column-start-4) {
|
|
80
|
-
grid-column: 4 / -1;
|
|
81
|
-
}
|
|
82
|
-
:where(.column-start-5) {
|
|
83
|
-
grid-column: 5 / -1;
|
|
84
|
-
}
|
|
85
|
-
:where(.column-start-6) {
|
|
86
|
-
grid-column: 6 / -1;
|
|
87
|
-
}
|
|
88
|
-
:where(.column-start-7) {
|
|
89
|
-
grid-column: 7 / -1;
|
|
90
|
-
}
|
|
91
|
-
:where(.column-start-8) {
|
|
92
|
-
grid-column: 8 / -1;
|
|
93
|
-
}
|
|
94
|
-
:where(.column-start-9) {
|
|
95
|
-
grid-column: 9 / -1;
|
|
96
|
-
}
|
|
97
|
-
:where(.column-start-10) {
|
|
98
|
-
grid-column: 10 / -1;
|
|
99
|
-
}
|
|
100
|
-
:where(.column-start-11) {
|
|
101
|
-
grid-column: 11 / -1;
|
|
102
|
-
}
|
|
103
|
-
:where(.column-start-12) {
|
|
104
|
-
grid-column: 12 / -1;
|
|
105
|
-
}
|
|
55
|
+
--column-start: initial;
|
|
56
|
+
--column-span: initial;
|
|
106
57
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
* just the offending region. */
|
|
111
|
-
:where(.column-span-1) {
|
|
112
|
-
grid-column-end: span 1;
|
|
113
|
-
}
|
|
114
|
-
:where(.column-span-2) {
|
|
115
|
-
grid-column-end: span 2;
|
|
116
|
-
}
|
|
117
|
-
:where(.column-span-3) {
|
|
118
|
-
grid-column-end: span 3;
|
|
119
|
-
}
|
|
120
|
-
:where(.column-span-4) {
|
|
121
|
-
grid-column-end: span 4;
|
|
122
|
-
}
|
|
123
|
-
:where(.column-span-5) {
|
|
124
|
-
grid-column-end: span 5;
|
|
125
|
-
}
|
|
126
|
-
:where(.column-span-6) {
|
|
127
|
-
grid-column-end: span 6;
|
|
128
|
-
}
|
|
129
|
-
:where(.column-span-7) {
|
|
130
|
-
grid-column-end: span 7;
|
|
131
|
-
}
|
|
132
|
-
:where(.column-span-8) {
|
|
133
|
-
grid-column-end: span 8;
|
|
134
|
-
}
|
|
135
|
-
:where(.column-span-9) {
|
|
136
|
-
grid-column-end: span 9;
|
|
137
|
-
}
|
|
138
|
-
:where(.column-span-10) {
|
|
139
|
-
grid-column-end: span 10;
|
|
140
|
-
}
|
|
141
|
-
:where(.column-span-11) {
|
|
142
|
-
grid-column-end: span 11;
|
|
143
|
-
}
|
|
144
|
-
:where(.column-span-12) {
|
|
145
|
-
grid-column-end: span 12;
|
|
58
|
+
grid-column-start: var(--column-start, auto);
|
|
59
|
+
grid-column-end: span var(--column-span, calc(13 - var(--column-start, 1)));
|
|
60
|
+
min-inline-size: 0;
|
|
146
61
|
}
|