actual-css 0.5.0 → 0.6.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 +17 -1
- package/dist/actual.css +1 -0
- package/dist/actual.full.css +480 -117
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/dist/actual.min.css +1 -1
- package/docs/design-notes/browser-support.md +14 -14
- package/docs/design-notes/enhancement-contract.md +30 -13
- package/docs/design-notes/forced-colors.md +11 -7
- package/docs/design-notes/grid.md +4 -4
- package/docs/design-notes/naming.md +15 -9
- package/docs/design-notes/platform-alignment.md +9 -0
- package/docs/design-notes/references.md +4 -0
- package/docs/design-notes/steps.md +748 -0
- package/docs/design-notes/widget-primitives.md +32 -15
- package/docs/navigation.json +1 -0
- package/docs/pages/components/alert.md +13 -8
- package/docs/pages/components/aura.md +102 -6
- package/docs/pages/components/avatar.md +9 -6
- package/docs/pages/components/badge.md +11 -7
- package/docs/pages/components/button.md +17 -17
- package/docs/pages/components/card.md +15 -8
- package/docs/pages/components/chat.md +1 -1
- package/docs/pages/components/dialog.md +10 -7
- package/docs/pages/components/drawer.md +1 -1
- package/docs/pages/components/fab.md +12 -10
- package/docs/pages/components/indicator.md +6 -6
- package/docs/pages/components/list.md +11 -2
- package/docs/pages/components/navbar.md +1 -1
- package/docs/pages/components/pagination.md +1 -1
- package/docs/pages/components/rating.md +4 -4
- package/docs/pages/components/skeleton.md +1 -1
- package/docs/pages/components/spinner.md +1 -1
- package/docs/pages/components/steps.md +376 -52
- package/docs/pages/components/table.md +5 -5
- package/docs/pages/enhancements/flyout.md +12 -10
- package/docs/pages/enhancements/overview.md +2 -2
- package/docs/pages/enhancements/scrollspy.md +1 -1
- package/docs/pages/enhancements/tabs.md +1 -1
- package/docs/pages/examples/overview.md +2 -0
- package/docs/pages/forms/choice-cards.md +4 -4
- package/docs/pages/forms/combobox.md +87 -0
- package/docs/pages/forms/floating-field.md +5 -3
- package/docs/pages/forms/joined-controls.md +4 -4
- package/docs/pages/forms/labels.md +8 -5
- package/docs/pages/forms/overview.md +25 -18
- package/docs/pages/forms/password.md +3 -2
- package/docs/pages/forms/range.md +13 -2
- package/docs/pages/forms/states-and-sizes.md +6 -4
- package/docs/pages/forms/validation.md +13 -9
- package/docs/pages/foundations/tokens.md +9 -6
- package/docs/pages/foundations/typography.md +1 -1
- package/docs/pages/guides/bootstrap.md +3 -3
- package/docs/pages/guides/introduction.md +1 -1
- package/docs/pages/guides/modular-import.md +21 -10
- package/docs/pages/guides/progressive-enhancement.md +75 -54
- package/docs/pages/guides/tailwind.md +2 -2
- package/docs/pages/layout/app-shell.md +4 -4
- package/docs/pages/layout/choosing.md +10 -6
- package/docs/pages/layout/grid.md +11 -10
- package/docs/pages/layout/stack.md +10 -6
- package/docs/pages/patterns/structured-lists.md +7 -7
- package/docs/pages/utilities/accessibility.md +1 -1
- package/docs/pages/utilities/flex.md +20 -13
- package/docs/pages/utilities/links.md +1 -1
- package/docs/pages/utilities/list.md +1 -1
- package/docs/pages/utilities/overflow.md +14 -7
- package/docs/pages/utilities/overview.md +22 -0
- package/docs/pages/utilities/shape.md +1 -1
- package/docs/pages/utilities/sizing-wrapping.md +10 -8
- package/docs/pages/utilities/spacing.md +26 -19
- package/docs/pages/utilities/text.md +1 -1
- package/llms.txt +27 -4
- package/package.json +3 -2
- package/scripts/reserved-classes.json +4 -1
- package/src/css/components/fab.css +44 -8
- package/src/css/components/list.css +3 -1
- package/src/css/components/steps.css +277 -69
- package/src/css/core/tokens.css +1 -0
- package/src/css/effects/aura.css +101 -11
- package/src/css/forms/custom-select.css +1 -1
- package/src/css/forms/floating-field.css +8 -6
- package/src/css/layout/container-query.css +37 -13
- package/src/css/layout/grid.css +7 -7
- package/src/css/layout/index.css +1 -1
- package/src/js/password.js +1 -1
|
@@ -1,20 +1,54 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Steps — semantic ordered-list progression
|
|
3
|
-
* the row when they fit and hold a minimum width otherwise, so the sequence
|
|
4
|
-
* scrolls horizontally only when it genuinely cannot fit. No breakpoint decides
|
|
5
|
-
* this — the item minimum tracks the real content, not an arbitrary width.
|
|
2
|
+
* Steps — semantic ordered-list progression for a multi-step process.
|
|
6
3
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* One primitive, two explicit compositions. Both classes are required:
|
|
5
|
+
*
|
|
6
|
+
* <ol class="steps steps-horizontal"> horizontal, 2 to 5 stages
|
|
7
|
+
* <ol class="steps steps-vertical"> vertical, any number of stages
|
|
8
|
+
*
|
|
9
|
+
* `.steps` is what a step *is* — the counter, the marker, `.step-label`,
|
|
10
|
+
* `.step-complete`, `aria-current="step"`. It carries no orientation, so it is
|
|
11
|
+
* never written alone. `.steps-horizontal` and `.steps-vertical` are what a
|
|
12
|
+
* sequence *looks like*, and they are peers: neither is the default the other
|
|
13
|
+
* has to exclude, so no rule says `:not(...)` and no rule undoes another's
|
|
14
|
+
* layout.
|
|
15
|
+
*
|
|
16
|
+
* Orientation and labels are independent dimensions. `.step-label` is optional
|
|
17
|
+
* in either orientation; when a sequence carries names, every step carries
|
|
18
|
+
* one:
|
|
19
|
+
*
|
|
20
|
+
* empty steps .step-label on every step
|
|
21
|
+
* horizontal compact progression named sequence, stacked or inline
|
|
22
|
+
* vertical compact rail named sequence, labels beside markers
|
|
23
|
+
*
|
|
24
|
+
* An empty step is a marker and nothing else, so it is compact by structure —
|
|
25
|
+
* `> li:empty` in both orientations, no variant class, and nothing authored
|
|
26
|
+
* only to be hidden again. The page around it owns the step name.
|
|
27
|
+
*
|
|
28
|
+
* A label may be an `a[href]`, which is the whole interactive surface: the
|
|
29
|
+
* marker is never navigable, so navigating a flow means naming its
|
|
30
|
+
* destinations. Anything else focusable in a step is out of contract.
|
|
31
|
+
*
|
|
32
|
+
* The framework never hides an authored label to solve a layout problem. A
|
|
33
|
+
* labelled row that outgrows its space scrolls. An `actual-container` size
|
|
34
|
+
* context buys it one thing: the wide representation, marker beside label.
|
|
35
|
+
*
|
|
36
|
+
* See docs/design-notes/steps.md for the threshold, the selector shapes, the
|
|
37
|
+
* rejected alternatives, and why there is no forced-colors branch.
|
|
38
|
+
*
|
|
39
|
+
* Each public hook is followed by the class that owns it.
|
|
9
40
|
*
|
|
10
41
|
* Public hooks:
|
|
11
|
-
* --step-size
|
|
12
|
-
* --step-
|
|
13
|
-
* --step-
|
|
14
|
-
* --step-
|
|
15
|
-
* --step-
|
|
16
|
-
* --step-
|
|
17
|
-
* --step-
|
|
42
|
+
* --step-size .steps
|
|
43
|
+
* --step-inline-gap .steps
|
|
44
|
+
* --step-line-size .steps
|
|
45
|
+
* --step-marker-radius .steps
|
|
46
|
+
* --step-complete-mark .steps
|
|
47
|
+
* --step-min .steps-horizontal
|
|
48
|
+
* --step-gap .steps-horizontal
|
|
49
|
+
* --step-connector .steps-horizontal
|
|
50
|
+
* --step-inline-connector .steps-horizontal
|
|
51
|
+
* --step-vertical-gap .steps-vertical
|
|
18
52
|
*
|
|
19
53
|
* Internal:
|
|
20
54
|
* --step-marker-bg
|
|
@@ -22,10 +56,12 @@
|
|
|
22
56
|
* --step-marker-fg
|
|
23
57
|
* --step-line
|
|
24
58
|
*/
|
|
59
|
+
|
|
60
|
+
/* Step primitive --------------------------------------------------------- */
|
|
61
|
+
|
|
25
62
|
.steps {
|
|
26
63
|
--step-size: 1.75rem;
|
|
27
|
-
--step-
|
|
28
|
-
--step-gap: var(--space-20);
|
|
64
|
+
--step-inline-gap: var(--space-20);
|
|
29
65
|
--step-line-size: var(--border-width);
|
|
30
66
|
--step-marker-bg: var(--surface);
|
|
31
67
|
--step-marker-border: var(--border);
|
|
@@ -35,29 +71,14 @@
|
|
|
35
71
|
|
|
36
72
|
counter-reset: actual-step;
|
|
37
73
|
display: flex;
|
|
38
|
-
flex-direction: row;
|
|
39
|
-
inline-size: 100%;
|
|
40
74
|
margin: 0;
|
|
41
75
|
padding: 0;
|
|
42
76
|
list-style: none;
|
|
43
|
-
/* Fill when it fits, scroll when it cannot: items never shrink below
|
|
44
|
-
--step-min, so labels keep a readable width and the sequence scrolls
|
|
45
|
-
horizontally instead of squishing. overscroll-behavior keeps the scroll
|
|
46
|
-
from chaining to the page. */
|
|
47
|
-
overflow-x: auto;
|
|
48
|
-
overscroll-behavior-inline: contain;
|
|
49
77
|
}
|
|
50
78
|
|
|
51
79
|
.steps > li {
|
|
52
80
|
position: relative;
|
|
53
|
-
display: grid;
|
|
54
|
-
flex: 1 0 var(--step-min);
|
|
55
|
-
grid-template-rows: var(--step-size) auto;
|
|
56
|
-
gap: var(--step-gap);
|
|
57
|
-
justify-items: center;
|
|
58
|
-
min-inline-size: max(var(--step-size), var(--step-min));
|
|
59
81
|
color: var(--text-muted);
|
|
60
|
-
text-align: center;
|
|
61
82
|
counter-increment: actual-step;
|
|
62
83
|
}
|
|
63
84
|
|
|
@@ -78,30 +99,38 @@
|
|
|
78
99
|
line-height: 1;
|
|
79
100
|
}
|
|
80
101
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
102
|
+
/* The marker carries the state, so the label reads a notch below it — set on
|
|
103
|
+
the component, never per representation: a size that changed with the layout
|
|
104
|
+
would resize the text under a reader dragging a window. `color: inherit` is
|
|
105
|
+
here rather than in a rule of its own for `a.step-label`, because a span
|
|
106
|
+
inherits its ink anyway; only a link has anything to take back, and taking it
|
|
107
|
+
back for every label costs nothing and one rule less. A navigable label keeps
|
|
108
|
+
its underline, hover and focus ring — just not a link color, which would read
|
|
109
|
+
ahead of the marker states.
|
|
110
|
+
|
|
111
|
+
Scoped to `.steps`, unlike `.menu-item-text` and the framework's other
|
|
112
|
+
sub-part classes: a bare `.step-label` weighs (0,1,0), exactly tying
|
|
113
|
+
`.prose :where(a)`, so a stepper in prose would be decided by bundle order. */
|
|
114
|
+
.steps .step-label {
|
|
115
|
+
font-size: var(--font-size-sm);
|
|
116
|
+
line-height: var(--line-height-tight);
|
|
117
|
+
color: inherit;
|
|
94
118
|
}
|
|
95
119
|
|
|
96
|
-
|
|
97
|
-
|
|
120
|
+
/* Each composition gives the connector its own geometry and background, so the
|
|
121
|
+
only shared fact is which steps get one — every step but the last. Stated as
|
|
122
|
+
the condition rather than as a `content: none` rule undoing a `content: ""`
|
|
123
|
+
rule: a pseudo-element with no content generates no box, so a composition can
|
|
124
|
+
address `> li::after` freely and still not trail a line past the last step. */
|
|
125
|
+
.steps > li:not(:last-child)::after {
|
|
126
|
+
content: "";
|
|
98
127
|
}
|
|
99
128
|
|
|
100
|
-
/*
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
.steps > .complete {
|
|
129
|
+
/* Prefixed like `.step-label`, unlike the framework's bare `.primary` /
|
|
130
|
+
`.outline` modifiers: those qualify a component on its own element, this
|
|
131
|
+
marks one item inside one. A bare `.complete` would also claim a very
|
|
132
|
+
ordinary English word globally, for a single component's single state. */
|
|
133
|
+
.steps > .step-complete {
|
|
105
134
|
--step-marker-bg: var(--state-selected);
|
|
106
135
|
--step-marker-border: var(--state-selected);
|
|
107
136
|
--step-marker-fg: var(--state-selected-fg);
|
|
@@ -110,10 +139,9 @@
|
|
|
110
139
|
color: var(--text);
|
|
111
140
|
}
|
|
112
141
|
|
|
113
|
-
/*
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
current step is owned by the previous step, which is complete. */
|
|
142
|
+
/* Complete owns the accented connector that reaches the current step, so the
|
|
143
|
+
connector leaving current goes back to neutral. Declared after
|
|
144
|
+
.step-complete at equal specificity: current wins on every channel. */
|
|
117
145
|
.steps > [aria-current="step"] {
|
|
118
146
|
--step-marker-bg: var(--surface);
|
|
119
147
|
--step-marker-border: var(--state-selected);
|
|
@@ -123,27 +151,207 @@
|
|
|
123
151
|
color: var(--text);
|
|
124
152
|
}
|
|
125
153
|
|
|
126
|
-
/* Thicker border is the current affordance in every mode — structural, so it
|
|
127
|
-
survives forced colors, and it never competes with the outline channel owned
|
|
128
|
-
by focus. */
|
|
129
154
|
.steps > [aria-current="step"]::before {
|
|
130
155
|
border-width: calc(var(--border-width) * 2);
|
|
131
156
|
}
|
|
132
157
|
|
|
133
|
-
/*
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
158
|
+
/* Weight marks "where I am now". Complete needs none: its filled disc already
|
|
159
|
+
reads as strongly. */
|
|
160
|
+
.steps > [aria-current="step"] .step-label {
|
|
161
|
+
font-weight: var(--font-weight-strong);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.steps > .step-complete:not([aria-current="step"])::before {
|
|
138
165
|
content: var(--step-complete-mark, counter(actual-step));
|
|
139
166
|
}
|
|
140
167
|
|
|
141
|
-
/*
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
168
|
+
/* Horizontal ------------------------------------------------------------- */
|
|
169
|
+
|
|
170
|
+
.steps-horizontal {
|
|
171
|
+
--step-min: 7rem;
|
|
172
|
+
--step-gap: var(--space-20);
|
|
173
|
+
|
|
174
|
+
inline-size: 100%;
|
|
175
|
+
overflow-x: auto;
|
|
176
|
+
/* Stated, never left to `overflow-x` alone: one scrolling axis computes the
|
|
177
|
+
other to `auto`, and a row hugs fractional content closely enough that a
|
|
178
|
+
rounding difference then raises a phantom vertical scrollbar. */
|
|
179
|
+
overflow-y: hidden;
|
|
180
|
+
overscroll-behavior-inline: contain;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* A scroll container clips at the padding edge, so a navigable row reserves the
|
|
184
|
+
focus ring's own width and offset. Only this composition scrolls, and only a
|
|
185
|
+
link has a ring to protect. */
|
|
186
|
+
.steps-horizontal:has(a[href]) {
|
|
187
|
+
padding-block: calc(var(--focus-outline-offset) + var(--border-width) * 2);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* One explicit row, the marker's: a `.step-label` is auto-placed into an
|
|
191
|
+
implicit second row, and two tracks are also the only thing `gap` has to sit
|
|
192
|
+
between. So an empty step measures exactly `--step-size`, and the rule below
|
|
193
|
+
takes back nothing. `var(--step-size) auto` would build that second row
|
|
194
|
+
whether or not a label ever arrives, and the compact form would then have to
|
|
195
|
+
remove both the track and the gap. */
|
|
196
|
+
.steps-horizontal > li {
|
|
197
|
+
display: grid;
|
|
198
|
+
flex: 1 0 var(--step-min);
|
|
199
|
+
grid-template-rows: var(--step-size);
|
|
200
|
+
gap: var(--step-gap);
|
|
201
|
+
justify-items: center;
|
|
202
|
+
min-inline-size: max(var(--step-size), var(--step-min));
|
|
203
|
+
text-align: center;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* An empty step is a marker and nothing else, so it needs a marker's width
|
|
207
|
+
rather than a reading width. That is what lets five of them fit a phone with
|
|
208
|
+
no size context and no variant class.
|
|
209
|
+
|
|
210
|
+
`justify-items` is not an override of the row's centring: the marker is
|
|
211
|
+
centred because a label sits under it and the two share a centre, and with no
|
|
212
|
+
label there is nothing to centre against. The marker takes the start of its
|
|
213
|
+
own share instead, the connector below fills the rest of it, and the last
|
|
214
|
+
step has no share to fill — so the track runs edge to edge and lines up with
|
|
215
|
+
the text around it, the way the inline representation does.
|
|
216
|
+
|
|
217
|
+
`:empty` says exactly what the markup says, the way `.badge:empty` does, and
|
|
218
|
+
it reads the item's own content: bare text in an `<li>` keeps a reading width
|
|
219
|
+
like its wrapped peers, where `> li:has(.step-label)` would give it a marker
|
|
220
|
+
floor and skew the row. It also predates `:has()` by a decade, so the compact
|
|
221
|
+
form reaches the Degraded tier and only the inline bonus needs `:has()`. */
|
|
222
|
+
.steps-horizontal > li:empty {
|
|
223
|
+
flex: 1 1 0;
|
|
224
|
+
justify-items: start;
|
|
225
|
+
min-inline-size: var(--step-size);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.steps-horizontal > li:empty::after {
|
|
229
|
+
inset-inline-start: var(--step-size);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.steps-horizontal > li:empty:last-child {
|
|
233
|
+
flex: 0 0 auto;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/* The pseudo-element is marker-height so --step-connector may be any complete
|
|
237
|
+
background value. */
|
|
238
|
+
.steps-horizontal > li::after {
|
|
239
|
+
position: absolute;
|
|
240
|
+
inset-block-start: 0;
|
|
241
|
+
inset-inline-start: calc(50% + var(--step-size) / 2);
|
|
242
|
+
inline-size: calc(100% - var(--step-size));
|
|
243
|
+
block-size: var(--step-size);
|
|
244
|
+
background: var(
|
|
245
|
+
--step-connector,
|
|
246
|
+
linear-gradient(var(--step-line), var(--step-line)) center /
|
|
247
|
+
100% var(--step-line-size) no-repeat
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/*
|
|
252
|
+
* The one horizontal container query. Past 60rem a labelled row has the room
|
|
253
|
+
* to put marker and label inline and still leave the connector a real region
|
|
254
|
+
* between groups, rather than a stub. Calibrated on five steps — the widest
|
|
255
|
+
* supported row, and therefore the binding case — and it counts nothing, so it
|
|
256
|
+
* serves every count. Below it, stacked; a marker row is already the shape
|
|
257
|
+
* this query produces and stays out of it.
|
|
258
|
+
*
|
|
259
|
+
* Horizontal is a 2-5 step component. Outside that domain the rule simply
|
|
260
|
+
* applies as written, because a row that is out of contract is not owed a
|
|
261
|
+
* curated fallback.
|
|
262
|
+
*/
|
|
263
|
+
@supports (container-type: inline-size) {
|
|
264
|
+
@container actual-container (inline-size >= 60rem) {
|
|
265
|
+
.steps-horizontal:has(.step-label) > li {
|
|
266
|
+
display: flex;
|
|
267
|
+
flex: 1 1 auto;
|
|
268
|
+
align-items: center;
|
|
269
|
+
gap: var(--step-inline-gap);
|
|
270
|
+
min-inline-size: 0;
|
|
271
|
+
text-align: start;
|
|
272
|
+
white-space: nowrap;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.steps-horizontal:has(.step-label) > li::after {
|
|
276
|
+
position: static;
|
|
277
|
+
flex: 1 1 2rem;
|
|
278
|
+
inline-size: auto;
|
|
279
|
+
block-size: var(--step-size);
|
|
280
|
+
margin-inline-end: var(--step-inline-gap);
|
|
281
|
+
background: var(
|
|
282
|
+
--step-inline-connector,
|
|
283
|
+
var(
|
|
284
|
+
--step-connector,
|
|
285
|
+
linear-gradient(var(--step-line), var(--step-line)) center /
|
|
286
|
+
100% var(--step-line-size) no-repeat
|
|
287
|
+
)
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.steps-horizontal:has(.step-label) > li:last-child {
|
|
292
|
+
flex: 0 0 auto;
|
|
293
|
+
}
|
|
148
294
|
}
|
|
149
295
|
}
|
|
296
|
+
|
|
297
|
+
/* Vertical --------------------------------------------------------------- */
|
|
298
|
+
|
|
299
|
+
.steps-vertical {
|
|
300
|
+
--step-vertical-gap: var(--space-40);
|
|
301
|
+
|
|
302
|
+
flex-direction: column;
|
|
303
|
+
gap: var(--step-vertical-gap);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* `justify-items` looks like a reset and is not one — nothing here resets
|
|
307
|
+
anything, since the horizontal rules never applied. Grid items blockify, so
|
|
308
|
+
without it a label stretches the whole 1fr track: 264px against 75px for an
|
|
309
|
+
`a.step-label`, a full-width focus ring the row does not give the same link. */
|
|
310
|
+
.steps-vertical > li {
|
|
311
|
+
display: grid;
|
|
312
|
+
grid-template-columns: var(--step-size) minmax(0, 1fr);
|
|
313
|
+
align-items: start;
|
|
314
|
+
justify-items: start;
|
|
315
|
+
gap: var(--step-inline-gap);
|
|
316
|
+
min-block-size: var(--step-size);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* An empty step is a marker rail. The column cannot use the row's implicit
|
|
320
|
+
second track, because `grid-auto-flow` is `row`: an auto-placed label would
|
|
321
|
+
land *under* its marker. Its second track is therefore explicit, and this
|
|
322
|
+
takes it back.
|
|
323
|
+
|
|
324
|
+
Silent in a stretched column, where the item fills its line whatever its
|
|
325
|
+
tracks say. It bites in a shrink-to-fit box — which is what a sticky
|
|
326
|
+
progress rail usually is — where the label track and the gap to it make the
|
|
327
|
+
rail `--step-inline-gap` wider than the markers it draws: measured 36px
|
|
328
|
+
against 28px. No `gap: 0` needed; one track has nothing to sit beside. */
|
|
329
|
+
.steps-vertical > li:empty {
|
|
330
|
+
grid-template-columns: var(--step-size);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* The row starts at the top so a wrapped label begins level with its marker.
|
|
334
|
+
align-self only bites on a single line, which would otherwise float above
|
|
335
|
+
the marker's centre.
|
|
336
|
+
|
|
337
|
+
The one `.step-label` rule that keeps a child combinator: `align-self` is a
|
|
338
|
+
grid-item property, so this declaration is meaningless on anything but a
|
|
339
|
+
direct child of the row. Elsewhere the label is addressed as a descendant —
|
|
340
|
+
the framework owns the structure, and repeating it buys nothing. */
|
|
341
|
+
.steps-vertical > li > .step-label {
|
|
342
|
+
align-self: center;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/* The vertical track publishes no hook of its own: a horizontal connector value
|
|
346
|
+
cannot transpose onto this geometry, and this selector is already a reachable
|
|
347
|
+
override for the design that finally asks. */
|
|
348
|
+
.steps-vertical > li::after {
|
|
349
|
+
position: absolute;
|
|
350
|
+
inset-block-start: var(--step-size);
|
|
351
|
+
inset-inline-start: calc((var(--step-size) - var(--step-line-size)) / 2);
|
|
352
|
+
inline-size: var(--step-line-size);
|
|
353
|
+
block-size: calc(100% - var(--step-size) + var(--step-vertical-gap));
|
|
354
|
+
background:
|
|
355
|
+
linear-gradient(var(--step-line), var(--step-line)) center / var(--step-line-size) 100%
|
|
356
|
+
no-repeat;
|
|
357
|
+
}
|
package/src/css/core/tokens.css
CHANGED
|
@@ -149,6 +149,7 @@
|
|
|
149
149
|
--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");
|
|
150
150
|
--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");
|
|
151
151
|
--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");
|
|
152
|
+
--icon-plus: 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='12' y1='5' x2='12' y2='19'/%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3C/svg%3E");
|
|
152
153
|
|
|
153
154
|
/* Viewport size tokens — fallback vh/vw with dvh/vi enhancement.
|
|
154
155
|
Consumed by full-viewport components (app-shell, drawer, modal, flyout).
|
package/src/css/effects/aura.css
CHANGED
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Decorative intent-colored frame for one direct child.
|
|
3
3
|
*
|
|
4
|
+
* The effect is built from three layers with distinct roles: a frame carrying
|
|
5
|
+
* a short accent of light, a small halo that belongs to the frame and keeps
|
|
6
|
+
* that light attached to the edge, and a wide static halo that belongs to the
|
|
7
|
+
* object and gives it a permanent, diffuse presence. Only the small halo
|
|
8
|
+
* follows the animated gradient, so a rotating aura repaints one blurred layer
|
|
9
|
+
* per frame, not two.
|
|
10
|
+
*
|
|
4
11
|
* Public hooks:
|
|
5
12
|
* --aura-color
|
|
6
13
|
* --aura-width
|
|
7
14
|
* --aura-blur
|
|
15
|
+
* --aura-intensity
|
|
8
16
|
* --aura-radius
|
|
9
17
|
* --aura-duration
|
|
18
|
+
*
|
|
19
|
+
* Internal:
|
|
20
|
+
* --aura-angle
|
|
10
21
|
*/
|
|
11
22
|
@property --aura-angle {
|
|
12
23
|
syntax: "<angle>";
|
|
@@ -23,7 +34,8 @@
|
|
|
23
34
|
.aura {
|
|
24
35
|
--aura-color: var(--intent, var(--primary));
|
|
25
36
|
--aura-width: 2px;
|
|
26
|
-
--aura-blur: 0.
|
|
37
|
+
--aura-blur: 0.25rem;
|
|
38
|
+
--aura-intensity: 1;
|
|
27
39
|
--aura-radius: var(--radius-lg);
|
|
28
40
|
--aura-duration: 6s;
|
|
29
41
|
|
|
@@ -34,9 +46,19 @@
|
|
|
34
46
|
border-radius: calc(var(--aura-radius) + var(--aura-width));
|
|
35
47
|
border: var(--border-width) solid transparent;
|
|
36
48
|
background: var(--aura-color);
|
|
37
|
-
|
|
49
|
+
|
|
50
|
+
/* A short feathered accent rather than a long wedge with a hard edge, which
|
|
51
|
+
reads as one lit side. Nothing tints the frame outside it: a constant tint
|
|
52
|
+
across 2px draws an outline, the opposite of the effect. */
|
|
53
|
+
background: conic-gradient(
|
|
54
|
+
from var(--aura-angle),
|
|
55
|
+
transparent 0 78%,
|
|
56
|
+
var(--aura-color) 93%,
|
|
57
|
+
transparent
|
|
58
|
+
);
|
|
38
59
|
}
|
|
39
60
|
|
|
61
|
+
/* Near halo — inherits the frame gradient, so it tracks the lit accent. */
|
|
40
62
|
.aura::before {
|
|
41
63
|
position: absolute;
|
|
42
64
|
z-index: -1;
|
|
@@ -44,15 +66,40 @@
|
|
|
44
66
|
border-radius: inherit;
|
|
45
67
|
background: inherit;
|
|
46
68
|
filter: blur(var(--aura-blur));
|
|
47
|
-
opacity: 0.
|
|
69
|
+
opacity: calc(0.55 * var(--aura-intensity));
|
|
48
70
|
pointer-events: none;
|
|
49
71
|
content: "";
|
|
50
72
|
}
|
|
51
73
|
|
|
74
|
+
/* Far halo — flat and static. At this blur the rotating detail is no longer
|
|
75
|
+
legible, so there is nothing to gain from animating a second wide blurred
|
|
76
|
+
layer. It outgrows the frame, otherwise the child covers it and the whole
|
|
77
|
+
effect reads as a drop shadow. This is the layer that stays lit at all
|
|
78
|
+
times, so it — not the frame — is what keeps the object present between two
|
|
79
|
+
passes of the accent. */
|
|
80
|
+
.aura::after {
|
|
81
|
+
position: absolute;
|
|
82
|
+
z-index: -1;
|
|
83
|
+
inset: calc(-1 * var(--aura-blur));
|
|
84
|
+
border-radius: calc(var(--aura-radius) + var(--aura-width) + var(--aura-blur));
|
|
85
|
+
background: var(--aura-color);
|
|
86
|
+
filter: blur(calc(var(--aura-blur) * 4));
|
|
87
|
+
opacity: calc(0.15 * var(--aura-intensity));
|
|
88
|
+
pointer-events: none;
|
|
89
|
+
content: "";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Lifts the child above both halos. It also has to be opaque: the frame paints
|
|
93
|
+
across the whole box and only its edge is meant to show, so a transparent
|
|
94
|
+
child (an .outline alert, a .ghost button) is filled with the frame color
|
|
95
|
+
rather than framed by it. Documented as a constraint, not worked around —
|
|
96
|
+
masking a ring would cost more than the case is worth. */
|
|
52
97
|
.aura > * {
|
|
53
98
|
position: relative;
|
|
54
99
|
}
|
|
55
100
|
|
|
101
|
+
/* Glow lights the whole perimeter instead of a short accent, so both halos
|
|
102
|
+
become a single soft field around the child. */
|
|
56
103
|
.aura-glow {
|
|
57
104
|
background: var(--aura-color);
|
|
58
105
|
}
|
|
@@ -61,6 +108,29 @@
|
|
|
61
108
|
.aura:not(.aura-glow) {
|
|
62
109
|
animation: aura-rotate var(--aura-duration) linear infinite;
|
|
63
110
|
}
|
|
111
|
+
|
|
112
|
+
/* Both halos breathe: moving the near one alone changes a line's brightness,
|
|
113
|
+
which barely registers. Opening the wide field with it is what reads as a
|
|
114
|
+
breath. */
|
|
115
|
+
.aura-glow::before {
|
|
116
|
+
animation: aura-breathe-near var(--aura-duration) ease-in-out infinite;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.aura-glow::after {
|
|
120
|
+
animation: aura-breathe-far var(--aura-duration) ease-in-out infinite;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Without motion the accent would freeze at an arbitrary angle and read as a
|
|
125
|
+
broken animation, so it becomes a deliberate composition: two opposite
|
|
126
|
+
corners. Fixed radii keep those corners stable across aspect ratios;
|
|
127
|
+
proportional radii turn wide or tall elements into lit edges instead. */
|
|
128
|
+
@media (prefers-reduced-motion: reduce) {
|
|
129
|
+
.aura:not(.aura-glow) {
|
|
130
|
+
background:
|
|
131
|
+
radial-gradient(4rem 4rem at 0% 0%, var(--aura-color), transparent 70%),
|
|
132
|
+
radial-gradient(4rem 4rem at 100% 100%, var(--aura-color), transparent 70%);
|
|
133
|
+
}
|
|
64
134
|
}
|
|
65
135
|
|
|
66
136
|
@keyframes aura-rotate {
|
|
@@ -69,16 +139,36 @@
|
|
|
69
139
|
}
|
|
70
140
|
}
|
|
71
141
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
142
|
+
/* Only the peak is declared, so each halo breathes up from its own resting
|
|
143
|
+
opacity and never dips below the static glow. The peaks scale with the same
|
|
144
|
+
intensity as the resting values, which keeps the breath proportional at
|
|
145
|
+
every setting. */
|
|
146
|
+
@keyframes aura-breathe-near {
|
|
147
|
+
50% {
|
|
148
|
+
opacity: calc(0.95 * var(--aura-intensity));
|
|
149
|
+
transform: scale(1.03);
|
|
75
150
|
}
|
|
151
|
+
}
|
|
76
152
|
|
|
77
|
-
|
|
78
|
-
|
|
153
|
+
@keyframes aura-breathe-far {
|
|
154
|
+
50% {
|
|
155
|
+
opacity: calc(0.28 * var(--aura-intensity));
|
|
156
|
+
transform: scale(1.07);
|
|
79
157
|
}
|
|
158
|
+
}
|
|
80
159
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
160
|
+
/* Each rule is a lone :has() setting a decorative radius, so a browser without
|
|
161
|
+
:has() simply drops it and keeps the default. No @supports needed — that
|
|
162
|
+
guard only earns its place around a selector list that mixes :has() with
|
|
163
|
+
selectors worth keeping. */
|
|
164
|
+
.aura:has(> .btn) {
|
|
165
|
+
--aura-radius: var(--radius);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.aura:has(> :is(.card, .alert)) {
|
|
169
|
+
--aura-radius: var(--radius-lg);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.aura:has(> .badge) {
|
|
173
|
+
--aura-radius: var(--radius-full);
|
|
84
174
|
}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
&::picker(select) {
|
|
26
26
|
appearance: base-select;
|
|
27
|
-
max-
|
|
27
|
+
max-block-size: min(24rem, calc(var(--viewport-block) * 0.7));
|
|
28
28
|
margin-block: var(--space-20);
|
|
29
29
|
padding: 0;
|
|
30
30
|
border: var(--border-width) solid var(--border);
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* messages stay in the surrounding .field, not inside the cell, so the
|
|
8
8
|
* resting label can center on the control alone.
|
|
9
9
|
*
|
|
10
|
-
* The control must precede the label: the float is driven by the
|
|
11
|
-
* combinator and :placeholder-shown.
|
|
10
|
+
* The control must precede the label: the float is driven by the general
|
|
11
|
+
* sibling combinator and :placeholder-shown. Injected nodes may appear between
|
|
12
|
+
* the control and the label (password managers, extension wrappers); the cell
|
|
13
|
+
* contract stays a single control plus one label. Every .input/.textarea needs
|
|
12
14
|
* placeholder=" " to expose a reliable empty state — without it the label
|
|
13
15
|
* only reacts to focus.
|
|
14
16
|
*
|
|
@@ -56,14 +58,14 @@
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
/* Single-line controls rest in the middle until they receive a value. */
|
|
59
|
-
.floating-field > :is(.input, .select)
|
|
61
|
+
.floating-field > :is(.input, .select) ~ .field-label {
|
|
60
62
|
inset-block-start: 50%;
|
|
61
63
|
transform: translateY(-50%);
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
/* An empty textarea label follows the control's normal first-line position;
|
|
65
67
|
the control itself keeps the extra headroom reserved for its floated state. */
|
|
66
|
-
.floating-field > .textarea
|
|
68
|
+
.floating-field > .textarea ~ .field-label {
|
|
67
69
|
inset-block-start: calc(1em + var(--border-width));
|
|
68
70
|
transform: none;
|
|
69
71
|
}
|
|
@@ -72,7 +74,7 @@
|
|
|
72
74
|
not have propagated the value to the placeholder state yet. */
|
|
73
75
|
.floating-field
|
|
74
76
|
> :is(.input, .textarea):is(:focus, :autofill, :not(:placeholder-shown))
|
|
75
|
-
|
|
77
|
+
~ .field-label {
|
|
76
78
|
inset-block-start: var(--floating-label-top);
|
|
77
79
|
transform: none;
|
|
78
80
|
font-size: var(--font-size-xs);
|
|
@@ -91,7 +93,7 @@
|
|
|
91
93
|
.input[type="month"],
|
|
92
94
|
.input[type="week"]
|
|
93
95
|
)
|
|
94
|
-
|
|
96
|
+
~ .field-label {
|
|
95
97
|
inset-block-start: var(--floating-label-top);
|
|
96
98
|
transform: none;
|
|
97
99
|
font-size: var(--font-size-xs);
|