actual-css 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +214 -1
- package/README.md +13 -11
- package/components.json +668 -0
- package/dist/actual.css +166 -37
- package/dist/actual.full.css +1005 -550
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/dist/actual.js +1 -1
- package/dist/actual.min.css +1 -1
- package/docs/design-notes/accessibility-decisions.md +33 -0
- package/docs/design-notes/browser-support.md +82 -25
- package/docs/design-notes/column-layout.md +3 -3
- package/docs/design-notes/css-anchor.md +75 -21
- package/docs/design-notes/enhancement-contract.md +14 -6
- package/docs/design-notes/grid.md +4 -4
- package/docs/design-notes/naming.md +6 -1
- package/docs/design-notes/platform-alignment.md +99 -2
- package/docs/design-notes/popover-manual-poc.md +328 -0
- package/docs/design-notes/surface-reparenting.md +131 -0
- package/docs/design-notes/tooltip-coordinate-space.md +132 -0
- package/docs/design-notes/widget-primitives.md +9 -6
- package/docs/pages/components/accordion.md +41 -2
- package/docs/pages/components/alert.md +80 -30
- package/docs/pages/components/app-navigation.md +6 -0
- package/docs/pages/components/avatar.md +6 -6
- package/docs/pages/components/badge.md +101 -16
- package/docs/pages/components/button.md +16 -8
- package/docs/pages/components/card.md +10 -10
- package/docs/pages/components/dialog.md +16 -6
- package/docs/pages/components/fab.md +4 -3
- package/docs/pages/components/list.md +42 -4
- package/docs/pages/components/navbar.md +17 -1
- package/docs/pages/components/pagination.md +2 -2
- package/docs/pages/components/steps.md +19 -0
- package/docs/pages/components/table.md +3 -2
- package/docs/pages/enhancements/flyout.md +144 -24
- package/docs/pages/enhancements/overview.md +25 -0
- package/docs/pages/enhancements/status-bar.md +20 -0
- package/docs/pages/enhancements/tabs.md +45 -37
- package/docs/pages/enhancements/tooltip.md +40 -2
- package/docs/pages/examples/overview.md +38 -5
- package/docs/pages/forms/choice-cards.md +6 -2
- package/docs/pages/forms/combobox.md +1 -1
- package/docs/pages/forms/floating-field.md +4 -3
- package/docs/pages/forms/joined-controls.md +22 -0
- package/docs/pages/forms/otp.md +2 -1
- package/docs/pages/forms/overview.md +1 -1
- package/docs/pages/forms/states-and-sizes.md +16 -18
- package/docs/pages/forms/sticky-actions.md +2 -2
- package/docs/pages/forms/switch.md +1 -1
- package/docs/pages/foundations/philosophy.md +67 -22
- package/docs/pages/foundations/theming.md +10 -2
- package/docs/pages/foundations/tokens.md +118 -21
- package/docs/pages/foundations/typography.md +35 -14
- package/docs/pages/guides/introduction.md +11 -9
- package/docs/pages/guides/progressive-enhancement.md +136 -49
- package/docs/pages/guides/tailwind.md +3 -2
- package/docs/pages/layout/media.md +1 -1
- package/docs/pages/layout/overview.md +11 -1
- package/docs/pages/layout/scroll-snap.md +3 -3
- package/docs/pages/layout/stack.md +17 -9
- package/docs/pages/patterns/icon-input.md +4 -2
- package/docs/pages/patterns/icon-slot.md +2 -0
- package/docs/pages/patterns/intent-nesting.md +1 -1
- package/docs/pages/utilities/overview.md +23 -2
- package/docs/pages/utilities/sizing-wrapping.md +1 -1
- package/docs/pages/utilities/spacing.md +10 -8
- package/llms.txt +2 -1
- package/package.json +19 -8
- package/{scripts/reserved-classes.json → reserved-classes.json} +4 -6
- package/src/css/components/accordion.css +42 -3
- package/src/css/components/alert.css +25 -13
- package/src/css/components/app-nav.css +18 -8
- package/src/css/components/avatar.css +13 -1
- package/src/css/components/badge.css +78 -12
- package/src/css/components/breadcrumb.css +2 -3
- package/src/css/components/button.css +9 -16
- package/src/css/components/card.css +9 -2
- package/src/css/components/chat.css +1 -0
- package/src/css/components/data-list.css +1 -1
- package/src/css/components/drawer.css +79 -25
- package/src/css/components/fab.css +0 -6
- package/src/css/components/flyout.css +46 -63
- package/src/css/components/index.css +0 -1
- package/src/css/components/indicator.css +21 -0
- package/src/css/components/join.css +7 -0
- package/src/css/components/key.css +10 -0
- package/src/css/components/list.css +40 -4
- package/src/css/components/menu.css +18 -11
- package/src/css/components/modal.css +48 -33
- package/src/css/components/navbar.css +37 -4
- package/src/css/components/pagination.css +1 -0
- package/src/css/components/spinner.css +6 -6
- package/src/css/components/status-bar.css +0 -5
- package/src/css/components/steps.css +45 -59
- package/src/css/components/tab.css +29 -9
- package/src/css/components/table.css +5 -5
- package/src/css/components/tooltip.css +45 -13
- package/src/css/core/base.css +23 -1
- package/src/css/core/intents.css +17 -6
- package/src/css/core/theme.css +27 -0
- package/src/css/core/tokens.css +35 -15
- package/src/css/core/variants.css +64 -15
- package/src/css/forms/choice-card.css +4 -4
- package/src/css/forms/choice.css +6 -3
- package/src/css/forms/control.css +2 -1
- package/src/css/forms/floating-field.css +3 -3
- package/src/css/forms/form.css +4 -3
- package/src/css/forms/input-icon.css +20 -14
- package/src/css/forms/native.css +6 -1
- package/src/css/forms/otp.css +9 -1
- package/src/css/forms/range.css +2 -0
- package/src/css/forms/select.css +29 -9
- package/src/css/forms/switch.css +19 -5
- package/src/css/layout/app-layout.css +1 -1
- package/src/css/layout/grid.css +50 -76
- package/src/css/layout/scroll-snap.css +11 -7
- package/src/css/layout/scroller.css +3 -4
- package/src/css/layout/stack.css +2 -2
- package/src/css/typography/fluid.css +4 -3
- package/src/css/typography/prose.css +6 -5
- package/src/css/utilities/base.css +2 -1
- package/src/css/utilities/extra.css +20 -17
- package/src/js/context-menu.js +27 -26
- package/src/js/dialog.js +2 -1
- package/src/js/dismiss.js +5 -0
- package/src/js/enhance.js +34 -2
- package/src/js/enhancement-loader.js +36 -43
- package/src/js/events.js +0 -3
- package/src/js/filter.js +0 -1
- package/src/js/floating-space.js +42 -0
- package/src/js/flyout.js +9 -44
- package/src/js/focus-group.js +7 -0
- package/src/js/focus.js +6 -0
- package/src/js/menu.js +10 -1
- package/src/js/selectors.js +5 -9
- package/src/js/status.js +44 -7
- package/src/js/surface.js +106 -132
- package/src/js/tab.js +6 -1
- package/src/js/tooltip.js +152 -53
- package/src/js/validation.js +1 -1
- package/src/css/components/surface.css +0 -49
- package/src/js/floating.js +0 -432
|
@@ -106,19 +106,17 @@
|
|
|
106
106
|
"flex-nowrap",
|
|
107
107
|
"flex-wrap",
|
|
108
108
|
"floating-field",
|
|
109
|
+
"flush",
|
|
109
110
|
"flyout",
|
|
110
111
|
"flyout-trigger",
|
|
111
112
|
"font-weight-bold",
|
|
112
113
|
"font-weight-medium",
|
|
113
114
|
"font-weight-normal",
|
|
114
|
-
"font-weight-
|
|
115
|
+
"font-weight-semibold",
|
|
115
116
|
"form-actions",
|
|
116
117
|
"frame",
|
|
117
118
|
"gap-context",
|
|
118
|
-
"gap-lg",
|
|
119
|
-
"gap-md",
|
|
120
119
|
"gap-none",
|
|
121
|
-
"gap-sm",
|
|
122
120
|
"ghost",
|
|
123
121
|
"grid",
|
|
124
122
|
"grid-2",
|
|
@@ -137,7 +135,6 @@
|
|
|
137
135
|
"intent-color",
|
|
138
136
|
"inverted",
|
|
139
137
|
"is-open",
|
|
140
|
-
"is-sheet",
|
|
141
138
|
"is-static",
|
|
142
139
|
"items-center",
|
|
143
140
|
"items-end",
|
|
@@ -229,6 +226,7 @@
|
|
|
229
226
|
"scrollspy",
|
|
230
227
|
"secondary",
|
|
231
228
|
"select",
|
|
229
|
+
"separated",
|
|
232
230
|
"shadow-context",
|
|
233
231
|
"shadow-popout-context",
|
|
234
232
|
"sidebar-layout",
|
|
@@ -236,6 +234,7 @@
|
|
|
236
234
|
"sm",
|
|
237
235
|
"soft",
|
|
238
236
|
"solid",
|
|
237
|
+
"spacious",
|
|
239
238
|
"spinner",
|
|
240
239
|
"sr-only",
|
|
241
240
|
"stable-gutter",
|
|
@@ -252,7 +251,6 @@
|
|
|
252
251
|
"subtle",
|
|
253
252
|
"success",
|
|
254
253
|
"surface",
|
|
255
|
-
"surface-backdrop",
|
|
256
254
|
"switch",
|
|
257
255
|
"switcher",
|
|
258
256
|
"tab",
|
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
* or demo styles. Decorative palettes and custom markers belong outside the
|
|
6
6
|
* base component.
|
|
7
7
|
*
|
|
8
|
+
* Container treatments (structural chrome, not skins):
|
|
9
|
+
* - default — the common raised shell;
|
|
10
|
+
* - .flush — the shell removed, only the item separators remain, so the group
|
|
11
|
+
* can sit directly inside an existing surface (card, drawer, page section);
|
|
12
|
+
* - .separated — each item becomes its own card.
|
|
13
|
+
*
|
|
8
14
|
* Public hooks:
|
|
9
15
|
* --accordion-radius
|
|
10
16
|
* --accordion-marker-color
|
|
@@ -30,11 +36,14 @@
|
|
|
30
36
|
list-style: none;
|
|
31
37
|
padding: var(--space-40);
|
|
32
38
|
color: var(--text);
|
|
33
|
-
font-weight: var(--font-weight-
|
|
39
|
+
font-weight: var(--font-weight-medium);
|
|
34
40
|
}
|
|
35
41
|
|
|
36
|
-
.accordion summary:hover {
|
|
37
|
-
|
|
42
|
+
.accordion summary:hover::after {
|
|
43
|
+
/* The whole row is one focus target; reinforcing only the end marker keeps
|
|
44
|
+
the affordance on the disclosure control instead of making the whole
|
|
45
|
+
label read as a link. */
|
|
46
|
+
--accordion-marker-color: currentColor;
|
|
38
47
|
}
|
|
39
48
|
|
|
40
49
|
.accordion summary::-webkit-details-marker {
|
|
@@ -78,6 +87,36 @@
|
|
|
78
87
|
padding-inline-start: 2rem;
|
|
79
88
|
}
|
|
80
89
|
|
|
90
|
+
/* Flush — the shell is dropped and only the shared item separators remain.
|
|
91
|
+
Zero inline padding puts the group edge to edge inside the surface that
|
|
92
|
+
already frames it, so no override is repeated per embedding. */
|
|
93
|
+
.accordion.flush {
|
|
94
|
+
border: 0;
|
|
95
|
+
border-radius: 0;
|
|
96
|
+
background: transparent;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.accordion.flush summary,
|
|
100
|
+
.accordion.flush details > summary + * {
|
|
101
|
+
padding-inline: 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Separated — each item owns a full card. The item's own border beats the
|
|
105
|
+
shared details + details separator on specificity alone, so nothing needs
|
|
106
|
+
undoing for the second item onwards. */
|
|
107
|
+
.accordion.separated {
|
|
108
|
+
display: grid;
|
|
109
|
+
gap: var(--space-20);
|
|
110
|
+
border: 0;
|
|
111
|
+
background: transparent;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.accordion.separated > details {
|
|
115
|
+
border: var(--border-width) solid var(--border);
|
|
116
|
+
border-radius: var(--accordion-radius);
|
|
117
|
+
background: var(--surface-raised);
|
|
118
|
+
}
|
|
119
|
+
|
|
81
120
|
@media (forced-colors: active) {
|
|
82
121
|
.accordion {
|
|
83
122
|
--accordion-marker-color: CanvasText;
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
:where(.alert) {
|
|
33
33
|
--intent: initial;
|
|
34
34
|
--intent-fg: initial;
|
|
35
|
+
--intent-soft-fg: initial;
|
|
35
36
|
--ui-bg: initial;
|
|
36
37
|
--ui-fg: initial;
|
|
37
38
|
--ui-border: initial;
|
|
@@ -64,12 +65,23 @@
|
|
|
64
65
|
min-inline-size: 0;
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
/* The alert owns the block spacing between its direct content blocks, the way
|
|
69
|
+
.stack and .prose own theirs, so a title and a description are plain
|
|
70
|
+
siblings instead of a wrapper carrying an inline --gap. What made that pair
|
|
71
|
+
read as detached is the UA paragraph margin: trimming only the outer edges
|
|
72
|
+
left it in place between them. Content keeps its own internal rhythm — a
|
|
73
|
+
nested list keeps its bullets and padding, a .prose child keeps its flow.
|
|
74
|
+
The slots are excluded because they are anatomy, not content: an
|
|
75
|
+
admonition's title bar has to stay flush against its body. The list follows
|
|
76
|
+
the anatomy, so it grows with it. */
|
|
77
|
+
.alert > :not(:where(.alert-icon, .alert-dismiss, .alert-title, .alert-body)) {
|
|
78
|
+
margin-block: 0;
|
|
69
79
|
}
|
|
70
80
|
|
|
71
|
-
.alert
|
|
72
|
-
|
|
81
|
+
.alert
|
|
82
|
+
> :not(:where(.alert-icon, .alert-dismiss, .alert-title, .alert-body))
|
|
83
|
+
+ :not(:where(.alert-icon, .alert-dismiss, .alert-title, .alert-body)) {
|
|
84
|
+
margin-block-start: var(--space-10);
|
|
73
85
|
}
|
|
74
86
|
|
|
75
87
|
/* Alerts are soft by default. The recipe writes local defaults rather than
|
|
@@ -87,10 +99,9 @@
|
|
|
87
99
|
var(--surface) var(--soft-border-mix),
|
|
88
100
|
var(--intent, var(--neutral))
|
|
89
101
|
);
|
|
90
|
-
--alert-default-fg:
|
|
91
|
-
|
|
92
|
-
var(--text),
|
|
93
|
-
var(--intent, var(--text)) var(--soft-fg-mix)
|
|
102
|
+
--alert-default-fg: var(
|
|
103
|
+
--intent-soft-fg,
|
|
104
|
+
color-mix(in oklab, var(--text), var(--intent, var(--text)) var(--soft-fg-mix))
|
|
94
105
|
);
|
|
95
106
|
/* @sync soft-recipe */
|
|
96
107
|
}
|
|
@@ -191,7 +202,7 @@
|
|
|
191
202
|
/* Links */
|
|
192
203
|
.alert :where(a:not(.btn)) {
|
|
193
204
|
color: inherit;
|
|
194
|
-
font-weight: var(--font-weight-
|
|
205
|
+
font-weight: var(--font-weight-semibold);
|
|
195
206
|
}
|
|
196
207
|
|
|
197
208
|
/* Forced colors: no override needed. The always-on border gets a forced
|
|
@@ -199,11 +210,12 @@
|
|
|
199
210
|
Canvas backplate (unreadable) — so .solid intentionally falls back to the
|
|
200
211
|
default look. */
|
|
201
212
|
|
|
202
|
-
/* Callout —
|
|
203
|
-
|
|
213
|
+
/* Callout — leading-edge flag treatment. It owns the border geometry only, so
|
|
214
|
+
the surface and the ink stay with the alert's own intent and shared
|
|
215
|
+
treatment: bare is the soft panel, .surface the page surface, .solid a
|
|
216
|
+
filled one, and .outline has no fill and no other side. Thickness and color
|
|
217
|
+
follow the --alert-border-inline-start-* hooks. */
|
|
204
218
|
.alert.callout {
|
|
205
|
-
--alert-bg: var(--surface-subtle);
|
|
206
|
-
--alert-fg: var(--text);
|
|
207
219
|
border: 0;
|
|
208
220
|
border-inline-start: var(--alert-border-inline-start-width, 4px) solid
|
|
209
221
|
var(--alert-border-inline-start-color, var(--intent, var(--neutral)));
|
|
@@ -6,8 +6,12 @@
|
|
|
6
6
|
* Public hooks:
|
|
7
7
|
* --app-nav-size
|
|
8
8
|
* --app-nav-gap
|
|
9
|
+
* --app-nav-icon-size
|
|
9
10
|
*/
|
|
10
11
|
:where(.app-nav) {
|
|
12
|
+
--intent: initial;
|
|
13
|
+
--intent-fg: initial;
|
|
14
|
+
--intent-soft-fg: initial;
|
|
11
15
|
--ui-bg: initial;
|
|
12
16
|
--ui-fg: initial;
|
|
13
17
|
--ui-border: initial;
|
|
@@ -16,6 +20,7 @@
|
|
|
16
20
|
.app-nav {
|
|
17
21
|
--app-nav-size: 4rem;
|
|
18
22
|
--app-nav-gap: var(--space-10);
|
|
23
|
+
--app-nav-icon-size: 1.5rem;
|
|
19
24
|
|
|
20
25
|
position: sticky;
|
|
21
26
|
inset-block-end: 0;
|
|
@@ -52,9 +57,9 @@
|
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
.app-nav > a > :where(svg, img, [aria-hidden="true"]) {
|
|
55
|
-
inline-size:
|
|
56
|
-
block-size:
|
|
57
|
-
font-size:
|
|
60
|
+
inline-size: var(--app-nav-icon-size);
|
|
61
|
+
block-size: var(--app-nav-icon-size);
|
|
62
|
+
font-size: var(--app-nav-icon-size);
|
|
58
63
|
line-height: 1;
|
|
59
64
|
}
|
|
60
65
|
|
|
@@ -63,14 +68,19 @@
|
|
|
63
68
|
color: var(--text);
|
|
64
69
|
}
|
|
65
70
|
|
|
66
|
-
|
|
71
|
+
/* Current tile is already a full selected surface. Selection never changes
|
|
72
|
+
text metrics: the shared medium weight is kept, no permanent indicator is
|
|
73
|
+
added here. */
|
|
74
|
+
.app-nav > a[aria-current="page"] {
|
|
67
75
|
background: var(--state-selected);
|
|
68
76
|
color: var(--state-selected-fg);
|
|
69
|
-
font-weight: var(--font-weight-bold);
|
|
70
77
|
}
|
|
71
78
|
|
|
72
|
-
@media
|
|
73
|
-
|
|
74
|
-
|
|
79
|
+
@media (forced-colors: active) {
|
|
80
|
+
/* --state-selected flattens to a UA color that no longer distinguishes the
|
|
81
|
+
current tile from the rest of the bar, so paint the system Highlight pair. */
|
|
82
|
+
.app-nav > a[aria-current="page"] {
|
|
83
|
+
background: Highlight;
|
|
84
|
+
color: HighlightText;
|
|
75
85
|
}
|
|
76
86
|
}
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
color: var(--avatar-fg);
|
|
47
47
|
font: inherit;
|
|
48
48
|
font-size: var(--avatar-font-size);
|
|
49
|
-
font-weight: var(--font-weight-
|
|
49
|
+
font-weight: var(--font-weight-semibold);
|
|
50
50
|
line-height: 1;
|
|
51
51
|
text-decoration: none;
|
|
52
52
|
vertical-align: middle;
|
|
@@ -160,6 +160,18 @@ a.avatar[aria-disabled="true"] {
|
|
|
160
160
|
transform: translate(var(--avatar-status-translate-x), 50%);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
/* Safari before 16.4 has no :dir(); the ancestor-attribute path is the
|
|
164
|
+
Degraded-tier fallback and the :dir() rules are dropped on their own there.
|
|
165
|
+
:dir() follows inherited directionality, so the explicit ltr rule
|
|
166
|
+
neutralizes an outer RTL fallback for a nested direction change. */
|
|
167
|
+
[dir="rtl"] .avatar > .badge:empty {
|
|
168
|
+
--avatar-status-translate-x: -50%;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.avatar:dir(ltr) > .badge:empty {
|
|
172
|
+
--avatar-status-translate-x: 50%;
|
|
173
|
+
}
|
|
174
|
+
|
|
163
175
|
.avatar:dir(rtl) > .badge:empty {
|
|
164
176
|
--avatar-status-translate-x: -50%;
|
|
165
177
|
}
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
* --badge-size
|
|
10
10
|
* --badge-pad-x
|
|
11
11
|
* --badge-font-size
|
|
12
|
+
* --badge-icon-size
|
|
12
13
|
* --badge-dot-size
|
|
13
14
|
* --badge-radius
|
|
15
|
+
* --badge-dismiss-icon-size
|
|
14
16
|
*
|
|
15
17
|
* Internal:
|
|
16
18
|
* --badge-default-bg
|
|
@@ -26,6 +28,7 @@
|
|
|
26
28
|
:where(.badge) {
|
|
27
29
|
--intent: initial;
|
|
28
30
|
--intent-fg: initial;
|
|
31
|
+
--intent-soft-fg: initial;
|
|
29
32
|
--ui-bg: initial;
|
|
30
33
|
--ui-fg: initial;
|
|
31
34
|
--ui-border: initial;
|
|
@@ -33,10 +36,14 @@
|
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
.badge {
|
|
36
|
-
--badge-size:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
--badge-size: 1.5rem;
|
|
40
|
+
/* A third of the height keeps the pill proportional across local sizes
|
|
41
|
+
without coupling it to the label typography. */
|
|
42
|
+
--badge-pad-x: calc(var(--badge-size) / 3);
|
|
43
|
+
--badge-font-size: var(--font-size-sm);
|
|
44
|
+
--badge-icon-size: 1em;
|
|
39
45
|
--badge-dot-size: 0.5rem;
|
|
46
|
+
--badge-dismiss-icon-size: 0.875em;
|
|
40
47
|
--badge-default-bg: var(--surface-subtle);
|
|
41
48
|
--badge-default-fg: var(--intent, var(--text));
|
|
42
49
|
--badge-default-border: var(--border);
|
|
@@ -49,7 +56,11 @@
|
|
|
49
56
|
display: inline-flex;
|
|
50
57
|
align-items: center;
|
|
51
58
|
justify-content: center;
|
|
59
|
+
vertical-align: middle;
|
|
52
60
|
gap: 0.35em;
|
|
61
|
+
/* Content-sized: a flex parent must not squeeze the pill and push its
|
|
62
|
+
nowrap label outside the background. */
|
|
63
|
+
flex: none;
|
|
53
64
|
inline-size: fit-content;
|
|
54
65
|
max-inline-size: 100%;
|
|
55
66
|
min-block-size: var(--badge-size);
|
|
@@ -59,7 +70,7 @@
|
|
|
59
70
|
background: var(--badge-bg);
|
|
60
71
|
color: var(--badge-fg);
|
|
61
72
|
font-size: var(--badge-font-size);
|
|
62
|
-
font-weight: var(--font-weight-
|
|
73
|
+
font-weight: var(--font-weight-medium);
|
|
63
74
|
line-height: 1;
|
|
64
75
|
text-decoration: none;
|
|
65
76
|
white-space: nowrap;
|
|
@@ -70,6 +81,31 @@
|
|
|
70
81
|
box-shadow var(--duration);
|
|
71
82
|
}
|
|
72
83
|
|
|
84
|
+
.badge.sm {
|
|
85
|
+
--badge-size: 1.25rem;
|
|
86
|
+
--badge-font-size: var(--font-size-xs);
|
|
87
|
+
--badge-dot-size: 0.375rem;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.badge.lg {
|
|
91
|
+
--badge-size: 1.875rem;
|
|
92
|
+
--badge-font-size: var(--font-size-md);
|
|
93
|
+
--badge-dot-size: 0.625rem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Decorative children follow the badge's optical size. Text and interactive
|
|
97
|
+
dismiss buttons retain their own boxes. */
|
|
98
|
+
.badge > :where(svg, img, [aria-hidden="true"]:not(.dot)) {
|
|
99
|
+
flex: none;
|
|
100
|
+
/* The shared rule maps --badge-icon-size onto font-size. One em is therefore
|
|
101
|
+
the requested optical size; reusing the em-valued hook here would apply its
|
|
102
|
+
factor a second time. */
|
|
103
|
+
inline-size: 1em;
|
|
104
|
+
block-size: 1em;
|
|
105
|
+
font-size: var(--badge-icon-size);
|
|
106
|
+
line-height: 1;
|
|
107
|
+
}
|
|
108
|
+
|
|
73
109
|
/* Soft by default. Local defaults remain behind --ui-* in the resolution
|
|
74
110
|
chain, so explicit shared treatments compose without selector exclusions. */
|
|
75
111
|
@supports (color: color-mix(in oklch, red, white)) {
|
|
@@ -85,10 +121,9 @@
|
|
|
85
121
|
var(--surface) var(--soft-border-mix),
|
|
86
122
|
var(--intent, var(--neutral))
|
|
87
123
|
);
|
|
88
|
-
--badge-default-fg:
|
|
89
|
-
|
|
90
|
-
var(--text),
|
|
91
|
-
var(--intent, var(--text)) var(--soft-fg-mix)
|
|
124
|
+
--badge-default-fg: var(
|
|
125
|
+
--intent-soft-fg,
|
|
126
|
+
color-mix(in oklab, var(--text), var(--intent, var(--text)) var(--soft-fg-mix))
|
|
92
127
|
);
|
|
93
128
|
/* @sync soft-recipe */
|
|
94
129
|
}
|
|
@@ -105,12 +140,22 @@
|
|
|
105
140
|
block-size: var(--badge-size);
|
|
106
141
|
}
|
|
107
142
|
|
|
143
|
+
/* The shared .dot separator doubles as a status dot inside a badge.
|
|
144
|
+
currentColor follows the badge's resolved ink, so the dot matches a soft
|
|
145
|
+
intent without repeating the intent class on it. */
|
|
146
|
+
.badge > .dot {
|
|
147
|
+
background-color: currentColor;
|
|
148
|
+
}
|
|
149
|
+
|
|
108
150
|
.badge > button {
|
|
109
151
|
display: inline-grid;
|
|
110
152
|
place-items: center;
|
|
111
153
|
flex: 0 0 auto;
|
|
112
|
-
|
|
113
|
-
|
|
154
|
+
/* WCAG 2.5.8 floors pointer targets at 24 CSS px. Density and local size may
|
|
155
|
+
keep the badge visually compact through the negative margins, but never
|
|
156
|
+
shrink its action target below that literal floor. */
|
|
157
|
+
min-inline-size: max(var(--badge-size), 24px);
|
|
158
|
+
min-block-size: max(var(--badge-size), 24px);
|
|
114
159
|
margin-block: -0.25rem;
|
|
115
160
|
margin-inline-start: -0.15em;
|
|
116
161
|
margin-inline-end: -0.45em;
|
|
@@ -124,6 +169,21 @@
|
|
|
124
169
|
cursor: pointer;
|
|
125
170
|
}
|
|
126
171
|
|
|
172
|
+
/* Dismiss mark — an empty dismiss button paints its own X from the shared
|
|
173
|
+
--icon-close mask, so no icon font or text glyph is required in the markup
|
|
174
|
+
(same contract as .alert-dismiss and .dialog-close). A mask box is a block
|
|
175
|
+
box, so place-items centers the ink itself; a text glyph would only center
|
|
176
|
+
its line box and land the ink low, because a lowercase x has no descender
|
|
177
|
+
and only x-height of ascent. A button with its own content keeps it. */
|
|
178
|
+
.badge > button:empty::before {
|
|
179
|
+
content: "";
|
|
180
|
+
inline-size: var(--badge-dismiss-icon-size);
|
|
181
|
+
block-size: var(--badge-dismiss-icon-size);
|
|
182
|
+
background: currentColor;
|
|
183
|
+
-webkit-mask: var(--icon-close) center / contain no-repeat;
|
|
184
|
+
mask: var(--icon-close) center / contain no-repeat;
|
|
185
|
+
}
|
|
186
|
+
|
|
127
187
|
.badge > button:not(:disabled):hover {
|
|
128
188
|
background: var(--hover-overlay);
|
|
129
189
|
}
|
|
@@ -156,7 +216,14 @@
|
|
|
156
216
|
background: var(--ui-hover-bg, var(--badge-default-hover-bg));
|
|
157
217
|
}
|
|
158
218
|
|
|
159
|
-
|
|
219
|
+
/* Solid and empty badges keep the overlay darkening on hover; soft badges do
|
|
220
|
+
not — their hover fill (--soft-hover-alpha) is already the state change, and
|
|
221
|
+
stacking a second darkening drags the soft ink below its resting contrast.
|
|
222
|
+
Soft here means :not(.solid, .outline, :empty), mirroring the soft hover-bg
|
|
223
|
+
recipe above. */
|
|
224
|
+
:where(a.badge, button.badge):is(.solid, :empty):not(
|
|
225
|
+
:where(:disabled, [aria-disabled="true"])
|
|
226
|
+
):hover {
|
|
160
227
|
/* Huge inset spread paints the overlay over the pill radius (see button.css). */
|
|
161
228
|
box-shadow: inset 0 0 0 999px var(--hover-overlay);
|
|
162
229
|
}
|
|
@@ -188,6 +255,5 @@ a.badge[aria-disabled="true"] {
|
|
|
188
255
|
@media print {
|
|
189
256
|
.badge {
|
|
190
257
|
border: var(--border-width) solid CanvasText;
|
|
191
|
-
break-inside: avoid;
|
|
192
258
|
}
|
|
193
259
|
}
|
|
@@ -33,8 +33,7 @@
|
|
|
33
33
|
text-decoration: none;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
.breadcrumb a:not([aria-current]):hover
|
|
37
|
-
.breadcrumb a[aria-current="false"]:hover {
|
|
36
|
+
.breadcrumb a:not([aria-current="page"]):hover {
|
|
38
37
|
color: var(--text);
|
|
39
38
|
text-decoration: underline;
|
|
40
39
|
}
|
|
@@ -43,7 +42,7 @@
|
|
|
43
42
|
border-radius: var(--radius-sm);
|
|
44
43
|
}
|
|
45
44
|
|
|
46
|
-
.breadcrumb :where(li, a, span)
|
|
45
|
+
.breadcrumb :where(li, a, span)[aria-current="page"] {
|
|
47
46
|
color: var(--text);
|
|
48
47
|
font-weight: var(--font-weight-medium);
|
|
49
48
|
}
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
:where(.btn) {
|
|
40
40
|
--intent: initial;
|
|
41
41
|
--intent-fg: initial;
|
|
42
|
+
--intent-soft-fg: initial;
|
|
42
43
|
--ui-bg: initial;
|
|
43
44
|
--ui-fg: initial;
|
|
44
45
|
--ui-border: initial;
|
|
@@ -110,13 +111,16 @@
|
|
|
110
111
|
--ui-border: transparent;
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
/* Solid
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
/* Solid keeps the overlay darkening on hover; soft/outline/ghost/link stay
|
|
115
|
+
flat. The soft hover is its own fill change (--soft-hover-alpha) — a second
|
|
116
|
+
overlay on top would double the fill step and drag its ink below the resting
|
|
117
|
+
contrast on every intent. Routed through a variable instead of a
|
|
118
|
+
:not(.outline, .ghost, .link) exclusion so the hover rule below only has to
|
|
119
|
+
state the interactive element list once. */
|
|
117
120
|
.btn.outline,
|
|
118
121
|
.btn.ghost,
|
|
119
|
-
.btn.link
|
|
122
|
+
.btn.link,
|
|
123
|
+
.btn.soft {
|
|
120
124
|
--btn-hover-overlay: transparent;
|
|
121
125
|
}
|
|
122
126
|
|
|
@@ -239,14 +243,3 @@ a.btn[aria-disabled="true"] {
|
|
|
239
243
|
border-color: GrayText;
|
|
240
244
|
}
|
|
241
245
|
}
|
|
242
|
-
|
|
243
|
-
@media print {
|
|
244
|
-
:is(button, input[type="button"], input[type="submit"], input[type="reset"]).btn {
|
|
245
|
-
display: none !important;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
a.btn[href^="http"]::after {
|
|
249
|
-
content: " (" attr(href) ")";
|
|
250
|
-
font-size: 0.875em;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
@@ -26,8 +26,15 @@
|
|
|
26
26
|
* --busy-overlay-bg
|
|
27
27
|
*/
|
|
28
28
|
/* Intent boundary — inherited intents do not tint this component.
|
|
29
|
-
Local intent classes still win because :where() has zero specificity.
|
|
29
|
+
Local intent classes still win because :where() has zero specificity.
|
|
30
|
+
The intent tokens are reset alongside --ui-*: the shared variants
|
|
31
|
+
(.soft/.solid/.outline/.surface) read --intent / --intent-fg / --intent-soft-fg
|
|
32
|
+
when they paint this surface, so an ancestor intent would otherwise leak
|
|
33
|
+
through a variant that has no local intent class. */
|
|
30
34
|
:where(.card) {
|
|
35
|
+
--intent: initial;
|
|
36
|
+
--intent-fg: initial;
|
|
37
|
+
--intent-soft-fg: initial;
|
|
31
38
|
--ui-bg: initial;
|
|
32
39
|
--ui-fg: initial;
|
|
33
40
|
--ui-border: initial;
|
|
@@ -100,7 +107,7 @@
|
|
|
100
107
|
box-shadow: var(--shadow);
|
|
101
108
|
}
|
|
102
109
|
.card.subtle {
|
|
103
|
-
|
|
110
|
+
--card-bg: var(--surface-subtle);
|
|
104
111
|
}
|
|
105
112
|
.card.compact {
|
|
106
113
|
--card-pad: var(--space-20);
|