actual-css 0.1.0 → 0.2.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 +53 -1
- package/README.md +2 -0
- package/dist/actual.css +356 -124
- package/dist/actual.js +2 -2
- package/dist/actual.js.map +13 -13
- package/dist/actual.min.css +1 -1
- package/docs/README.md +2 -0
- package/docs/components.md +2 -2
- package/docs/design-notes/browser-support.md +9 -0
- package/docs/design-notes/enhancement-contract.md +104 -0
- package/docs/design-notes/naming.md +1 -0
- package/docs/design-notes/widget-primitives.md +83 -0
- package/docs/forms.md +4 -4
- package/docs/javascript.md +104 -34
- package/docs/patterns.md +89 -0
- package/docs/ui.md +22 -30
- package/llms.txt +6 -0
- package/package.json +14 -8
- package/src/css/base.css +0 -4
- package/src/css/components/alert.css +88 -2
- package/src/css/components/badge.css +30 -7
- package/src/css/components/busy.css +1 -0
- package/src/css/components/button.css +6 -8
- package/src/css/components/flyout.css +0 -1
- package/src/css/components/join.css +1 -2
- package/src/css/components/scrollspy.css +2 -2
- package/src/css/components/spinner.css +4 -4
- package/src/css/components/status-bar.css +3 -4
- package/src/css/components/tab.css +1 -0
- package/src/css/focus.css +37 -0
- package/src/css/forms/choice-card.css +1 -2
- package/src/css/forms/choice.css +1 -2
- package/src/css/forms/control.css +1 -10
- package/src/css/forms/index.css +1 -0
- package/src/css/forms/native.css +8 -32
- package/src/css/forms/range.css +95 -0
- package/src/css/forms/switch.css +1 -2
- package/src/css/optional/scroller.css +6 -0
- package/src/css/theme.css +9 -9
- package/src/css/themes/edge.css +3 -27
- package/src/css/tokens.css +9 -3
- package/src/js/context-menu.js +21 -34
- package/src/js/enhance.js +26 -0
- package/src/js/flyout.js +12 -19
- package/src/js/menu.js +15 -14
- package/src/js/scrollspy.js +125 -62
- package/src/js/selectors.js +14 -24
- package/src/js/status.js +13 -13
- package/src/js/surface.js +57 -12
- package/src/js/tab.js +10 -13
- package/src/js/validation.js +11 -43
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
.alert {
|
|
25
|
-
--alert-pad-block: var(--variant-pad-block, var(--space-
|
|
25
|
+
--alert-pad-block: var(--variant-pad-block, var(--space-3));
|
|
26
26
|
--alert-pad-inline: var(--space-4);
|
|
27
27
|
--alert-font-size: var(--variant-font-size, var(--font-size-md));
|
|
28
28
|
--alert-icon-size: var(--variant-icon-size, 1.25rem);
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
@supports (color: color-mix(in oklch, red, white)) {
|
|
75
|
-
.alert:where(:not(.solid, .outline)) {
|
|
75
|
+
.alert:where(:not(.solid, .outline, .callout)) {
|
|
76
76
|
/* @sync soft-recipe */
|
|
77
77
|
--ui-bg: color-mix(in oklch, var(--surface) var(--soft-bg-mix), var(--intent, var(--neutral)));
|
|
78
78
|
--ui-border: color-mix(
|
|
@@ -135,3 +135,89 @@
|
|
|
135
135
|
readable color, and text on a Highlight surface would trigger Chromium's
|
|
136
136
|
Canvas backplate (unreadable) — so .solid intentionally falls back to the
|
|
137
137
|
default look. */
|
|
138
|
+
|
|
139
|
+
/* Callout — neutral surface with an inset accent band on the leading edge.
|
|
140
|
+
The ::before pseudo is absolutely positioned and inset to respect the
|
|
141
|
+
outer border-radius, so it never needs its own radius. */
|
|
142
|
+
.alert.callout {
|
|
143
|
+
--alert-bg: var(--surface-subtle);
|
|
144
|
+
--ui-bg: var(--alert-bg);
|
|
145
|
+
--ui-fg: var(--text);
|
|
146
|
+
--ui-border: var(--border);
|
|
147
|
+
position: relative;
|
|
148
|
+
padding-inline-start: calc(var(--alert-pad-inline) + var(--space-3));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.alert.callout::before {
|
|
152
|
+
content: "";
|
|
153
|
+
position: absolute;
|
|
154
|
+
inset-block: var(--space-2);
|
|
155
|
+
inset-inline-start: var(--space-2);
|
|
156
|
+
inline-size: 0.25rem;
|
|
157
|
+
border-radius: var(--radius-full);
|
|
158
|
+
background: var(--intent, var(--neutral));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* Admonition — structured title/body anatomy with a tinted title band.
|
|
162
|
+
The root is layout-neutralized: no padding, no gap, no icon grid. */
|
|
163
|
+
.alert.admonition {
|
|
164
|
+
padding: 0;
|
|
165
|
+
gap: 0;
|
|
166
|
+
display: block;
|
|
167
|
+
overflow: clip;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.alert-title {
|
|
171
|
+
padding-block: var(--alert-pad-block);
|
|
172
|
+
padding-inline: var(--alert-pad-inline);
|
|
173
|
+
|
|
174
|
+
@supports (color: color-mix(in oklch, red, white)) {
|
|
175
|
+
background: color-mix(
|
|
176
|
+
in oklch,
|
|
177
|
+
var(--surface) var(--soft-bg-mix),
|
|
178
|
+
var(--intent, var(--neutral))
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@supports not (color: color-mix(in oklch, red, white)) {
|
|
183
|
+
background: var(--intent, var(--neutral));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
border-start-start-radius: calc(var(--radius-lg) - var(--border-width));
|
|
187
|
+
border-start-end-radius: calc(var(--radius-lg) - var(--border-width));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.alert-title:where(summary) {
|
|
191
|
+
cursor: pointer;
|
|
192
|
+
user-select: none;
|
|
193
|
+
list-style: none;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.alert-title:where(summary)::-webkit-details-marker {
|
|
197
|
+
display: none;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.alert-title[aria-hidden] {
|
|
201
|
+
display: inline-flex;
|
|
202
|
+
gap: var(--space-2);
|
|
203
|
+
align-items: center;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.alert-body {
|
|
207
|
+
padding-block: var(--alert-pad-block);
|
|
208
|
+
padding-inline: var(--alert-pad-inline);
|
|
209
|
+
color: var(--text);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.alert-body > :first-child {
|
|
213
|
+
margin-block-start: 0;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.alert-body > :last-child {
|
|
217
|
+
margin-block-end: 0;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
details.alert.admonition[open] .alert-title {
|
|
221
|
+
border-end-start-radius: 0;
|
|
222
|
+
border-end-end-radius: 0;
|
|
223
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Badges for compact labels, counts, and state hints.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Soft by default. .solid for counters and notification badges. The dot
|
|
5
|
+
* (.badge:empty) stays solid regardless — a soft dot is pointless.
|
|
6
|
+
* Shared .outline variant stays available.
|
|
6
7
|
*/
|
|
7
8
|
/* Intent boundary — inherited intents do not tint this component.
|
|
8
9
|
Local intent classes still win because :where() has zero specificity. */
|
|
@@ -22,9 +23,9 @@
|
|
|
22
23
|
--badge-pad-x: 0.55em;
|
|
23
24
|
--badge-font-size: var(--variant-compact-font-size, 0.8125rem);
|
|
24
25
|
--badge-dot-size: 0.5rem;
|
|
25
|
-
--badge-bg: var(--ui-bg,
|
|
26
|
-
--badge-fg: var(--ui-fg, var(--intent
|
|
27
|
-
--badge-border: var(--ui-border,
|
|
26
|
+
--badge-bg: var(--ui-bg, transparent);
|
|
27
|
+
--badge-fg: var(--ui-fg, var(--intent, var(--text)));
|
|
28
|
+
--badge-border: var(--ui-border, var(--intent, var(--border)));
|
|
28
29
|
--badge-radius: 999px;
|
|
29
30
|
|
|
30
31
|
display: inline-flex;
|
|
@@ -49,8 +50,31 @@
|
|
|
49
50
|
box-shadow var(--duration);
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
/* Soft by default — duplicated here with @sync markers for check-sync.js
|
|
54
|
+
to guard, the same way alert.css guards its own soft recipe. */
|
|
55
|
+
@supports (color: color-mix(in oklch, red, white)) {
|
|
56
|
+
.badge:where(:not(.solid, .outline)) {
|
|
57
|
+
/* @sync soft-recipe */
|
|
58
|
+
--badge-bg: color-mix(
|
|
59
|
+
in oklch,
|
|
60
|
+
var(--surface) var(--soft-bg-mix),
|
|
61
|
+
var(--intent, var(--neutral))
|
|
62
|
+
);
|
|
63
|
+
--badge-fg: var(--intent, var(--text));
|
|
64
|
+
--badge-border: color-mix(
|
|
65
|
+
in oklch,
|
|
66
|
+
var(--surface) var(--soft-border-mix),
|
|
67
|
+
var(--intent, var(--neutral))
|
|
68
|
+
);
|
|
69
|
+
/* @sync soft-recipe */
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
52
73
|
.badge:empty {
|
|
53
74
|
--badge-size: var(--badge-dot-size);
|
|
75
|
+
--badge-bg: var(--intent, var(--neutral));
|
|
76
|
+
--badge-fg: var(--intent-fg, var(--neutral-fg));
|
|
77
|
+
--badge-border: transparent;
|
|
54
78
|
padding: 0;
|
|
55
79
|
border: 0;
|
|
56
80
|
inline-size: var(--badge-size);
|
|
@@ -83,8 +107,7 @@
|
|
|
83
107
|
}
|
|
84
108
|
|
|
85
109
|
/* .solid/.soft/.outline are shared via variants.css, including the
|
|
86
|
-
.soft/.outline hover recipe (--ui-hover-bg).
|
|
87
|
-
.badge solid by default. */
|
|
110
|
+
.soft/.outline hover recipe (--ui-hover-bg). */
|
|
88
111
|
@media (hover: hover) {
|
|
89
112
|
:where(a.badge, button.badge):not(:where(:disabled, [aria-disabled="true"])):hover {
|
|
90
113
|
background: var(--ui-hover-bg, var(--badge-bg));
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
--btn-border: var(--ui-border, var(--btn-default-border, transparent));
|
|
34
34
|
--btn-radius: var(--radius);
|
|
35
35
|
--btn-focus-color: var(--intent, var(--focus));
|
|
36
|
-
--btn-focus-outline-width: calc(var(--border-width) * 2);
|
|
37
36
|
--btn-hover-bg: var(--ui-hover-bg, var(--btn-bg));
|
|
38
37
|
--btn-hover-overlay: var(--hover-overlay, rgb(0 0 0 / 0.08));
|
|
39
38
|
|
|
@@ -41,7 +40,7 @@
|
|
|
41
40
|
display: inline-flex;
|
|
42
41
|
align-items: center;
|
|
43
42
|
justify-content: center;
|
|
44
|
-
gap: 0.
|
|
43
|
+
gap: 0.375em;
|
|
45
44
|
min-block-size: var(--btn-min-size);
|
|
46
45
|
padding-block: var(--space-2);
|
|
47
46
|
padding-block: max(var(--space-1), calc((var(--btn-min-size) - 1lh) / 2));
|
|
@@ -57,7 +56,7 @@
|
|
|
57
56
|
color: var(--btn-fg);
|
|
58
57
|
font: inherit;
|
|
59
58
|
font-size: var(--btn-font-size);
|
|
60
|
-
font-weight: var(--font-weight-
|
|
59
|
+
font-weight: var(--font-weight-medium);
|
|
61
60
|
line-height: 1;
|
|
62
61
|
text-decoration: none;
|
|
63
62
|
cursor: pointer;
|
|
@@ -133,14 +132,14 @@
|
|
|
133
132
|
}
|
|
134
133
|
|
|
135
134
|
.btn:focus-visible {
|
|
136
|
-
outline:
|
|
137
|
-
|
|
138
|
-
box-shadow: 0 0 0 3px var(--btn-focus-ring-color, var(--focus-ring));
|
|
135
|
+
outline: none;
|
|
136
|
+
box-shadow: 0 0 0 var(--focus-ring-width) var(--btn-focus-ring-color, var(--focus-ring));
|
|
139
137
|
}
|
|
140
138
|
|
|
141
139
|
.btn.link:focus-visible {
|
|
142
|
-
outline
|
|
140
|
+
outline: var(--focus-outline);
|
|
143
141
|
outline-offset: 0.2em;
|
|
142
|
+
box-shadow: none;
|
|
144
143
|
}
|
|
145
144
|
|
|
146
145
|
.btn:disabled {
|
|
@@ -164,7 +163,6 @@ a.btn[aria-disabled="true"] {
|
|
|
164
163
|
}
|
|
165
164
|
|
|
166
165
|
.btn[aria-busy="true"] > .spinner {
|
|
167
|
-
--spinner-size: 1em;
|
|
168
166
|
--spinner-width: 0.15em;
|
|
169
167
|
}
|
|
170
168
|
|
|
@@ -95,8 +95,7 @@
|
|
|
95
95
|
focus, and unsupported :has() browsers retain the control's normal ring. */
|
|
96
96
|
.join:has(> :is(.input, .textarea, .select):focus-visible) {
|
|
97
97
|
border-radius: var(--join-radius);
|
|
98
|
-
outline:
|
|
99
|
-
outline-offset: var(--focus-outline-offset);
|
|
98
|
+
outline: none;
|
|
100
99
|
box-shadow: var(--focus-ring-shadow);
|
|
101
100
|
}
|
|
102
101
|
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* Progressive enhancement: on browsers that support :target-current,
|
|
12
12
|
* this file enables the native target group and marks the active link.
|
|
13
13
|
*
|
|
14
|
-
* Falls back gracefully: links still work when
|
|
15
|
-
*
|
|
14
|
+
* Falls back gracefully: links still work when :target-current is unavailable
|
|
15
|
+
* and when the JavaScript enhancer is not loaded at all.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
@supports selector(.scrollspy a:target-current) {
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
.spinner {
|
|
21
|
-
--spinner-size:
|
|
22
|
-
--spinner-width:
|
|
21
|
+
--spinner-size: 1em;
|
|
22
|
+
--spinner-width: 0.125em;
|
|
23
23
|
--spinner-track: currentColor;
|
|
24
24
|
--spinner-value: transparent;
|
|
25
25
|
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
.spinner.sm {
|
|
48
|
-
--spinner-size:
|
|
48
|
+
--spinner-size: 0.75em;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
.spinner.lg {
|
|
52
|
-
--spinner-size:
|
|
52
|
+
--spinner-size: 2rem;
|
|
53
53
|
--spinner-width: 3px;
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -10,10 +10,9 @@
|
|
|
10
10
|
* the coloring via --intent / --intent-fg. status.js applies these as plain
|
|
11
11
|
* classes on the target; it does not know their names or that they exist.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* that carry their own generic `data-status`.
|
|
13
|
+
* CSS API: .status-bar
|
|
14
|
+
* JS target contract: [data-status][role="status"]
|
|
15
|
+
* Normally combined, but they describe separate concerns.
|
|
17
16
|
*/
|
|
18
17
|
|
|
19
18
|
/* Intent boundary — inherited intents do not tint this component.
|
package/src/css/focus.css
CHANGED
|
@@ -11,3 +11,40 @@
|
|
|
11
11
|
outline: none;
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* Forced-colors focus — centralised so individual components do not need
|
|
17
|
+
* their own fallback. The double :focus keeps specificity high enough
|
|
18
|
+
* (0,0,2,0) to beat a single :focus-visible; against a class selector like
|
|
19
|
+
* .input:focus-visible it ties, but this file loads last so it wins on
|
|
20
|
+
* source order.
|
|
21
|
+
*
|
|
22
|
+
* .choice-card and .join receive focus indirectly (:has on a wrapper that
|
|
23
|
+
* never matches :focus), so they are listed explicitly.
|
|
24
|
+
*
|
|
25
|
+
* .range is excluded from the rectangular fallback: its focus is drawn on
|
|
26
|
+
* the thumb pseudo-element, which is the manipulated surface.
|
|
27
|
+
*
|
|
28
|
+
* box-shadow: none is redundant when forced-color-adjust is auto, but safe
|
|
29
|
+
* when a component uses forced-color-adjust: none (e.g. checked switches /
|
|
30
|
+
* checkboxes).
|
|
31
|
+
*/
|
|
32
|
+
@media (forced-colors: active) {
|
|
33
|
+
:focus:focus-visible:not(.range),
|
|
34
|
+
.choice-card:has(:focus-visible),
|
|
35
|
+
.join:has(> :is(.input, .textarea, .select):focus-visible) {
|
|
36
|
+
outline: var(--focus-outline);
|
|
37
|
+
outline-offset: var(--focus-outline-offset);
|
|
38
|
+
box-shadow: none;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.range:focus-visible::-webkit-slider-thumb {
|
|
42
|
+
outline: var(--focus-outline);
|
|
43
|
+
outline-offset: var(--focus-outline-offset);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.range:focus-visible::-moz-range-thumb {
|
|
47
|
+
outline: var(--focus-outline);
|
|
48
|
+
outline-offset: var(--focus-outline-offset);
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/css/forms/choice.css
CHANGED
|
@@ -71,8 +71,7 @@
|
|
|
71
71
|
.textarea:focus-visible,
|
|
72
72
|
.select:focus-visible {
|
|
73
73
|
--control-border: var(--focus);
|
|
74
|
-
outline:
|
|
75
|
-
outline-offset: var(--focus-outline-offset);
|
|
74
|
+
outline: none;
|
|
76
75
|
box-shadow: var(--focus-ring-shadow);
|
|
77
76
|
}
|
|
78
77
|
|
|
@@ -96,11 +95,3 @@
|
|
|
96
95
|
.textarea::placeholder {
|
|
97
96
|
color: var(--text-muted);
|
|
98
97
|
}
|
|
99
|
-
|
|
100
|
-
@media (forced-colors: active) {
|
|
101
|
-
.input:focus-visible,
|
|
102
|
-
.textarea:focus-visible,
|
|
103
|
-
.select:focus-visible {
|
|
104
|
-
box-shadow: none;
|
|
105
|
-
}
|
|
106
|
-
}
|
package/src/css/forms/index.css
CHANGED
package/src/css/forms/native.css
CHANGED
|
@@ -1,21 +1,11 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Native-only control polish: file
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Native-only control polish: file input that mostly defeats custom styling,
|
|
3
|
+
* so the framework stays out of the way and just keeps it readable and
|
|
4
|
+
* theme-aware.
|
|
5
|
+
*
|
|
6
|
+
* Range has its own custom-styled file (range.css) so the focus ring can
|
|
7
|
+
* target the thumb pseudo-element rather than the rectangular CSS box.
|
|
5
8
|
*/
|
|
6
|
-
/* Intent boundary — inherited intents do not tint this component.
|
|
7
|
-
Local intent classes still win because :where() has zero specificity. */
|
|
8
|
-
:where(.range) {
|
|
9
|
-
/* @sync intent-boundary */
|
|
10
|
-
--intent: initial;
|
|
11
|
-
--intent-fg: initial;
|
|
12
|
-
--ui-bg: initial;
|
|
13
|
-
--ui-fg: initial;
|
|
14
|
-
--ui-border: initial;
|
|
15
|
-
--ui-hover-bg: initial;
|
|
16
|
-
/* @sync intent-boundary */
|
|
17
|
-
}
|
|
18
|
-
|
|
19
9
|
.file {
|
|
20
10
|
color: var(--text-muted);
|
|
21
11
|
}
|
|
@@ -48,21 +38,7 @@
|
|
|
48
38
|
}
|
|
49
39
|
}
|
|
50
40
|
|
|
51
|
-
.
|
|
52
|
-
|
|
53
|
-
accent-color: var(--intent, var(--primary));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
.file:focus-visible,
|
|
57
|
-
.range:focus-visible {
|
|
58
|
-
outline: var(--focus-outline);
|
|
59
|
-
outline-offset: var(--focus-outline-offset);
|
|
41
|
+
.file:focus-visible {
|
|
42
|
+
outline: none;
|
|
60
43
|
box-shadow: var(--focus-ring-shadow);
|
|
61
44
|
}
|
|
62
|
-
|
|
63
|
-
@media (forced-colors: active) {
|
|
64
|
-
.file:focus-visible,
|
|
65
|
-
.range:focus-visible {
|
|
66
|
-
box-shadow: none;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Range slider — custom-styled so the focus ring always targets the
|
|
3
|
+
* user-manipulated surface (the thumb) rather than the CSS box.
|
|
4
|
+
*
|
|
5
|
+
* appearance: none gives us full control over track and thumb; without it,
|
|
6
|
+
* pseudo-element rules for ::-webkit-slider-thumb and ::-moz-range-thumb
|
|
7
|
+
* are not reliably applied across browsers.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/* Intent boundary — inherited intents do not tint this component.
|
|
11
|
+
Local intent classes still win because :where() has zero specificity. */
|
|
12
|
+
:where(.range) {
|
|
13
|
+
/* @sync intent-boundary */
|
|
14
|
+
--intent: initial;
|
|
15
|
+
--intent-fg: initial;
|
|
16
|
+
--ui-bg: initial;
|
|
17
|
+
--ui-fg: initial;
|
|
18
|
+
--ui-border: initial;
|
|
19
|
+
--ui-hover-bg: initial;
|
|
20
|
+
/* @sync intent-boundary */
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.range {
|
|
24
|
+
--range-thumb-size: 1.25rem;
|
|
25
|
+
--range-track-height: 0.25rem;
|
|
26
|
+
--range-thumb-bg: var(--intent, var(--primary));
|
|
27
|
+
--range-track-bg: var(--border);
|
|
28
|
+
|
|
29
|
+
appearance: none;
|
|
30
|
+
inline-size: 100%;
|
|
31
|
+
block-size: var(--range-thumb-size);
|
|
32
|
+
background: transparent;
|
|
33
|
+
cursor: pointer;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.range::-webkit-slider-runnable-track {
|
|
37
|
+
block-size: var(--range-track-height);
|
|
38
|
+
border-radius: var(--radius-full);
|
|
39
|
+
background: var(--range-track-bg);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.range::-moz-range-track {
|
|
43
|
+
block-size: var(--range-track-height);
|
|
44
|
+
border-radius: var(--radius-full);
|
|
45
|
+
background: var(--range-track-bg);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.range::-webkit-slider-thumb {
|
|
49
|
+
appearance: none;
|
|
50
|
+
inline-size: var(--range-thumb-size);
|
|
51
|
+
block-size: var(--range-thumb-size);
|
|
52
|
+
border-radius: var(--radius-full);
|
|
53
|
+
border: 0;
|
|
54
|
+
background: var(--range-thumb-bg);
|
|
55
|
+
margin-block-start: calc((var(--range-track-height) - var(--range-thumb-size)) / 2);
|
|
56
|
+
cursor: inherit;
|
|
57
|
+
transition: box-shadow var(--duration);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.range::-moz-range-thumb {
|
|
61
|
+
inline-size: var(--range-thumb-size);
|
|
62
|
+
block-size: var(--range-thumb-size);
|
|
63
|
+
border-radius: var(--radius-full);
|
|
64
|
+
border: 0;
|
|
65
|
+
background: var(--range-thumb-bg);
|
|
66
|
+
cursor: inherit;
|
|
67
|
+
transition: box-shadow var(--duration);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.range:focus-visible {
|
|
71
|
+
outline: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/*
|
|
75
|
+
* The 1px surface-coloured separator keeps the ring visually distinct from
|
|
76
|
+
* the small, coloured thumb — unlike a full-size input where the same gap
|
|
77
|
+
* would read as an unwanted void.
|
|
78
|
+
*/
|
|
79
|
+
.range:focus-visible::-webkit-slider-thumb {
|
|
80
|
+
box-shadow:
|
|
81
|
+
0 0 0 1px var(--surface),
|
|
82
|
+
0 0 0 calc(1px + var(--focus-ring-width)) var(--focus-ring);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.range:focus-visible::-moz-range-thumb {
|
|
86
|
+
box-shadow:
|
|
87
|
+
0 0 0 1px var(--surface),
|
|
88
|
+
0 0 0 calc(1px + var(--focus-ring-width)) var(--focus-ring);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.range:disabled {
|
|
92
|
+
--range-thumb-bg: var(--text-subtle);
|
|
93
|
+
cursor: not-allowed;
|
|
94
|
+
opacity: var(--disabled-opacity);
|
|
95
|
+
}
|
package/src/css/forms/switch.css
CHANGED
|
@@ -16,6 +16,12 @@
|
|
|
16
16
|
scrollbar-width: thin;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/* Opt-in gutter: reserves space so the layout does not shift when
|
|
20
|
+
the scrollbar appears. Use alongside a known-overflow container. */
|
|
21
|
+
.scroller.stable-gutter {
|
|
22
|
+
scrollbar-gutter: stable;
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
.scroller::-webkit-scrollbar {
|
|
20
26
|
width: var(--scroller-size);
|
|
21
27
|
height: var(--scroller-size);
|
package/src/css/theme.css
CHANGED
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
|
|
62
62
|
/* Lines and focus */
|
|
63
63
|
--border: hsl(260 15% 88%);
|
|
64
|
-
--focus: var(--
|
|
64
|
+
--focus: var(--neutral);
|
|
65
65
|
/* Pre-color-mix fallback only — superseded below by a derivation from
|
|
66
|
-
--focus so overriding --
|
|
67
|
-
--focus-ring: hsl(
|
|
68
|
-
--focus-ring-shadow: 0 0 0
|
|
66
|
+
--focus so overriding --neutral/--focus never leaves a stale ring. */
|
|
67
|
+
--focus-ring: hsl(258 7% 43% / 0.3);
|
|
68
|
+
--focus-ring-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring);
|
|
69
69
|
--hover-overlay: hsl(260 20% 10% / 0.04);
|
|
70
70
|
|
|
71
71
|
/* Radius scale — themes may override the whole scale (e.g. [data-theme="square"]) */
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
:root,
|
|
107
107
|
[data-theme="light"],
|
|
108
108
|
[data-theme="dark"] {
|
|
109
|
-
--focus-ring: color-mix(in oklch, var(--focus)
|
|
109
|
+
--focus-ring: color-mix(in oklch, var(--focus) 30%, transparent);
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
--text-subtle: light-dark(hsl(258 7% 57%), hsl(258 7% 54%));
|
|
147
147
|
|
|
148
148
|
--border: light-dark(hsl(260 15% 88%), hsl(262 12% 24%));
|
|
149
|
-
--focus-ring: light-dark(hsl(
|
|
149
|
+
--focus-ring: light-dark(hsl(258 7% 43% / 0.3), hsl(258 10% 72% / 0.28));
|
|
150
150
|
--hover-overlay: light-dark(hsl(260 20% 10% / 0.04), hsl(0 0% 100% / 0.06));
|
|
151
151
|
--shadow-color: light-dark(hsl(260 25% 10%), hsl(262 40% 2%));
|
|
152
152
|
}
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
:root,
|
|
157
157
|
[data-theme="light"] {
|
|
158
158
|
--focus-ring: light-dark(
|
|
159
|
-
color-mix(in oklch, var(--focus)
|
|
159
|
+
color-mix(in oklch, var(--focus) 30%, transparent),
|
|
160
160
|
color-mix(in oklch, var(--focus) 28%, transparent)
|
|
161
161
|
);
|
|
162
162
|
}
|
|
@@ -166,7 +166,7 @@
|
|
|
166
166
|
:root,
|
|
167
167
|
[data-theme="light"],
|
|
168
168
|
[data-theme="dark"] {
|
|
169
|
-
--focus-outline:
|
|
169
|
+
--focus-outline-color: Highlight;
|
|
170
170
|
--focus-ring-shadow: none;
|
|
171
171
|
|
|
172
172
|
--backdrop-fill: Canvas;
|
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
[data-theme="dark"] {
|
|
180
180
|
--border-width: 2px;
|
|
181
181
|
--focus-outline-offset: 3px;
|
|
182
|
-
--focus-ring-
|
|
182
|
+
--focus-ring-width: 4px;
|
|
183
183
|
--text-muted: var(--text);
|
|
184
184
|
--text-subtle: var(--text);
|
|
185
185
|
}
|