actual-css 0.2.0 → 0.3.1
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 +49 -1
- package/README.md +24 -9
- package/dist/actual-themes.min.css +1 -0
- package/dist/actual.css +1532 -1114
- package/dist/actual.js +1 -3
- package/dist/actual.min.css +1 -1
- package/dist/optional.css +1048 -0
- package/dist/optional.min.css +1 -0
- package/docs/design-notes/browser-support.md +94 -14
- package/docs/design-notes/cascade-layer.md +7 -6
- package/docs/design-notes/enhancement-contract.md +11 -14
- package/docs/design-notes/forced-colors.md +105 -0
- package/docs/design-notes/naming.md +2 -1
- package/docs/design-notes/platform-alignment.md +492 -0
- package/docs/design-notes/references.md +8 -0
- package/docs/design-notes/widget-primitives.md +37 -1
- package/docs/navigation.json +135 -0
- package/docs/pages/components/accordion.md +80 -0
- package/docs/pages/components/alert.md +203 -0
- package/docs/pages/components/aura.md +35 -0
- package/docs/pages/components/avatar.md +95 -0
- package/docs/pages/components/badge.md +102 -0
- package/docs/pages/components/breadcrumb.md +19 -0
- package/docs/pages/components/button.md +210 -0
- package/docs/pages/components/card.md +195 -0
- package/docs/pages/components/chat.md +50 -0
- package/docs/pages/components/dialog.md +367 -0
- package/docs/pages/components/drawer.md +113 -0
- package/docs/pages/components/fab.md +118 -0
- package/docs/pages/components/key.md +33 -0
- package/docs/pages/components/meter.md +23 -0
- package/docs/pages/components/navbar.md +92 -0
- package/docs/pages/components/pagination.md +55 -0
- package/docs/pages/components/progress.md +29 -0
- package/docs/pages/components/skeleton.md +38 -0
- package/docs/pages/components/spinner.md +93 -0
- package/docs/pages/components/table.md +102 -0
- package/docs/pages/enhancements/flyout.md +391 -0
- package/docs/pages/enhancements/overview.md +36 -0
- package/docs/pages/enhancements/scrollspy.md +71 -0
- package/docs/pages/enhancements/status-bar.md +72 -0
- package/docs/pages/enhancements/tabs.md +80 -0
- package/docs/pages/enhancements/tooltip.md +130 -0
- package/docs/pages/examples/overview.md +43 -0
- package/docs/pages/forms/choice-cards.md +102 -0
- package/docs/pages/forms/controls.md +32 -0
- package/docs/pages/forms/detached-actions.md +59 -0
- package/docs/pages/forms/floating-field.md +88 -0
- package/docs/pages/forms/form-actions.md +56 -0
- package/docs/pages/forms/input-policy.md +91 -0
- package/docs/pages/forms/joined-controls.md +37 -0
- package/docs/pages/forms/labels.md +42 -0
- package/docs/pages/forms/otp.md +50 -0
- package/docs/pages/forms/overview.md +188 -0
- package/docs/pages/forms/password.md +40 -0
- package/docs/pages/forms/range.md +17 -0
- package/docs/pages/forms/states-and-sizes.md +321 -0
- package/docs/pages/forms/sticky-actions.md +187 -0
- package/docs/pages/forms/switch.md +31 -0
- package/docs/pages/forms/validation.md +241 -0
- package/docs/{mental-model.md → pages/foundations/philosophy.md} +33 -18
- package/docs/pages/foundations/theming.md +99 -0
- package/docs/pages/foundations/tokens.md +421 -0
- package/docs/{typography.md → pages/foundations/typography.md} +27 -9
- package/docs/pages/guides/bootstrap.md +438 -0
- package/docs/pages/guides/introduction.md +157 -0
- package/docs/pages/guides/optional-components.md +33 -0
- package/docs/pages/guides/progressive-enhancement.md +686 -0
- package/docs/pages/guides/tailwind.md +455 -0
- package/docs/pages/layout/app-shell.md +25 -0
- package/docs/pages/layout/center.md +29 -0
- package/docs/pages/layout/cluster.md +59 -0
- package/docs/pages/layout/frame.md +30 -0
- package/docs/pages/layout/grid.md +142 -0
- package/docs/pages/layout/list.md +41 -0
- package/docs/pages/layout/media.md +28 -0
- package/docs/pages/layout/overview.md +37 -0
- package/docs/pages/layout/scroll-snap.md +54 -0
- package/docs/pages/layout/sidebar-layout.md +27 -0
- package/docs/pages/layout/stack.md +55 -0
- package/docs/pages/layout/switcher.md +36 -0
- package/docs/pages/layout/topbar.md +23 -0
- package/docs/pages/patterns/actions.md +52 -0
- package/docs/pages/patterns/code-block.md +36 -0
- package/docs/pages/patterns/exclusive-accordion.md +16 -0
- package/docs/pages/patterns/hierarchical-nav.md +26 -0
- package/docs/pages/patterns/icon-input.md +25 -0
- package/docs/pages/patterns/intent-nesting.md +3 -0
- package/docs/pages/patterns/navigation-list.md +235 -0
- package/docs/pages/patterns/overview.md +58 -0
- package/docs/pages/patterns/structured-lists.md +47 -0
- package/docs/pages/utilities/accessibility.md +34 -0
- package/docs/pages/utilities/flex.md +105 -0
- package/docs/pages/utilities/links.md +18 -0
- package/docs/pages/utilities/list.md +32 -0
- package/docs/pages/utilities/overflow.md +43 -0
- package/docs/pages/utilities/overview.md +18 -0
- package/docs/pages/utilities/shape.md +25 -0
- package/docs/pages/utilities/sizing-wrapping.md +115 -0
- package/docs/pages/utilities/spacing.md +79 -0
- package/docs/pages/utilities/text.md +43 -0
- package/llms.txt +4 -4
- package/package.json +21 -12
- package/src/css/actual.css +16 -2
- package/src/css/base.css +3 -5
- package/src/css/components/accordion.css +18 -30
- package/src/css/components/actions.css +4 -1
- package/src/css/components/alert.css +139 -102
- package/src/css/components/avatar.css +16 -10
- package/src/css/components/badge.css +69 -42
- package/src/css/components/breadcrumb.css +7 -15
- package/src/css/components/busy.css +6 -0
- package/src/css/components/button.css +84 -34
- package/src/css/components/card.css +26 -38
- package/src/css/components/drawer.css +30 -13
- package/src/css/components/flyout.css +39 -109
- package/src/css/components/index.css +1 -0
- package/src/css/components/join.css +14 -2
- package/src/css/components/key.css +2 -0
- package/src/css/components/menu.css +125 -0
- package/src/css/components/meter.css +0 -23
- package/src/css/components/modal.css +162 -7
- package/src/css/components/navbar.css +30 -23
- package/src/css/components/overline.css +8 -9
- package/src/css/components/pagination.css +1 -1
- package/src/css/components/progress.css +8 -26
- package/src/css/components/skeleton.css +7 -0
- package/src/css/components/spinner.css +6 -7
- package/src/css/components/status-bar.css +15 -24
- package/src/css/components/surface.css +2 -8
- package/src/css/components/tab.css +19 -48
- package/src/css/components/table.css +7 -5
- package/src/css/components/tooltip.css +14 -7
- package/src/css/focus.css +14 -38
- package/src/css/forms/choice-card.css +13 -14
- package/src/css/forms/choice.css +21 -27
- package/src/css/forms/control.css +18 -17
- package/src/css/forms/custom-select.css +9 -11
- package/src/css/forms/form-actions.css +14 -3
- package/src/css/forms/form.css +7 -4
- package/src/css/forms/index.css +4 -4
- package/src/css/forms/input-icon.css +30 -12
- package/src/css/forms/native.css +50 -11
- package/src/css/forms/range.css +17 -9
- package/src/css/forms/select.css +8 -8
- package/src/css/forms/switch.css +14 -12
- package/src/css/forms/validation.css +32 -5
- package/src/css/grid.css +32 -18
- package/src/css/intents.css +2 -2
- package/src/css/layout.css +45 -2
- package/src/css/optional/aura.css +93 -0
- package/src/css/optional/chat.css +105 -0
- package/src/css/optional/fab.css +104 -0
- package/src/css/optional/floating-field.css +87 -0
- package/src/css/optional/index.css +5 -0
- package/src/css/optional/layout-extra.css +7 -4
- package/src/css/optional/otp.css +115 -0
- package/src/css/optional/scroll-snap.css +6 -0
- package/src/css/optional/scroller.css +13 -7
- package/src/css/optional/typography-fluid.css +5 -0
- package/src/css/optional/utilities-extra.css +17 -2
- package/src/css/prose.css +18 -8
- package/src/css/reset.css +0 -10
- package/src/css/theme.css +21 -5
- package/src/css/themes/cyberpunk.css +1 -1
- package/src/css/themes/dim.css +3 -3
- package/src/css/themes/edge.css +8 -10
- package/src/css/themes/index.css +8 -4
- package/src/css/themes/{dark.css → indigo.css} +5 -4
- package/src/css/themes/neon.css +5 -5
- package/src/css/themes/square.css +0 -3
- package/src/css/tokens.css +74 -20
- package/src/css/utilities.css +8 -10
- package/src/css/variants.css +51 -66
- package/src/js/command.js +1 -3
- package/src/js/context-menu.js +67 -35
- package/src/js/dialog.js +68 -79
- package/src/js/dismiss.js +15 -0
- package/src/js/enhance.js +54 -23
- package/src/js/enhancement-loader.js +301 -0
- package/src/js/events.js +3 -0
- package/src/js/filter.js +3 -3
- package/src/js/floating.js +101 -131
- package/src/js/flyout.js +104 -87
- package/src/js/focus-group.js +116 -0
- package/src/js/focus.js +6 -1
- package/src/js/index.js +4 -2
- package/src/js/input.js +1 -1
- package/src/js/keys.js +27 -0
- package/src/js/mask.js +7 -6
- package/src/js/menu.js +87 -34
- package/src/js/parse-config.js +47 -0
- package/src/js/password.js +9 -1
- package/src/js/scrollspy.js +12 -4
- package/src/js/selectors.js +0 -7
- package/src/js/status.js +3 -1
- package/src/js/surface.js +187 -136
- package/src/js/tab.js +36 -53
- package/src/js/tooltip.js +70 -35
- package/src/js/validation.js +34 -8
- package/dist/actual.js.map +0 -31
- package/docs/README.md +0 -28
- package/docs/components.md +0 -860
- package/docs/design-notes/previous-work.md +0 -5
- package/docs/forms.md +0 -1265
- package/docs/javascript.md +0 -496
- package/docs/layout.md +0 -480
- package/docs/patterns.md +0 -504
- package/docs/reserved.md +0 -7
- package/docs/tokens.md +0 -455
- package/docs/ui.md +0 -929
- package/docs/utilities.md +0 -384
- package/src/css/dialog-fallback.css +0 -56
- package/src/js/dialog-fallback.js +0 -163
package/dist/actual.css
CHANGED
|
@@ -24,16 +24,6 @@ select {
|
|
|
24
24
|
display: none !important;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/* Legacy dialog fallback only. Modern dialogs keep the browser's native closed
|
|
28
|
-
state so display/overlay transitions are not flattened by the reset.
|
|
29
|
-
The presentation for the dialog-fallback.js shim lives in the separate
|
|
30
|
-
dialog-fallback.css, so it can be dropped from custom bundles. */
|
|
31
|
-
@supports not (selector(dialog:modal)) {
|
|
32
|
-
dialog:not([open]) {
|
|
33
|
-
display: none;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
27
|
:where(img, svg, video) {
|
|
38
28
|
max-inline-size: 100%;
|
|
39
29
|
block-size: auto;
|
|
@@ -55,64 +45,24 @@ select {
|
|
|
55
45
|
}
|
|
56
46
|
}
|
|
57
47
|
|
|
58
|
-
/*
|
|
59
|
-
* Legacy dialog fallback — presentation for the dialog-fallback.js shim
|
|
60
|
-
* (browsers without native <dialog>: Safari <= 15.3, Firefox <= 97).
|
|
61
|
-
*
|
|
62
|
-
* Isolated in its own file so a custom bundle can opt out by dropping this
|
|
63
|
-
* import; the JS side is disabled with data-dialog-fallback="false" on the
|
|
64
|
-
* root element or on a dialog. Must load before component styles so ties
|
|
65
|
-
* such as dialog.modal's padding resolve in the component's favor.
|
|
66
|
-
*
|
|
67
|
-
* The whole file is inert on modern browsers: rules key on the
|
|
68
|
-
* .dialog-fallback marker written by the shim, so browsers that support
|
|
69
|
-
* <dialog> but not :modal are never affected either.
|
|
70
|
-
*/
|
|
71
|
-
@supports not (selector(dialog:modal)) {
|
|
72
|
-
/* Browsers without native <dialog> ship no UA dialog styles at all;
|
|
73
|
-
approximate them. :where() keeps specificity at dialog[open] level so
|
|
74
|
-
component styles such as dialog.modal still override the emulation. */
|
|
75
|
-
dialog[open]:where(.dialog-fallback) {
|
|
76
|
-
position: absolute;
|
|
77
|
-
inset-inline: 0;
|
|
78
|
-
width: -moz-fit-content;
|
|
79
|
-
width: fit-content;
|
|
80
|
-
max-inline-size: calc(100% - 2rem);
|
|
81
|
-
margin: auto;
|
|
82
|
-
padding: var(--space-4, 1em);
|
|
83
|
-
border: var(--border-width, 1px) solid var(--border, currentColor);
|
|
84
|
-
background: var(--surface-raised, #fff);
|
|
85
|
-
color: var(--text, inherit);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/* Open modally: fixed near the top (no fit-content in the block axis on
|
|
89
|
-
these browsers, so no vertical centering) and above menus, below
|
|
90
|
-
tooltips. Legacy-only layer, hence no z-index token. The spread shadow is
|
|
91
|
-
the visible backdrop: the dialog is its own stacking context, where a
|
|
92
|
-
negative z-index child paints above the element's background, so the
|
|
93
|
-
dimming cannot live on the ::before. */
|
|
94
|
-
dialog[open].is-fallback-modal {
|
|
95
|
-
position: fixed;
|
|
96
|
-
inset-block: 8vh auto;
|
|
97
|
-
max-block-size: 84vh;
|
|
98
|
-
overflow: auto;
|
|
99
|
-
z-index: 40;
|
|
100
|
-
box-shadow: 0 0 0 100vmax var(--backdrop-fill, rgb(0 0 0 / 35%));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/* Transparent stand-in for ::backdrop, hit-testing only. Clicks on it hit
|
|
104
|
-
the dialog element outside its own box, which dialog.js already treats as
|
|
105
|
-
a backdrop click, and it blocks pointer interaction with the page behind
|
|
106
|
-
the modal. */
|
|
107
|
-
dialog[open].is-fallback-modal::before {
|
|
108
|
-
content: "";
|
|
109
|
-
position: fixed;
|
|
110
|
-
inset: 0;
|
|
111
|
-
z-index: -1;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
48
|
:root {
|
|
49
|
+
/*
|
|
50
|
+
* Global framework tokens.
|
|
51
|
+
*
|
|
52
|
+
* Public: properties in the named groups below are public API (typography,
|
|
53
|
+
* spacing, shape, motion, focus, controls, viewport, z-index, icons) unless
|
|
54
|
+
* a group is explicitly marked Internal or Derived.
|
|
55
|
+
*
|
|
56
|
+
* Derived: "Theme-derived aliases" and "State tokens" reference intent and
|
|
57
|
+
* surface tokens and follow the theme; do not hard-code values into them.
|
|
58
|
+
* Internal: indicator offsets, --bar-height, --disabled-opacity and the
|
|
59
|
+
* like are consumed by components, not authored.
|
|
60
|
+
*
|
|
61
|
+
* Component-local hooks and internal state relays live with their
|
|
62
|
+
* components. See docs/pages/foundations/tokens.md for the public/internal
|
|
63
|
+
* token contract.
|
|
64
|
+
*/
|
|
65
|
+
|
|
116
66
|
/* Typography */
|
|
117
67
|
--font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
118
68
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
@@ -130,20 +80,22 @@ select {
|
|
|
130
80
|
--font-size-md: 1rem;
|
|
131
81
|
--font-size-lg: 1.125rem;
|
|
132
82
|
|
|
133
|
-
/* Spacing
|
|
83
|
+
/* Spacing — official steps are named in tens so the scale stays open for
|
|
84
|
+
application themes to intercalate intermediate values (--space-15,
|
|
85
|
+
--space-45…) without colliding with future framework steps. */
|
|
134
86
|
--gap: 0.75rem;
|
|
135
|
-
--
|
|
136
|
-
--
|
|
137
|
-
--space-
|
|
138
|
-
--space-
|
|
139
|
-
--space-
|
|
140
|
-
--space-
|
|
141
|
-
--space-
|
|
87
|
+
--density-space: var(--space-40);
|
|
88
|
+
--density-compact-size: 1.5rem;
|
|
89
|
+
--space-10: 0.25rem;
|
|
90
|
+
--space-20: 0.5rem;
|
|
91
|
+
--space-30: 0.75rem;
|
|
92
|
+
--space-40: 1rem;
|
|
93
|
+
--space-50: 1.5rem;
|
|
94
|
+
--space-60: 2rem;
|
|
142
95
|
|
|
143
96
|
/* Shape */
|
|
144
97
|
--border-width: 1px;
|
|
145
98
|
--radius-full: 9999px;
|
|
146
|
-
--shadow-xs: 0 1px 2px rgb(0 0 0 / 0.04);
|
|
147
99
|
--shadow: 0 1px 3px rgb(0 0 0 / 0.05), 0 4px 12px rgb(0 0 0 / 0.04);
|
|
148
100
|
--shadow-popout: 0 0.25rem 0.75rem rgb(0 0 0 / 0.15);
|
|
149
101
|
|
|
@@ -167,7 +119,6 @@ select {
|
|
|
167
119
|
the circle-aware inset (1 - 1/√2) / 2 ≈ 14.65%; the ring surrounds the
|
|
168
120
|
dot with the parent surface color. */
|
|
169
121
|
--indicator-offset: 14.65%;
|
|
170
|
-
--indicator-ring: var(--surface);
|
|
171
122
|
|
|
172
123
|
/* Shared backdrop — consumed by modal, drawer, and mobile flyout sheets. */
|
|
173
124
|
--backdrop-color: rgb(0 0 0);
|
|
@@ -178,7 +129,8 @@ select {
|
|
|
178
129
|
--bar-height: 0.65rem;
|
|
179
130
|
|
|
180
131
|
/* Controls — inputs and buttons share --control-size so heights always match.
|
|
181
|
-
.sm/.lg (variants.css) switch --control-size
|
|
132
|
+
.sm/.lg (variants.css) switch --control-size; --control-font-size stays
|
|
133
|
+
at its baseline because density does not change typography. */
|
|
182
134
|
--control-size-sm: 2rem;
|
|
183
135
|
--control-size-md: 2.375rem;
|
|
184
136
|
--control-size-lg: 2.75rem;
|
|
@@ -186,19 +138,38 @@ select {
|
|
|
186
138
|
--control-pad-x: 1em;
|
|
187
139
|
--control-font-size: var(--font-size-md);
|
|
188
140
|
--disabled-opacity: 0.65;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* Theme-derived aliases. These reference intent/surface tokens, so they must
|
|
144
|
+
be re-declared on every theme boundary: a custom property resolves its
|
|
145
|
+
var() references at computed-value time on the element that declares it,
|
|
146
|
+
so an alias declared only on :root is inherited as an already-resolved
|
|
147
|
+
value and would not follow a [data-theme] island's overridden tokens.
|
|
148
|
+
Themes that want a distinct alias override it explicitly afterwards. */
|
|
149
|
+
:root,
|
|
150
|
+
[data-theme] {
|
|
151
|
+
/* State tokens — semantic colors for selected/pressed/current and disabled.
|
|
152
|
+
Components consume these instead of hard-wiring --primary/--text-subtle
|
|
153
|
+
so forced-colors can override them in one place. */
|
|
154
|
+
--state-selected: var(--primary);
|
|
155
|
+
--state-selected-fg: var(--primary-fg);
|
|
156
|
+
--state-disabled: var(--text-subtle);
|
|
157
|
+
|
|
158
|
+
/* Status dot ring — follows the island's surface. */
|
|
159
|
+
--indicator-ring: var(--surface);
|
|
160
|
+
}
|
|
189
161
|
|
|
162
|
+
:root {
|
|
190
163
|
/* Variant tuning — themes may adjust generated soft surfaces without
|
|
191
164
|
replacing the component rules. */
|
|
192
165
|
--soft-bg-mix: 88%;
|
|
193
166
|
--soft-border-mix: 65%;
|
|
194
167
|
--soft-hover-alpha: 12%;
|
|
195
168
|
|
|
196
|
-
/* Shared
|
|
197
|
-
--icon-chevron
|
|
198
|
-
--icon-check
|
|
199
|
-
|
|
200
|
-
The stroke matches the default theme's text-subtle (hue 258 neutral). */
|
|
201
|
-
--icon-chevron-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%238a8594' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
|
|
169
|
+
/* Shared icon assets. Override these URLs when a strict CSP disallows data:. */
|
|
170
|
+
--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%238a8594' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
|
|
171
|
+
--icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
|
|
172
|
+
--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3C/svg%3E");
|
|
202
173
|
|
|
203
174
|
/* Viewport size tokens — fallback vh/vw with dvh/vi enhancement.
|
|
204
175
|
Consumed by full-viewport components (app-shell, drawer, modal, flyout).
|
|
@@ -207,13 +178,24 @@ select {
|
|
|
207
178
|
--viewport-block: 100vh;
|
|
208
179
|
--viewport-inline: 100vw;
|
|
209
180
|
|
|
210
|
-
/*
|
|
181
|
+
/* Z-index scale — used ONLY by non-dialog overlays (see the layering
|
|
182
|
+
note below for the full model). Actual CSS reserves multiples of 10 for
|
|
183
|
+
framework layers; applications may use intermediate values for local
|
|
184
|
+
layering. */
|
|
211
185
|
--z-sticky: 10;
|
|
212
186
|
--z-menu: 20;
|
|
213
187
|
--z-tooltip: 50;
|
|
214
188
|
--z-status: 60;
|
|
215
189
|
}
|
|
216
190
|
|
|
191
|
+
@media (forced-colors: active) {
|
|
192
|
+
:root {
|
|
193
|
+
--state-selected: Highlight;
|
|
194
|
+
--state-selected-fg: HighlightText;
|
|
195
|
+
--state-disabled: GrayText;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
217
199
|
@supports (block-size: 100dvh) {
|
|
218
200
|
:root {
|
|
219
201
|
--viewport-block: 100dvh;
|
|
@@ -227,12 +209,17 @@ select {
|
|
|
227
209
|
}
|
|
228
210
|
|
|
229
211
|
@supports (color: color-mix(in oklch, red, white)) {
|
|
230
|
-
:root
|
|
231
|
-
|
|
212
|
+
:root,
|
|
213
|
+
[data-theme] {
|
|
214
|
+
/* Shadows derive from --shadow-color (theme.css) — re-declared on every
|
|
215
|
+
theme boundary so islands follow their own shadow-color. */
|
|
232
216
|
--shadow:
|
|
233
217
|
0 1px 3px color-mix(in oklch, var(--shadow-color) 5%, transparent),
|
|
234
218
|
0 4px 12px color-mix(in oklch, var(--shadow-color) 4%, transparent);
|
|
235
219
|
--shadow-popout: 0 0.25rem 0.75rem color-mix(in oklch, var(--shadow-color) 15%, transparent);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
:root {
|
|
236
223
|
--backdrop-fill: color-mix(
|
|
237
224
|
in oklch,
|
|
238
225
|
var(--backdrop-color) calc(var(--backdrop-opacity) * 100%),
|
|
@@ -241,6 +228,17 @@ select {
|
|
|
241
228
|
}
|
|
242
229
|
}
|
|
243
230
|
|
|
231
|
+
/*
|
|
232
|
+
* Theme tokens — the palette, surfaces, text, borders, radius and shadow
|
|
233
|
+
* color. Named themes (themes/*.css) override this file; authors can set the
|
|
234
|
+
* same tokens on :root or on [data-theme="…"] islands.
|
|
235
|
+
*
|
|
236
|
+
* Public: Intent colors, Surfaces, Text, Lines and focus, Radius scale,
|
|
237
|
+
* Shadow color, Focus ring derivation.
|
|
238
|
+
* Derived: "Theme-derived aliases", Tone subsystem, Backdrop fill — reference
|
|
239
|
+
* other tokens; hard-coded overrides on a custom theme will break the intent.
|
|
240
|
+
* Internal: --hgroup-gap.
|
|
241
|
+
*/
|
|
244
242
|
:root {
|
|
245
243
|
color-scheme: light dark;
|
|
246
244
|
}
|
|
@@ -308,7 +306,6 @@ select {
|
|
|
308
306
|
/* Pre-color-mix fallback only — superseded below by a derivation from
|
|
309
307
|
--focus so overriding --neutral/--focus never leaves a stale ring. */
|
|
310
308
|
--focus-ring: hsl(258 7% 43% / 0.3);
|
|
311
|
-
--focus-ring-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring);
|
|
312
309
|
--hover-overlay: hsl(260 20% 10% / 0.04);
|
|
313
310
|
|
|
314
311
|
/* Radius scale — themes may override the whole scale (e.g. [data-theme="square"]) */
|
|
@@ -319,13 +316,19 @@ select {
|
|
|
319
316
|
/* Shadow color — consumed by --shadow / --shadow-popout in tokens.css */
|
|
320
317
|
--shadow-color: hsl(260 25% 10%);
|
|
321
318
|
|
|
322
|
-
/*
|
|
319
|
+
/* hgroup internal gap */
|
|
320
|
+
--hgroup-gap: 0.25em;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* Theme-derived aliases — re-declared on every theme boundary (see tokens.css
|
|
324
|
+
for the custom-property resolution rationale): islands recompute these from
|
|
325
|
+
their own tokens instead of inheriting :root's resolved values. */
|
|
326
|
+
:root,
|
|
327
|
+
[data-theme] {
|
|
323
328
|
--heading: var(--text);
|
|
324
329
|
--selection-bg: var(--primary);
|
|
325
330
|
--selection-fg: var(--primary-fg);
|
|
326
|
-
|
|
327
|
-
/* hgroup internal gap */
|
|
328
|
-
--hgroup-gap: 0.25em;
|
|
331
|
+
--focus-ring-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring);
|
|
329
332
|
}
|
|
330
333
|
|
|
331
334
|
/* Tone subsystem — fixed-backdrop foreground for components placed over media
|
|
@@ -447,10 +450,8 @@ a {
|
|
|
447
450
|
text-underline-offset: 0.18em;
|
|
448
451
|
}
|
|
449
452
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
text-decoration-thickness: 0.12em;
|
|
453
|
-
}
|
|
453
|
+
a:hover {
|
|
454
|
+
text-decoration-thickness: 0.12em;
|
|
454
455
|
}
|
|
455
456
|
|
|
456
457
|
/* Global heading chrome: color (--heading or inherit), a tight line-height,
|
|
@@ -476,7 +477,7 @@ hgroup > * {
|
|
|
476
477
|
hr {
|
|
477
478
|
border: 0;
|
|
478
479
|
border-block-start: var(--border-width) solid var(--border);
|
|
479
|
-
margin-block: var(--space-
|
|
480
|
+
margin-block: var(--space-60);
|
|
480
481
|
}
|
|
481
482
|
|
|
482
483
|
code,
|
|
@@ -499,8 +500,8 @@ small {
|
|
|
499
500
|
* Intent classes do not style elements directly.
|
|
500
501
|
* They only expose --intent and --intent-fg.
|
|
501
502
|
* Components and variants decide how to consume them. Each component that
|
|
502
|
-
* consumes --intent or --ui-*
|
|
503
|
-
* block that resets
|
|
503
|
+
* consumes --intent or --ui-* opens a local intent boundary in a :where()
|
|
504
|
+
* block that resets exactly the tokens it consumes to initial. Parent intents
|
|
504
505
|
* do not leak into nested components. Local intent classes (.primary, .danger,
|
|
505
506
|
* etc.) still apply because they have higher specificity than :where().
|
|
506
507
|
*/
|
|
@@ -529,6 +530,242 @@ small {
|
|
|
529
530
|
--intent-fg: var(--neutral-fg);
|
|
530
531
|
}
|
|
531
532
|
|
|
533
|
+
/* Prose before layout: layout primitives (.stack, .cluster) reset child
|
|
534
|
+
margins and must win over .prose's flow margins at equal specificity —
|
|
535
|
+
source order is the tie-breaker, so prose loads first. */
|
|
536
|
+
/*
|
|
537
|
+
* Prose is the opt-in rich text scope for authored content.
|
|
538
|
+
*
|
|
539
|
+
* Global element styles stay mild so app UI is not shaped accidentally. Page
|
|
540
|
+
* layout, forms, cards, navigation, and app tables remain outside this scope.
|
|
541
|
+
*
|
|
542
|
+
* Public hooks:
|
|
543
|
+
* --prose-size
|
|
544
|
+
* --prose-line-height
|
|
545
|
+
* --prose-measure
|
|
546
|
+
* --prose-flow
|
|
547
|
+
* --prose-heading-line-height
|
|
548
|
+
* --prose-h1
|
|
549
|
+
* --prose-h2
|
|
550
|
+
* --prose-h3
|
|
551
|
+
* --prose-h4
|
|
552
|
+
*/
|
|
553
|
+
|
|
554
|
+
.prose {
|
|
555
|
+
/* Prose scale */
|
|
556
|
+
--prose-size: 1rem;
|
|
557
|
+
--prose-line-height: 1.7;
|
|
558
|
+
--prose-measure: 70ch;
|
|
559
|
+
--prose-flow: 1em;
|
|
560
|
+
--prose-heading-line-height: 1.2;
|
|
561
|
+
--prose-h1: 2rem;
|
|
562
|
+
--prose-h2: 1.5rem;
|
|
563
|
+
--prose-h3: 1.25rem;
|
|
564
|
+
--prose-h4: 1.125rem;
|
|
565
|
+
|
|
566
|
+
max-inline-size: var(--prose-measure);
|
|
567
|
+
color: var(--text);
|
|
568
|
+
font-size: var(--prose-size);
|
|
569
|
+
line-height: var(--prose-line-height);
|
|
570
|
+
overflow-wrap: break-word;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.prose :where(p, ul, ol, dl, blockquote, pre, table, figure) {
|
|
574
|
+
margin-block: var(--prose-flow) 0;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.prose :where(h1, h2, h3, h4, h5, h6) {
|
|
578
|
+
margin-block: 1.5em 0;
|
|
579
|
+
color: var(--text);
|
|
580
|
+
font-weight: var(--font-weight-strong);
|
|
581
|
+
line-height: var(--prose-heading-line-height);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
.prose :where(p, li, blockquote) {
|
|
585
|
+
text-wrap: pretty;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.prose > :first-child {
|
|
589
|
+
margin-block-start: 0;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.prose h1 {
|
|
593
|
+
font-size: var(--prose-h1);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.prose h2 {
|
|
597
|
+
font-size: var(--prose-h2);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
.prose h3 {
|
|
601
|
+
font-size: var(--prose-h3);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.prose h4 {
|
|
605
|
+
font-size: var(--prose-h4);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.prose a {
|
|
609
|
+
color: var(--primary);
|
|
610
|
+
text-decoration-line: underline;
|
|
611
|
+
text-decoration-thickness: 0.08em;
|
|
612
|
+
text-underline-offset: 0.18em;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.prose a:hover {
|
|
616
|
+
text-decoration-thickness: 0.12em;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
@supports (color: color-mix(in oklch, red, white)) {
|
|
620
|
+
.prose a:hover {
|
|
621
|
+
color: color-mix(in oklch, var(--primary), var(--text) 15%);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.prose :where(ul, ol) {
|
|
626
|
+
padding-inline-start: 1.5em;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
.prose li + li {
|
|
630
|
+
margin-block-start: 0.35em;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.prose code {
|
|
634
|
+
padding: 0.15em 0.3em;
|
|
635
|
+
border-radius: var(--radius-sm);
|
|
636
|
+
background: var(--surface-subtle);
|
|
637
|
+
color: var(--text);
|
|
638
|
+
font-size: 0.875em;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/* Visual twin of .key (components/key.css) — keep the two recipes in sync. */
|
|
642
|
+
.prose kbd {
|
|
643
|
+
display: inline-flex;
|
|
644
|
+
align-items: center;
|
|
645
|
+
justify-content: center;
|
|
646
|
+
min-inline-size: 1.75em;
|
|
647
|
+
min-block-size: 1.6em;
|
|
648
|
+
padding-inline: 0.45em;
|
|
649
|
+
border: var(--border-width) solid var(--border);
|
|
650
|
+
border-block-end-width: calc(var(--border-width) * 2);
|
|
651
|
+
border-radius: var(--radius-sm);
|
|
652
|
+
background: var(--surface-raised);
|
|
653
|
+
color: var(--text);
|
|
654
|
+
font-family: var(--font-mono);
|
|
655
|
+
font-size: 0.875em;
|
|
656
|
+
font-weight: var(--font-weight-medium);
|
|
657
|
+
line-height: 1;
|
|
658
|
+
white-space: nowrap;
|
|
659
|
+
vertical-align: 0.08em;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
.prose pre {
|
|
663
|
+
overflow-x: auto;
|
|
664
|
+
padding: 1em;
|
|
665
|
+
border: var(--border-width) solid var(--border);
|
|
666
|
+
border-radius: var(--radius);
|
|
667
|
+
background: var(--surface-subtle);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
.prose pre code {
|
|
671
|
+
padding: 0;
|
|
672
|
+
background: transparent;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.prose blockquote {
|
|
676
|
+
padding-inline-start: 1em;
|
|
677
|
+
border-inline-start: var(--border-width) solid var(--border);
|
|
678
|
+
color: var(--text-muted);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
.prose figure {
|
|
682
|
+
margin-inline: 0;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.prose figcaption {
|
|
686
|
+
margin-block-start: 0.5em;
|
|
687
|
+
color: var(--text-muted);
|
|
688
|
+
font-size: 0.875em;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
.prose table {
|
|
692
|
+
inline-size: 100%;
|
|
693
|
+
border-collapse: collapse;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
.prose th,
|
|
697
|
+
.prose td {
|
|
698
|
+
padding: 0.5em;
|
|
699
|
+
border-block-end: var(--border-width) solid var(--border);
|
|
700
|
+
text-align: start;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/* Lead paragraph: a slightly larger, more relaxed reading-introduction text.
|
|
704
|
+
Non-fluid and always available — use .text-lead from the optional fluid
|
|
705
|
+
module for marketing/display surfaces. */
|
|
706
|
+
.lead {
|
|
707
|
+
font-size: 1.125em;
|
|
708
|
+
line-height: var(--line-height-relaxed);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/* Readable content measure. Composes with .center for centering or with
|
|
712
|
+
margin-inline: auto for a one-off centered block. */
|
|
713
|
+
.measure {
|
|
714
|
+
max-inline-size: var(--prose-measure, 70ch);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
.prose.sm {
|
|
718
|
+
--prose-size: 0.9375rem;
|
|
719
|
+
--prose-h1: 1.75rem;
|
|
720
|
+
--prose-h2: 1.375rem;
|
|
721
|
+
--prose-h3: 1.125rem;
|
|
722
|
+
--prose-h4: 1rem;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
.prose.lg {
|
|
726
|
+
--prose-size: 1.125rem;
|
|
727
|
+
--prose-measure: 72ch;
|
|
728
|
+
--prose-h1: 2.25rem;
|
|
729
|
+
--prose-h2: 1.625rem;
|
|
730
|
+
--prose-h3: 1.375rem;
|
|
731
|
+
--prose-h4: 1.25rem;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/*
|
|
735
|
+
* Core layout primitives: stack, cluster, center, frame, app-shell, media,
|
|
736
|
+
* sidebar-layout. Each primitive owns a single, readable behavior; they are
|
|
737
|
+
* meant to compose in markup.
|
|
738
|
+
*
|
|
739
|
+
* Public hooks:
|
|
740
|
+
* --cluster-align
|
|
741
|
+
* --cluster-justify
|
|
742
|
+
* --cluster-wrap
|
|
743
|
+
* --center-size
|
|
744
|
+
* --center-pad
|
|
745
|
+
* --frame-ratio
|
|
746
|
+
* --sidebar-layout-size
|
|
747
|
+
*
|
|
748
|
+
* Child contracts:
|
|
749
|
+
* .stack owns vertical rhythm: direct-child block-axis margins are reset,
|
|
750
|
+
* spacing comes from --gap, inline margins stay free.
|
|
751
|
+
* .cluster owns inline rhythm: direct-child margins are reset, spacing comes
|
|
752
|
+
* from --gap.
|
|
753
|
+
* .frame sizes direct media children (img/video/iframe/canvas/svg) to fill the
|
|
754
|
+
* frame.
|
|
755
|
+
*/
|
|
756
|
+
|
|
757
|
+
/* Responsive recipes:
|
|
758
|
+
- avatar/icon + flexible content -> .media
|
|
759
|
+
- inline items that wrap when space runs out -> .cluster (keep wrapping)
|
|
760
|
+
- content + aside -> .sidebar-layout inside .container-query
|
|
761
|
+
- row that becomes a column when constrained -> .switcher (optional module)
|
|
762
|
+
|
|
763
|
+
Anti-patterns:
|
|
764
|
+
- viewport breakpoints for ordinary composition
|
|
765
|
+
- fixed grid templates (--grid-columns) where .grid/.sidebar-layout express
|
|
766
|
+
the layout
|
|
767
|
+
- nowrap on page-level compositions; prefer wrapping and container-aware
|
|
768
|
+
primitives */
|
|
532
769
|
.container-query {
|
|
533
770
|
container-type: inline-size;
|
|
534
771
|
}
|
|
@@ -539,18 +776,24 @@ small {
|
|
|
539
776
|
gap: var(--gap);
|
|
540
777
|
}
|
|
541
778
|
|
|
779
|
+
/* .stack owns the block axis only: block margins reset, inline margins stay
|
|
780
|
+
free (self-centering children via margin-inline: auto keep working). */
|
|
542
781
|
.stack > * {
|
|
543
|
-
margin: 0;
|
|
782
|
+
margin-block: 0;
|
|
544
783
|
}
|
|
545
784
|
|
|
546
785
|
.cluster {
|
|
547
786
|
display: flex;
|
|
548
|
-
flex-wrap: wrap;
|
|
787
|
+
flex-wrap: var(--cluster-wrap, wrap);
|
|
549
788
|
align-items: var(--cluster-align, center);
|
|
550
789
|
justify-content: var(--cluster-justify, flex-start);
|
|
551
790
|
gap: var(--gap);
|
|
552
791
|
}
|
|
553
792
|
|
|
793
|
+
.cluster > * {
|
|
794
|
+
margin: 0;
|
|
795
|
+
}
|
|
796
|
+
|
|
554
797
|
.center {
|
|
555
798
|
box-sizing: content-box;
|
|
556
799
|
max-inline-size: var(--center-size, 72rem);
|
|
@@ -594,6 +837,8 @@ small {
|
|
|
594
837
|
grid-template-columns: minmax(0, 1fr);
|
|
595
838
|
}
|
|
596
839
|
|
|
840
|
+
/* Container queries are the preferred path; without them the @supports not
|
|
841
|
+
block below restores the same columns at the same viewport breakpoint. */
|
|
597
842
|
@container (width >= 64rem) {
|
|
598
843
|
.container-query .sidebar-layout {
|
|
599
844
|
grid-template-columns: minmax(0, 1fr) var(--sidebar-layout-size, 18rem);
|
|
@@ -610,29 +855,40 @@ small {
|
|
|
610
855
|
}
|
|
611
856
|
}
|
|
612
857
|
|
|
858
|
+
/*
|
|
859
|
+
* Responsive equal-width grids, with fixed-column presets below.
|
|
860
|
+
*
|
|
861
|
+
* Public hooks:
|
|
862
|
+
* --grid-min
|
|
863
|
+
* --grid-columns
|
|
864
|
+
*/
|
|
865
|
+
|
|
866
|
+
/* --grid-columns replaces the intrinsic auto-fit template: once set, the
|
|
867
|
+
grid no longer collapses on its own — the author owns narrow-container
|
|
868
|
+
collapse. Prefer .sidebar-layout inside .container-query for content +
|
|
869
|
+
aside layouts; reserve --grid-columns for genuinely fixed or asymmetric
|
|
870
|
+
templates. */
|
|
871
|
+
|
|
872
|
+
/* Recipe:
|
|
873
|
+
- N equal cards that collapse when narrow -> .grid (auto-fit, tune
|
|
874
|
+
--grid-min) or .grid-2/3/4/6 inside .container-query
|
|
875
|
+
- content + aside -> .sidebar-layout inside .container-query
|
|
876
|
+
- fixed or asymmetric columns -> --grid-columns with an explicit collapse
|
|
877
|
+
rule of your own */
|
|
613
878
|
.grid {
|
|
614
879
|
display: grid;
|
|
615
880
|
gap: var(--gap);
|
|
616
|
-
grid-template-columns:
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
* .grid-6: 6 cols
|
|
628
|
-
* When placed inside .container-query, the column count collapses
|
|
629
|
-
* on narrow wrappers. The intermediate 2-col step applies to
|
|
630
|
-
* .grid-3/.grid-4; .grid-6 starts at 2 columns because it is meant
|
|
631
|
-
* for compact items that do not need a full single-column stack.
|
|
632
|
-
* .grid-2: 1 col -> 2 cols
|
|
633
|
-
* .grid-3: 1 col -> 2 cols -> 3 cols
|
|
634
|
-
* .grid-4: 1 col -> 2 cols -> 4 cols
|
|
635
|
-
* .grid-6: 2 cols -> 3 cols -> 6 cols */
|
|
881
|
+
grid-template-columns: var(
|
|
882
|
+
--grid-columns,
|
|
883
|
+
repeat(auto-fit, minmax(min(100%, var(--grid-min, 16rem)), 1fr))
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/* Fixed-column grids at their full count by default: .grid-2/3/4/6 stay at
|
|
888
|
+
2/3/4/6 columns unless wrapped in .container-query, which collapses the
|
|
889
|
+
column count on narrow wrappers (a 2-col intermediate step for
|
|
890
|
+
.grid-3/.grid-4; .grid-6 starts at 2 columns because it is meant for
|
|
891
|
+
compact items that do not need a full single-column stack). */
|
|
636
892
|
.grid-2,
|
|
637
893
|
.grid-3,
|
|
638
894
|
.grid-4,
|
|
@@ -654,6 +910,9 @@ small {
|
|
|
654
910
|
grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
655
911
|
}
|
|
656
912
|
|
|
913
|
+
/* Container queries are the preferred responsive path; browsers without them
|
|
914
|
+
ignore these rules and get the equivalent @media rules from the
|
|
915
|
+
@supports not (container-type) block below — same columns, same breakpoints. */
|
|
657
916
|
@container (width < 28rem) {
|
|
658
917
|
.container-query .grid-2,
|
|
659
918
|
.container-query .grid-3,
|
|
@@ -710,15 +969,24 @@ small {
|
|
|
710
969
|
* Cross-cutting variant token mapping.
|
|
711
970
|
*
|
|
712
971
|
* Variant scope: .solid/.soft/.outline set --ui-* for components that opt into
|
|
713
|
-
* shared variants.
|
|
714
|
-
*
|
|
972
|
+
* shared variants. .inverted is a surface modifier, not a variant: it paints
|
|
973
|
+
* the element directly (background/color/border-color) so it works on any bare
|
|
974
|
+
* block, and remaps the shared --ui-* tokens so participating components
|
|
975
|
+
* (.card, .navbar, .alert, .badge) invert through their own rules. Loaded
|
|
976
|
+
* before components, so a component with its own explicit surface (e.g.
|
|
977
|
+
* .accordion) naturally keeps that surface — .inverted never forces a
|
|
978
|
+
* non-participant.
|
|
979
|
+
* Component-specific defaults (.btn solid-by-default, .badge
|
|
980
|
+
* soft-by-default, .alert soft-by-default) live with their components:
|
|
715
981
|
* components/button.css — .btn default solid + .btn.ghost + .btn.link
|
|
716
|
-
* components/badge.css — .badge default
|
|
717
|
-
* components/alert.css — .alert default
|
|
982
|
+
* components/badge.css — .badge default soft behind explicit --ui-*
|
|
983
|
+
* components/alert.css — .alert default soft behind explicit --ui-*
|
|
718
984
|
*
|
|
719
|
-
* .sm/.lg set shared
|
|
720
|
-
*
|
|
721
|
-
*
|
|
985
|
+
* .sm/.lg set shared density tokens: spacing rhythm and geometry only.
|
|
986
|
+
* Density never changes typography — controls keep their font-size and
|
|
987
|
+
* content components keep theirs. Controls consume --control-size; non-control
|
|
988
|
+
* components may consume --density-* when the density context should affect
|
|
989
|
+
* their local rhythm. Compact components may consume --density-compact-size.
|
|
722
990
|
* Components with bespoke sizing (spinner, avatar) define local .sm/.lg;
|
|
723
991
|
* applying .sm/.lg to card, dialog, accordion, navbar, table is
|
|
724
992
|
* a no-op.
|
|
@@ -740,31 +1008,43 @@ small {
|
|
|
740
1008
|
|
|
741
1009
|
.outline {
|
|
742
1010
|
--ui-bg: transparent;
|
|
743
|
-
--ui-fg: var(--intent,
|
|
744
|
-
--ui-border: var(--intent,
|
|
745
|
-
}
|
|
1011
|
+
--ui-fg: var(--intent, currentColor);
|
|
1012
|
+
--ui-border: var(--intent, currentColor);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/* Inverse surface treatment. Paints the element directly (so it works on any
|
|
1016
|
+
bare block) and remaps the shared --ui-* tokens so opt-in components
|
|
1017
|
+
(.card, .navbar) invert through their own rules. Because this loads before
|
|
1018
|
+
component base surfaces, components with their own explicit surface
|
|
1019
|
+
(e.g. .accordion) keep it — participation is opt-in, never forced. --heading
|
|
1020
|
+
is set so global headings (var(--heading, inherit)) do not inherit an outside
|
|
1021
|
+
heading color onto the dark surface; surface-owning descendants derive
|
|
1022
|
+
their contextual aliases from their own local surface. */
|
|
1023
|
+
.inverted {
|
|
1024
|
+
--ui-bg: var(--surface-solid);
|
|
1025
|
+
--ui-fg: var(--surface);
|
|
1026
|
+
--ui-border: transparent;
|
|
1027
|
+
--ui-hover-bg: var(--surface-solid);
|
|
746
1028
|
|
|
747
|
-
|
|
748
|
-
--
|
|
749
|
-
|
|
1029
|
+
--heading: var(--surface);
|
|
1030
|
+
--busy-overlay-bg: var(--surface-solid);
|
|
1031
|
+
|
|
1032
|
+
background: var(--ui-bg);
|
|
1033
|
+
color: var(--ui-fg);
|
|
1034
|
+
border-color: var(--ui-border);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
.sm {
|
|
1038
|
+
--gap: var(--space-20);
|
|
1039
|
+
--density-space: var(--space-20);
|
|
750
1040
|
--control-size: var(--control-size-sm);
|
|
751
|
-
--
|
|
752
|
-
--variant-font-size: var(--font-size-sm);
|
|
753
|
-
--variant-icon-size: 1rem;
|
|
754
|
-
--variant-pad-block: var(--space-3);
|
|
755
|
-
--variant-compact-size: 1.25rem;
|
|
756
|
-
--variant-compact-font-size: 0.75rem;
|
|
1041
|
+
--density-compact-size: 1.25rem;
|
|
757
1042
|
}
|
|
758
1043
|
.lg {
|
|
759
|
-
--gap: var(--space-
|
|
760
|
-
--
|
|
1044
|
+
--gap: var(--space-50);
|
|
1045
|
+
--density-space: var(--space-50);
|
|
761
1046
|
--control-size: var(--control-size-lg);
|
|
762
|
-
--
|
|
763
|
-
--variant-font-size: var(--font-size-lg);
|
|
764
|
-
--variant-icon-size: 1.5rem;
|
|
765
|
-
--variant-pad-block: var(--space-5);
|
|
766
|
-
--variant-compact-size: 1.875rem;
|
|
767
|
-
--variant-compact-font-size: var(--font-size-sm);
|
|
1047
|
+
--density-compact-size: 1.875rem;
|
|
768
1048
|
}
|
|
769
1049
|
|
|
770
1050
|
/* Modern color enhancement. Baseline variant values above remain usable
|
|
@@ -780,238 +1060,14 @@ small {
|
|
|
780
1060
|
);
|
|
781
1061
|
/* @sync soft-recipe */
|
|
782
1062
|
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
/* Interactive hover recipe shared by .btn and .badge. Declared outside :hover
|
|
786
|
-
on purpose: --ui-hover-bg is only consumed inside their @media (hover: hover)
|
|
787
|
-
rules, so these values are inert until hovered. :is() carries real
|
|
788
|
-
specificity so the components' :where() intent boundaries
|
|
789
|
-
(--ui-hover-bg: initial) cannot override the recipe. The solid/soft overlay
|
|
790
|
-
darkening lives in the consuming components. */
|
|
791
|
-
:is(.btn, a.badge, button.badge):is(.outline, .ghost) {
|
|
792
|
-
--ui-hover-bg: var(--surface-subtle);
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
@supports (color: color-mix(in oklch, red, white)) {
|
|
796
|
-
/* Solid has no literal class requirement: bare .btn/.badge default to solid,
|
|
797
|
-
so this must match anything that isn't one of the other variants rather
|
|
798
|
-
than an explicit .solid class. Stacks with the box-shadow overlay the
|
|
799
|
-
consuming components apply on top for extra depth. */
|
|
800
|
-
:is(.btn, a.badge, button.badge):not(:where(.soft, .outline, .ghost, .link)) {
|
|
801
|
-
--ui-hover-bg: color-mix(in oklch, var(--intent, var(--neutral)) 88%, black);
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
/* Same recipe as the resting .soft bg with the surface share reduced by
|
|
805
|
-
--soft-hover-alpha: stays opaque, so it reads on any backdrop instead of
|
|
806
|
-
letting the container show through on hover. */
|
|
807
|
-
:is(.btn, a.badge, button.badge):is(.soft) {
|
|
808
|
-
--ui-hover-bg: color-mix(
|
|
809
|
-
in oklch,
|
|
810
|
-
var(--surface) calc(var(--soft-bg-mix) - var(--soft-hover-alpha)),
|
|
811
|
-
var(--intent, var(--neutral))
|
|
812
|
-
);
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
/* Mixed against the opaque --surface-subtle rather than transparent: a
|
|
816
|
-
transparent-based tint reads clean on light surfaces but goes muddy and
|
|
817
|
-
low-contrast on dark ones, since the same low alpha lands very
|
|
818
|
-
differently over a light vs. a near-black backdrop. */
|
|
819
|
-
:is(.btn, a.badge, button.badge):is(.outline) {
|
|
820
|
-
--ui-hover-bg: color-mix(in oklch, var(--surface-subtle) 92%, var(--intent, var(--neutral)));
|
|
821
|
-
}
|
|
822
1063
|
|
|
823
|
-
|
|
824
|
-
--ui-hover-bg: color-mix(in oklch, var(--
|
|
1064
|
+
.inverted {
|
|
1065
|
+
--ui-hover-bg: color-mix(in oklch, var(--surface-solid) 88%, black);
|
|
825
1066
|
}
|
|
826
1067
|
}
|
|
827
1068
|
|
|
828
|
-
/*
|
|
829
|
-
|
|
830
|
-
*
|
|
831
|
-
* Global element styles stay mild so app UI is not shaped accidentally. Page
|
|
832
|
-
* layout, forms, cards, navigation, and app tables remain outside this scope.
|
|
833
|
-
*/
|
|
834
|
-
|
|
835
|
-
.prose {
|
|
836
|
-
/* Prose scale */
|
|
837
|
-
--prose-size: 1rem;
|
|
838
|
-
--prose-line-height: 1.7;
|
|
839
|
-
--prose-measure: 70ch;
|
|
840
|
-
--prose-flow: 1em;
|
|
841
|
-
--prose-heading-line-height: 1.2;
|
|
842
|
-
--prose-h1: 2rem;
|
|
843
|
-
--prose-h2: 1.5rem;
|
|
844
|
-
--prose-h3: 1.25rem;
|
|
845
|
-
--prose-h4: 1.125rem;
|
|
846
|
-
|
|
847
|
-
max-inline-size: var(--prose-measure);
|
|
848
|
-
color: var(--text);
|
|
849
|
-
font-size: var(--prose-size);
|
|
850
|
-
line-height: var(--prose-line-height);
|
|
851
|
-
overflow-wrap: break-word;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
.prose :where(p, ul, ol, dl, blockquote, pre, table, figure) {
|
|
855
|
-
margin-block: var(--prose-flow) 0;
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
.prose :where(h1, h2, h3, h4, h5, h6) {
|
|
859
|
-
margin-block: 1.5em 0;
|
|
860
|
-
color: var(--text);
|
|
861
|
-
font-weight: var(--font-weight-strong);
|
|
862
|
-
line-height: var(--prose-heading-line-height);
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
.prose :where(p, li, blockquote) {
|
|
866
|
-
text-wrap: pretty;
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
.prose > :first-child {
|
|
870
|
-
margin-block-start: 0;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
.prose h1 {
|
|
874
|
-
font-size: var(--prose-h1);
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
.prose h2 {
|
|
878
|
-
font-size: var(--prose-h2);
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
.prose h3 {
|
|
882
|
-
font-size: var(--prose-h3);
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
.prose h4 {
|
|
886
|
-
font-size: var(--prose-h4);
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
.prose a {
|
|
890
|
-
color: var(--primary);
|
|
891
|
-
text-decoration-line: underline;
|
|
892
|
-
text-decoration-thickness: 0.08em;
|
|
893
|
-
text-underline-offset: 0.18em;
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
@media (hover: hover) {
|
|
897
|
-
.prose a:hover {
|
|
898
|
-
text-decoration-thickness: 0.12em;
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
@supports (color: color-mix(in oklch, red, white)) {
|
|
902
|
-
.prose a:hover {
|
|
903
|
-
color: color-mix(in oklch, var(--primary), var(--text) 15%);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
.prose :where(ul, ol) {
|
|
909
|
-
padding-inline-start: 1.5em;
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
.prose li + li {
|
|
913
|
-
margin-block-start: 0.35em;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
.prose code {
|
|
917
|
-
padding: 0.15em 0.3em;
|
|
918
|
-
border-radius: var(--radius-sm);
|
|
919
|
-
background: var(--surface-subtle);
|
|
920
|
-
color: var(--text);
|
|
921
|
-
font-size: 0.875em;
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
.prose kbd {
|
|
925
|
-
display: inline-flex;
|
|
926
|
-
align-items: center;
|
|
927
|
-
justify-content: center;
|
|
928
|
-
min-inline-size: 1.75em;
|
|
929
|
-
min-block-size: 1.6em;
|
|
930
|
-
padding-inline: 0.45em;
|
|
931
|
-
border: var(--border-width) solid var(--border);
|
|
932
|
-
border-block-end-width: calc(var(--border-width) * 2);
|
|
933
|
-
border-radius: var(--radius-sm);
|
|
934
|
-
background: var(--surface-raised);
|
|
935
|
-
color: var(--text);
|
|
936
|
-
font-family: var(--font-mono);
|
|
937
|
-
font-size: 0.875em;
|
|
938
|
-
font-weight: var(--font-weight-medium);
|
|
939
|
-
line-height: 1;
|
|
940
|
-
white-space: nowrap;
|
|
941
|
-
vertical-align: 0.08em;
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
.prose pre {
|
|
945
|
-
overflow-x: auto;
|
|
946
|
-
padding: 1em;
|
|
947
|
-
border: var(--border-width) solid var(--border);
|
|
948
|
-
border-radius: var(--radius);
|
|
949
|
-
background: var(--surface-subtle);
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
.prose pre code {
|
|
953
|
-
padding: 0;
|
|
954
|
-
background: transparent;
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
.prose blockquote {
|
|
958
|
-
padding-inline-start: 1em;
|
|
959
|
-
border-inline-start: var(--border-width) solid var(--border);
|
|
960
|
-
color: var(--text-muted);
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
.prose figure {
|
|
964
|
-
margin-inline: 0;
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
.prose figcaption {
|
|
968
|
-
margin-block-start: 0.5em;
|
|
969
|
-
color: var(--text-muted);
|
|
970
|
-
font-size: 0.875em;
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
.prose table {
|
|
974
|
-
inline-size: 100%;
|
|
975
|
-
border-collapse: collapse;
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
.prose th,
|
|
979
|
-
.prose td {
|
|
980
|
-
padding: 0.5em;
|
|
981
|
-
border-block-end: var(--border-width) solid var(--border);
|
|
982
|
-
text-align: start;
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
/* Lead paragraph: a slightly larger, more relaxed reading-introduction text.
|
|
986
|
-
Non-fluid and always available — use .text-lead from the optional fluid
|
|
987
|
-
module for marketing/display surfaces. */
|
|
988
|
-
.lead {
|
|
989
|
-
font-size: 1.125em;
|
|
990
|
-
line-height: var(--line-height-relaxed);
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
/* Readable content measure. Composes with .center for centering or with
|
|
994
|
-
margin-inline: auto for a one-off centered block. */
|
|
995
|
-
.measure {
|
|
996
|
-
max-inline-size: var(--prose-measure, 70ch);
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
.prose.sm {
|
|
1000
|
-
--prose-size: 0.9375rem;
|
|
1001
|
-
--prose-h1: 1.75rem;
|
|
1002
|
-
--prose-h2: 1.375rem;
|
|
1003
|
-
--prose-h3: 1.125rem;
|
|
1004
|
-
--prose-h4: 1rem;
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
.prose.lg {
|
|
1008
|
-
--prose-size: 1.125rem;
|
|
1009
|
-
--prose-measure: 72ch;
|
|
1010
|
-
--prose-h1: 2.25rem;
|
|
1011
|
-
--prose-h2: 1.625rem;
|
|
1012
|
-
--prose-h3: 1.375rem;
|
|
1013
|
-
--prose-h4: 1.25rem;
|
|
1014
|
-
}
|
|
1069
|
+
/* Interactive hover recipes live in the components that consume them:
|
|
1070
|
+
components/button.css and components/badge.css. */
|
|
1015
1071
|
|
|
1016
1072
|
/* Controls */
|
|
1017
1073
|
/*
|
|
@@ -1025,19 +1081,40 @@ small {
|
|
|
1025
1081
|
* Disabled state uses the native disabled attribute for buttons and inputs.
|
|
1026
1082
|
* Anchors have no native disabled state, so a.btn uses aria-disabled="true"
|
|
1027
1083
|
* for disabled styling and activation guards.
|
|
1084
|
+
*
|
|
1085
|
+
* Public hooks:
|
|
1086
|
+
* --btn-font-size
|
|
1087
|
+
* --btn-min-size
|
|
1088
|
+
* --btn-pad-x
|
|
1089
|
+
* --btn-radius
|
|
1090
|
+
* --btn-focus-color
|
|
1091
|
+
* --btn-gap
|
|
1092
|
+
* --btn-hover-overlay
|
|
1093
|
+
* --btn-default-bg
|
|
1094
|
+
* --btn-default-fg
|
|
1095
|
+
* --btn-default-border
|
|
1096
|
+
* --btn-border-color
|
|
1097
|
+
*
|
|
1098
|
+
* Internal:
|
|
1099
|
+
* --btn-bg
|
|
1100
|
+
* --btn-fg
|
|
1101
|
+
* --btn-border
|
|
1102
|
+
* --btn-default-hover-bg
|
|
1103
|
+
* --btn-focus-ring-color
|
|
1104
|
+
* --btn-border-block-end-color
|
|
1105
|
+
* --btn-border-block-end-width
|
|
1106
|
+
* --spinner-width
|
|
1028
1107
|
*/
|
|
1029
1108
|
|
|
1030
1109
|
/* Intent boundary — inherited intents do not tint this component.
|
|
1031
1110
|
Local intent classes still win because :where() has zero specificity. */
|
|
1032
1111
|
:where(.btn) {
|
|
1033
|
-
/* @sync intent-boundary */
|
|
1034
1112
|
--intent: initial;
|
|
1035
1113
|
--intent-fg: initial;
|
|
1036
1114
|
--ui-bg: initial;
|
|
1037
1115
|
--ui-fg: initial;
|
|
1038
1116
|
--ui-border: initial;
|
|
1039
1117
|
--ui-hover-bg: initial;
|
|
1040
|
-
/* @sync intent-boundary */
|
|
1041
1118
|
}
|
|
1042
1119
|
|
|
1043
1120
|
.btn {
|
|
@@ -1047,19 +1124,20 @@ small {
|
|
|
1047
1124
|
--btn-bg: var(--ui-bg, var(--btn-default-bg, var(--intent, var(--neutral))));
|
|
1048
1125
|
--btn-fg: var(--ui-fg, var(--btn-default-fg, var(--intent-fg, var(--neutral-fg))));
|
|
1049
1126
|
--btn-border: var(--ui-border, var(--btn-default-border, transparent));
|
|
1127
|
+
--btn-default-hover-bg: var(--btn-bg);
|
|
1050
1128
|
--btn-radius: var(--radius);
|
|
1051
1129
|
--btn-focus-color: var(--intent, var(--focus));
|
|
1052
|
-
--btn-hover-bg: var(--ui-hover-bg, var(--btn-bg));
|
|
1053
1130
|
--btn-hover-overlay: var(--hover-overlay, rgb(0 0 0 / 0.08));
|
|
1054
1131
|
|
|
1132
|
+
/* Positioning context for indicators, overlays, and grouped states. */
|
|
1055
1133
|
position: relative;
|
|
1056
1134
|
display: inline-flex;
|
|
1057
1135
|
align-items: center;
|
|
1058
1136
|
justify-content: center;
|
|
1059
|
-
gap: 0.375em;
|
|
1137
|
+
gap: var(--btn-gap, 0.375em);
|
|
1060
1138
|
min-block-size: var(--btn-min-size);
|
|
1061
|
-
padding-block: var(--space-
|
|
1062
|
-
padding-block: max(var(--space-
|
|
1139
|
+
padding-block: var(--space-20);
|
|
1140
|
+
padding-block: max(var(--space-10), calc((var(--btn-min-size) - 1lh) / 2));
|
|
1063
1141
|
padding-inline: var(--btn-pad-x);
|
|
1064
1142
|
border: var(--border-width) solid var(--btn-border-color, var(--btn-border));
|
|
1065
1143
|
border-block-end-color: var(
|
|
@@ -1084,6 +1162,11 @@ small {
|
|
|
1084
1162
|
transform var(--duration);
|
|
1085
1163
|
}
|
|
1086
1164
|
|
|
1165
|
+
.btn.icon-only {
|
|
1166
|
+
inline-size: var(--btn-min-size);
|
|
1167
|
+
padding-inline: 0;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1087
1170
|
@supports (color: color-mix(in oklch, red, white)) {
|
|
1088
1171
|
.btn {
|
|
1089
1172
|
--btn-focus-ring-color: color-mix(in oklch, var(--btn-focus-color) 28%, transparent);
|
|
@@ -1094,7 +1177,7 @@ small {
|
|
|
1094
1177
|
fallbacks make .btn solid by default; .ghost/.link are .btn-only variants. */
|
|
1095
1178
|
.btn.ghost {
|
|
1096
1179
|
--ui-bg: transparent;
|
|
1097
|
-
--ui-fg: var(--intent,
|
|
1180
|
+
--ui-fg: var(--intent, currentColor);
|
|
1098
1181
|
--ui-border: transparent;
|
|
1099
1182
|
}
|
|
1100
1183
|
|
|
@@ -1108,13 +1191,37 @@ small {
|
|
|
1108
1191
|
--btn-hover-overlay: transparent;
|
|
1109
1192
|
}
|
|
1110
1193
|
|
|
1111
|
-
/*
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1194
|
+
/* Shared hover recipe — sets --ui-hover-bg consumed by the hover rule below.
|
|
1195
|
+
Declared outside :hover: the variables resolve only when the hover rule
|
|
1196
|
+
reads them, so the values are inert until interaction. */
|
|
1197
|
+
.btn:is(.outline, .ghost) {
|
|
1198
|
+
--ui-hover-bg: transparent;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
@supports (color: color-mix(in oklch, red, white)) {
|
|
1202
|
+
.btn {
|
|
1203
|
+
--btn-default-hover-bg: color-mix(in oklch, var(--btn-bg) 88%, black);
|
|
1117
1204
|
}
|
|
1205
|
+
|
|
1206
|
+
.btn.soft {
|
|
1207
|
+
--ui-hover-bg: color-mix(
|
|
1208
|
+
in oklch,
|
|
1209
|
+
var(--surface) calc(var(--soft-bg-mix) - var(--soft-hover-alpha)),
|
|
1210
|
+
var(--intent, var(--neutral))
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
.btn.outline,
|
|
1215
|
+
.btn.ghost {
|
|
1216
|
+
--ui-hover-bg: color-mix(in oklch, currentColor 10%, transparent);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/* The .soft/.outline/.ghost hover recipe (--ui-hover-bg) is set locally above;
|
|
1221
|
+
the overlay darkening for solid/soft is .btn-specific. */
|
|
1222
|
+
.btn:not(:where(:disabled, [aria-disabled="true"])):hover {
|
|
1223
|
+
background: var(--ui-hover-bg, var(--btn-default-hover-bg));
|
|
1224
|
+
box-shadow: inset 0 0 0 999px var(--btn-hover-overlay);
|
|
1118
1225
|
}
|
|
1119
1226
|
|
|
1120
1227
|
.btn:not(:where(:disabled, [aria-disabled="true"])):active {
|
|
@@ -1123,7 +1230,7 @@ small {
|
|
|
1123
1230
|
|
|
1124
1231
|
.btn.link {
|
|
1125
1232
|
--ui-bg: transparent;
|
|
1126
|
-
--ui-fg: var(--intent,
|
|
1233
|
+
--ui-fg: var(--intent, currentColor);
|
|
1127
1234
|
--ui-border: transparent;
|
|
1128
1235
|
--btn-radius: var(--radius-sm);
|
|
1129
1236
|
--btn-border-block-end-color: transparent;
|
|
@@ -1131,6 +1238,8 @@ small {
|
|
|
1131
1238
|
|
|
1132
1239
|
min-block-size: auto;
|
|
1133
1240
|
padding-inline: 0;
|
|
1241
|
+
inline-size: fit-content;
|
|
1242
|
+
max-inline-size: 100%;
|
|
1134
1243
|
border-color: transparent;
|
|
1135
1244
|
text-decoration: underline;
|
|
1136
1245
|
text-underline-offset: 0.18em;
|
|
@@ -1139,16 +1248,13 @@ small {
|
|
|
1139
1248
|
/* .btn.circle needs no local rule: the .circle utility (utilities.css) loads
|
|
1140
1249
|
after components and overrides border-radius at equal specificity. */
|
|
1141
1250
|
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
color: var(--intent, var(--text));
|
|
1146
|
-
text-decoration-thickness: 0.12em;
|
|
1147
|
-
}
|
|
1251
|
+
.btn.link:not(:where(:disabled, [aria-disabled="true"])):hover {
|
|
1252
|
+
background: transparent;
|
|
1253
|
+
text-decoration-thickness: 0.12em;
|
|
1148
1254
|
}
|
|
1149
1255
|
|
|
1150
1256
|
.btn:focus-visible {
|
|
1151
|
-
outline:
|
|
1257
|
+
outline: 2px solid transparent;
|
|
1152
1258
|
box-shadow: 0 0 0 var(--focus-ring-width) var(--btn-focus-ring-color, var(--focus-ring));
|
|
1153
1259
|
}
|
|
1154
1260
|
|
|
@@ -1182,13 +1288,15 @@ a.btn[aria-disabled="true"] {
|
|
|
1182
1288
|
--spinner-width: 0.15em;
|
|
1183
1289
|
}
|
|
1184
1290
|
|
|
1185
|
-
/* Toggle
|
|
1186
|
-
|
|
1187
|
-
button keeps its intent background and foreground while hovered.
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
--btn-
|
|
1291
|
+
/* Toggle and current state.
|
|
1292
|
+
State must outrank the shared .outline/.ghost hover recipes: a pressed or
|
|
1293
|
+
current button keeps its intent background and foreground while hovered.
|
|
1294
|
+
aria-current="page" is the pagination current-page marker; aria-pressed
|
|
1295
|
+
covers toggle buttons. */
|
|
1296
|
+
.btn:is([aria-pressed="true"], [aria-current="page"]):not(.link) {
|
|
1297
|
+
--btn-bg: var(--intent, var(--state-selected));
|
|
1298
|
+
--btn-fg: var(--intent-fg, var(--state-selected-fg));
|
|
1299
|
+
--btn-border: var(--intent, var(--state-selected));
|
|
1192
1300
|
--ui-hover-bg: var(--btn-bg);
|
|
1193
1301
|
}
|
|
1194
1302
|
|
|
@@ -1206,18 +1314,16 @@ a.btn[aria-disabled="true"] {
|
|
|
1206
1314
|
color: LinkText;
|
|
1207
1315
|
}
|
|
1208
1316
|
|
|
1209
|
-
.btn:is([aria-pressed="true"], [aria-current
|
|
1317
|
+
.btn:is([aria-pressed="true"], [aria-current="page"]):not(.link) {
|
|
1210
1318
|
border-color: Highlight;
|
|
1211
1319
|
background: Highlight;
|
|
1212
1320
|
color: HighlightText;
|
|
1213
1321
|
forced-color-adjust: none;
|
|
1214
1322
|
}
|
|
1215
1323
|
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
outline-offset: 2px;
|
|
1220
|
-
}
|
|
1324
|
+
.btn:hover:not(:where(:disabled, [aria-disabled="true"], .link)) {
|
|
1325
|
+
outline: var(--border-width) solid ButtonText;
|
|
1326
|
+
outline-offset: 2px;
|
|
1221
1327
|
}
|
|
1222
1328
|
|
|
1223
1329
|
.btn:disabled,
|
|
@@ -1235,34 +1341,54 @@ a.btn[aria-disabled="true"] {
|
|
|
1235
1341
|
* Soft by default. .solid for counters and notification badges. The dot
|
|
1236
1342
|
* (.badge:empty) stays solid regardless — a soft dot is pointless.
|
|
1237
1343
|
* Shared .outline variant stays available.
|
|
1344
|
+
*
|
|
1345
|
+
* Public hooks:
|
|
1346
|
+
* --badge-size
|
|
1347
|
+
* --badge-pad-x
|
|
1348
|
+
* --badge-font-size
|
|
1349
|
+
* --badge-dot-size
|
|
1350
|
+
* --badge-radius
|
|
1351
|
+
*
|
|
1352
|
+
* Internal:
|
|
1353
|
+
* --badge-default-bg
|
|
1354
|
+
* --badge-default-fg
|
|
1355
|
+
* --badge-default-border
|
|
1356
|
+
* --badge-default-hover-bg
|
|
1357
|
+
* --badge-bg
|
|
1358
|
+
* --badge-fg
|
|
1359
|
+
* --badge-border
|
|
1238
1360
|
*/
|
|
1239
1361
|
/* Intent boundary — inherited intents do not tint this component.
|
|
1240
1362
|
Local intent classes still win because :where() has zero specificity. */
|
|
1241
1363
|
:where(.badge) {
|
|
1242
|
-
/* @sync intent-boundary */
|
|
1243
1364
|
--intent: initial;
|
|
1244
1365
|
--intent-fg: initial;
|
|
1245
1366
|
--ui-bg: initial;
|
|
1246
1367
|
--ui-fg: initial;
|
|
1247
1368
|
--ui-border: initial;
|
|
1248
1369
|
--ui-hover-bg: initial;
|
|
1249
|
-
/* @sync intent-boundary */
|
|
1250
1370
|
}
|
|
1251
1371
|
|
|
1252
1372
|
.badge {
|
|
1253
|
-
--badge-size: var(--
|
|
1373
|
+
--badge-size: var(--density-compact-size);
|
|
1254
1374
|
--badge-pad-x: 0.55em;
|
|
1255
|
-
--badge-font-size:
|
|
1375
|
+
--badge-font-size: 0.8125rem;
|
|
1256
1376
|
--badge-dot-size: 0.5rem;
|
|
1257
|
-
--badge-bg: var(--
|
|
1258
|
-
--badge-fg: var(--
|
|
1259
|
-
--badge-border: var(--
|
|
1260
|
-
--badge-
|
|
1377
|
+
--badge-default-bg: var(--surface-subtle);
|
|
1378
|
+
--badge-default-fg: var(--intent, var(--text));
|
|
1379
|
+
--badge-default-border: var(--border);
|
|
1380
|
+
--badge-default-hover-bg: var(--badge-bg);
|
|
1381
|
+
--badge-bg: var(--ui-bg, var(--badge-default-bg));
|
|
1382
|
+
--badge-fg: var(--ui-fg, var(--badge-default-fg));
|
|
1383
|
+
--badge-border: var(--ui-border, var(--badge-default-border));
|
|
1384
|
+
--badge-radius: var(--radius-full);
|
|
1261
1385
|
|
|
1262
1386
|
display: inline-flex;
|
|
1263
1387
|
align-items: center;
|
|
1264
1388
|
justify-content: center;
|
|
1265
1389
|
gap: 0.35em;
|
|
1390
|
+
inline-size: fit-content;
|
|
1391
|
+
max-inline-size: 100%;
|
|
1266
1392
|
min-block-size: var(--badge-size);
|
|
1267
1393
|
padding-inline: var(--badge-pad-x);
|
|
1268
1394
|
border: var(--border-width) solid var(--badge-border);
|
|
@@ -1281,18 +1407,17 @@ a.btn[aria-disabled="true"] {
|
|
|
1281
1407
|
box-shadow var(--duration);
|
|
1282
1408
|
}
|
|
1283
1409
|
|
|
1284
|
-
/* Soft by default
|
|
1285
|
-
|
|
1410
|
+
/* Soft by default. Local defaults remain behind --ui-* in the resolution
|
|
1411
|
+
chain, so explicit shared treatments compose without selector exclusions. */
|
|
1286
1412
|
@supports (color: color-mix(in oklch, red, white)) {
|
|
1287
|
-
.badge
|
|
1413
|
+
.badge {
|
|
1288
1414
|
/* @sync soft-recipe */
|
|
1289
|
-
--badge-bg: color-mix(
|
|
1415
|
+
--badge-default-bg: color-mix(
|
|
1290
1416
|
in oklch,
|
|
1291
1417
|
var(--surface) var(--soft-bg-mix),
|
|
1292
1418
|
var(--intent, var(--neutral))
|
|
1293
1419
|
);
|
|
1294
|
-
--badge-
|
|
1295
|
-
--badge-border: color-mix(
|
|
1420
|
+
--badge-default-border: color-mix(
|
|
1296
1421
|
in oklch,
|
|
1297
1422
|
var(--surface) var(--soft-border-mix),
|
|
1298
1423
|
var(--intent, var(--neutral))
|
|
@@ -1331,27 +1456,44 @@ a.btn[aria-disabled="true"] {
|
|
|
1331
1456
|
cursor: pointer;
|
|
1332
1457
|
}
|
|
1333
1458
|
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
background: var(--hover-overlay);
|
|
1337
|
-
}
|
|
1459
|
+
.badge > button:not(:disabled):hover {
|
|
1460
|
+
background: var(--hover-overlay);
|
|
1338
1461
|
}
|
|
1339
1462
|
|
|
1340
|
-
/*
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1463
|
+
/* Hover recipes — sets --ui-hover-bg consumed by the hover rule below.
|
|
1464
|
+
Declared outside :hover like button.css for the same reason. */
|
|
1465
|
+
:is(a.badge, button.badge).outline {
|
|
1466
|
+
--ui-hover-bg: transparent;
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
@supports (color: color-mix(in oklch, red, white)) {
|
|
1470
|
+
:is(a.badge, button.badge):is(.solid, :empty) {
|
|
1471
|
+
--ui-hover-bg: color-mix(in oklch, var(--intent, var(--neutral)) 88%, black);
|
|
1345
1472
|
}
|
|
1346
1473
|
|
|
1347
|
-
:
|
|
1348
|
-
|
|
1474
|
+
:is(a.badge, button.badge):where(:not(.solid, .outline, :empty)) {
|
|
1475
|
+
--badge-default-hover-bg: color-mix(
|
|
1476
|
+
in oklch,
|
|
1477
|
+
var(--surface) calc(var(--soft-bg-mix) - var(--soft-hover-alpha)),
|
|
1478
|
+
var(--intent, var(--neutral))
|
|
1479
|
+
);
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
:is(a.badge, button.badge).outline {
|
|
1483
|
+
--ui-hover-bg: color-mix(in oklch, currentColor 10%, transparent);
|
|
1349
1484
|
}
|
|
1350
1485
|
}
|
|
1351
1486
|
|
|
1487
|
+
:where(a.badge, button.badge):not(:where(:disabled, [aria-disabled="true"])):hover {
|
|
1488
|
+
background: var(--ui-hover-bg, var(--badge-default-hover-bg));
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
:where(a.badge, button.badge):not(:where(:disabled, [aria-disabled="true"], .outline)):hover {
|
|
1492
|
+
box-shadow: inset 0 0 0 999px var(--hover-overlay);
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1352
1495
|
button.badge {
|
|
1353
1496
|
cursor: pointer;
|
|
1354
|
-
font: inherit;
|
|
1355
1497
|
}
|
|
1356
1498
|
|
|
1357
1499
|
button.badge:disabled,
|
|
@@ -1365,27 +1507,18 @@ a.badge[aria-disabled="true"] {
|
|
|
1365
1507
|
}
|
|
1366
1508
|
|
|
1367
1509
|
@media (forced-colors: active) {
|
|
1368
|
-
.badge {
|
|
1369
|
-
border-color: ButtonText;
|
|
1370
|
-
background: Canvas;
|
|
1371
|
-
color: CanvasText;
|
|
1372
|
-
box-shadow: none;
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
1510
|
.badge:empty {
|
|
1376
1511
|
background: ButtonText;
|
|
1377
1512
|
}
|
|
1378
1513
|
|
|
1379
|
-
|
|
1380
|
-
:
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
}
|
|
1514
|
+
:where(a.badge, button.badge):hover:not(:where(:disabled, [aria-disabled="true"])) {
|
|
1515
|
+
outline: var(--border-width) solid ButtonText;
|
|
1516
|
+
outline-offset: 2px;
|
|
1517
|
+
}
|
|
1384
1518
|
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
}
|
|
1519
|
+
.badge > button:not(:disabled):hover {
|
|
1520
|
+
outline: var(--border-width) solid currentColor;
|
|
1521
|
+
outline-offset: 1px;
|
|
1389
1522
|
}
|
|
1390
1523
|
|
|
1391
1524
|
button.badge:disabled,
|
|
@@ -1405,42 +1538,54 @@ a.badge[aria-disabled="true"] {
|
|
|
1405
1538
|
* --alert-border-inline-start-color/-width are unset extension points, mirroring
|
|
1406
1539
|
* --btn-border-block-end-* (button.css), for themes that give alerts a
|
|
1407
1540
|
* callout-style colored flag on the leading edge instead of a uniform border.
|
|
1541
|
+
*
|
|
1542
|
+
* Public hooks:
|
|
1543
|
+
* --alert-pad-block
|
|
1544
|
+
* --alert-pad-inline
|
|
1545
|
+
* --alert-font-size
|
|
1546
|
+
* --alert-radius
|
|
1547
|
+
* --alert-icon-size
|
|
1548
|
+
* --alert-dismiss-size
|
|
1549
|
+
* --alert-border-inline-start-color
|
|
1550
|
+
* --alert-border-inline-start-width
|
|
1551
|
+
*
|
|
1552
|
+
* Internal:
|
|
1553
|
+
* --alert-default-bg
|
|
1554
|
+
* --alert-default-border
|
|
1555
|
+
* --alert-bg
|
|
1556
|
+
* --alert-fg
|
|
1557
|
+
* --alert-border
|
|
1558
|
+
* --alert-dismiss-icon-size
|
|
1408
1559
|
*/
|
|
1409
1560
|
/* Intent boundary — inherited intents do not tint this component.
|
|
1410
1561
|
Local intent classes still win because :where() has zero specificity. */
|
|
1411
1562
|
:where(.alert) {
|
|
1412
|
-
/* @sync intent-boundary */
|
|
1413
1563
|
--intent: initial;
|
|
1414
1564
|
--intent-fg: initial;
|
|
1415
1565
|
--ui-bg: initial;
|
|
1416
1566
|
--ui-fg: initial;
|
|
1417
1567
|
--ui-border: initial;
|
|
1418
|
-
--ui-hover-bg: initial;
|
|
1419
|
-
/* @sync intent-boundary */
|
|
1420
1568
|
}
|
|
1421
1569
|
|
|
1422
1570
|
.alert {
|
|
1423
|
-
--alert-pad-block: var(--
|
|
1424
|
-
--alert-pad-inline: var(--space-
|
|
1425
|
-
--alert-font-size: var(--
|
|
1426
|
-
--alert-icon-size:
|
|
1427
|
-
--alert-bg: var(--surface-subtle);
|
|
1428
|
-
--alert-border: var(--border);
|
|
1429
|
-
--
|
|
1430
|
-
--
|
|
1431
|
-
--
|
|
1571
|
+
--alert-pad-block: var(--space-30);
|
|
1572
|
+
--alert-pad-inline: var(--space-40);
|
|
1573
|
+
--alert-font-size: var(--font-size-md);
|
|
1574
|
+
--alert-icon-size: 1.25rem;
|
|
1575
|
+
--alert-default-bg: var(--surface-subtle);
|
|
1576
|
+
--alert-default-border: var(--border);
|
|
1577
|
+
--alert-bg: var(--ui-bg, var(--alert-default-bg));
|
|
1578
|
+
--alert-fg: var(--ui-fg, var(--intent, var(--text)));
|
|
1579
|
+
--alert-border: var(--ui-border, var(--alert-default-border));
|
|
1432
1580
|
|
|
1433
1581
|
padding-block: var(--alert-pad-block);
|
|
1434
1582
|
padding-inline: var(--alert-pad-inline);
|
|
1435
|
-
background: var(--
|
|
1436
|
-
color: var(--
|
|
1437
|
-
border: var(--border-width) solid var(--
|
|
1438
|
-
border-inline-start-color: var(
|
|
1439
|
-
--alert-border-inline-start-color,
|
|
1440
|
-
var(--ui-border, var(--border))
|
|
1441
|
-
);
|
|
1583
|
+
background: var(--alert-bg);
|
|
1584
|
+
color: var(--alert-fg);
|
|
1585
|
+
border: var(--border-width) solid var(--alert-border);
|
|
1586
|
+
border-inline-start-color: var(--alert-border-inline-start-color, var(--alert-border));
|
|
1442
1587
|
border-inline-start-width: var(--alert-border-inline-start-width, var(--border-width));
|
|
1443
|
-
border-radius: var(--radius-lg);
|
|
1588
|
+
border-radius: var(--alert-radius, var(--radius-lg));
|
|
1444
1589
|
font-size: var(--alert-font-size);
|
|
1445
1590
|
}
|
|
1446
1591
|
|
|
@@ -1456,24 +1601,17 @@ a.badge[aria-disabled="true"] {
|
|
|
1456
1601
|
margin-block-end: 0;
|
|
1457
1602
|
}
|
|
1458
1603
|
|
|
1459
|
-
/* Alerts are soft by default.
|
|
1460
|
-
.
|
|
1461
|
-
--ui-bg: var(--intent, var(--neutral));
|
|
1462
|
-
--ui-fg: var(--intent-fg, var(--neutral-fg));
|
|
1463
|
-
--ui-border: transparent;
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1466
|
-
.alert.outline {
|
|
1467
|
-
--ui-bg: transparent;
|
|
1468
|
-
--ui-fg: var(--intent, var(--text));
|
|
1469
|
-
--ui-border: var(--intent, var(--border));
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1604
|
+
/* Alerts are soft by default. The recipe writes local defaults rather than
|
|
1605
|
+
--ui-* so an explicit shared treatment always wins without exclusions. */
|
|
1472
1606
|
@supports (color: color-mix(in oklch, red, white)) {
|
|
1473
|
-
.alert
|
|
1607
|
+
.alert {
|
|
1474
1608
|
/* @sync soft-recipe */
|
|
1475
|
-
--
|
|
1476
|
-
|
|
1609
|
+
--alert-default-bg: color-mix(
|
|
1610
|
+
in oklch,
|
|
1611
|
+
var(--surface) var(--soft-bg-mix),
|
|
1612
|
+
var(--intent, var(--neutral))
|
|
1613
|
+
);
|
|
1614
|
+
--alert-default-border: color-mix(
|
|
1477
1615
|
in oklch,
|
|
1478
1616
|
var(--surface) var(--soft-border-mix),
|
|
1479
1617
|
var(--intent, var(--neutral))
|
|
@@ -1482,8 +1620,8 @@ a.badge[aria-disabled="true"] {
|
|
|
1482
1620
|
}
|
|
1483
1621
|
}
|
|
1484
1622
|
|
|
1485
|
-
/* Decorative leading icon */
|
|
1486
|
-
.alert >
|
|
1623
|
+
/* Decorative leading icon — use .alert-icon on the icon element. */
|
|
1624
|
+
.alert > .alert-icon {
|
|
1487
1625
|
display: inline-block;
|
|
1488
1626
|
flex: none;
|
|
1489
1627
|
inline-size: var(--alert-icon-size);
|
|
@@ -1493,33 +1631,84 @@ a.badge[aria-disabled="true"] {
|
|
|
1493
1631
|
vertical-align: -0.125em;
|
|
1494
1632
|
}
|
|
1495
1633
|
|
|
1634
|
+
/* Dismiss — compact close button for inline alerts. Transparent at rest with
|
|
1635
|
+
a reduced opacity glyph so it stays secondary inside an already-colored
|
|
1636
|
+
alert; hover reinforces it via the shared overlay. Uses the shared
|
|
1637
|
+
--icon-close mask, so no icon font or text glyph is required in the markup.
|
|
1638
|
+
Standard alerts place it as a direct trailing child; admonitions place it
|
|
1639
|
+
inside .alert-title (see below). */
|
|
1640
|
+
.alert-dismiss {
|
|
1641
|
+
--alert-dismiss-size: 1.75rem;
|
|
1642
|
+
--alert-dismiss-icon-size: 0.875rem;
|
|
1643
|
+
|
|
1644
|
+
display: inline-grid;
|
|
1645
|
+
place-items: center;
|
|
1646
|
+
inline-size: var(--alert-dismiss-size);
|
|
1647
|
+
block-size: var(--alert-dismiss-size);
|
|
1648
|
+
flex: none;
|
|
1649
|
+
padding: 0;
|
|
1650
|
+
border: 0;
|
|
1651
|
+
border-radius: var(--radius);
|
|
1652
|
+
background: transparent;
|
|
1653
|
+
color: currentColor;
|
|
1654
|
+
opacity: 0.65;
|
|
1655
|
+
cursor: pointer;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
.alert-dismiss::before {
|
|
1659
|
+
content: "";
|
|
1660
|
+
inline-size: var(--alert-dismiss-icon-size);
|
|
1661
|
+
block-size: var(--alert-dismiss-icon-size);
|
|
1662
|
+
background: currentColor;
|
|
1663
|
+
-webkit-mask: var(--icon-close) center / contain no-repeat;
|
|
1664
|
+
mask: var(--icon-close) center / contain no-repeat;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
@media (hover: hover) {
|
|
1668
|
+
.alert-dismiss:hover {
|
|
1669
|
+
background: var(--hover-overlay);
|
|
1670
|
+
opacity: 1;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
/* Grid anatomy — activated by a leading icon, a trailing dismiss button, or
|
|
1675
|
+
both. Icon and dismiss are pinned to the outer columns; anything else (the
|
|
1676
|
+
message content) flows into the remaining text column. */
|
|
1496
1677
|
@supports selector(.alert:has(> *)) {
|
|
1497
|
-
.alert:has(>
|
|
1678
|
+
.alert:has(> .alert-icon, > .alert-dismiss) {
|
|
1498
1679
|
display: grid;
|
|
1499
|
-
grid-template-columns:
|
|
1680
|
+
grid-template-columns: minmax(0, 1fr);
|
|
1500
1681
|
align-items: var(--items-align, start);
|
|
1501
|
-
column-gap: var(--space-
|
|
1682
|
+
column-gap: var(--space-30);
|
|
1502
1683
|
}
|
|
1503
1684
|
|
|
1504
|
-
.alert:has(>
|
|
1685
|
+
.alert:has(> .alert-icon) {
|
|
1686
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
.alert:has(> .alert-icon) > .alert-icon {
|
|
1505
1690
|
grid-column: 1;
|
|
1506
1691
|
grid-row: 1 / -1;
|
|
1507
1692
|
display: inline-grid;
|
|
1508
1693
|
place-items: center;
|
|
1509
|
-
inline-size: var(--alert-icon-size);
|
|
1510
|
-
block-size: var(--alert-icon-size);
|
|
1511
1694
|
margin-block-start: 0.125em;
|
|
1512
|
-
font-size: var(--alert-icon-size);
|
|
1513
|
-
line-height: 1;
|
|
1514
1695
|
}
|
|
1515
1696
|
|
|
1516
|
-
.alert:has(>
|
|
1517
|
-
|
|
1518
|
-
block-size: var(--alert-icon-size);
|
|
1697
|
+
.alert:has(> .alert-icon) > :not(.alert-icon, .alert-dismiss) {
|
|
1698
|
+
grid-column: 2;
|
|
1519
1699
|
}
|
|
1520
1700
|
|
|
1521
|
-
.alert:has(>
|
|
1522
|
-
grid-
|
|
1701
|
+
.alert:has(> .alert-dismiss) {
|
|
1702
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
.alert:has(> .alert-icon):has(> .alert-dismiss) {
|
|
1706
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
.alert:has(> .alert-dismiss) > .alert-dismiss {
|
|
1710
|
+
grid-column: -2 / -1;
|
|
1711
|
+
grid-row: 1;
|
|
1523
1712
|
}
|
|
1524
1713
|
}
|
|
1525
1714
|
|
|
@@ -1534,100 +1723,84 @@ a.badge[aria-disabled="true"] {
|
|
|
1534
1723
|
Canvas backplate (unreadable) — so .solid intentionally falls back to the
|
|
1535
1724
|
default look. */
|
|
1536
1725
|
|
|
1537
|
-
/* Callout — neutral surface with
|
|
1538
|
-
The
|
|
1539
|
-
outer border-radius, so it never needs its own radius. */
|
|
1726
|
+
/* Callout — neutral surface with a thick accent border on the leading edge.
|
|
1727
|
+
The thickness follows the shared --alert-border-inline-start-width hook. */
|
|
1540
1728
|
.alert.callout {
|
|
1541
1729
|
--alert-bg: var(--surface-subtle);
|
|
1542
|
-
--
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
padding-inline-start: calc(var(--alert-pad-inline) + var(--space-3));
|
|
1547
|
-
}
|
|
1548
|
-
|
|
1549
|
-
.alert.callout::before {
|
|
1550
|
-
content: "";
|
|
1551
|
-
position: absolute;
|
|
1552
|
-
inset-block: var(--space-2);
|
|
1553
|
-
inset-inline-start: var(--space-2);
|
|
1554
|
-
inline-size: 0.25rem;
|
|
1555
|
-
border-radius: var(--radius-full);
|
|
1556
|
-
background: var(--intent, var(--neutral));
|
|
1730
|
+
--alert-fg: var(--text);
|
|
1731
|
+
border: 0;
|
|
1732
|
+
border-inline-start: var(--alert-border-inline-start-width, 4px) solid
|
|
1733
|
+
var(--alert-border-inline-start-color, var(--intent, var(--neutral)));
|
|
1557
1734
|
}
|
|
1558
1735
|
|
|
1559
|
-
/* Admonition — structured
|
|
1560
|
-
|
|
1736
|
+
/* Admonition — structured static box with a tinted title bar and a body
|
|
1737
|
+
that rests on the page surface. Not collapsible — use .accordion inside
|
|
1738
|
+
the body if the content needs to be expandable. */
|
|
1561
1739
|
.alert.admonition {
|
|
1562
1740
|
padding: 0;
|
|
1563
|
-
gap: 0;
|
|
1564
1741
|
display: block;
|
|
1565
1742
|
overflow: clip;
|
|
1743
|
+
border-color: var(--intent, var(--border));
|
|
1566
1744
|
}
|
|
1567
1745
|
|
|
1568
|
-
.alert-title {
|
|
1746
|
+
.alert.admonition > .alert-title {
|
|
1747
|
+
display: flex;
|
|
1748
|
+
align-items: center;
|
|
1749
|
+
gap: var(--space-20);
|
|
1569
1750
|
padding-block: var(--alert-pad-block);
|
|
1570
1751
|
padding-inline: var(--alert-pad-inline);
|
|
1752
|
+
border-start-start-radius: calc(var(--alert-radius, var(--radius-lg)) - var(--border-width));
|
|
1753
|
+
border-start-end-radius: calc(var(--alert-radius, var(--radius-lg)) - var(--border-width));
|
|
1754
|
+
}
|
|
1571
1755
|
|
|
1572
|
-
|
|
1756
|
+
/* Dismiss inside an admonition title bar rides the inline end. */
|
|
1757
|
+
.alert.admonition > .alert-title > .alert-dismiss {
|
|
1758
|
+
margin-inline-start: auto;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
@supports (color: color-mix(in oklch, red, white)) {
|
|
1762
|
+
.alert.admonition > .alert-title {
|
|
1573
1763
|
background: color-mix(
|
|
1574
1764
|
in oklch,
|
|
1575
1765
|
var(--surface) var(--soft-bg-mix),
|
|
1576
1766
|
var(--intent, var(--neutral))
|
|
1577
1767
|
);
|
|
1578
1768
|
}
|
|
1769
|
+
}
|
|
1579
1770
|
|
|
1580
|
-
|
|
1771
|
+
@supports not (color: color-mix(in oklch, red, white)) {
|
|
1772
|
+
.alert.admonition > .alert-title {
|
|
1581
1773
|
background: var(--intent, var(--neutral));
|
|
1774
|
+
color: var(--intent-fg, var(--neutral-fg));
|
|
1582
1775
|
}
|
|
1583
|
-
|
|
1584
|
-
border-start-start-radius: calc(var(--radius-lg) - var(--border-width));
|
|
1585
|
-
border-start-end-radius: calc(var(--radius-lg) - var(--border-width));
|
|
1586
|
-
}
|
|
1587
|
-
|
|
1588
|
-
.alert-title:where(summary) {
|
|
1589
|
-
cursor: pointer;
|
|
1590
|
-
user-select: none;
|
|
1591
|
-
list-style: none;
|
|
1592
|
-
}
|
|
1593
|
-
|
|
1594
|
-
.alert-title:where(summary)::-webkit-details-marker {
|
|
1595
|
-
display: none;
|
|
1596
|
-
}
|
|
1597
|
-
|
|
1598
|
-
.alert-title[aria-hidden] {
|
|
1599
|
-
display: inline-flex;
|
|
1600
|
-
gap: var(--space-2);
|
|
1601
|
-
align-items: center;
|
|
1602
1776
|
}
|
|
1603
1777
|
|
|
1604
|
-
.alert-body {
|
|
1778
|
+
.alert.admonition > .alert-body {
|
|
1605
1779
|
padding-block: var(--alert-pad-block);
|
|
1606
1780
|
padding-inline: var(--alert-pad-inline);
|
|
1781
|
+
background: var(--surface);
|
|
1607
1782
|
color: var(--text);
|
|
1608
1783
|
}
|
|
1609
1784
|
|
|
1610
|
-
.alert-body > :first-child {
|
|
1785
|
+
.alert.admonition > .alert-body > :first-child {
|
|
1611
1786
|
margin-block-start: 0;
|
|
1612
1787
|
}
|
|
1613
1788
|
|
|
1614
|
-
.alert-body > :last-child {
|
|
1789
|
+
.alert.admonition > .alert-body > :last-child {
|
|
1615
1790
|
margin-block-end: 0;
|
|
1616
1791
|
}
|
|
1617
1792
|
|
|
1618
|
-
details.alert.admonition[open] .alert-title {
|
|
1619
|
-
border-end-start-radius: 0;
|
|
1620
|
-
border-end-end-radius: 0;
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
1793
|
/*
|
|
1624
1794
|
* Actions — a horizontal action-button cluster.
|
|
1625
1795
|
*
|
|
1626
1796
|
* Composes with .btn (or any inline element) and feeds layout via the shared
|
|
1627
1797
|
* --gap token.
|
|
1798
|
+
*
|
|
1799
|
+
* Internal:
|
|
1800
|
+
* --gap (local value for the shared gap token)
|
|
1628
1801
|
*/
|
|
1629
1802
|
.actions {
|
|
1630
|
-
--gap: var(--space-
|
|
1803
|
+
--gap: var(--space-20);
|
|
1631
1804
|
|
|
1632
1805
|
display: flex;
|
|
1633
1806
|
flex-wrap: wrap;
|
|
@@ -1650,29 +1823,45 @@ details.alert.admonition[open] .alert-title {
|
|
|
1650
1823
|
* Surface, border, radius, padding, density, and elevation stay local here.
|
|
1651
1824
|
* Direct header/footer children are structural slots. Page layout remains the
|
|
1652
1825
|
* job of markup and layout primitives outside the card.
|
|
1826
|
+
*
|
|
1827
|
+
* Public hooks:
|
|
1828
|
+
* --card-pad
|
|
1829
|
+
* --card-radius
|
|
1830
|
+
* --card-max-inline-size
|
|
1831
|
+
*
|
|
1832
|
+
* Child contract:
|
|
1833
|
+
* Direct .bleed children escape --card-pad (negative inset, wider media);
|
|
1834
|
+
* edge children inherit the card corners. .bleed must be a direct child.
|
|
1835
|
+
*
|
|
1836
|
+
* Internal:
|
|
1837
|
+
* --card-bg
|
|
1838
|
+
* --card-fg
|
|
1839
|
+
* --heading
|
|
1840
|
+
* --busy-overlay-bg
|
|
1653
1841
|
*/
|
|
1654
1842
|
/* Intent boundary — inherited intents do not tint this component.
|
|
1655
1843
|
Local intent classes still win because :where() has zero specificity. */
|
|
1656
1844
|
:where(.card) {
|
|
1657
|
-
/* @sync intent-boundary */
|
|
1658
|
-
--intent: initial;
|
|
1659
|
-
--intent-fg: initial;
|
|
1660
1845
|
--ui-bg: initial;
|
|
1661
1846
|
--ui-fg: initial;
|
|
1662
1847
|
--ui-border: initial;
|
|
1663
|
-
--ui-hover-bg: initial;
|
|
1664
|
-
/* @sync intent-boundary */
|
|
1665
1848
|
}
|
|
1666
1849
|
|
|
1667
1850
|
.card {
|
|
1668
|
-
--card-pad: var(--space-
|
|
1851
|
+
--card-pad: var(--space-40);
|
|
1669
1852
|
--card-radius: var(--radius-lg);
|
|
1670
1853
|
--card-max-inline-size: none;
|
|
1854
|
+
--card-bg: var(--ui-bg, var(--surface-raised));
|
|
1855
|
+
--card-fg: var(--ui-fg, var(--text));
|
|
1856
|
+
|
|
1857
|
+
/* Keep contextual descendants tied to the surface this card actually owns. */
|
|
1858
|
+
--heading: var(--card-fg);
|
|
1859
|
+
--busy-overlay-bg: var(--card-bg);
|
|
1671
1860
|
|
|
1672
1861
|
max-inline-size: var(--card-max-inline-size);
|
|
1673
1862
|
padding: var(--card-pad);
|
|
1674
|
-
background: var(--
|
|
1675
|
-
color: var(--
|
|
1863
|
+
background: var(--card-bg);
|
|
1864
|
+
color: var(--card-fg);
|
|
1676
1865
|
border: var(--border-width) solid var(--ui-border, var(--border));
|
|
1677
1866
|
border-radius: var(--card-radius);
|
|
1678
1867
|
}
|
|
@@ -1686,7 +1875,7 @@ details.alert.admonition[open] .alert-title {
|
|
|
1686
1875
|
flex-wrap: wrap;
|
|
1687
1876
|
align-items: var(--items-align, center);
|
|
1688
1877
|
justify-content: space-between;
|
|
1689
|
-
gap: var(--space-
|
|
1878
|
+
gap: var(--space-30);
|
|
1690
1879
|
min-inline-size: 0;
|
|
1691
1880
|
}
|
|
1692
1881
|
|
|
@@ -1713,16 +1902,8 @@ details.alert.admonition[open] .alert-title {
|
|
|
1713
1902
|
.card.subtle {
|
|
1714
1903
|
background: var(--surface-subtle);
|
|
1715
1904
|
}
|
|
1716
|
-
.card.inverted {
|
|
1717
|
-
background: var(--surface-solid);
|
|
1718
|
-
color: var(--surface);
|
|
1719
|
-
border-color: transparent;
|
|
1720
|
-
--heading: var(--surface);
|
|
1721
|
-
--busy-overlay-bg: var(--surface-solid);
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
1905
|
.card.compact {
|
|
1725
|
-
--card-pad: var(--space-
|
|
1906
|
+
--card-pad: var(--space-20);
|
|
1726
1907
|
}
|
|
1727
1908
|
|
|
1728
1909
|
/* Full-bleed children */
|
|
@@ -1765,26 +1946,6 @@ details.alert.admonition[open] .alert-title {
|
|
|
1765
1946
|
padding: var(--card-pad);
|
|
1766
1947
|
}
|
|
1767
1948
|
|
|
1768
|
-
@media (forced-colors: active) {
|
|
1769
|
-
.card {
|
|
1770
|
-
border-color: ButtonText;
|
|
1771
|
-
background: Canvas;
|
|
1772
|
-
color: CanvasText;
|
|
1773
|
-
}
|
|
1774
|
-
|
|
1775
|
-
.card.raised {
|
|
1776
|
-
box-shadow: none;
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
.card.inverted {
|
|
1780
|
-
--heading: CanvasText;
|
|
1781
|
-
|
|
1782
|
-
border-color: ButtonText;
|
|
1783
|
-
background: Canvas;
|
|
1784
|
-
color: CanvasText;
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
|
|
1788
1949
|
/*
|
|
1789
1950
|
* Surface — shared modal scrim and backdrop for flyout and drawer.
|
|
1790
1951
|
*
|
|
@@ -1806,7 +1967,7 @@ details.alert.admonition[open] .alert-title {
|
|
|
1806
1967
|
z-index: calc(var(--z-menu) - 1);
|
|
1807
1968
|
background: var(--backdrop-fill);
|
|
1808
1969
|
opacity: 1;
|
|
1809
|
-
transition: opacity var(--duration) var(--ease
|
|
1970
|
+
transition: opacity var(--duration) var(--ease);
|
|
1810
1971
|
}
|
|
1811
1972
|
|
|
1812
1973
|
.surface-backdrop[hidden] {
|
|
@@ -1823,28 +1984,40 @@ details.alert.admonition[open] .alert-title {
|
|
|
1823
1984
|
@supports (transition-behavior: allow-discrete) {
|
|
1824
1985
|
.surface-backdrop {
|
|
1825
1986
|
transition:
|
|
1826
|
-
opacity var(--duration) var(--ease
|
|
1987
|
+
opacity var(--duration) var(--ease),
|
|
1827
1988
|
display var(--duration) allow-discrete;
|
|
1828
1989
|
}
|
|
1829
1990
|
}
|
|
1830
1991
|
|
|
1831
|
-
@media (forced-colors: active) {
|
|
1832
|
-
.surface-backdrop {
|
|
1833
|
-
background: Canvas;
|
|
1834
|
-
}
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
1992
|
/*
|
|
1838
1993
|
* Modal shell styling for native dialog elements.
|
|
1839
1994
|
*
|
|
1840
1995
|
* Spacing, radius, transitions, and focus treatment come from shared tokens.
|
|
1841
1996
|
* Opening behavior and intent-specific decisions stay outside this component.
|
|
1997
|
+
*
|
|
1998
|
+
* Public hooks:
|
|
1999
|
+
* --modal-size
|
|
2000
|
+
* --dialog-viewport-gap
|
|
2001
|
+
* --dialog-icon-size
|
|
2002
|
+
* --dialog-icon-glyph-size
|
|
2003
|
+
*
|
|
2004
|
+
* Internal:
|
|
2005
|
+
* --dialog-close-icon-size
|
|
2006
|
+
* --dialog-viewport-gap-2x
|
|
1842
2007
|
*/
|
|
1843
2008
|
dialog.modal {
|
|
1844
2009
|
--modal-size: 32rem;
|
|
2010
|
+
--dialog-viewport-gap: var(--space-40);
|
|
2011
|
+
/* calc() multiplication is not in the supported browser range, so the
|
|
2012
|
+
doubled gap is spelled out with an additive calc. */
|
|
2013
|
+
--dialog-viewport-gap-2x: calc(var(--dialog-viewport-gap) + var(--dialog-viewport-gap));
|
|
2014
|
+
|
|
2015
|
+
/* Keep the surface anchored to the viewport in the top layer. This also
|
|
2016
|
+
provides the containing block for the absolutely positioned close button. */
|
|
2017
|
+
position: fixed;
|
|
1845
2018
|
|
|
1846
|
-
max-inline-size: min(100% -
|
|
1847
|
-
max-block-size: calc(var(--viewport-block) -
|
|
2019
|
+
max-inline-size: min(calc(100% - var(--dialog-viewport-gap-2x)), var(--modal-size));
|
|
2020
|
+
max-block-size: calc(var(--viewport-block) - var(--dialog-viewport-gap-2x));
|
|
1848
2021
|
border: var(--border-width) solid var(--border);
|
|
1849
2022
|
border-radius: var(--radius-lg);
|
|
1850
2023
|
background: var(--surface-raised);
|
|
@@ -1868,11 +2041,18 @@ dialog.modal {
|
|
|
1868
2041
|
dialog.modal > form,
|
|
1869
2042
|
dialog.modal > .stack {
|
|
1870
2043
|
display: grid;
|
|
1871
|
-
gap: var(--space-
|
|
1872
|
-
max-block-size: calc(var(--viewport-block) -
|
|
2044
|
+
gap: var(--space-40);
|
|
2045
|
+
max-block-size: calc(var(--viewport-block) - var(--dialog-viewport-gap-2x));
|
|
1873
2046
|
overflow: auto;
|
|
1874
2047
|
overscroll-behavior: contain;
|
|
1875
|
-
padding: var(--space-
|
|
2048
|
+
padding: var(--space-40);
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
/* Keep the dialog off the viewport edges on small screens. */
|
|
2052
|
+
@media (max-width: 30rem) {
|
|
2053
|
+
dialog.modal {
|
|
2054
|
+
--dialog-viewport-gap: var(--space-30);
|
|
2055
|
+
}
|
|
1876
2056
|
}
|
|
1877
2057
|
|
|
1878
2058
|
dialog.modal.scrollable > form,
|
|
@@ -1898,6 +2078,13 @@ dialog.modal footer > :last-child {
|
|
|
1898
2078
|
margin-block-end: 0;
|
|
1899
2079
|
}
|
|
1900
2080
|
|
|
2081
|
+
/* The close button is anchored to the dialog at top inline-end, out of the
|
|
2082
|
+
content flow; headers reserve inline room so titles never run under it. */
|
|
2083
|
+
dialog.modal > form > header,
|
|
2084
|
+
dialog.modal > .stack > header {
|
|
2085
|
+
padding-inline-end: calc(var(--control-size) + var(--space-30));
|
|
2086
|
+
}
|
|
2087
|
+
|
|
1901
2088
|
dialog.modal footer {
|
|
1902
2089
|
display: flex;
|
|
1903
2090
|
flex-wrap: wrap;
|
|
@@ -1905,6 +2092,65 @@ dialog.modal footer {
|
|
|
1905
2092
|
gap: var(--gap);
|
|
1906
2093
|
}
|
|
1907
2094
|
|
|
2095
|
+
/* Confirmation layout — a focused message followed by a visually distinct
|
|
2096
|
+
action band. The media primitive owns icon/content alignment; this variant
|
|
2097
|
+
only removes the standard form inset so both regions can own their padding. */
|
|
2098
|
+
dialog.modal.dialog-confirmation > form {
|
|
2099
|
+
gap: 0;
|
|
2100
|
+
padding: 0;
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
dialog.modal.dialog-confirmation > form > .media {
|
|
2104
|
+
gap: var(--space-40);
|
|
2105
|
+
padding: var(--space-50);
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
dialog.modal.dialog-confirmation > form > footer {
|
|
2109
|
+
padding: var(--space-40) var(--space-50);
|
|
2110
|
+
border-end-start-radius: calc(var(--radius-lg) - var(--border-width));
|
|
2111
|
+
border-end-end-radius: calc(var(--radius-lg) - var(--border-width));
|
|
2112
|
+
background: var(--surface-subtle);
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
/* Intent boundary — inherited intents do not tint the icon well. A local
|
|
2116
|
+
intent plus .soft opts into the shared soft-variant recipe. */
|
|
2117
|
+
:where(.dialog-icon) {
|
|
2118
|
+
--intent: initial;
|
|
2119
|
+
--intent-fg: initial;
|
|
2120
|
+
--ui-bg: initial;
|
|
2121
|
+
--ui-fg: initial;
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
.dialog-icon {
|
|
2125
|
+
--dialog-icon-size: 3rem;
|
|
2126
|
+
--dialog-icon-glyph-size: 1.5rem;
|
|
2127
|
+
|
|
2128
|
+
display: inline-grid;
|
|
2129
|
+
place-items: center;
|
|
2130
|
+
inline-size: var(--dialog-icon-size);
|
|
2131
|
+
block-size: var(--dialog-icon-size);
|
|
2132
|
+
flex: none;
|
|
2133
|
+
border-radius: var(--radius-full);
|
|
2134
|
+
background: var(--ui-bg, var(--surface-subtle));
|
|
2135
|
+
color: var(--ui-fg, var(--intent, var(--text-muted)));
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
.dialog-icon > * {
|
|
2139
|
+
display: inline-grid;
|
|
2140
|
+
place-items: center;
|
|
2141
|
+
inline-size: var(--dialog-icon-glyph-size);
|
|
2142
|
+
block-size: var(--dialog-icon-glyph-size);
|
|
2143
|
+
font-size: var(--dialog-icon-glyph-size);
|
|
2144
|
+
line-height: 1;
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
@media (max-width: 30rem) {
|
|
2148
|
+
dialog.modal.dialog-confirmation > form > .media,
|
|
2149
|
+
dialog.modal.dialog-confirmation > form > footer {
|
|
2150
|
+
padding: var(--space-40);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
|
|
1908
2154
|
dialog.modal[open] {
|
|
1909
2155
|
opacity: 1;
|
|
1910
2156
|
transform: none;
|
|
@@ -1960,7 +2206,7 @@ html.has-modal-open.had-scrollbar {
|
|
|
1960
2206
|
|
|
1961
2207
|
@media (prefers-reduced-motion: no-preference) {
|
|
1962
2208
|
dialog.modal.is-static {
|
|
1963
|
-
animation: actual-
|
|
2209
|
+
animation: actual-dialog-static var(--duration-slow) var(--ease);
|
|
1964
2210
|
}
|
|
1965
2211
|
|
|
1966
2212
|
::view-transition-group(actual-dialog) {
|
|
@@ -1969,7 +2215,7 @@ html.has-modal-open.had-scrollbar {
|
|
|
1969
2215
|
}
|
|
1970
2216
|
}
|
|
1971
2217
|
|
|
1972
|
-
@keyframes actual-
|
|
2218
|
+
@keyframes actual-dialog-static {
|
|
1973
2219
|
0%,
|
|
1974
2220
|
100% {
|
|
1975
2221
|
transform: none;
|
|
@@ -1995,6 +2241,70 @@ html.has-modal-open.had-scrollbar {
|
|
|
1995
2241
|
dialog.modal::backdrop {
|
|
1996
2242
|
background: Canvas;
|
|
1997
2243
|
}
|
|
2244
|
+
|
|
2245
|
+
.dialog-icon {
|
|
2246
|
+
border: var(--border-width) solid CanvasText;
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
/* Close controls. A close button is an action attached to a surface, so it
|
|
2251
|
+
never participates in the content flow. The modal and the drawer share the
|
|
2252
|
+
same simple placement — absolute at the panel's top inline-end, inside the
|
|
2253
|
+
surface — so both close buttons look and behave identically. The glyph comes
|
|
2254
|
+
from --icon-close, masked to currentColor, so no icon font or text glyph is
|
|
2255
|
+
required in the markup. */
|
|
2256
|
+
.dialog-close,
|
|
2257
|
+
.drawer-close {
|
|
2258
|
+
--dialog-close-icon-size: 1rem;
|
|
2259
|
+
|
|
2260
|
+
display: inline-grid;
|
|
2261
|
+
place-items: center;
|
|
2262
|
+
padding: 0;
|
|
2263
|
+
border: 0;
|
|
2264
|
+
border-radius: var(--radius);
|
|
2265
|
+
color: var(--text-muted);
|
|
2266
|
+
cursor: pointer;
|
|
2267
|
+
|
|
2268
|
+
/* Out of flow, anchored to the positioned dialog surface, not the header or
|
|
2269
|
+
any scroll container inside it. */
|
|
2270
|
+
position: absolute;
|
|
2271
|
+
z-index: 1;
|
|
2272
|
+
inset-block-start: var(--space-20);
|
|
2273
|
+
inset-inline-end: var(--space-20);
|
|
2274
|
+
|
|
2275
|
+
inline-size: var(--control-size);
|
|
2276
|
+
block-size: var(--control-size);
|
|
2277
|
+
|
|
2278
|
+
background: var(--surface-subtle);
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
.dialog-close::before,
|
|
2282
|
+
.drawer-close::before {
|
|
2283
|
+
content: "";
|
|
2284
|
+
inline-size: var(--dialog-close-icon-size);
|
|
2285
|
+
block-size: var(--dialog-close-icon-size);
|
|
2286
|
+
background: currentColor;
|
|
2287
|
+
-webkit-mask: var(--icon-close) center / contain no-repeat;
|
|
2288
|
+
mask: var(--icon-close) center / contain no-repeat;
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
@media (hover: hover) {
|
|
2292
|
+
.dialog-close:hover,
|
|
2293
|
+
.drawer-close:hover {
|
|
2294
|
+
box-shadow: inset 0 0 0 999px var(--hover-overlay);
|
|
2295
|
+
color: var(--text);
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
.dialog-close:active,
|
|
2300
|
+
.drawer-close:active {
|
|
2301
|
+
box-shadow: inset 0 0 0 999px var(--hover-overlay);
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
.dialog-close:focus-visible,
|
|
2305
|
+
.drawer-close:focus-visible {
|
|
2306
|
+
outline: 2px solid transparent;
|
|
2307
|
+
box-shadow: var(--focus-ring-shadow);
|
|
1998
2308
|
}
|
|
1999
2309
|
|
|
2000
2310
|
/*
|
|
@@ -2005,16 +2315,30 @@ html.has-modal-open.had-scrollbar {
|
|
|
2005
2315
|
* discrete display/overlay transitions. The drawer is a layout shell only —
|
|
2006
2316
|
* navigation content inside uses .nav-link and .accordion, which carry their
|
|
2007
2317
|
* own link/summary styling.
|
|
2318
|
+
*
|
|
2319
|
+
* Public hooks:
|
|
2320
|
+
* --drawer-size
|
|
2321
|
+
* --drawer-pad
|
|
2322
|
+
*
|
|
2323
|
+
* Internal:
|
|
2324
|
+
* --drawer-closed-translate (side/RTL positioning state)
|
|
2008
2325
|
*/
|
|
2009
2326
|
dialog.drawer {
|
|
2010
2327
|
--drawer-size: 20rem;
|
|
2011
|
-
--drawer-pad: var(--space-
|
|
2328
|
+
--drawer-pad: var(--space-40);
|
|
2012
2329
|
--drawer-closed-translate: -100%;
|
|
2013
2330
|
|
|
2331
|
+
/* Keep the surface anchored to the viewport in the top layer. This also
|
|
2332
|
+
provides the containing block for the absolutely positioned close button.
|
|
2333
|
+
A percentage height resolves against the fixed containing viewport, while
|
|
2334
|
+
dynamic viewport units can be stale in embedded previews. */
|
|
2335
|
+
position: fixed;
|
|
2336
|
+
inset-block-start: 0;
|
|
2337
|
+
|
|
2014
2338
|
inline-size: min(calc(var(--viewport-inline) * 0.9), var(--drawer-size));
|
|
2015
|
-
block-size:
|
|
2339
|
+
block-size: 100%;
|
|
2016
2340
|
max-inline-size: none;
|
|
2017
|
-
max-block-size:
|
|
2341
|
+
max-block-size: 100%;
|
|
2018
2342
|
margin: 0;
|
|
2019
2343
|
padding: var(--drawer-pad);
|
|
2020
2344
|
border: 0;
|
|
@@ -2033,7 +2357,7 @@ dialog.drawer {
|
|
|
2033
2357
|
dialog.drawer[open] {
|
|
2034
2358
|
display: grid;
|
|
2035
2359
|
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
2036
|
-
gap: var(--space-
|
|
2360
|
+
gap: var(--space-40);
|
|
2037
2361
|
opacity: 1;
|
|
2038
2362
|
transform: translateX(0);
|
|
2039
2363
|
}
|
|
@@ -2104,20 +2428,23 @@ dialog.drawer nav {
|
|
|
2104
2428
|
}
|
|
2105
2429
|
|
|
2106
2430
|
@media (forced-colors: active) {
|
|
2107
|
-
dialog.drawer {
|
|
2108
|
-
border-inline-end-color: ButtonText;
|
|
2431
|
+
dialog.drawer::backdrop {
|
|
2109
2432
|
background: Canvas;
|
|
2110
|
-
color: CanvasText;
|
|
2111
|
-
box-shadow: none;
|
|
2112
2433
|
}
|
|
2434
|
+
}
|
|
2113
2435
|
|
|
2114
|
-
|
|
2115
|
-
|
|
2436
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
2437
|
+
dialog.drawer.is-static {
|
|
2438
|
+
animation: actual-dialog-static var(--duration-slow) var(--ease);
|
|
2116
2439
|
}
|
|
2440
|
+
}
|
|
2117
2441
|
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2442
|
+
/* The close button shares the modal's interior placement (modal.css): absolute
|
|
2443
|
+
at the panel's top inline-end, out of the content flow. The header reserves
|
|
2444
|
+
inline room so the title never runs under it. */
|
|
2445
|
+
dialog.drawer > header,
|
|
2446
|
+
dialog.drawer > form > header {
|
|
2447
|
+
padding-inline-end: calc(var(--control-size) + var(--space-30));
|
|
2121
2448
|
}
|
|
2122
2449
|
|
|
2123
2450
|
|
|
@@ -2133,7 +2460,16 @@ dialog.drawer nav {
|
|
|
2133
2460
|
* .is-sheet marks "auto" resolved to bottom-sheet presentation on coarse
|
|
2134
2461
|
* pointers. Authors opt into mobile behavior with
|
|
2135
2462
|
* [data-flyout-mobile="sheet|auto|none|anchored"].
|
|
2136
|
-
* --
|
|
2463
|
+
* --surface-anchor-width is set by surface.js while an open flyout is positioned.
|
|
2464
|
+
*
|
|
2465
|
+
* Public hooks:
|
|
2466
|
+
* --flyout-inline-size
|
|
2467
|
+
* --flyout-max-inline-size
|
|
2468
|
+
*
|
|
2469
|
+
* Internal:
|
|
2470
|
+
* --menu-item-size (derived from --control-size for menu rows)
|
|
2471
|
+
* --available-height (measured by floating.js)
|
|
2472
|
+
* --surface-anchor-width (measured by surface.js)
|
|
2137
2473
|
*/
|
|
2138
2474
|
|
|
2139
2475
|
.flyout-trigger {
|
|
@@ -2156,28 +2492,32 @@ dialog.drawer nav {
|
|
|
2156
2492
|
}
|
|
2157
2493
|
|
|
2158
2494
|
.flyout {
|
|
2495
|
+
--menu-item-size: var(--control-size);
|
|
2159
2496
|
z-index: var(--z-menu);
|
|
2160
2497
|
/* No-JS fallback: below the trigger inside the relative .flyout-trigger.
|
|
2161
2498
|
surface.js switches an open flyout to position: fixed with measured
|
|
2162
2499
|
coordinates; this only covers a flyout revealed without the runtime. */
|
|
2163
2500
|
position: absolute;
|
|
2164
|
-
inset-block-start: calc(100% + var(--space-
|
|
2501
|
+
inset-block-start: calc(100% + var(--space-10));
|
|
2165
2502
|
inset-inline-start: 0;
|
|
2166
2503
|
display: grid;
|
|
2167
|
-
gap: var(--space-
|
|
2504
|
+
gap: var(--space-10);
|
|
2168
2505
|
inline-size: var(--flyout-inline-size, max-content);
|
|
2169
2506
|
min-inline-size: min(
|
|
2170
|
-
var(--
|
|
2171
|
-
min(var(--flyout-max-inline-size, 20rem), calc(var(--viewport-inline) - var(--space-
|
|
2507
|
+
var(--surface-anchor-width, 0px),
|
|
2508
|
+
min(var(--flyout-max-inline-size, 20rem), calc(var(--viewport-inline) - var(--space-60)))
|
|
2172
2509
|
);
|
|
2173
2510
|
max-inline-size: min(
|
|
2174
2511
|
var(--flyout-max-inline-size, 20rem),
|
|
2175
|
-
calc(var(--viewport-inline) - var(--space-
|
|
2512
|
+
calc(var(--viewport-inline) - var(--space-60))
|
|
2513
|
+
);
|
|
2514
|
+
max-block-size: min(
|
|
2515
|
+
24rem,
|
|
2516
|
+
var(--available-height, calc(var(--viewport-block) - var(--space-60)))
|
|
2176
2517
|
);
|
|
2177
|
-
max-block-size: min(24rem, var(--available-height, calc(var(--viewport-block) - var(--space-6))));
|
|
2178
2518
|
overflow: auto;
|
|
2179
2519
|
margin: 0;
|
|
2180
|
-
padding: var(--space-
|
|
2520
|
+
padding: var(--space-10);
|
|
2181
2521
|
border: var(--border-width) solid var(--border);
|
|
2182
2522
|
border-radius: var(--radius);
|
|
2183
2523
|
background: var(--surface-raised);
|
|
@@ -2186,9 +2526,9 @@ dialog.drawer nav {
|
|
|
2186
2526
|
opacity: 1;
|
|
2187
2527
|
scale: 1;
|
|
2188
2528
|
transition:
|
|
2189
|
-
opacity var(--duration-fast
|
|
2190
|
-
scale var(--duration-fast
|
|
2191
|
-
translate var(--duration-fast
|
|
2529
|
+
opacity var(--duration-fast) var(--ease),
|
|
2530
|
+
scale var(--duration-fast) var(--ease),
|
|
2531
|
+
translate var(--duration-fast) var(--ease);
|
|
2192
2532
|
}
|
|
2193
2533
|
|
|
2194
2534
|
.flyout.is-open:not(.is-sheet) {
|
|
@@ -2205,6 +2545,7 @@ dialog.drawer nav {
|
|
|
2205
2545
|
/* Bottom-sheet presentation. surface.js toggles .is-sheet when the flyout is
|
|
2206
2546
|
opened on coarse pointer / narrow viewports. */
|
|
2207
2547
|
.flyout.is-sheet {
|
|
2548
|
+
--menu-item-size: var(--control-size-lg);
|
|
2208
2549
|
position: fixed;
|
|
2209
2550
|
inset-inline: 0;
|
|
2210
2551
|
inset-block-start: auto;
|
|
@@ -2215,8 +2556,8 @@ dialog.drawer nav {
|
|
|
2215
2556
|
max-block-size: min(calc(var(--viewport-block) * 0.75), 32rem);
|
|
2216
2557
|
overflow: auto;
|
|
2217
2558
|
grid-template-columns: 1fr;
|
|
2218
|
-
padding: max(var(--space-
|
|
2219
|
-
max(var(--space-
|
|
2559
|
+
padding: max(var(--space-30), env(safe-area-inset-top)) var(--space-30)
|
|
2560
|
+
max(var(--space-30), env(safe-area-inset-bottom));
|
|
2220
2561
|
border-end-start-radius: 0;
|
|
2221
2562
|
border-end-end-radius: 0;
|
|
2222
2563
|
}
|
|
@@ -2229,23 +2570,23 @@ dialog.drawer nav {
|
|
|
2229
2570
|
|
|
2230
2571
|
.flyout.is-sheet[hidden] {
|
|
2231
2572
|
scale: 1;
|
|
2232
|
-
translate: 0 var(--space-
|
|
2573
|
+
translate: 0 var(--space-30);
|
|
2233
2574
|
}
|
|
2234
2575
|
|
|
2235
2576
|
@starting-style {
|
|
2236
2577
|
.flyout.is-sheet.is-open {
|
|
2237
2578
|
scale: 1;
|
|
2238
|
-
translate: 0 var(--space-
|
|
2579
|
+
translate: 0 var(--space-30);
|
|
2239
2580
|
}
|
|
2240
2581
|
}
|
|
2241
2582
|
|
|
2242
2583
|
@supports (transition-behavior: allow-discrete) {
|
|
2243
2584
|
.flyout {
|
|
2244
2585
|
transition:
|
|
2245
|
-
opacity var(--duration-fast
|
|
2246
|
-
scale var(--duration-fast
|
|
2247
|
-
translate var(--duration-fast
|
|
2248
|
-
display var(--duration-fast
|
|
2586
|
+
opacity var(--duration-fast) var(--ease),
|
|
2587
|
+
scale var(--duration-fast) var(--ease),
|
|
2588
|
+
translate var(--duration-fast) var(--ease),
|
|
2589
|
+
display var(--duration-fast) allow-discrete;
|
|
2249
2590
|
}
|
|
2250
2591
|
}
|
|
2251
2592
|
|
|
@@ -2255,7 +2596,7 @@ dialog.drawer nav {
|
|
|
2255
2596
|
|
|
2256
2597
|
.flyout > :where(section, footer) {
|
|
2257
2598
|
min-inline-size: 0;
|
|
2258
|
-
padding: var(--space-
|
|
2599
|
+
padding: var(--space-20);
|
|
2259
2600
|
}
|
|
2260
2601
|
|
|
2261
2602
|
.flyout > section > :where(h1, h2, h3, h4, h5, h6) {
|
|
@@ -2267,8 +2608,8 @@ dialog.drawer nav {
|
|
|
2267
2608
|
|
|
2268
2609
|
.flyout > section > ul {
|
|
2269
2610
|
display: grid;
|
|
2270
|
-
gap: var(--space-
|
|
2271
|
-
margin: var(--space-
|
|
2611
|
+
gap: var(--space-10);
|
|
2612
|
+
margin: var(--space-10) 0 0;
|
|
2272
2613
|
padding: 0;
|
|
2273
2614
|
list-style: none;
|
|
2274
2615
|
}
|
|
@@ -2277,91 +2618,133 @@ dialog.drawer nav {
|
|
|
2277
2618
|
margin: 0;
|
|
2278
2619
|
}
|
|
2279
2620
|
|
|
2280
|
-
/*
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2621
|
+
/* Vertical dividers — .menu-separator (menu.css) is the canonical way to
|
|
2622
|
+
separate menu items. Flyout panels that are not menus use auto grid gap
|
|
2623
|
+
declared on .flyout itself. */
|
|
2624
|
+
|
|
2625
|
+
/*
|
|
2626
|
+
* Menu — action list primitive consumed by flyout and context-menu.
|
|
2627
|
+
*
|
|
2628
|
+
* Strict anatomy: .menu > li > .menu-item. Separators and section labels
|
|
2629
|
+
* sit directly under .menu as siblings of <li>.
|
|
2630
|
+
*
|
|
2631
|
+
* Public hooks:
|
|
2632
|
+
* --menu-item-icon-size
|
|
2633
|
+
*/
|
|
2634
|
+
|
|
2635
|
+
.menu {
|
|
2636
|
+
display: grid;
|
|
2637
|
+
gap: var(--space-10);
|
|
2638
|
+
margin: 0;
|
|
2639
|
+
padding: var(--space-10);
|
|
2640
|
+
list-style: none;
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
.menu > li {
|
|
2644
|
+
margin: 0;
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2647
|
+
.menu-item {
|
|
2648
|
+
--menu-item-icon-size: 1.25em;
|
|
2649
|
+
|
|
2286
2650
|
display: flex;
|
|
2287
2651
|
align-items: center;
|
|
2288
|
-
|
|
2289
|
-
gap: var(--space-2);
|
|
2652
|
+
gap: var(--space-20);
|
|
2290
2653
|
inline-size: 100%;
|
|
2291
|
-
min-block-size: var(--control-size);
|
|
2292
|
-
padding-inline: var(--space-
|
|
2654
|
+
min-block-size: var(--menu-item-size, var(--control-size));
|
|
2655
|
+
padding-inline: var(--space-20);
|
|
2293
2656
|
border: 0;
|
|
2294
2657
|
border-radius: var(--radius-sm);
|
|
2295
|
-
background:
|
|
2658
|
+
background: transparent;
|
|
2296
2659
|
color: var(--text);
|
|
2297
2660
|
font: inherit;
|
|
2298
|
-
font-weight: var(--font-weight);
|
|
2299
|
-
text-decoration: none;
|
|
2300
2661
|
text-align: start;
|
|
2662
|
+
text-decoration: none;
|
|
2301
2663
|
white-space: nowrap;
|
|
2302
2664
|
cursor: pointer;
|
|
2303
2665
|
}
|
|
2304
2666
|
|
|
2305
|
-
|
|
2306
|
-
|
|
2667
|
+
.menu-item-icon {
|
|
2668
|
+
display: grid;
|
|
2669
|
+
flex: 0 0 var(--menu-item-icon-size);
|
|
2670
|
+
inline-size: var(--menu-item-icon-size);
|
|
2671
|
+
place-items: center;
|
|
2672
|
+
}
|
|
2673
|
+
|
|
2674
|
+
.menu-item-text {
|
|
2675
|
+
flex: 1;
|
|
2676
|
+
min-inline-size: 0;
|
|
2677
|
+
overflow: hidden;
|
|
2678
|
+
text-overflow: ellipsis;
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2681
|
+
.menu-item-end {
|
|
2682
|
+
display: inline-flex;
|
|
2683
|
+
align-items: center;
|
|
2684
|
+
gap: var(--space-10);
|
|
2685
|
+
margin-inline-start: auto;
|
|
2686
|
+
color: var(--text-muted);
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2689
|
+
/* Checkable menu items are one command with one accessible state. Their
|
|
2690
|
+
indicator is generated so authors do not add a second interactive input. */
|
|
2691
|
+
.menu-item:is([role="menuitemcheckbox"], [role="menuitemradio"])::before {
|
|
2692
|
+
display: grid;
|
|
2693
|
+
flex: 0 0 var(--menu-item-icon-size);
|
|
2694
|
+
inline-size: var(--menu-item-icon-size);
|
|
2695
|
+
place-items: center;
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
.menu-item[role="menuitemcheckbox"]::before {
|
|
2699
|
+
content: "";
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
.menu-item[role="menuitemcheckbox"][aria-checked="true"]::before {
|
|
2703
|
+
content: "✓";
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
.menu-item[role="menuitemcheckbox"][aria-checked="mixed"]::before {
|
|
2707
|
+
content: "−";
|
|
2708
|
+
}
|
|
2709
|
+
|
|
2710
|
+
.menu-item[role="menuitemradio"]::before {
|
|
2711
|
+
content: "○";
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2714
|
+
.menu-item[role="menuitemradio"][aria-checked="true"]::before {
|
|
2715
|
+
content: "●";
|
|
2307
2716
|
}
|
|
2308
2717
|
|
|
2309
|
-
|
|
2718
|
+
.menu-item:focus-visible {
|
|
2310
2719
|
background: var(--surface-subtle);
|
|
2311
|
-
outline: var(--focus-outline);
|
|
2312
|
-
outline-offset: var(--focus-outline-offset);
|
|
2313
2720
|
}
|
|
2314
2721
|
|
|
2315
|
-
:
|
|
2316
|
-
|
|
2317
|
-
cursor: not-allowed;
|
|
2318
|
-
opacity: var(--disabled-opacity);
|
|
2722
|
+
.menu-item:hover:not(:where(:disabled, [aria-disabled="true"])) {
|
|
2723
|
+
background: var(--surface-subtle);
|
|
2319
2724
|
}
|
|
2320
2725
|
|
|
2321
|
-
:is(
|
|
2322
|
-
|
|
2726
|
+
.menu-item:is(:disabled, [aria-disabled="true"]) {
|
|
2727
|
+
cursor: not-allowed;
|
|
2728
|
+
opacity: var(--disabled-opacity);
|
|
2323
2729
|
}
|
|
2324
2730
|
|
|
2325
|
-
|
|
2326
|
-
:
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
background: var(--surface-subtle);
|
|
2331
|
-
outline: none;
|
|
2332
|
-
}
|
|
2731
|
+
.menu-label {
|
|
2732
|
+
padding: var(--space-20);
|
|
2733
|
+
color: var(--text-muted);
|
|
2734
|
+
font-size: var(--font-size-xs);
|
|
2735
|
+
font-weight: var(--font-weight-strong);
|
|
2333
2736
|
}
|
|
2334
2737
|
|
|
2335
|
-
.
|
|
2336
|
-
.flyout > li > hr {
|
|
2337
|
-
inline-size: 100%;
|
|
2738
|
+
.menu-separator {
|
|
2338
2739
|
margin: 0;
|
|
2339
|
-
margin-block: var(--space-1);
|
|
2340
2740
|
border: 0;
|
|
2341
2741
|
border-block-start: var(--border-width) solid var(--border);
|
|
2342
2742
|
}
|
|
2343
2743
|
|
|
2344
2744
|
@media (forced-colors: active) {
|
|
2345
|
-
.
|
|
2346
|
-
border-
|
|
2347
|
-
|
|
2348
|
-
color: CanvasText;
|
|
2349
|
-
box-shadow: none;
|
|
2350
|
-
}
|
|
2351
|
-
|
|
2352
|
-
:is(.flyout, .flyout :where(li)) > :where(button, a, [role="menuitem"]):focus-visible {
|
|
2353
|
-
background: Canvas;
|
|
2354
|
-
color: CanvasText;
|
|
2355
|
-
}
|
|
2356
|
-
|
|
2357
|
-
@media (hover: hover) {
|
|
2358
|
-
:is(.flyout, .flyout :where(li))
|
|
2359
|
-
> :where(button, a, [role="menuitem"]):hover:not(:where(:disabled, [aria-disabled="true"])) {
|
|
2360
|
-
outline: var(--border-width) solid ButtonText;
|
|
2361
|
-
outline-offset: -1px;
|
|
2362
|
-
background: Canvas;
|
|
2363
|
-
color: CanvasText;
|
|
2364
|
-
}
|
|
2745
|
+
.menu-item:focus-visible {
|
|
2746
|
+
outline: var(--border-width) solid ButtonText;
|
|
2747
|
+
outline-offset: -1px;
|
|
2365
2748
|
}
|
|
2366
2749
|
}
|
|
2367
2750
|
|
|
@@ -2371,6 +2754,15 @@ dialog.drawer nav {
|
|
|
2371
2754
|
* Position, placement, and arrow position are managed by JavaScript
|
|
2372
2755
|
* via position: fixed, left, top, data-placement, and --arrow-x.
|
|
2373
2756
|
* Visibility is managed with hidden; CSS owns entry and exit animation.
|
|
2757
|
+
*
|
|
2758
|
+
* Public hooks:
|
|
2759
|
+
* --tooltip-bg
|
|
2760
|
+
* --tooltip-fg
|
|
2761
|
+
* --tooltip-arrow-size
|
|
2762
|
+
*
|
|
2763
|
+
* Internal:
|
|
2764
|
+
* --arrow-x (arrow position, written by floating.js)
|
|
2765
|
+
* --arrow-y (arrow position, written by floating.js)
|
|
2374
2766
|
*/
|
|
2375
2767
|
.tooltip {
|
|
2376
2768
|
--tooltip-bg: var(--surface-solid);
|
|
@@ -2392,8 +2784,8 @@ dialog.drawer nav {
|
|
|
2392
2784
|
opacity: 1;
|
|
2393
2785
|
scale: 1;
|
|
2394
2786
|
transition:
|
|
2395
|
-
opacity var(--duration-fast
|
|
2396
|
-
scale var(--duration-fast
|
|
2787
|
+
opacity var(--duration-fast) var(--ease),
|
|
2788
|
+
scale var(--duration-fast) var(--ease);
|
|
2397
2789
|
}
|
|
2398
2790
|
|
|
2399
2791
|
.tooltip[hidden] {
|
|
@@ -2412,9 +2804,9 @@ dialog.drawer nav {
|
|
|
2412
2804
|
@supports (transition-behavior: allow-discrete) {
|
|
2413
2805
|
.tooltip {
|
|
2414
2806
|
transition:
|
|
2415
|
-
opacity var(--duration-fast
|
|
2416
|
-
scale var(--duration-fast
|
|
2417
|
-
display var(--duration-fast
|
|
2807
|
+
opacity var(--duration-fast) var(--ease),
|
|
2808
|
+
scale var(--duration-fast) var(--ease),
|
|
2809
|
+
display var(--duration-fast) allow-discrete;
|
|
2418
2810
|
}
|
|
2419
2811
|
}
|
|
2420
2812
|
|
|
@@ -2464,8 +2856,6 @@ dialog.drawer nav {
|
|
|
2464
2856
|
.tooltip {
|
|
2465
2857
|
--tooltip-bg: Canvas;
|
|
2466
2858
|
--tooltip-fg: CanvasText;
|
|
2467
|
-
|
|
2468
|
-
border: var(--border-width) solid ButtonText;
|
|
2469
2859
|
}
|
|
2470
2860
|
|
|
2471
2861
|
.tooltip::before {
|
|
@@ -2481,9 +2871,14 @@ dialog.drawer nav {
|
|
|
2481
2871
|
* Keeps the clickable summary affordance aligned and usable without JavaScript
|
|
2482
2872
|
* or demo styles. Decorative palettes and custom markers belong outside the
|
|
2483
2873
|
* base component.
|
|
2874
|
+
*
|
|
2875
|
+
* Public hooks:
|
|
2876
|
+
* --accordion-radius
|
|
2877
|
+
* --accordion-marker-color
|
|
2484
2878
|
*/
|
|
2485
2879
|
.accordion {
|
|
2486
2880
|
--accordion-radius: var(--radius);
|
|
2881
|
+
--accordion-marker-color: var(--text-muted);
|
|
2487
2882
|
|
|
2488
2883
|
border: var(--border-width) solid var(--border);
|
|
2489
2884
|
border-radius: var(--accordion-radius);
|
|
@@ -2497,14 +2892,18 @@ dialog.drawer nav {
|
|
|
2497
2892
|
.accordion summary {
|
|
2498
2893
|
display: flex;
|
|
2499
2894
|
align-items: center;
|
|
2500
|
-
gap: var(--space-
|
|
2895
|
+
gap: var(--space-30);
|
|
2501
2896
|
cursor: pointer;
|
|
2502
2897
|
list-style: none;
|
|
2503
|
-
padding: var(--space-
|
|
2898
|
+
padding: var(--space-40);
|
|
2504
2899
|
color: var(--text);
|
|
2505
2900
|
font-weight: var(--font-weight-bold);
|
|
2506
2901
|
}
|
|
2507
2902
|
|
|
2903
|
+
.accordion summary:hover {
|
|
2904
|
+
color: var(--primary);
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2508
2907
|
.accordion summary::-webkit-details-marker {
|
|
2509
2908
|
display: none;
|
|
2510
2909
|
}
|
|
@@ -2515,15 +2914,9 @@ dialog.drawer nav {
|
|
|
2515
2914
|
margin-inline-start: auto;
|
|
2516
2915
|
inline-size: 1rem;
|
|
2517
2916
|
block-size: 1rem;
|
|
2518
|
-
background-color: var(--
|
|
2519
|
-
-webkit-mask
|
|
2520
|
-
mask
|
|
2521
|
-
-webkit-mask-repeat: no-repeat;
|
|
2522
|
-
mask-repeat: no-repeat;
|
|
2523
|
-
-webkit-mask-size: contain;
|
|
2524
|
-
mask-size: contain;
|
|
2525
|
-
-webkit-mask-position: center;
|
|
2526
|
-
mask-position: center;
|
|
2917
|
+
background-color: var(--accordion-marker-color);
|
|
2918
|
+
-webkit-mask: var(--icon-chevron) center / contain no-repeat;
|
|
2919
|
+
mask: var(--icon-chevron) center / contain no-repeat;
|
|
2527
2920
|
transition: transform var(--duration);
|
|
2528
2921
|
}
|
|
2529
2922
|
|
|
@@ -2532,14 +2925,14 @@ dialog.drawer nav {
|
|
|
2532
2925
|
}
|
|
2533
2926
|
|
|
2534
2927
|
.accordion details > summary + * {
|
|
2535
|
-
padding-block: 0 var(--space-
|
|
2536
|
-
padding-inline: var(--space-
|
|
2928
|
+
padding-block: 0 var(--space-40);
|
|
2929
|
+
padding-inline: var(--space-40);
|
|
2537
2930
|
color: var(--text-muted);
|
|
2538
2931
|
margin-block: 0;
|
|
2539
2932
|
}
|
|
2540
2933
|
|
|
2541
2934
|
.accordion details[open] > summary + * {
|
|
2542
|
-
padding-block-start: var(--space-
|
|
2935
|
+
padding-block-start: var(--space-10);
|
|
2543
2936
|
}
|
|
2544
2937
|
|
|
2545
2938
|
.accordion details > summary + * > :first-child {
|
|
@@ -2554,22 +2947,7 @@ dialog.drawer nav {
|
|
|
2554
2947
|
|
|
2555
2948
|
@media (forced-colors: active) {
|
|
2556
2949
|
.accordion {
|
|
2557
|
-
|
|
2558
|
-
background: Canvas;
|
|
2559
|
-
color: CanvasText;
|
|
2560
|
-
}
|
|
2561
|
-
|
|
2562
|
-
.accordion > details + details {
|
|
2563
|
-
border-block-start-color: ButtonText;
|
|
2564
|
-
}
|
|
2565
|
-
|
|
2566
|
-
.accordion summary {
|
|
2567
|
-
color: CanvasText;
|
|
2568
|
-
}
|
|
2569
|
-
|
|
2570
|
-
.accordion summary::after {
|
|
2571
|
-
background-color: ButtonText;
|
|
2572
|
-
forced-color-adjust: none;
|
|
2950
|
+
--accordion-marker-color: CanvasText;
|
|
2573
2951
|
}
|
|
2574
2952
|
}
|
|
2575
2953
|
|
|
@@ -2580,18 +2958,17 @@ dialog.drawer nav {
|
|
|
2580
2958
|
*
|
|
2581
2959
|
* Uses currentColor so it adapts to buttons, alerts, badges, and text. The
|
|
2582
2960
|
* [aria-busy] overlay composition lives in components/busy.css.
|
|
2961
|
+
*
|
|
2962
|
+
* Public hooks:
|
|
2963
|
+
* --spinner-size
|
|
2964
|
+
* --spinner-width
|
|
2965
|
+
* --spinner-track
|
|
2966
|
+
* --spinner-value
|
|
2583
2967
|
*/
|
|
2584
2968
|
/* Intent boundary — inherited intents do not tint this component.
|
|
2585
2969
|
Local intent classes still win because :where() has zero specificity. */
|
|
2586
2970
|
:where(.spinner) {
|
|
2587
|
-
/* @sync intent-boundary */
|
|
2588
2971
|
--intent: initial;
|
|
2589
|
-
--intent-fg: initial;
|
|
2590
|
-
--ui-bg: initial;
|
|
2591
|
-
--ui-fg: initial;
|
|
2592
|
-
--ui-border: initial;
|
|
2593
|
-
--ui-hover-bg: initial;
|
|
2594
|
-
/* @sync intent-boundary */
|
|
2595
2972
|
}
|
|
2596
2973
|
|
|
2597
2974
|
.spinner {
|
|
@@ -2641,6 +3018,13 @@ dialog.drawer nav {
|
|
|
2641
3018
|
*
|
|
2642
3019
|
* Shows approximate loading structure without implying real content hierarchy.
|
|
2643
3020
|
* Geometry, animation, and color stay theme-aware.
|
|
3021
|
+
*
|
|
3022
|
+
* Public hooks:
|
|
3023
|
+
* --skeleton-size
|
|
3024
|
+
* --skeleton-width
|
|
3025
|
+
* --skeleton-radius
|
|
3026
|
+
* --skeleton-track
|
|
3027
|
+
* --skeleton-highlight
|
|
2644
3028
|
*/
|
|
2645
3029
|
.skeleton {
|
|
2646
3030
|
--skeleton-size: 1rem;
|
|
@@ -2710,6 +3094,12 @@ dialog.drawer nav {
|
|
|
2710
3094
|
* A non-button element with aria-busy="true" and a direct last-child .spinner
|
|
2711
3095
|
* shows that spinner as a centered overlay over a faded surface. Buttons keep
|
|
2712
3096
|
* their spinners inline (e.g. .btn[aria-busy] > .spinner stays inline-flex).
|
|
3097
|
+
*
|
|
3098
|
+
* Public hooks:
|
|
3099
|
+
* --busy-overlay-bg
|
|
3100
|
+
*
|
|
3101
|
+
* Internal:
|
|
3102
|
+
* --spinner-size (overlay copy of the shared spinner hook)
|
|
2713
3103
|
*/
|
|
2714
3104
|
[aria-busy] > .spinner:last-child {
|
|
2715
3105
|
display: none;
|
|
@@ -2755,26 +3145,21 @@ dialog.drawer nav {
|
|
|
2755
3145
|
*
|
|
2756
3146
|
* Consumes the shared --intent/--primary for the value bar and --bar-height
|
|
2757
3147
|
* for thickness. Indeterminate state animates a shimmer using --duration-shimmer.
|
|
3148
|
+
*
|
|
3149
|
+
* Public hooks:
|
|
3150
|
+
* --progress-track
|
|
3151
|
+
* --progress-value
|
|
2758
3152
|
*/
|
|
2759
3153
|
/* Intent boundary — inherited intents do not tint this component.
|
|
2760
3154
|
Local intent classes still win because :where() has zero specificity. */
|
|
2761
3155
|
:where(.progress) {
|
|
2762
|
-
/* @sync intent-boundary */
|
|
2763
3156
|
--intent: initial;
|
|
2764
|
-
--intent-fg: initial;
|
|
2765
|
-
--ui-bg: initial;
|
|
2766
|
-
--ui-fg: initial;
|
|
2767
|
-
--ui-border: initial;
|
|
2768
|
-
--ui-hover-bg: initial;
|
|
2769
|
-
/* @sync intent-boundary */
|
|
2770
3157
|
}
|
|
2771
3158
|
|
|
2772
3159
|
.progress {
|
|
2773
3160
|
--progress-track: var(--surface-subtle);
|
|
2774
3161
|
--progress-value: var(--intent, var(--primary));
|
|
2775
|
-
}
|
|
2776
3162
|
|
|
2777
|
-
.progress {
|
|
2778
3163
|
appearance: none;
|
|
2779
3164
|
-webkit-appearance: none;
|
|
2780
3165
|
display: block;
|
|
@@ -2853,27 +3238,14 @@ dialog.drawer nav {
|
|
|
2853
3238
|
}
|
|
2854
3239
|
|
|
2855
3240
|
@media (forced-colors: active) {
|
|
2856
|
-
.progress
|
|
2857
|
-
|
|
2858
|
-
background: Canvas;
|
|
2859
|
-
}
|
|
2860
|
-
|
|
2861
|
-
.progress::-webkit-progress-bar {
|
|
2862
|
-
background: Canvas;
|
|
2863
|
-
}
|
|
2864
|
-
|
|
2865
|
-
.progress::-webkit-progress-value {
|
|
3241
|
+
.progress::-webkit-progress-value,
|
|
3242
|
+
.progress:indeterminate::-webkit-progress-value {
|
|
2866
3243
|
background: Highlight;
|
|
2867
|
-
forced-color-adjust: none;
|
|
2868
3244
|
}
|
|
2869
3245
|
|
|
2870
|
-
.progress::-moz-progress-bar
|
|
3246
|
+
.progress::-moz-progress-bar,
|
|
3247
|
+
.progress:indeterminate::-moz-progress-bar {
|
|
2871
3248
|
background: Highlight;
|
|
2872
|
-
forced-color-adjust: none;
|
|
2873
|
-
}
|
|
2874
|
-
|
|
2875
|
-
.progress:indeterminate {
|
|
2876
|
-
background: Canvas;
|
|
2877
3249
|
}
|
|
2878
3250
|
}
|
|
2879
3251
|
|
|
@@ -2892,19 +3264,17 @@ dialog.drawer nav {
|
|
|
2892
3264
|
* CSS API: .status-bar
|
|
2893
3265
|
* JS target contract: [data-status][role="status"]
|
|
2894
3266
|
* Normally combined, but they describe separate concerns.
|
|
3267
|
+
*
|
|
3268
|
+
* Internal:
|
|
3269
|
+
* --status-bg
|
|
3270
|
+
* --status-fg
|
|
2895
3271
|
*/
|
|
2896
3272
|
|
|
2897
3273
|
/* Intent boundary — inherited intents do not tint this component.
|
|
2898
3274
|
Local intent classes still win because :where() has zero specificity. */
|
|
2899
3275
|
:where(.status-bar) {
|
|
2900
|
-
/* @sync intent-boundary */
|
|
2901
3276
|
--intent: initial;
|
|
2902
3277
|
--intent-fg: initial;
|
|
2903
|
-
--ui-bg: initial;
|
|
2904
|
-
--ui-fg: initial;
|
|
2905
|
-
--ui-border: initial;
|
|
2906
|
-
--ui-hover-bg: initial;
|
|
2907
|
-
/* @sync intent-boundary */
|
|
2908
3278
|
}
|
|
2909
3279
|
|
|
2910
3280
|
.status-bar {
|
|
@@ -2912,22 +3282,22 @@ dialog.drawer nav {
|
|
|
2912
3282
|
--status-fg: var(--intent-fg, var(--surface));
|
|
2913
3283
|
|
|
2914
3284
|
position: fixed;
|
|
2915
|
-
inset-block-end: var(--space-
|
|
2916
|
-
inset-inline: var(--space-
|
|
2917
|
-
z-index: var(--z-status
|
|
3285
|
+
inset-block-end: var(--space-40);
|
|
3286
|
+
inset-inline: var(--space-40);
|
|
3287
|
+
z-index: var(--z-status);
|
|
2918
3288
|
|
|
2919
3289
|
display: inline-flex;
|
|
2920
3290
|
align-items: center;
|
|
2921
3291
|
justify-content: center;
|
|
2922
|
-
gap: var(--space-
|
|
3292
|
+
gap: var(--space-20);
|
|
2923
3293
|
|
|
2924
3294
|
inline-size: fit-content;
|
|
2925
|
-
max-inline-size: min(calc(
|
|
3295
|
+
max-inline-size: min(calc(var(--viewport-inline) - var(--space-60)), 32rem);
|
|
2926
3296
|
margin-inline: auto;
|
|
2927
3297
|
overflow-wrap: anywhere;
|
|
2928
3298
|
|
|
2929
|
-
padding-block: var(--space-
|
|
2930
|
-
padding-inline: var(--space-
|
|
3299
|
+
padding-block: var(--space-20);
|
|
3300
|
+
padding-inline: var(--space-30);
|
|
2931
3301
|
|
|
2932
3302
|
border-radius: var(--radius-full);
|
|
2933
3303
|
background: var(--status-bg);
|
|
@@ -2940,25 +3310,18 @@ dialog.drawer nav {
|
|
|
2940
3310
|
pointer-events: none;
|
|
2941
3311
|
|
|
2942
3312
|
transition:
|
|
2943
|
-
opacity var(--duration-slow) var(--ease
|
|
2944
|
-
scale var(--duration-slow) var(--ease
|
|
2945
|
-
translate var(--duration-slow) var(--ease
|
|
3313
|
+
opacity var(--duration-slow) var(--ease),
|
|
3314
|
+
scale var(--duration-slow) var(--ease),
|
|
3315
|
+
translate var(--duration-slow) var(--ease);
|
|
2946
3316
|
}
|
|
2947
3317
|
|
|
2948
3318
|
.status-bar:empty {
|
|
2949
3319
|
opacity: 0;
|
|
2950
3320
|
scale: 0.96;
|
|
2951
|
-
translate: 0 var(--space-
|
|
3321
|
+
translate: 0 var(--space-20);
|
|
2952
3322
|
pointer-events: none;
|
|
2953
3323
|
}
|
|
2954
3324
|
|
|
2955
|
-
@media (forced-colors: active) {
|
|
2956
|
-
.status-bar {
|
|
2957
|
-
border: var(--border-width) solid ButtonText;
|
|
2958
|
-
box-shadow: none;
|
|
2959
|
-
}
|
|
2960
|
-
}
|
|
2961
|
-
|
|
2962
3325
|
|
|
2963
3326
|
/* Display */
|
|
2964
3327
|
/*
|
|
@@ -2967,18 +3330,26 @@ dialog.drawer nav {
|
|
|
2967
3330
|
* The visual box stays the same across semantic elements. Grouping and status
|
|
2968
3331
|
* dots (an empty .badge child) compose around the avatar without changing its
|
|
2969
3332
|
* meaning.
|
|
3333
|
+
*
|
|
3334
|
+
* Public hooks:
|
|
3335
|
+
* --avatar-size
|
|
3336
|
+
* --avatar-radius
|
|
3337
|
+
* --avatar-font-size
|
|
3338
|
+
* --avatar-stack-size
|
|
3339
|
+
* --avatar-stack-font-size
|
|
3340
|
+
* --avatar-stack-overlap
|
|
3341
|
+
* --avatar-stack-ring
|
|
3342
|
+
*
|
|
3343
|
+
* Internal:
|
|
3344
|
+
* --avatar-bg
|
|
3345
|
+
* --avatar-fg
|
|
3346
|
+
* --avatar-status-translate-x
|
|
2970
3347
|
*/
|
|
2971
3348
|
/* Intent boundary — inherited intents do not tint this component.
|
|
2972
3349
|
Local intent classes still win because :where() has zero specificity. */
|
|
2973
3350
|
:where(.avatar) {
|
|
2974
|
-
/* @sync intent-boundary */
|
|
2975
3351
|
--intent: initial;
|
|
2976
3352
|
--intent-fg: initial;
|
|
2977
|
-
--ui-bg: initial;
|
|
2978
|
-
--ui-fg: initial;
|
|
2979
|
-
--ui-border: initial;
|
|
2980
|
-
--ui-hover-bg: initial;
|
|
2981
|
-
/* @sync intent-boundary */
|
|
2982
3353
|
}
|
|
2983
3354
|
|
|
2984
3355
|
.avatar {
|
|
@@ -3070,10 +3441,8 @@ button.avatar {
|
|
|
3070
3441
|
transition: opacity var(--duration);
|
|
3071
3442
|
}
|
|
3072
3443
|
|
|
3073
|
-
|
|
3074
|
-
:
|
|
3075
|
-
opacity: 1;
|
|
3076
|
-
}
|
|
3444
|
+
:where(a.avatar, button.avatar):not(:where(:disabled, [aria-disabled="true"])):hover::after {
|
|
3445
|
+
opacity: 1;
|
|
3077
3446
|
}
|
|
3078
3447
|
|
|
3079
3448
|
:where(a.avatar, button.avatar):not(:where(:disabled, [aria-disabled="true"])):active {
|
|
@@ -3166,6 +3535,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
3166
3535
|
*
|
|
3167
3536
|
* Use .key outside prose. Plain kbd inside .prose gets the same visual
|
|
3168
3537
|
* treatment from prose.css so authored content stays convenient.
|
|
3538
|
+
*
|
|
3539
|
+
* Visual twin of .prose kbd (prose.css) — keep the two recipes in sync.
|
|
3169
3540
|
*/
|
|
3170
3541
|
.key {
|
|
3171
3542
|
display: inline-flex;
|
|
@@ -3193,22 +3564,17 @@ a.avatar[aria-disabled="true"] {
|
|
|
3193
3564
|
* Base form is unstyled chrome that takes its surrounding color. Add .pill for
|
|
3194
3565
|
* the bordered rounded shape, and combine with an intent class (.primary,
|
|
3195
3566
|
* .success, etc.) to tint the text, border, and background.
|
|
3567
|
+
*
|
|
3568
|
+
* Public hooks:
|
|
3569
|
+
* --overline-radius
|
|
3196
3570
|
*/
|
|
3197
3571
|
/* Intent boundary — inherited intents do not tint this component.
|
|
3198
3572
|
Local intent classes still win because :where() has zero specificity. */
|
|
3199
3573
|
:where(.overline) {
|
|
3200
|
-
/* @sync intent-boundary */
|
|
3201
3574
|
--intent: initial;
|
|
3202
|
-
--intent-fg: initial;
|
|
3203
|
-
--ui-bg: initial;
|
|
3204
|
-
--ui-fg: initial;
|
|
3205
|
-
--ui-border: initial;
|
|
3206
|
-
--ui-hover-bg: initial;
|
|
3207
|
-
/* @sync intent-boundary */
|
|
3208
3575
|
}
|
|
3209
3576
|
|
|
3210
3577
|
.overline {
|
|
3211
|
-
align-self: flex-start;
|
|
3212
3578
|
color: var(--text-muted);
|
|
3213
3579
|
font-size: var(--font-size-sm);
|
|
3214
3580
|
font-weight: var(--font-weight-medium);
|
|
@@ -3217,14 +3583,18 @@ a.avatar[aria-disabled="true"] {
|
|
|
3217
3583
|
}
|
|
3218
3584
|
|
|
3219
3585
|
.overline.pill {
|
|
3586
|
+
--overline-radius: var(--radius-full);
|
|
3587
|
+
|
|
3220
3588
|
display: inline-flex;
|
|
3221
3589
|
align-items: center;
|
|
3222
3590
|
gap: 0.5em;
|
|
3223
3591
|
padding-inline: 0.6em;
|
|
3224
3592
|
padding-block: 0.25em;
|
|
3593
|
+
inline-size: fit-content;
|
|
3594
|
+
max-inline-size: 100%;
|
|
3225
3595
|
color: var(--intent, var(--text-muted));
|
|
3226
3596
|
border: var(--border-width) solid var(--border);
|
|
3227
|
-
border-radius: var(--radius
|
|
3597
|
+
border-radius: var(--overline-radius);
|
|
3228
3598
|
background: var(--surface);
|
|
3229
3599
|
}
|
|
3230
3600
|
|
|
@@ -3245,26 +3615,37 @@ a.avatar[aria-disabled="true"] {
|
|
|
3245
3615
|
* .navbar is a horizontal bar; .navbar-brand is the brand link; .navbar-nav is
|
|
3246
3616
|
* the inline link list using .nav-link items. .nav-list is the vertical nav
|
|
3247
3617
|
* list shared with .drawer, fed by the shared --gap token.
|
|
3618
|
+
*
|
|
3619
|
+
* Internal:
|
|
3620
|
+
* --gap (local value for the shared gap token)
|
|
3248
3621
|
*/
|
|
3622
|
+
/* Intent boundary — inherited intents do not tint this component.
|
|
3623
|
+
Local intent classes still win because :where() has zero specificity. */
|
|
3624
|
+
:where(.navbar) {
|
|
3625
|
+
--ui-bg: initial;
|
|
3626
|
+
--ui-fg: initial;
|
|
3627
|
+
--ui-border: initial;
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3249
3630
|
.navbar {
|
|
3250
3631
|
display: flex;
|
|
3251
3632
|
flex-wrap: wrap;
|
|
3252
3633
|
align-items: center;
|
|
3253
3634
|
justify-content: space-between;
|
|
3254
3635
|
gap: var(--gap);
|
|
3255
|
-
padding-block: var(--space-
|
|
3256
|
-
padding-inline: var(--space-
|
|
3257
|
-
border: var(--border-width) solid var(--border);
|
|
3636
|
+
padding-block: var(--space-30);
|
|
3637
|
+
padding-inline: var(--space-40);
|
|
3638
|
+
border: var(--border-width) solid var(--ui-border, var(--border));
|
|
3258
3639
|
border-radius: var(--radius-lg);
|
|
3259
|
-
background: var(--surface-raised);
|
|
3260
|
-
color: var(--text);
|
|
3640
|
+
background: var(--ui-bg, var(--surface-raised));
|
|
3641
|
+
color: var(--ui-fg, var(--text));
|
|
3261
3642
|
}
|
|
3262
3643
|
|
|
3263
3644
|
.navbar-brand {
|
|
3264
3645
|
display: inline-flex;
|
|
3265
3646
|
align-items: center;
|
|
3266
|
-
gap: var(--space-
|
|
3267
|
-
color: var(--text);
|
|
3647
|
+
gap: var(--space-20);
|
|
3648
|
+
color: var(--ui-fg, var(--text));
|
|
3268
3649
|
font-weight: var(--font-weight-bold);
|
|
3269
3650
|
text-decoration: none;
|
|
3270
3651
|
}
|
|
@@ -3277,7 +3658,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
3277
3658
|
display: flex;
|
|
3278
3659
|
flex-wrap: wrap;
|
|
3279
3660
|
align-items: center;
|
|
3280
|
-
gap: var(--space-
|
|
3661
|
+
gap: var(--space-10);
|
|
3281
3662
|
margin: 0;
|
|
3282
3663
|
padding: 0;
|
|
3283
3664
|
list-style: none;
|
|
@@ -3287,9 +3668,9 @@ a.avatar[aria-disabled="true"] {
|
|
|
3287
3668
|
display: inline-flex;
|
|
3288
3669
|
align-items: center;
|
|
3289
3670
|
min-block-size: var(--control-size-sm);
|
|
3290
|
-
padding-inline: var(--space-
|
|
3671
|
+
padding-inline: var(--space-20);
|
|
3291
3672
|
border-radius: var(--radius);
|
|
3292
|
-
color: var(--text-muted);
|
|
3673
|
+
color: var(--ui-fg, var(--text-muted));
|
|
3293
3674
|
font-weight: var(--font-weight-medium);
|
|
3294
3675
|
text-decoration: none;
|
|
3295
3676
|
}
|
|
@@ -3299,21 +3680,19 @@ a.avatar[aria-disabled="true"] {
|
|
|
3299
3680
|
color: var(--text);
|
|
3300
3681
|
}
|
|
3301
3682
|
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
color: var(--text);
|
|
3306
|
-
}
|
|
3683
|
+
.nav-link:hover {
|
|
3684
|
+
background: var(--surface-subtle);
|
|
3685
|
+
color: var(--text);
|
|
3307
3686
|
}
|
|
3308
3687
|
|
|
3309
3688
|
/* Nav list — a vertical navigation list. Used by .drawer for stacked links. */
|
|
3310
3689
|
.nav-list {
|
|
3311
3690
|
display: grid;
|
|
3691
|
+
--gap: var(--space-20);
|
|
3312
3692
|
gap: var(--gap);
|
|
3313
3693
|
margin: 0;
|
|
3314
3694
|
padding: 0;
|
|
3315
3695
|
list-style: none;
|
|
3316
|
-
--gap: var(--space-2);
|
|
3317
3696
|
}
|
|
3318
3697
|
|
|
3319
3698
|
.nav-list > li {
|
|
@@ -3328,13 +3707,11 @@ a.avatar[aria-disabled="true"] {
|
|
|
3328
3707
|
forced-color-adjust: none;
|
|
3329
3708
|
}
|
|
3330
3709
|
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
color: CanvasText;
|
|
3337
|
-
}
|
|
3710
|
+
.nav-link:hover:not(:where([aria-current]:not([aria-current="false"]))) {
|
|
3711
|
+
outline: var(--border-width) solid ButtonText;
|
|
3712
|
+
outline-offset: 2px;
|
|
3713
|
+
background: Canvas;
|
|
3714
|
+
color: CanvasText;
|
|
3338
3715
|
}
|
|
3339
3716
|
}
|
|
3340
3717
|
|
|
@@ -3348,7 +3725,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
3348
3725
|
display: flex;
|
|
3349
3726
|
flex-wrap: wrap;
|
|
3350
3727
|
align-items: center;
|
|
3351
|
-
gap: var(--space-
|
|
3728
|
+
gap: var(--space-10);
|
|
3352
3729
|
margin: 0;
|
|
3353
3730
|
padding: 0;
|
|
3354
3731
|
color: var(--text-subtle);
|
|
@@ -3359,7 +3736,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
3359
3736
|
.breadcrumb li {
|
|
3360
3737
|
display: inline-flex;
|
|
3361
3738
|
align-items: center;
|
|
3362
|
-
gap: var(--space-
|
|
3739
|
+
gap: var(--space-10);
|
|
3363
3740
|
}
|
|
3364
3741
|
|
|
3365
3742
|
.breadcrumb li + li::before {
|
|
@@ -3372,25 +3749,17 @@ a.avatar[aria-disabled="true"] {
|
|
|
3372
3749
|
text-decoration: none;
|
|
3373
3750
|
}
|
|
3374
3751
|
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
color: var(--text);
|
|
3380
|
-
text-decoration: underline;
|
|
3381
|
-
}
|
|
3752
|
+
.breadcrumb a:not([aria-current]):hover,
|
|
3753
|
+
.breadcrumb a[aria-current="false"]:hover {
|
|
3754
|
+
color: var(--text);
|
|
3755
|
+
text-decoration: underline;
|
|
3382
3756
|
}
|
|
3383
3757
|
|
|
3384
3758
|
.breadcrumb a:focus-visible {
|
|
3385
3759
|
border-radius: var(--radius-sm);
|
|
3386
3760
|
}
|
|
3387
3761
|
|
|
3388
|
-
.breadcrumb a:where([aria-current]:not([aria-current="false"])) {
|
|
3389
|
-
color: var(--text);
|
|
3390
|
-
font-weight: var(--font-weight-medium);
|
|
3391
|
-
}
|
|
3392
|
-
|
|
3393
|
-
.breadcrumb li:where([aria-current]:not([aria-current="false"])) > :where(a, span) {
|
|
3762
|
+
.breadcrumb :where(a, span):where([aria-current]:not([aria-current="false"])) {
|
|
3394
3763
|
color: var(--text);
|
|
3395
3764
|
font-weight: var(--font-weight-medium);
|
|
3396
3765
|
}
|
|
@@ -3409,7 +3778,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
3409
3778
|
display: flex;
|
|
3410
3779
|
flex-wrap: wrap;
|
|
3411
3780
|
align-items: center;
|
|
3412
|
-
gap: var(--space-
|
|
3781
|
+
gap: var(--space-10);
|
|
3413
3782
|
margin: 0;
|
|
3414
3783
|
padding: 0;
|
|
3415
3784
|
list-style: none;
|
|
@@ -3432,24 +3801,20 @@ a.avatar[aria-disabled="true"] {
|
|
|
3432
3801
|
* JavaScript owns roving tabindex, aria-selected, panel visibility,
|
|
3433
3802
|
* and arrow-key navigation. CSS owns the tab strip appearance and
|
|
3434
3803
|
* active indicator.
|
|
3804
|
+
*
|
|
3805
|
+
* Public hooks:
|
|
3806
|
+
* --tab-gap
|
|
3435
3807
|
*/
|
|
3436
3808
|
/* Intent boundary — inherited intents do not tint this component.
|
|
3437
3809
|
Local intent classes still win because :where() has zero specificity. */
|
|
3438
3810
|
:where(.tab) {
|
|
3439
|
-
/* @sync intent-boundary */
|
|
3440
3811
|
--intent: initial;
|
|
3441
|
-
--intent-fg: initial;
|
|
3442
|
-
--ui-bg: initial;
|
|
3443
|
-
--ui-fg: initial;
|
|
3444
|
-
--ui-border: initial;
|
|
3445
|
-
--ui-hover-bg: initial;
|
|
3446
|
-
/* @sync intent-boundary */
|
|
3447
3812
|
}
|
|
3448
3813
|
|
|
3449
3814
|
.tabs {
|
|
3450
3815
|
display: flex;
|
|
3451
3816
|
flex-wrap: wrap;
|
|
3452
|
-
gap: var(--space-
|
|
3817
|
+
gap: var(--space-10);
|
|
3453
3818
|
margin: 0;
|
|
3454
3819
|
padding: 0;
|
|
3455
3820
|
border-block-end: var(--border-width) solid var(--border);
|
|
@@ -3467,8 +3832,9 @@ a.avatar[aria-disabled="true"] {
|
|
|
3467
3832
|
.tab {
|
|
3468
3833
|
display: inline-flex;
|
|
3469
3834
|
align-items: center;
|
|
3835
|
+
gap: var(--tab-gap, 0.375em);
|
|
3470
3836
|
min-block-size: var(--control-size);
|
|
3471
|
-
padding-inline: var(--space-
|
|
3837
|
+
padding-inline: var(--space-30);
|
|
3472
3838
|
border: 0;
|
|
3473
3839
|
border-block-end: 2px solid transparent;
|
|
3474
3840
|
border-radius: 0;
|
|
@@ -3484,7 +3850,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
3484
3850
|
justify-content: flex-start;
|
|
3485
3851
|
border-block-end: 0;
|
|
3486
3852
|
border-inline-end: 2px solid transparent;
|
|
3487
|
-
padding-inline-end: var(--space-
|
|
3853
|
+
padding-inline-end: var(--space-40);
|
|
3488
3854
|
}
|
|
3489
3855
|
|
|
3490
3856
|
.tab:is(:disabled, [aria-disabled="true"]) {
|
|
@@ -3492,10 +3858,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
3492
3858
|
opacity: var(--disabled-opacity);
|
|
3493
3859
|
}
|
|
3494
3860
|
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
color: var(--text);
|
|
3498
|
-
}
|
|
3861
|
+
.tab:hover:not(:where(:disabled, [aria-disabled="true"])) {
|
|
3862
|
+
color: var(--text);
|
|
3499
3863
|
}
|
|
3500
3864
|
|
|
3501
3865
|
.tab:focus-visible {
|
|
@@ -3505,52 +3869,28 @@ a.avatar[aria-disabled="true"] {
|
|
|
3505
3869
|
border-radius: var(--radius-sm) var(--radius-sm) 0 0;
|
|
3506
3870
|
}
|
|
3507
3871
|
|
|
3508
|
-
.tab[aria-selected="true"]
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
border-block-end-color: var(--intent, var(--primary));
|
|
3512
|
-
color: var(--intent, var(--primary));
|
|
3872
|
+
.tab[aria-selected="true"] {
|
|
3873
|
+
border-block-end-color: var(--intent, var(--state-selected));
|
|
3874
|
+
color: var(--intent, var(--state-selected));
|
|
3513
3875
|
}
|
|
3514
3876
|
|
|
3515
|
-
.tabs[aria-orientation="vertical"] .tab[aria-selected="true"]
|
|
3516
|
-
.tabs[aria-orientation="vertical"] .tab[aria-current]:not([aria-current="false"]),
|
|
3517
|
-
.tabs[aria-orientation="vertical"] .tab[aria-pressed="true"] {
|
|
3877
|
+
.tabs[aria-orientation="vertical"] .tab[aria-selected="true"] {
|
|
3518
3878
|
border-block-end-color: transparent;
|
|
3519
|
-
border-inline-end-color: var(--intent, var(--
|
|
3879
|
+
border-inline-end-color: var(--intent, var(--state-selected));
|
|
3520
3880
|
}
|
|
3521
3881
|
|
|
3522
3882
|
@media (forced-colors: active) {
|
|
3523
|
-
.
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
.tabs[aria-orientation="vertical"] {
|
|
3528
|
-
border-block-end-color: transparent;
|
|
3529
|
-
border-inline-end-color: ButtonText;
|
|
3530
|
-
}
|
|
3531
|
-
|
|
3532
|
-
.tab {
|
|
3533
|
-
color: ButtonText;
|
|
3534
|
-
}
|
|
3535
|
-
|
|
3536
|
-
@media (hover: hover) {
|
|
3537
|
-
.tab:hover:not(:where(:disabled, [aria-disabled="true"])) {
|
|
3538
|
-
color: ButtonText;
|
|
3539
|
-
text-decoration: underline;
|
|
3540
|
-
text-underline-offset: 0.18em;
|
|
3541
|
-
}
|
|
3883
|
+
.tab:hover:not(:where(:disabled, [aria-disabled="true"])) {
|
|
3884
|
+
text-decoration: underline;
|
|
3885
|
+
text-underline-offset: 0.18em;
|
|
3542
3886
|
}
|
|
3543
3887
|
|
|
3544
|
-
.tab[aria-selected="true"]
|
|
3545
|
-
.tab[aria-current]:not([aria-current="false"]),
|
|
3546
|
-
.tab[aria-pressed="true"] {
|
|
3888
|
+
.tab[aria-selected="true"] {
|
|
3547
3889
|
border-block-end-color: Highlight;
|
|
3548
3890
|
color: Highlight;
|
|
3549
3891
|
}
|
|
3550
3892
|
|
|
3551
|
-
.tabs[aria-orientation="vertical"] .tab[aria-selected="true"]
|
|
3552
|
-
.tabs[aria-orientation="vertical"] .tab[aria-current]:not([aria-current="false"]),
|
|
3553
|
-
.tabs[aria-orientation="vertical"] .tab[aria-pressed="true"] {
|
|
3893
|
+
.tabs[aria-orientation="vertical"] .tab[aria-selected="true"] {
|
|
3554
3894
|
border-block-end-color: transparent;
|
|
3555
3895
|
border-inline-end-color: Highlight;
|
|
3556
3896
|
}
|
|
@@ -3591,6 +3931,10 @@ a.avatar[aria-disabled="true"] {
|
|
|
3591
3931
|
*
|
|
3592
3932
|
* .table-wrap provides the scroll container and outer chrome; .table owns row
|
|
3593
3933
|
* and cell rules. Tokens are sourced from the shared scale.
|
|
3934
|
+
*
|
|
3935
|
+
* Public hooks:
|
|
3936
|
+
* --table-min
|
|
3937
|
+
* --table-cell-pad
|
|
3594
3938
|
*/
|
|
3595
3939
|
.table-wrap {
|
|
3596
3940
|
inline-size: 100%;
|
|
@@ -3604,7 +3948,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
3604
3948
|
|
|
3605
3949
|
.table {
|
|
3606
3950
|
--table-min: 100%;
|
|
3607
|
-
--table-cell-pad: var(--space-
|
|
3951
|
+
--table-cell-pad: var(--space-30);
|
|
3608
3952
|
|
|
3609
3953
|
inline-size: max(var(--table-min), 100%);
|
|
3610
3954
|
border-collapse: collapse;
|
|
@@ -3640,10 +3984,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
3640
3984
|
background: var(--surface-subtle);
|
|
3641
3985
|
}
|
|
3642
3986
|
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
background-color: var(--hover-overlay);
|
|
3646
|
-
}
|
|
3987
|
+
.table tbody tr:hover {
|
|
3988
|
+
background-color: var(--hover-overlay);
|
|
3647
3989
|
}
|
|
3648
3990
|
|
|
3649
3991
|
.table > tr:last-child > *,
|
|
@@ -3657,18 +3999,6 @@ a.avatar[aria-disabled="true"] {
|
|
|
3657
3999
|
* Maps the three standard meter zones (optimum / suboptimum / even-less-good)
|
|
3658
4000
|
* to the shared success / warning / danger intent colors.
|
|
3659
4001
|
*/
|
|
3660
|
-
/* Intent boundary — inherited intents do not tint this component.
|
|
3661
|
-
Local intent classes still win because :where() has zero specificity. */
|
|
3662
|
-
:where(.meter) {
|
|
3663
|
-
/* @sync intent-boundary */
|
|
3664
|
-
--intent: initial;
|
|
3665
|
-
--intent-fg: initial;
|
|
3666
|
-
--ui-bg: initial;
|
|
3667
|
-
--ui-fg: initial;
|
|
3668
|
-
--ui-border: initial;
|
|
3669
|
-
--ui-hover-bg: initial;
|
|
3670
|
-
/* @sync intent-boundary */
|
|
3671
|
-
}
|
|
3672
4002
|
|
|
3673
4003
|
.meter {
|
|
3674
4004
|
appearance: none;
|
|
@@ -3714,29 +4044,18 @@ a.avatar[aria-disabled="true"] {
|
|
|
3714
4044
|
}
|
|
3715
4045
|
|
|
3716
4046
|
@media (forced-colors: active) {
|
|
3717
|
-
.meter {
|
|
3718
|
-
border: var(--border-width) solid ButtonText;
|
|
3719
|
-
background: Canvas;
|
|
3720
|
-
}
|
|
3721
|
-
|
|
3722
|
-
.meter::-webkit-meter-bar {
|
|
3723
|
-
border: 0;
|
|
3724
|
-
background: Canvas;
|
|
3725
|
-
}
|
|
3726
|
-
|
|
3727
4047
|
.meter::-webkit-meter-optimum-value,
|
|
3728
4048
|
.meter::-webkit-meter-suboptimum-value,
|
|
3729
4049
|
.meter::-webkit-meter-even-less-good-value,
|
|
3730
4050
|
.meter::-moz-meter-bar {
|
|
3731
4051
|
background: Highlight;
|
|
3732
|
-
forced-color-adjust: none;
|
|
3733
4052
|
}
|
|
3734
4053
|
}
|
|
3735
4054
|
|
|
3736
|
-
/* Forms — import order matters: switch.css
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
4055
|
+
/* Forms — import order matters: switch.css follows choice.css so its rules
|
|
4056
|
+
land after the shared choice recipe. validation.css is part of the default
|
|
4057
|
+
forms bundle, but custom builds may omit it when validation styling is
|
|
4058
|
+
app-owned. */
|
|
3740
4059
|
/*
|
|
3741
4060
|
* Form layout and supporting text.
|
|
3742
4061
|
*
|
|
@@ -3745,23 +4064,26 @@ a.avatar[aria-disabled="true"] {
|
|
|
3745
4064
|
*
|
|
3746
4065
|
* Public API: .field, .field-label, .field-help, .field-error, .field-group,
|
|
3747
4066
|
* .required-mark.
|
|
4067
|
+
*
|
|
4068
|
+
* Public hooks:
|
|
4069
|
+
* --fieldset-legend-padding-inline
|
|
3748
4070
|
*/
|
|
3749
4071
|
.field {
|
|
3750
4072
|
display: grid;
|
|
3751
|
-
gap: var(--space-
|
|
4073
|
+
gap: var(--space-10);
|
|
3752
4074
|
color: var(--text);
|
|
3753
4075
|
}
|
|
3754
4076
|
|
|
3755
4077
|
.field-group {
|
|
3756
4078
|
min-inline-size: 0;
|
|
3757
4079
|
margin: 0;
|
|
3758
|
-
padding: var(--space-
|
|
4080
|
+
padding: var(--space-30);
|
|
3759
4081
|
border: var(--border-width) solid var(--border);
|
|
3760
4082
|
border-radius: var(--radius);
|
|
3761
4083
|
}
|
|
3762
4084
|
|
|
3763
4085
|
.field-group + .field-group {
|
|
3764
|
-
margin-block-start: var(--space-
|
|
4086
|
+
margin-block-start: var(--space-40);
|
|
3765
4087
|
}
|
|
3766
4088
|
|
|
3767
4089
|
.field-label,
|
|
@@ -3772,7 +4094,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
3772
4094
|
|
|
3773
4095
|
.field-group > legend {
|
|
3774
4096
|
padding-block: 0;
|
|
3775
|
-
padding-inline: var(--fieldset-legend-padding-inline, var(--space-
|
|
4097
|
+
padding-inline: var(--fieldset-legend-padding-inline, var(--space-10));
|
|
3776
4098
|
font-size: var(--font-size-xs);
|
|
3777
4099
|
}
|
|
3778
4100
|
|
|
@@ -3811,6 +4133,14 @@ a.avatar[aria-disabled="true"] {
|
|
|
3811
4133
|
* --control-border-block-end-color/-width are unset extension points, mirroring
|
|
3812
4134
|
* --btn-border-block-end-* (button.css), for themes that give text controls an
|
|
3813
4135
|
* underline-style lower edge instead of a uniform border.
|
|
4136
|
+
*
|
|
4137
|
+
* Public hooks:
|
|
4138
|
+
* --control-border
|
|
4139
|
+
* --control-bg
|
|
4140
|
+
* --control-fg
|
|
4141
|
+
* --control-cursor
|
|
4142
|
+
* --control-border-block-end-color
|
|
4143
|
+
* --control-border-block-end-width
|
|
3814
4144
|
*/
|
|
3815
4145
|
.input,
|
|
3816
4146
|
.textarea,
|
|
@@ -3818,14 +4148,12 @@ a.avatar[aria-disabled="true"] {
|
|
|
3818
4148
|
--control-border: var(--border);
|
|
3819
4149
|
--control-bg: var(--surface);
|
|
3820
4150
|
--control-fg: var(--text);
|
|
3821
|
-
--control-ring: none;
|
|
3822
4151
|
--control-cursor: auto;
|
|
3823
|
-
--control-opacity: 1;
|
|
3824
4152
|
|
|
3825
4153
|
appearance: none;
|
|
3826
4154
|
inline-size: 100%;
|
|
3827
4155
|
min-block-size: var(--control-size);
|
|
3828
|
-
padding-inline: var(--space-
|
|
4156
|
+
padding-inline: var(--space-30);
|
|
3829
4157
|
border: var(--border-width) solid var(--form-invalid-border, var(--control-border));
|
|
3830
4158
|
border-block-end-color: var(
|
|
3831
4159
|
--form-invalid-border,
|
|
@@ -3836,9 +4164,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
3836
4164
|
background: var(--control-bg);
|
|
3837
4165
|
color: var(--control-fg);
|
|
3838
4166
|
font-size: var(--control-font-size);
|
|
3839
|
-
box-shadow: var(--control-ring);
|
|
3840
4167
|
cursor: var(--control-cursor);
|
|
3841
|
-
opacity: var(--control-opacity);
|
|
3842
4168
|
outline: none;
|
|
3843
4169
|
transition:
|
|
3844
4170
|
border-color var(--duration),
|
|
@@ -3849,26 +4175,24 @@ a.avatar[aria-disabled="true"] {
|
|
|
3849
4175
|
|
|
3850
4176
|
.textarea {
|
|
3851
4177
|
min-block-size: 6rem;
|
|
3852
|
-
padding-block: var(--space-
|
|
4178
|
+
padding-block: var(--space-20);
|
|
3853
4179
|
resize: vertical;
|
|
3854
4180
|
}
|
|
3855
4181
|
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
}
|
|
4182
|
+
.input:hover:not(:disabled),
|
|
4183
|
+
.textarea:hover:not(:disabled) {
|
|
4184
|
+
--control-border: var(--text-subtle);
|
|
4185
|
+
}
|
|
3861
4186
|
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
}
|
|
4187
|
+
.select:hover:not(:disabled) {
|
|
4188
|
+
--control-bg: var(--surface-subtle);
|
|
3865
4189
|
}
|
|
3866
4190
|
|
|
3867
4191
|
.input:focus-visible,
|
|
3868
4192
|
.textarea:focus-visible,
|
|
3869
4193
|
.select:focus-visible {
|
|
3870
4194
|
--control-border: var(--focus);
|
|
3871
|
-
outline:
|
|
4195
|
+
outline: 2px solid transparent;
|
|
3872
4196
|
box-shadow: var(--focus-ring-shadow);
|
|
3873
4197
|
}
|
|
3874
4198
|
|
|
@@ -3882,10 +4206,9 @@ a.avatar[aria-disabled="true"] {
|
|
|
3882
4206
|
.textarea:disabled,
|
|
3883
4207
|
.select:disabled {
|
|
3884
4208
|
--control-bg: var(--surface-subtle);
|
|
3885
|
-
--control-fg: var(--
|
|
4209
|
+
--control-fg: var(--state-disabled);
|
|
3886
4210
|
--control-border: var(--border);
|
|
3887
4211
|
--control-cursor: not-allowed;
|
|
3888
|
-
--control-opacity: 1;
|
|
3889
4212
|
}
|
|
3890
4213
|
|
|
3891
4214
|
.input::placeholder,
|
|
@@ -3894,40 +4217,58 @@ a.avatar[aria-disabled="true"] {
|
|
|
3894
4217
|
}
|
|
3895
4218
|
|
|
3896
4219
|
/*
|
|
3897
|
-
* Input Icon —
|
|
4220
|
+
* Input Icon — decorative icon inside a relative wrapper, leading or trailing.
|
|
3898
4221
|
*
|
|
3899
|
-
* Icon-library agnostic:
|
|
3900
|
-
*
|
|
3901
|
-
*
|
|
3902
|
-
* the
|
|
3903
|
-
*
|
|
4222
|
+
* Icon-library agnostic: detects position by DOM order. First child that is
|
|
4223
|
+
* not .input = leading icon; icon after .input = trailing. The icon keeps a
|
|
4224
|
+
* fixed size; density contexts shrink the control geometry around it, not
|
|
4225
|
+
* the pictogram.
|
|
4226
|
+
*
|
|
4227
|
+
* Public hooks:
|
|
4228
|
+
* --input-icon-size
|
|
3904
4229
|
*/
|
|
3905
4230
|
.input-icon {
|
|
3906
|
-
--input-icon-size:
|
|
4231
|
+
--input-icon-size: 1.25rem;
|
|
3907
4232
|
|
|
3908
4233
|
display: block;
|
|
3909
4234
|
position: relative;
|
|
3910
4235
|
}
|
|
3911
4236
|
|
|
3912
|
-
|
|
4237
|
+
/* Icon — shared by leading and trailing */
|
|
4238
|
+
.input-icon > :where(:not(.input)) {
|
|
3913
4239
|
position: absolute;
|
|
3914
4240
|
inset-block-start: 50%;
|
|
3915
|
-
inset-inline-start: var(--space-3);
|
|
3916
4241
|
transform: translateY(-50%);
|
|
3917
4242
|
font-size: var(--input-icon-size);
|
|
3918
4243
|
color: var(--text-muted);
|
|
3919
4244
|
pointer-events: none;
|
|
3920
4245
|
}
|
|
3921
4246
|
|
|
4247
|
+
/* Leading: icon is the first child */
|
|
4248
|
+
.input-icon > :first-child:where(:not(.input)) {
|
|
4249
|
+
inset-inline-start: var(--space-30);
|
|
4250
|
+
}
|
|
4251
|
+
|
|
4252
|
+
/* Trailing: input is the first child, icon follows */
|
|
4253
|
+
.input-icon > .input:first-child ~ :where(:not(.input)) {
|
|
4254
|
+
inset-inline-end: var(--space-30);
|
|
4255
|
+
}
|
|
4256
|
+
|
|
3922
4257
|
/* font-size only sizes text-metric icons (webfont <i>, <span>). An inline
|
|
3923
4258
|
<svg> with just a viewBox ignores it, so give it explicit dimensions. */
|
|
3924
|
-
.input-icon > svg:
|
|
4259
|
+
.input-icon > svg:where(:not(.input)) {
|
|
3925
4260
|
inline-size: var(--input-icon-size);
|
|
3926
4261
|
block-size: var(--input-icon-size);
|
|
3927
4262
|
}
|
|
3928
4263
|
|
|
3929
|
-
|
|
3930
|
-
|
|
4264
|
+
/* Leading: input gets start padding to clear the icon */
|
|
4265
|
+
.input-icon > :first-child:where(:not(.input)) ~ .input {
|
|
4266
|
+
padding-inline-start: calc(var(--space-30) * 2 + var(--input-icon-size));
|
|
4267
|
+
}
|
|
4268
|
+
|
|
4269
|
+
/* Trailing: input is first child and has a sibling → end padding */
|
|
4270
|
+
.input-icon > .input:first-child:where(:not(:last-child)) {
|
|
4271
|
+
padding-inline-end: calc(var(--space-30) * 2 + var(--input-icon-size));
|
|
3931
4272
|
}
|
|
3932
4273
|
|
|
3933
4274
|
/*
|
|
@@ -3940,23 +4281,23 @@ a.avatar[aria-disabled="true"] {
|
|
|
3940
4281
|
*
|
|
3941
4282
|
* Switches (input[role="switch"]) are excluded so switch.css owns their
|
|
3942
4283
|
* geometry and states. switch.css is imported after this file.
|
|
4284
|
+
*
|
|
4285
|
+
* Public hooks:
|
|
4286
|
+
* --choice-control-offset
|
|
4287
|
+
* --choice-border
|
|
4288
|
+
* --choice-bg
|
|
4289
|
+
* --choice-mark
|
|
3943
4290
|
*/
|
|
3944
4291
|
/* Intent boundary — inherited intents do not tint this component.
|
|
3945
4292
|
Local intent classes still win because :where() has zero specificity. */
|
|
3946
4293
|
:where(.choice, .check, .radio) {
|
|
3947
|
-
/* @sync intent-boundary */
|
|
3948
4294
|
--intent: initial;
|
|
3949
4295
|
--intent-fg: initial;
|
|
3950
|
-
--ui-bg: initial;
|
|
3951
|
-
--ui-fg: initial;
|
|
3952
|
-
--ui-border: initial;
|
|
3953
|
-
--ui-hover-bg: initial;
|
|
3954
|
-
/* @sync intent-boundary */
|
|
3955
4296
|
}
|
|
3956
4297
|
|
|
3957
|
-
/* A classed .choice is the component surface. Let its
|
|
4298
|
+
/* A classed .choice is the component surface. Let its direct input consume a
|
|
3958
4299
|
local intent without reopening inherited page-level intents. */
|
|
3959
|
-
:where(.choice) :where(input[type="checkbox"]:not([role="switch"]), input[type="radio"]) {
|
|
4300
|
+
:where(.choice) > :where(input[type="checkbox"]:not([role="switch"]), input[type="radio"]) {
|
|
3960
4301
|
--intent: inherit;
|
|
3961
4302
|
--intent-fg: inherit;
|
|
3962
4303
|
}
|
|
@@ -3966,7 +4307,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
3966
4307
|
|
|
3967
4308
|
display: inline-flex;
|
|
3968
4309
|
align-items: flex-start;
|
|
3969
|
-
gap: var(--space-
|
|
4310
|
+
gap: var(--space-20);
|
|
3970
4311
|
color: var(--text);
|
|
3971
4312
|
font-size: var(--control-font-size);
|
|
3972
4313
|
cursor: pointer;
|
|
@@ -3989,8 +4330,11 @@ a.avatar[aria-disabled="true"] {
|
|
|
3989
4330
|
display: block;
|
|
3990
4331
|
}
|
|
3991
4332
|
|
|
3992
|
-
|
|
3993
|
-
|
|
4333
|
+
/* Progressive enhancement: dim the whole row when one of its controls is
|
|
4334
|
+
disabled. Without :has() the native disabled control keeps its own state
|
|
4335
|
+
styling — the row-level dim is lost but the field stays clearly disabled. */
|
|
4336
|
+
.choice:has(> :disabled) {
|
|
4337
|
+
color: var(--state-disabled);
|
|
3994
4338
|
cursor: not-allowed;
|
|
3995
4339
|
}
|
|
3996
4340
|
|
|
@@ -4007,8 +4351,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
4007
4351
|
place-content: center;
|
|
4008
4352
|
inline-size: 1.125em;
|
|
4009
4353
|
block-size: 1.125em;
|
|
4010
|
-
margin
|
|
4011
|
-
margin-block-end: 0;
|
|
4354
|
+
margin: 0;
|
|
4012
4355
|
border: var(--border-width) solid var(--form-invalid-border, var(--choice-border));
|
|
4013
4356
|
background: var(--choice-bg);
|
|
4014
4357
|
color: var(--choice-mark);
|
|
@@ -4049,7 +4392,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
4049
4392
|
background: currentColor;
|
|
4050
4393
|
}
|
|
4051
4394
|
|
|
4052
|
-
.check:checked,
|
|
4395
|
+
.check:is(:checked, :indeterminate),
|
|
4053
4396
|
.radio:checked {
|
|
4054
4397
|
--choice-border: var(--intent, var(--primary));
|
|
4055
4398
|
--choice-bg: var(--intent, var(--primary));
|
|
@@ -4060,12 +4403,6 @@ a.avatar[aria-disabled="true"] {
|
|
|
4060
4403
|
transform: translateY(-0.06em) rotate(-45deg) scale(1);
|
|
4061
4404
|
}
|
|
4062
4405
|
|
|
4063
|
-
.check:indeterminate {
|
|
4064
|
-
--choice-border: var(--intent, var(--primary));
|
|
4065
|
-
--choice-bg: var(--intent, var(--primary));
|
|
4066
|
-
--choice-mark: var(--intent-fg, var(--primary-fg));
|
|
4067
|
-
}
|
|
4068
|
-
|
|
4069
4406
|
.check:indeterminate::before {
|
|
4070
4407
|
border-inline-start: 0;
|
|
4071
4408
|
inline-size: 0.6em;
|
|
@@ -4078,17 +4415,15 @@ a.avatar[aria-disabled="true"] {
|
|
|
4078
4415
|
transform: scale(1);
|
|
4079
4416
|
}
|
|
4080
4417
|
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
--choice-border: var(--text-subtle);
|
|
4085
|
-
}
|
|
4418
|
+
.check:hover:not(:checked, :indeterminate, :disabled),
|
|
4419
|
+
.radio:hover:not(:checked, :indeterminate, :disabled) {
|
|
4420
|
+
--choice-border: var(--text-subtle);
|
|
4086
4421
|
}
|
|
4087
4422
|
|
|
4088
4423
|
.check:focus-visible,
|
|
4089
4424
|
.radio:focus-visible {
|
|
4090
4425
|
--choice-border: var(--focus);
|
|
4091
|
-
outline:
|
|
4426
|
+
outline: 2px solid transparent;
|
|
4092
4427
|
box-shadow: var(--focus-ring-shadow);
|
|
4093
4428
|
}
|
|
4094
4429
|
|
|
@@ -4096,7 +4431,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
4096
4431
|
.radio:disabled {
|
|
4097
4432
|
--choice-bg: var(--surface-subtle);
|
|
4098
4433
|
--choice-border: var(--border);
|
|
4099
|
-
--choice-mark: var(--
|
|
4434
|
+
--choice-mark: var(--state-disabled);
|
|
4100
4435
|
cursor: not-allowed;
|
|
4101
4436
|
opacity: 1;
|
|
4102
4437
|
}
|
|
@@ -4138,20 +4473,21 @@ a.avatar[aria-disabled="true"] {
|
|
|
4138
4473
|
* The badge overhangs the top-right corner by 0.375em; ancestors with
|
|
4139
4474
|
* overflow: hidden will clip it. Pair with grid or flex utilities for group
|
|
4140
4475
|
* layout.
|
|
4476
|
+
*
|
|
4477
|
+
* Public hooks:
|
|
4478
|
+
* --choice-card-pad
|
|
4479
|
+
* --choice-card-radius
|
|
4480
|
+
* --choice-card-border
|
|
4481
|
+
* --choice-card-bg
|
|
4482
|
+
* --choice-card-check-size
|
|
4141
4483
|
*/
|
|
4142
4484
|
:where(.choice-card) {
|
|
4143
|
-
/* @sync intent-boundary */
|
|
4144
4485
|
--intent: initial;
|
|
4145
4486
|
--intent-fg: initial;
|
|
4146
|
-
--ui-bg: initial;
|
|
4147
|
-
--ui-fg: initial;
|
|
4148
|
-
--ui-border: initial;
|
|
4149
|
-
--ui-hover-bg: initial;
|
|
4150
|
-
/* @sync intent-boundary */
|
|
4151
4487
|
}
|
|
4152
4488
|
|
|
4153
4489
|
.choice-card {
|
|
4154
|
-
--choice-card-pad: var(--
|
|
4490
|
+
--choice-card-pad: var(--density-space, var(--space-40));
|
|
4155
4491
|
--choice-card-radius: var(--radius);
|
|
4156
4492
|
--choice-card-border: var(--border);
|
|
4157
4493
|
--choice-card-bg: var(--surface);
|
|
@@ -4191,7 +4527,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
4191
4527
|
|
|
4192
4528
|
/* Focus ring on the label follows the hidden input's focus. */
|
|
4193
4529
|
.choice-card:has(:focus-visible) {
|
|
4194
|
-
outline:
|
|
4530
|
+
outline: 2px solid transparent;
|
|
4195
4531
|
box-shadow: var(--focus-ring-shadow);
|
|
4196
4532
|
}
|
|
4197
4533
|
|
|
@@ -4200,10 +4536,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
4200
4536
|
cursor: not-allowed;
|
|
4201
4537
|
}
|
|
4202
4538
|
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
--choice-card-border: var(--text-subtle);
|
|
4206
|
-
}
|
|
4539
|
+
.choice-card:hover:not(:has(:disabled)) {
|
|
4540
|
+
--choice-card-border: var(--text-subtle);
|
|
4207
4541
|
}
|
|
4208
4542
|
|
|
4209
4543
|
/* Checked border — shared by radio and checkbox */
|
|
@@ -4250,8 +4584,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
4250
4584
|
/* Check mark */
|
|
4251
4585
|
.choice-card::after {
|
|
4252
4586
|
background: var(--intent-fg, var(--primary-fg));
|
|
4253
|
-
-webkit-mask: var(--icon-check
|
|
4254
|
-
mask: var(--icon-check
|
|
4587
|
+
-webkit-mask: var(--icon-check) center / 60% no-repeat;
|
|
4588
|
+
mask: var(--icon-check) center / 60% no-repeat;
|
|
4255
4589
|
z-index: 2;
|
|
4256
4590
|
}
|
|
4257
4591
|
|
|
@@ -4294,19 +4628,19 @@ a.avatar[aria-disabled="true"] {
|
|
|
4294
4628
|
* custom-select.css can be opted out independently. Add .native to a specific
|
|
4295
4629
|
* select to opt out of the enhancement.
|
|
4296
4630
|
*/
|
|
4297
|
-
.select:
|
|
4631
|
+
.select:not([multiple], [size]:not([size="1"])) {
|
|
4298
4632
|
appearance: none;
|
|
4299
|
-
padding-inline-end: calc(var(--space-
|
|
4300
|
-
background-image: var(--icon-chevron
|
|
4301
|
-
background-position: center right var(--space-
|
|
4633
|
+
padding-inline-end: calc(var(--space-30) + var(--space-60));
|
|
4634
|
+
background-image: var(--icon-chevron);
|
|
4635
|
+
background-position: center right var(--space-30);
|
|
4302
4636
|
background-size: 1rem auto;
|
|
4303
4637
|
background-repeat: no-repeat;
|
|
4304
4638
|
}
|
|
4305
4639
|
|
|
4306
4640
|
/* The chevron sits on the inline-end side; flip it in RTL so it follows
|
|
4307
4641
|
the padding-inline-end space reserved for it. */
|
|
4308
|
-
.select:
|
|
4309
|
-
background-position: center left var(--space-
|
|
4642
|
+
.select:not([multiple], [size]:not([size="1"])):dir(rtl) {
|
|
4643
|
+
background-position: center left var(--space-30);
|
|
4310
4644
|
}
|
|
4311
4645
|
|
|
4312
4646
|
/* Both forced-colors and prefers-contrast hand the arrow back to the
|
|
@@ -4314,10 +4648,10 @@ a.avatar[aria-disabled="true"] {
|
|
|
4314
4648
|
simpler than maintaining contrast variants, and native rendering already
|
|
4315
4649
|
matches whatever contrast mode the user picked. */
|
|
4316
4650
|
@media (forced-colors: active), (prefers-contrast: more) {
|
|
4317
|
-
.select:
|
|
4651
|
+
.select:not([multiple], [size]:not([size="1"])) {
|
|
4318
4652
|
appearance: auto;
|
|
4319
4653
|
background-image: none;
|
|
4320
|
-
padding-inline-end: var(--space-
|
|
4654
|
+
padding-inline-end: var(--space-30);
|
|
4321
4655
|
}
|
|
4322
4656
|
}
|
|
4323
4657
|
|
|
@@ -4330,6 +4664,10 @@ a.avatar[aria-disabled="true"] {
|
|
|
4330
4664
|
*
|
|
4331
4665
|
* No layout or interaction contract may depend on appearance: base-select.
|
|
4332
4666
|
* Arbitrary menu and popover behavior belongs elsewhere.
|
|
4667
|
+
*
|
|
4668
|
+
* Internal:
|
|
4669
|
+
* --control-border
|
|
4670
|
+
* --control-bg
|
|
4333
4671
|
*/
|
|
4334
4672
|
@media (forced-colors: none) and (prefers-contrast: no-preference) and (hover: hover) and (
|
|
4335
4673
|
pointer: fine
|
|
@@ -4342,7 +4680,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
4342
4680
|
&::picker(select) {
|
|
4343
4681
|
appearance: base-select;
|
|
4344
4682
|
max-height: min(24rem, calc(var(--viewport-block) * 0.7));
|
|
4345
|
-
margin-block: var(--space-
|
|
4683
|
+
margin-block: var(--space-20);
|
|
4346
4684
|
padding: 0;
|
|
4347
4685
|
border: var(--border-width) solid var(--border);
|
|
4348
4686
|
border-radius: var(--radius);
|
|
@@ -4364,8 +4702,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
4364
4702
|
|
|
4365
4703
|
& option {
|
|
4366
4704
|
position: relative;
|
|
4367
|
-
padding-block: var(--space-
|
|
4368
|
-
padding-inline: var(--space-
|
|
4705
|
+
padding-block: var(--space-20);
|
|
4706
|
+
padding-inline: var(--space-30);
|
|
4369
4707
|
white-space: normal;
|
|
4370
4708
|
transition-property: color, background-color;
|
|
4371
4709
|
transition-duration: var(--duration);
|
|
@@ -4393,14 +4731,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
4393
4731
|
inline-size: 1rem;
|
|
4394
4732
|
block-size: 1rem;
|
|
4395
4733
|
background-color: currentColor;
|
|
4396
|
-
-webkit-mask
|
|
4397
|
-
mask
|
|
4398
|
-
-webkit-mask-repeat: no-repeat;
|
|
4399
|
-
mask-repeat: no-repeat;
|
|
4400
|
-
-webkit-mask-size: contain;
|
|
4401
|
-
mask-size: contain;
|
|
4402
|
-
-webkit-mask-position: center;
|
|
4403
|
-
mask-position: center;
|
|
4734
|
+
-webkit-mask: var(--icon-check) center / contain no-repeat;
|
|
4735
|
+
mask: var(--icon-check) center / contain no-repeat;
|
|
4404
4736
|
}
|
|
4405
4737
|
}
|
|
4406
4738
|
|
|
@@ -4416,9 +4748,9 @@ a.avatar[aria-disabled="true"] {
|
|
|
4416
4748
|
}
|
|
4417
4749
|
|
|
4418
4750
|
/*
|
|
4419
|
-
* Native-only control polish: file input
|
|
4420
|
-
* so the framework stays out of the way and just keeps
|
|
4421
|
-
* theme-aware.
|
|
4751
|
+
* Native-only control polish: the file input and color picker mostly defeat
|
|
4752
|
+
* custom styling, so the framework stays out of the way and just keeps them
|
|
4753
|
+
* readable and theme-aware.
|
|
4422
4754
|
*
|
|
4423
4755
|
* Range has its own custom-styled file (range.css) so the focus ring can
|
|
4424
4756
|
* target the thumb pseudo-element rather than the rectangular CSS box.
|
|
@@ -4428,9 +4760,9 @@ a.avatar[aria-disabled="true"] {
|
|
|
4428
4760
|
}
|
|
4429
4761
|
|
|
4430
4762
|
.file::file-selector-button {
|
|
4431
|
-
margin-inline-end: var(--space-
|
|
4432
|
-
padding-block: var(--space-
|
|
4433
|
-
padding-inline: var(--space-
|
|
4763
|
+
margin-inline-end: var(--space-30);
|
|
4764
|
+
padding-block: var(--space-10);
|
|
4765
|
+
padding-inline: var(--space-20);
|
|
4434
4766
|
border: var(--border-width) solid var(--border);
|
|
4435
4767
|
border-radius: var(--radius-sm);
|
|
4436
4768
|
background: var(--surface-subtle);
|
|
@@ -4449,17 +4781,56 @@ a.avatar[aria-disabled="true"] {
|
|
|
4449
4781
|
cursor: not-allowed;
|
|
4450
4782
|
}
|
|
4451
4783
|
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
background: var(--border);
|
|
4455
|
-
}
|
|
4784
|
+
.file:not(:disabled)::file-selector-button:hover {
|
|
4785
|
+
background: var(--border);
|
|
4456
4786
|
}
|
|
4457
4787
|
|
|
4458
4788
|
.file:focus-visible {
|
|
4459
|
-
outline:
|
|
4789
|
+
outline: 2px solid transparent;
|
|
4790
|
+
box-shadow: var(--focus-ring-shadow);
|
|
4791
|
+
}
|
|
4792
|
+
|
|
4793
|
+
/* Native color picker — keep the browser's dialog and selected-color UI. */
|
|
4794
|
+
.color {
|
|
4795
|
+
inline-size: var(--control-size);
|
|
4796
|
+
block-size: var(--control-size);
|
|
4797
|
+
padding: var(--space-10);
|
|
4798
|
+
border: var(--border-width) solid var(--border);
|
|
4799
|
+
border-radius: var(--radius);
|
|
4800
|
+
background: var(--surface);
|
|
4801
|
+
cursor: pointer;
|
|
4802
|
+
}
|
|
4803
|
+
|
|
4804
|
+
.color::-webkit-color-swatch-wrapper {
|
|
4805
|
+
padding: 0;
|
|
4806
|
+
}
|
|
4807
|
+
|
|
4808
|
+
.color::-webkit-color-swatch {
|
|
4809
|
+
border: 0;
|
|
4810
|
+
}
|
|
4811
|
+
|
|
4812
|
+
.color::-moz-color-swatch {
|
|
4813
|
+
border: 0;
|
|
4814
|
+
}
|
|
4815
|
+
|
|
4816
|
+
.color:disabled {
|
|
4817
|
+
cursor: not-allowed;
|
|
4818
|
+
opacity: var(--disabled-opacity);
|
|
4819
|
+
}
|
|
4820
|
+
|
|
4821
|
+
.color:focus-visible {
|
|
4822
|
+
outline: 2px solid transparent;
|
|
4460
4823
|
box-shadow: var(--focus-ring-shadow);
|
|
4461
4824
|
}
|
|
4462
4825
|
|
|
4826
|
+
@media (forced-colors: active) {
|
|
4827
|
+
.color {
|
|
4828
|
+
border-color: ButtonText;
|
|
4829
|
+
background: ButtonFace;
|
|
4830
|
+
forced-color-adjust: none;
|
|
4831
|
+
}
|
|
4832
|
+
}
|
|
4833
|
+
|
|
4463
4834
|
/*
|
|
4464
4835
|
* Range slider — custom-styled so the focus ring always targets the
|
|
4465
4836
|
* user-manipulated surface (the thumb) rather than the CSS box.
|
|
@@ -4467,19 +4838,18 @@ a.avatar[aria-disabled="true"] {
|
|
|
4467
4838
|
* appearance: none gives us full control over track and thumb; without it,
|
|
4468
4839
|
* pseudo-element rules for ::-webkit-slider-thumb and ::-moz-range-thumb
|
|
4469
4840
|
* are not reliably applied across browsers.
|
|
4841
|
+
*
|
|
4842
|
+
* Public hooks:
|
|
4843
|
+
* --range-thumb-size
|
|
4844
|
+
* --range-track-height
|
|
4845
|
+
* --range-thumb-bg
|
|
4846
|
+
* --range-track-bg
|
|
4470
4847
|
*/
|
|
4471
4848
|
|
|
4472
4849
|
/* Intent boundary — inherited intents do not tint this component.
|
|
4473
4850
|
Local intent classes still win because :where() has zero specificity. */
|
|
4474
4851
|
:where(.range) {
|
|
4475
|
-
/* @sync intent-boundary */
|
|
4476
4852
|
--intent: initial;
|
|
4477
|
-
--intent-fg: initial;
|
|
4478
|
-
--ui-bg: initial;
|
|
4479
|
-
--ui-fg: initial;
|
|
4480
|
-
--ui-border: initial;
|
|
4481
|
-
--ui-hover-bg: initial;
|
|
4482
|
-
/* @sync intent-boundary */
|
|
4483
4853
|
}
|
|
4484
4854
|
|
|
4485
4855
|
.range {
|
|
@@ -4530,7 +4900,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
4530
4900
|
}
|
|
4531
4901
|
|
|
4532
4902
|
.range:focus-visible {
|
|
4533
|
-
outline:
|
|
4903
|
+
outline: 2px solid transparent;
|
|
4534
4904
|
}
|
|
4535
4905
|
|
|
4536
4906
|
/*
|
|
@@ -4551,28 +4921,41 @@ a.avatar[aria-disabled="true"] {
|
|
|
4551
4921
|
}
|
|
4552
4922
|
|
|
4553
4923
|
.range:disabled {
|
|
4554
|
-
--range-thumb-bg: var(--
|
|
4924
|
+
--range-thumb-bg: var(--state-disabled);
|
|
4555
4925
|
cursor: not-allowed;
|
|
4556
4926
|
opacity: var(--disabled-opacity);
|
|
4557
4927
|
}
|
|
4558
4928
|
|
|
4929
|
+
/* Forced colors: map the custom thumb/track to system colors. The focus
|
|
4930
|
+
indicator stays visible through the transparent outline on :focus-visible. */
|
|
4931
|
+
@media (forced-colors: active) {
|
|
4932
|
+
.range {
|
|
4933
|
+
--range-thumb-bg: ButtonText;
|
|
4934
|
+
--range-track-bg: Canvas;
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
|
|
4559
4938
|
/*
|
|
4560
4939
|
* Switch controls for checkbox inputs with switch semantics.
|
|
4561
4940
|
*
|
|
4562
4941
|
* The switch remains an input control, not a decorative toggle. Checked, hover,
|
|
4563
4942
|
* focus-visible, and disabled states stay clear.
|
|
4943
|
+
*
|
|
4944
|
+
* Public hooks:
|
|
4945
|
+
* --switch-bg
|
|
4946
|
+
* --switch-border
|
|
4947
|
+
* --switch-knob
|
|
4948
|
+
* --switch-width
|
|
4949
|
+
* --switch-block-size
|
|
4950
|
+
* --switch-knob-margin
|
|
4951
|
+
* --switch-knob-size
|
|
4952
|
+
* --switch-travel
|
|
4564
4953
|
*/
|
|
4565
4954
|
/* Intent boundary — inherited intents do not tint this component.
|
|
4566
4955
|
Local intent classes still win because :where() has zero specificity. */
|
|
4567
4956
|
:where(.switch) {
|
|
4568
|
-
/* @sync intent-boundary */
|
|
4569
4957
|
--intent: initial;
|
|
4570
4958
|
--intent-fg: initial;
|
|
4571
|
-
--ui-bg: initial;
|
|
4572
|
-
--ui-fg: initial;
|
|
4573
|
-
--ui-border: initial;
|
|
4574
|
-
--ui-hover-bg: initial;
|
|
4575
|
-
/* @sync intent-boundary */
|
|
4576
4959
|
}
|
|
4577
4960
|
|
|
4578
4961
|
.switch {
|
|
@@ -4620,10 +5003,8 @@ a.avatar[aria-disabled="true"] {
|
|
|
4620
5003
|
box-shadow var(--duration);
|
|
4621
5004
|
}
|
|
4622
5005
|
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
--switch-border: var(--intent, var(--primary));
|
|
4626
|
-
}
|
|
5006
|
+
.switch:hover:not(:disabled) {
|
|
5007
|
+
--switch-border: var(--intent, var(--primary));
|
|
4627
5008
|
}
|
|
4628
5009
|
|
|
4629
5010
|
.switch:checked {
|
|
@@ -4644,14 +5025,14 @@ a.avatar[aria-disabled="true"] {
|
|
|
4644
5025
|
|
|
4645
5026
|
.switch:focus-visible {
|
|
4646
5027
|
--switch-border: var(--focus);
|
|
4647
|
-
outline:
|
|
5028
|
+
outline: 2px solid transparent;
|
|
4648
5029
|
box-shadow: var(--focus-ring-shadow);
|
|
4649
5030
|
}
|
|
4650
5031
|
|
|
4651
5032
|
.switch:disabled {
|
|
4652
5033
|
--switch-bg: var(--surface-subtle);
|
|
4653
5034
|
--switch-border: var(--border);
|
|
4654
|
-
--switch-knob: var(--
|
|
5035
|
+
--switch-knob: var(--state-disabled);
|
|
4655
5036
|
opacity: 1;
|
|
4656
5037
|
cursor: not-allowed;
|
|
4657
5038
|
}
|
|
@@ -4688,6 +5069,17 @@ a.avatar[aria-disabled="true"] {
|
|
|
4688
5069
|
*
|
|
4689
5070
|
* Imported by default through forms/index.css. Custom builds may omit this
|
|
4690
5071
|
* file when an app wants to own validation styling completely.
|
|
5072
|
+
*
|
|
5073
|
+
* State coverage (which states each component ships) is audited in
|
|
5074
|
+
* tests/validation.test.js and summarized in docs/pages/forms/validation.md —
|
|
5075
|
+
* not kept as a source comment that can drift from the implementation.
|
|
5076
|
+
*
|
|
5077
|
+
* Public hooks:
|
|
5078
|
+
* --form-invalid-border
|
|
5079
|
+
*
|
|
5080
|
+
* Internal:
|
|
5081
|
+
* --choice-bg
|
|
5082
|
+
* --switch-bg
|
|
4691
5083
|
*/
|
|
4692
5084
|
|
|
4693
5085
|
.field.danger {
|
|
@@ -4710,17 +5102,33 @@ a.avatar[aria-disabled="true"] {
|
|
|
4710
5102
|
--form-invalid-border: var(--danger);
|
|
4711
5103
|
}
|
|
4712
5104
|
|
|
4713
|
-
/* Override checked/indeterminate
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
5105
|
+
/* Override checked/indeterminate fill so both border and fill signal danger
|
|
5106
|
+
instead of a red border around a primary-colored control. All three
|
|
5107
|
+
invalid routes must converge: aria-invalid, was-validated :invalid, and
|
|
5108
|
+
:user-invalid. */
|
|
5109
|
+
.check:is(:checked, :indeterminate)[aria-invalid="true"],
|
|
5110
|
+
.needs-validation.was-validated .check:is(:checked, :indeterminate):invalid,
|
|
5111
|
+
.radio:checked[aria-invalid="true"],
|
|
5112
|
+
.needs-validation.was-validated .radio:checked:invalid {
|
|
4717
5113
|
--choice-bg: var(--danger);
|
|
4718
5114
|
}
|
|
4719
5115
|
|
|
4720
|
-
.switch[aria-invalid="true"]
|
|
5116
|
+
.switch:checked[aria-invalid="true"],
|
|
5117
|
+
.needs-validation.was-validated .switch:checked:invalid {
|
|
4721
5118
|
--switch-bg: var(--danger);
|
|
4722
5119
|
}
|
|
4723
5120
|
|
|
5121
|
+
@supports selector(:user-invalid) {
|
|
5122
|
+
.needs-validation .check:is(:checked, :indeterminate):user-invalid,
|
|
5123
|
+
.needs-validation .radio:checked:user-invalid {
|
|
5124
|
+
--choice-bg: var(--danger);
|
|
5125
|
+
}
|
|
5126
|
+
|
|
5127
|
+
.needs-validation .switch:checked:user-invalid {
|
|
5128
|
+
--switch-bg: var(--danger);
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
5131
|
+
|
|
4724
5132
|
/* Choice cards hide their native input, so invalid state must surface on the
|
|
4725
5133
|
card itself. Sets the same --form-invalid-border hook the card's border
|
|
4726
5134
|
consumes. */
|
|
@@ -4753,13 +5161,24 @@ a.avatar[aria-disabled="true"] {
|
|
|
4753
5161
|
* A flex cluster of buttons/actions that closes a form. The sticky variant
|
|
4754
5162
|
* docks to the viewport bottom with a translucent blur where supported; at
|
|
4755
5163
|
* rest it stays visually neutral so it does not double as a separator.
|
|
5164
|
+
*
|
|
5165
|
+
* Public hooks:
|
|
5166
|
+
* --form-actions-align
|
|
5167
|
+
* --form-actions-justify
|
|
5168
|
+
* --form-actions-margin-block-start
|
|
5169
|
+
*
|
|
5170
|
+
* Internal:
|
|
5171
|
+
* --form-actions-sticky-block-offset (owned by .form-actions.sticky)
|
|
5172
|
+
* --form-actions-sticky-inline-offset (owned by .form-actions.sticky)
|
|
5173
|
+
* --form-actions-sticky-padding (owned by .form-actions.sticky)
|
|
4756
5174
|
*/
|
|
4757
5175
|
.form-actions {
|
|
4758
5176
|
display: flex;
|
|
4759
5177
|
flex-wrap: wrap;
|
|
4760
5178
|
gap: var(--gap);
|
|
4761
|
-
align-items: center;
|
|
4762
|
-
|
|
5179
|
+
align-items: var(--form-actions-align, center);
|
|
5180
|
+
justify-content: var(--form-actions-justify, flex-start);
|
|
5181
|
+
margin-block-start: var(--form-actions-margin-block-start, var(--space-50));
|
|
4763
5182
|
}
|
|
4764
5183
|
|
|
4765
5184
|
/* Opt-in sticky variant. When the bar is not stuck (at rest) the translucent
|
|
@@ -4773,7 +5192,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
4773
5192
|
isolation: isolate;
|
|
4774
5193
|
|
|
4775
5194
|
margin-inline: calc(var(--form-actions-sticky-inline-offset, 0px) * -1);
|
|
4776
|
-
padding: var(--form-actions-sticky-padding, var(--space-
|
|
5195
|
+
padding: var(--form-actions-sticky-padding, var(--space-30));
|
|
4777
5196
|
|
|
4778
5197
|
border-block-start: var(--border-width) solid var(--border);
|
|
4779
5198
|
background: var(--surface);
|
|
@@ -4823,7 +5242,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
4823
5242
|
min-inline-size: 0;
|
|
4824
5243
|
}
|
|
4825
5244
|
|
|
4826
|
-
.nowrap {
|
|
5245
|
+
.text-nowrap {
|
|
4827
5246
|
white-space: nowrap;
|
|
4828
5247
|
}
|
|
4829
5248
|
|
|
@@ -4918,15 +5337,13 @@ a.avatar[aria-disabled="true"] {
|
|
|
4918
5337
|
text-decoration: none;
|
|
4919
5338
|
}
|
|
4920
5339
|
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
color: var(--text);
|
|
4924
|
-
}
|
|
5340
|
+
.link-muted:hover {
|
|
5341
|
+
color: var(--text);
|
|
4925
5342
|
}
|
|
4926
5343
|
|
|
4927
5344
|
/* Spacing step helpers — semantic scale (sm / default / lg).
|
|
4928
5345
|
Names map to logical properties for writing-direction safety. The default
|
|
4929
|
-
step uses --space-
|
|
5346
|
+
step uses --space-40, which matches the framework's built-in padding on
|
|
4930
5347
|
cards, dialogs, and form-actions. */
|
|
4931
5348
|
|
|
4932
5349
|
/* Gap */
|
|
@@ -4936,22 +5353,22 @@ a.avatar[aria-disabled="true"] {
|
|
|
4936
5353
|
|
|
4937
5354
|
/* Padding — block (vertical) */
|
|
4938
5355
|
.py {
|
|
4939
|
-
padding-block: var(--
|
|
5356
|
+
padding-block: var(--density-space);
|
|
4940
5357
|
}
|
|
4941
5358
|
|
|
4942
5359
|
/* Padding — inline (horizontal) */
|
|
4943
5360
|
.px {
|
|
4944
|
-
padding-inline: var(--
|
|
5361
|
+
padding-inline: var(--density-space);
|
|
4945
5362
|
}
|
|
4946
5363
|
|
|
4947
5364
|
/* Margin — block-start */
|
|
4948
5365
|
.mbs {
|
|
4949
|
-
margin-block-start: var(--
|
|
5366
|
+
margin-block-start: var(--density-space);
|
|
4950
5367
|
}
|
|
4951
5368
|
|
|
4952
5369
|
/* Margin — block-end */
|
|
4953
5370
|
.mbe {
|
|
4954
|
-
margin-block-end: var(--
|
|
5371
|
+
margin-block-end: var(--density-space);
|
|
4955
5372
|
}
|
|
4956
5373
|
|
|
4957
5374
|
/* Joined controls are a component, but must load after the controls it groups. */
|
|
@@ -4964,6 +5381,18 @@ a.avatar[aria-disabled="true"] {
|
|
|
4964
5381
|
* Cascade note: .join writes border-radius longhands on its direct children.
|
|
4965
5382
|
* Keep this file loaded after control/component styles so those corners win at
|
|
4966
5383
|
* equal specificity.
|
|
5384
|
+
*
|
|
5385
|
+
* Public hooks:
|
|
5386
|
+
* --join-radius
|
|
5387
|
+
*
|
|
5388
|
+
* Internal:
|
|
5389
|
+
* --join-ss
|
|
5390
|
+
* --join-se
|
|
5391
|
+
* --join-es
|
|
5392
|
+
* --join-ee
|
|
5393
|
+
* --control-border
|
|
5394
|
+
* --control-bg
|
|
5395
|
+
* --control-fg
|
|
4967
5396
|
*/
|
|
4968
5397
|
.join {
|
|
4969
5398
|
--join-radius: var(--radius);
|
|
@@ -4991,7 +5420,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
4991
5420
|
display: inline-flex;
|
|
4992
5421
|
justify-content: center;
|
|
4993
5422
|
min-block-size: var(--control-size);
|
|
4994
|
-
padding-inline: var(--space-
|
|
5423
|
+
padding-inline: var(--space-30);
|
|
4995
5424
|
border: var(--border-width) solid var(--control-border);
|
|
4996
5425
|
border-radius: var(--radius);
|
|
4997
5426
|
background: var(--control-bg);
|
|
@@ -5052,7 +5481,7 @@ a.avatar[aria-disabled="true"] {
|
|
|
5052
5481
|
focus, and unsupported :has() browsers retain the control's normal ring. */
|
|
5053
5482
|
.join:has(> :is(.input, .textarea, .select):focus-visible) {
|
|
5054
5483
|
border-radius: var(--join-radius);
|
|
5055
|
-
outline:
|
|
5484
|
+
outline: 2px solid transparent;
|
|
5056
5485
|
box-shadow: var(--focus-ring-shadow);
|
|
5057
5486
|
}
|
|
5058
5487
|
|
|
@@ -5061,9 +5490,14 @@ a.avatar[aria-disabled="true"] {
|
|
|
5061
5490
|
box-shadow: none;
|
|
5062
5491
|
}
|
|
5063
5492
|
|
|
5064
|
-
/* Focus baseline —
|
|
5065
|
-
|
|
5066
|
-
|
|
5493
|
+
/* Focus baseline — a visible outline is the guaranteed focus affordance. The
|
|
5494
|
+
:focus outline covers legacy browsers; modern browsers hide it for
|
|
5495
|
+
pointer/mouse focus and interactive components layer their ring on top.
|
|
5496
|
+
|
|
5497
|
+
Invariant: an interactive component must never rely on box-shadow alone
|
|
5498
|
+
for its keyboard focus indicator. Components that remove the native
|
|
5499
|
+
outline on :focus-visible replace it with a transparent outline — invisible
|
|
5500
|
+
normally, forced to the system focus color under forced-colors. */
|
|
5067
5501
|
:focus {
|
|
5068
5502
|
outline: var(--focus-outline);
|
|
5069
5503
|
outline-offset: var(--focus-outline-offset);
|
|
@@ -5073,42 +5507,13 @@ a.avatar[aria-disabled="true"] {
|
|
|
5073
5507
|
:focus:not(:focus-visible) {
|
|
5074
5508
|
outline: none;
|
|
5075
5509
|
}
|
|
5076
|
-
}
|
|
5077
|
-
|
|
5078
|
-
/*
|
|
5079
|
-
* Forced-colors focus — centralised so individual components do not need
|
|
5080
|
-
* their own fallback. The double :focus keeps specificity high enough
|
|
5081
|
-
* (0,0,2,0) to beat a single :focus-visible; against a class selector like
|
|
5082
|
-
* .input:focus-visible it ties, but this file loads last so it wins on
|
|
5083
|
-
* source order.
|
|
5084
|
-
*
|
|
5085
|
-
* .choice-card and .join receive focus indirectly (:has on a wrapper that
|
|
5086
|
-
* never matches :focus), so they are listed explicitly.
|
|
5087
|
-
*
|
|
5088
|
-
* .range is excluded from the rectangular fallback: its focus is drawn on
|
|
5089
|
-
* the thumb pseudo-element, which is the manipulated surface.
|
|
5090
|
-
*
|
|
5091
|
-
* box-shadow: none is redundant when forced-color-adjust is auto, but safe
|
|
5092
|
-
* when a component uses forced-color-adjust: none (e.g. checked switches /
|
|
5093
|
-
* checkboxes).
|
|
5094
|
-
*/
|
|
5095
|
-
@media (forced-colors: active) {
|
|
5096
|
-
:focus:focus-visible:not(.range),
|
|
5097
|
-
.choice-card:has(:focus-visible),
|
|
5098
|
-
.join:has(> :is(.input, .textarea, .select):focus-visible) {
|
|
5099
|
-
outline: var(--focus-outline);
|
|
5100
|
-
outline-offset: var(--focus-outline-offset);
|
|
5101
|
-
box-shadow: none;
|
|
5102
|
-
}
|
|
5103
|
-
|
|
5104
|
-
.range:focus-visible::-webkit-slider-thumb {
|
|
5105
|
-
outline: var(--focus-outline);
|
|
5106
|
-
outline-offset: var(--focus-outline-offset);
|
|
5107
|
-
}
|
|
5108
5510
|
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5511
|
+
/* Pointer focus must still signal in high-contrast mode, where the shared
|
|
5512
|
+
box-shadow rings are disabled (--focus-ring-shadow: none). */
|
|
5513
|
+
@media (forced-colors: active) {
|
|
5514
|
+
:focus:not(:focus-visible) {
|
|
5515
|
+
outline: var(--focus-outline);
|
|
5516
|
+
}
|
|
5112
5517
|
}
|
|
5113
5518
|
}
|
|
5114
5519
|
|
|
@@ -5159,3 +5564,16 @@ a.avatar[aria-disabled="true"] {
|
|
|
5159
5564
|
break-after: avoid;
|
|
5160
5565
|
}
|
|
5161
5566
|
}
|
|
5567
|
+
|
|
5568
|
+
|
|
5569
|
+
/*
|
|
5570
|
+
* NOTE: The 'optional/' directory contains layout and utility extensions
|
|
5571
|
+
* (such as optional grids, split, visual effects, and extra utility classes
|
|
5572
|
+
* like .gap-sm, .flex-nowrap, .shadow-sm, etc.). These are intentionally not
|
|
5573
|
+
* bundled here. Two entry points ship them:
|
|
5574
|
+
*
|
|
5575
|
+
* actual-css/css/optional/index optional layer only
|
|
5576
|
+
* actual-css/css/actual.full core + optional
|
|
5577
|
+
*
|
|
5578
|
+
* Import one of these paths in your project when you need them.
|
|
5579
|
+
*/
|