actual-css 0.11.1 → 0.12.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 +47 -0
- package/README.md +26 -6
- package/components.json +4 -16
- package/dist/actual.full.css +296 -267
- package/dist/actual.full.min.css +1 -1
- package/docs/brand/favicon.svg +5 -0
- package/docs/brand/lockup.svg +12 -0
- package/docs/brand/logo-dark.svg +9 -0
- package/docs/brand/logo-light.svg +9 -0
- package/docs/brand/logo.svg +11 -0
- package/docs/brand/mark.svg +11 -0
- package/docs/navigation.json +3 -2
- package/docs/pages/components/alert.md +2 -10
- package/docs/pages/components/badge.md +3 -9
- package/docs/pages/components/card.md +31 -2
- package/docs/pages/components/dialog.md +121 -33
- package/docs/pages/components/drawer.md +2 -2
- package/docs/pages/components/navbar.md +8 -9
- package/docs/pages/components/table.md +3 -3
- package/docs/pages/enhancements/flyout.md +2 -2
- package/docs/pages/enhancements/scrollspy.md +1 -1
- package/docs/pages/enhancements/status-bar.md +2 -2
- package/docs/pages/examples/overview.md +2 -1
- package/docs/pages/forms/date-picker.md +90 -0
- package/docs/pages/forms/detached-actions.md +37 -30
- package/docs/pages/forms/form-actions.md +61 -28
- package/docs/pages/forms/input-policy.md +1 -1
- package/docs/pages/forms/overview.md +6 -5
- package/docs/pages/forms/sticky-actions.md +85 -83
- package/docs/pages/forms/validation.md +5 -5
- package/docs/pages/foundations/philosophy.md +5 -3
- package/docs/pages/foundations/theming.md +15 -4
- package/docs/pages/foundations/tokens.md +50 -33
- package/docs/pages/foundations/typography.md +4 -7
- package/docs/pages/guides/bootstrap.md +2 -2
- package/docs/pages/guides/design-tools.md +2 -2
- package/docs/pages/guides/integrating-actual-css.md +5 -1
- package/docs/pages/guides/introduction.md +3 -4
- package/docs/pages/guides/modular-import.md +3 -0
- package/docs/pages/guides/progressive-enhancement.md +2 -2
- package/docs/pages/guides/tailwind.md +70 -0
- package/docs/pages/guides/upgrading.md +74 -0
- package/docs/pages/layout/cluster.md +28 -2
- package/docs/pages/layout/overview.md +1 -1
- package/docs/pages/layout/stack.md +21 -16
- package/docs/pages/layout/topbar.md +1 -1
- package/docs/pages/patterns/navigation-list.md +16 -46
- package/docs/pages/patterns/overview.md +3 -4
- package/docs/pages/utilities/flex.md +30 -20
- package/docs/pages/utilities/sizing-wrapping.md +1 -1
- package/docs/pages/utilities/spacing.md +4 -3
- package/package.json +2 -2
- package/reserved-classes.json +3 -3
- package/skills/actual-css/references/enhancements.md +10 -7
- package/src/cli/actual-css.js +39 -13
- package/src/css/components/avatar.css +16 -9
- package/src/css/components/badge.css +1 -5
- package/src/css/components/button.css +3 -9
- package/src/css/components/card.css +18 -10
- package/src/css/components/chat.css +4 -5
- package/src/css/components/drawer.css +19 -3
- package/src/css/components/flyout.css +3 -0
- package/src/css/components/index.css +0 -1
- package/src/css/components/modal.css +96 -124
- package/src/css/components/navbar.css +14 -25
- package/src/css/core/base.css +1 -1
- package/src/css/core/reset.css +2 -1
- package/src/css/core/theme.css +4 -0
- package/src/css/core/tokens.css +28 -10
- package/src/css/core/variants.css +41 -14
- package/src/css/forms/form-actions.css +15 -10
- package/src/css/layout/cluster.css +11 -0
- package/src/css/layout/topbar.css +12 -6
- package/src/css/utilities/base.css +5 -0
- package/src/css/utilities/extra.css +3 -7
- package/src/tooling/color.js +65 -0
- package/src/tooling/design-tokens.js +9 -1
- package/src/tooling/theme-contrast.js +209 -0
- package/docs/pages/patterns/actions.md +0 -52
- package/src/css/components/actions.css +0 -27
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* Internal:
|
|
23
23
|
* --drawer-closed-translate (side/RTL positioning state)
|
|
24
24
|
* --drawer-focus-bleed
|
|
25
|
+
* --drawer-close-reserve (header room for a corner close; zero without a close)
|
|
25
26
|
* --drawer-motion-duration (shared by panel and ::backdrop so exit duration
|
|
26
27
|
* changes in one place; entry state redefines it longer)
|
|
27
28
|
*/
|
|
@@ -31,6 +32,10 @@
|
|
|
31
32
|
/* The inset a direct .bleed child cancels (components/bleed.css). */
|
|
32
33
|
--surface-pad: var(--drawer-pad);
|
|
33
34
|
--drawer-closed-translate: -100%;
|
|
35
|
+
/* Header room for a corner close. Unconditional baseline: without :has()
|
|
36
|
+
(Degraded tier) a header keeps too much room rather than a title running
|
|
37
|
+
under the close. Zeroed when the drawer has no close. */
|
|
38
|
+
--drawer-close-reserve: calc(var(--close-size, var(--control-size)) + var(--space-30));
|
|
34
39
|
/* Exit is shorter than entry so the panel clears quickly; the scrim reads
|
|
35
40
|
the same variable through inheritance and never lags the panel. */
|
|
36
41
|
--drawer-motion-duration: var(--duration);
|
|
@@ -210,9 +215,20 @@
|
|
|
210
215
|
}
|
|
211
216
|
}
|
|
212
217
|
|
|
218
|
+
/* No close anywhere: reclaim the reserve. Deliberately coarse — a close in
|
|
219
|
+
the body keeps the reserve, which costs only room, never overlap. */
|
|
220
|
+
:where(dialog).drawer:not(:has(.close)) {
|
|
221
|
+
--drawer-close-reserve: 0px;
|
|
222
|
+
}
|
|
223
|
+
|
|
213
224
|
/* The header reserves inline room so the title never runs under the close
|
|
214
|
-
control. */
|
|
225
|
+
control. A .bleed header keeps at least its band inset. */
|
|
215
226
|
:where(dialog).drawer > header,
|
|
216
|
-
:where(dialog).drawer > form > header {
|
|
217
|
-
padding-inline-end:
|
|
227
|
+
:where(dialog).drawer > :where(form) > header {
|
|
228
|
+
padding-inline-end: var(--drawer-close-reserve);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
:where(dialog).drawer > header:where(.bleed),
|
|
232
|
+
:where(dialog).drawer > :where(form) > header:where(.bleed) {
|
|
233
|
+
padding-inline-end: max(var(--bleed-pad, 0px), var(--drawer-close-reserve));
|
|
218
234
|
}
|
|
@@ -50,6 +50,9 @@
|
|
|
50
50
|
position: absolute;
|
|
51
51
|
inset-block-start: calc(100% + var(--space-10));
|
|
52
52
|
inset-inline-start: 0;
|
|
53
|
+
/* Anatomy, not a default to replace: `flyout grid-3` (mega menu) takes its
|
|
54
|
+
columns from the grid preset and keeps this tight gap, because each
|
|
55
|
+
section pads itself — the visible gutter is section padding + gap. */
|
|
53
56
|
display: grid;
|
|
54
57
|
gap: var(--space-10);
|
|
55
58
|
inline-size: var(--flyout-inline-size, max-content);
|
|
@@ -9,15 +9,19 @@
|
|
|
9
9
|
* Public hooks:
|
|
10
10
|
* --modal-size
|
|
11
11
|
* --modal-pad
|
|
12
|
-
* --modal-header-bg
|
|
13
12
|
* --dialog-viewport-gap
|
|
14
13
|
* --dialog-icon-size
|
|
15
14
|
* --dialog-icon-glyph-size
|
|
16
15
|
*
|
|
17
16
|
* The corner close control is .close (components/close.css).
|
|
18
17
|
*
|
|
18
|
+
* The body footer is a structural slot with no layout of its own: its action
|
|
19
|
+
* row is composed in markup (`<footer class="cluster justify-content-end">`),
|
|
20
|
+
* as in the drawer. A default row here would fight every composed one.
|
|
21
|
+
*
|
|
19
22
|
* Internal:
|
|
20
23
|
* --dialog-inner-radius
|
|
24
|
+
* --dialog-close-reserve (header room for a corner close; zero without a close)
|
|
21
25
|
* --dialog-focus-bleed
|
|
22
26
|
* --dialog-viewport-block
|
|
23
27
|
* --dialog-viewport-gap-default
|
|
@@ -26,10 +30,8 @@
|
|
|
26
30
|
:where(dialog).modal {
|
|
27
31
|
/* --dialog-viewport-gap stays fallback-only so the responsive default can
|
|
28
32
|
live in an internal token: a media query moves it without closing the
|
|
29
|
-
public hook.
|
|
30
|
-
|
|
31
|
-
calc() multiplication is not in the supported browser range, so the
|
|
32
|
-
doubled gap is spelled out with an additive calc. */
|
|
33
|
+
public hook. `-2x` is the gap on the two viewport edges, shared by the
|
|
34
|
+
width and height caps. */
|
|
33
35
|
--dialog-viewport-gap-default: var(--space-40);
|
|
34
36
|
--dialog-viewport-gap-2x: calc(
|
|
35
37
|
var(--dialog-viewport-gap, var(--dialog-viewport-gap-default)) +
|
|
@@ -37,10 +39,19 @@
|
|
|
37
39
|
);
|
|
38
40
|
|
|
39
41
|
/* The panel is borderless, so its inner and outer radius are the same. Kept
|
|
40
|
-
as one name for the padded wrapper and
|
|
41
|
-
|
|
42
|
+
as one name for the padded wrapper and the header band; a .bleed band
|
|
43
|
+
inherits it from the wrapper, so none of them drifts from the corner. */
|
|
42
44
|
--dialog-inner-radius: var(--radius-lg);
|
|
43
45
|
|
|
46
|
+
/* Header room for a corner close. Unconditional baseline: without :has()
|
|
47
|
+
(Degraded tier) a header keeps too much room rather than a title running
|
|
48
|
+
under the close. Zeroed when the dialog has no close. */
|
|
49
|
+
--dialog-close-reserve: calc(
|
|
50
|
+
var(--close-size, var(--control-size)) +
|
|
51
|
+
var(--space-20) +
|
|
52
|
+
var(--space-30)
|
|
53
|
+
);
|
|
54
|
+
|
|
44
55
|
/* Keep the surface anchored to the viewport in the top layer. This also
|
|
45
56
|
provides the containing block for the absolutely positioned close button. */
|
|
46
57
|
position: fixed;
|
|
@@ -73,9 +84,9 @@
|
|
|
73
84
|
* both edges, which reads as horizontal movement.
|
|
74
85
|
*/
|
|
75
86
|
transform: translateY(0.35rem);
|
|
76
|
-
transition:
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
transition-property: opacity, transform;
|
|
88
|
+
transition-duration: var(--duration);
|
|
89
|
+
transition-timing-function: var(--ease-exit);
|
|
79
90
|
}
|
|
80
91
|
|
|
81
92
|
@media (forced-colors: active) {
|
|
@@ -99,10 +110,8 @@
|
|
|
99
110
|
padding: var(--surface-pad);
|
|
100
111
|
}
|
|
101
112
|
|
|
102
|
-
/*
|
|
103
|
-
|
|
104
|
-
the trailing cluster keeps it in flow instead of invoking the absolute
|
|
105
|
-
corner treatment from close.css. */
|
|
113
|
+
/* Dialog chrome beside the padded body wrapper. Wrapping the close in the
|
|
114
|
+
trailing cluster keeps it in flow, not the absolute corner overlay. */
|
|
106
115
|
:where(dialog).modal > header {
|
|
107
116
|
display: flex;
|
|
108
117
|
align-items: center;
|
|
@@ -112,7 +121,14 @@
|
|
|
112
121
|
padding: var(--modal-pad, var(--space-40));
|
|
113
122
|
border-start-start-radius: var(--dialog-inner-radius);
|
|
114
123
|
border-start-end-radius: var(--dialog-inner-radius);
|
|
115
|
-
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* Paint is not anatomy: the default band fill has zero specificity, so a
|
|
127
|
+
.background-* utility on the header replaces it. The layout above stays at
|
|
128
|
+
the component's weight — a composed .cluster must not reset the title/close
|
|
129
|
+
alignment. */
|
|
130
|
+
:where(dialog.modal > header) {
|
|
131
|
+
background: var(--surface-subtle);
|
|
116
132
|
}
|
|
117
133
|
|
|
118
134
|
:where(dialog).modal > header > .media {
|
|
@@ -133,18 +149,16 @@
|
|
|
133
149
|
flex: none;
|
|
134
150
|
}
|
|
135
151
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
152
|
+
/* The header stays fixed while the body wrapper owns overflow. Without :has()
|
|
153
|
+
these drop individually and compact dialogs keep ordinary block flow. */
|
|
154
|
+
:where(dialog).modal[open]:has(> header) {
|
|
155
|
+
display: grid;
|
|
156
|
+
grid-template-rows: auto minmax(0, 1fr);
|
|
157
|
+
}
|
|
143
158
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
159
|
+
:where(dialog).modal:has(> header) > :is(form, .stack) {
|
|
160
|
+
min-block-size: 0;
|
|
161
|
+
max-block-size: none;
|
|
148
162
|
}
|
|
149
163
|
|
|
150
164
|
@supports (block-size: 100svh) {
|
|
@@ -163,10 +177,21 @@
|
|
|
163
177
|
|
|
164
178
|
:where(dialog).modal.scrollable > form,
|
|
165
179
|
:where(dialog).modal.scrollable > .stack {
|
|
166
|
-
|
|
180
|
+
display: flex;
|
|
181
|
+
flex-direction: column;
|
|
167
182
|
overflow: hidden;
|
|
168
183
|
}
|
|
169
184
|
|
|
185
|
+
/* Regions carry their own behaviour: a structural header or footer keeps its
|
|
186
|
+
size, every other child absorbs the free space and scrolls. Nothing here has
|
|
187
|
+
to detect which regions exist — an external header leaves the wrapper with
|
|
188
|
+
content + footer and this still holds. Each non-chrome child is its own
|
|
189
|
+
scrollport; wrap the body in one region for a single one. */
|
|
190
|
+
:where(dialog).modal.scrollable > form > :is(header, footer),
|
|
191
|
+
:where(dialog).modal.scrollable > .stack > :is(header, footer) {
|
|
192
|
+
flex: none;
|
|
193
|
+
}
|
|
194
|
+
|
|
170
195
|
:where(dialog).modal.scrollable > form > :not(:is(header, footer)),
|
|
171
196
|
:where(dialog).modal.scrollable > .stack > :not(:is(header, footer)) {
|
|
172
197
|
--dialog-focus-bleed: calc(
|
|
@@ -174,6 +199,7 @@
|
|
|
174
199
|
max(var(--focus-ring-width), var(--border-width) * 2)
|
|
175
200
|
);
|
|
176
201
|
|
|
202
|
+
flex: 1 1 auto;
|
|
177
203
|
min-block-size: 0;
|
|
178
204
|
/* Keep focus paint inside the clip without moving the body off the fixed
|
|
179
205
|
header and footer edges. The reserve covers the thicker of the two
|
|
@@ -202,59 +228,30 @@
|
|
|
202
228
|
margin-block: 0;
|
|
203
229
|
}
|
|
204
230
|
|
|
231
|
+
/* No close anywhere: reclaim the reserve. Deliberately coarse — a close in
|
|
232
|
+
the body keeps the reserve, which costs only room, never overlap. */
|
|
233
|
+
:where(dialog).modal:not(:has(.close)) {
|
|
234
|
+
--dialog-close-reserve: 0px;
|
|
235
|
+
}
|
|
236
|
+
|
|
205
237
|
/* The close button is anchored to the dialog at top inline-end, out of the
|
|
206
|
-
content flow; headers reserve inline room so titles never run under it.
|
|
207
|
-
|
|
208
|
-
lends .stack's specificity to
|
|
238
|
+
content flow; headers reserve inline room so titles never run under it. A
|
|
239
|
+
.bleed header keeps at least its band inset. The form/.stack anatomy is
|
|
240
|
+
structural, so it stays in :where() and never lends .stack's specificity to
|
|
241
|
+
a bare <form>. */
|
|
209
242
|
:where(dialog).modal > :where(form, .stack) > header {
|
|
210
|
-
padding-inline-end:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
);
|
|
243
|
+
padding-inline-end: var(--dialog-close-reserve);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
:where(dialog).modal > :where(form, .stack) > header:where(.bleed) {
|
|
247
|
+
padding-inline-end: max(var(--bleed-pad, 0px), var(--dialog-close-reserve));
|
|
215
248
|
}
|
|
216
249
|
|
|
217
250
|
/* A corner .close that is a direct child of the chrome header needs the same
|
|
218
251
|
reserve, but never below the header's own pad. Kept as its own rule so an
|
|
219
252
|
unsupported :has() drops only this geometry, not the wrapper reserve. */
|
|
220
253
|
:where(dialog).modal > header:has(> .close) {
|
|
221
|
-
padding-inline-end: max(
|
|
222
|
-
var(--modal-pad, var(--space-40)),
|
|
223
|
-
calc(var(--close-size, var(--control-size)) + var(--space-20) + var(--space-30))
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
:where(dialog).modal > :where(form, .stack) > footer {
|
|
228
|
-
display: flex;
|
|
229
|
-
flex-wrap: wrap;
|
|
230
|
-
justify-content: flex-end;
|
|
231
|
-
gap: var(--gap);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/* Confirmation layout — a focused message followed by a visually distinct
|
|
235
|
-
action band. The media primitive owns icon/content alignment; this variant
|
|
236
|
-
only removes the standard form inset so both regions can own their padding. */
|
|
237
|
-
:where(dialog).modal.dialog-confirmation > form {
|
|
238
|
-
--surface-pad: 0px;
|
|
239
|
-
|
|
240
|
-
gap: 0;
|
|
241
|
-
padding: 0;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
:where(dialog).modal.dialog-confirmation > form > :is(.media, header) {
|
|
245
|
-
gap: var(--space-40);
|
|
246
|
-
padding: var(--space-50);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/* The action band splits a decision pair: the cancel-style action holds the
|
|
250
|
-
leading edge and the committing action the trailing one, so the destructive
|
|
251
|
-
choice is never adjacent to the dismiss. */
|
|
252
|
-
:where(dialog).modal.dialog-confirmation > form > footer {
|
|
253
|
-
justify-content: space-between;
|
|
254
|
-
padding: var(--space-40) var(--space-50);
|
|
255
|
-
border-end-start-radius: var(--dialog-inner-radius);
|
|
256
|
-
border-end-end-radius: var(--dialog-inner-radius);
|
|
257
|
-
background: var(--surface-subtle);
|
|
254
|
+
padding-inline-end: max(var(--modal-pad, var(--space-40)), var(--dialog-close-reserve));
|
|
258
255
|
}
|
|
259
256
|
|
|
260
257
|
/* Intent boundary — inherited intents do not tint the icon well. A local
|
|
@@ -267,14 +264,17 @@
|
|
|
267
264
|
--ui-fg: initial;
|
|
268
265
|
}
|
|
269
266
|
|
|
267
|
+
/* The size hooks are read here, never declared: an author sets them on the
|
|
268
|
+
icon's media row (or the dialog) so both the circle and the title that
|
|
269
|
+
centers on it inherit one value. A local declaration on .dialog-icon would
|
|
270
|
+
shadow that value for the circle while the title kept the inherited one —
|
|
271
|
+
the two centers would drift. An override on the icon alone is deliberately
|
|
272
|
+
local and moves only the circle. */
|
|
270
273
|
.dialog-icon {
|
|
271
|
-
--dialog-icon-size: 3rem;
|
|
272
|
-
--dialog-icon-glyph-size: 1.5rem;
|
|
273
|
-
|
|
274
274
|
display: inline-grid;
|
|
275
275
|
place-items: center;
|
|
276
|
-
inline-size: var(--dialog-icon-size);
|
|
277
|
-
block-size: var(--dialog-icon-size);
|
|
276
|
+
inline-size: var(--dialog-icon-size, 3rem);
|
|
277
|
+
block-size: var(--dialog-icon-size, 3rem);
|
|
278
278
|
flex: none;
|
|
279
279
|
border-radius: var(--radius-full);
|
|
280
280
|
background: var(--ui-bg, var(--surface-subtle));
|
|
@@ -284,46 +284,27 @@
|
|
|
284
284
|
.dialog-icon > * {
|
|
285
285
|
display: inline-grid;
|
|
286
286
|
place-items: center;
|
|
287
|
-
inline-size: var(--dialog-icon-glyph-size);
|
|
288
|
-
block-size: var(--dialog-icon-glyph-size);
|
|
289
|
-
font-size: var(--dialog-icon-glyph-size);
|
|
287
|
+
inline-size: var(--dialog-icon-glyph-size, 1.5rem);
|
|
288
|
+
block-size: var(--dialog-icon-glyph-size, 1.5rem);
|
|
289
|
+
font-size: var(--dialog-icon-glyph-size, 1.5rem);
|
|
290
290
|
line-height: 1;
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
:where(dialog).modal.dialog-confirmation > form > .media > header > * {
|
|
299
|
-
margin-block: 0;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/* The first heading occupies the icon row, so its text centers on the icon
|
|
303
|
-
without a font-relative nudge. Following copy stays in the same column. */
|
|
304
|
-
:where(dialog).modal.dialog-confirmation
|
|
305
|
-
> form
|
|
306
|
-
> .media
|
|
307
|
-
> header
|
|
308
|
-
> :is(h1, h2, h3, h4, h5, h6):first-child {
|
|
293
|
+
/* A leading icon well centers the first heading of the text column beside it:
|
|
294
|
+
the heading occupies the icon row, so its text centers without a
|
|
295
|
+
font-relative nudge, and following copy stays in the same column. Keyed on
|
|
296
|
+
the media anatomy, not on the dialog, so a card or page row composes it too. */
|
|
297
|
+
.media > .dialog-icon + * > :is(h1, h2, h3, h4, h5, h6):first-child {
|
|
309
298
|
display: flex;
|
|
310
299
|
align-items: center;
|
|
311
300
|
min-block-size: var(--dialog-icon-size, 3rem);
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
@media (max-width: 30rem) {
|
|
315
|
-
:where(dialog).modal.dialog-confirmation > form > :is(.media, header),
|
|
316
|
-
:where(dialog).modal.dialog-confirmation > form > footer {
|
|
317
|
-
padding: var(--space-40);
|
|
318
|
-
}
|
|
301
|
+
margin-block: 0;
|
|
319
302
|
}
|
|
320
303
|
|
|
321
304
|
:where(dialog).modal[open] {
|
|
322
305
|
opacity: 1;
|
|
323
306
|
transform: none;
|
|
324
|
-
transition:
|
|
325
|
-
opacity var(--duration) var(--ease-enter),
|
|
326
|
-
transform var(--duration) var(--ease-enter);
|
|
307
|
+
transition-timing-function: var(--ease-enter);
|
|
327
308
|
}
|
|
328
309
|
|
|
329
310
|
@starting-style {
|
|
@@ -338,31 +319,22 @@
|
|
|
338
319
|
opacity: 0;
|
|
339
320
|
/* Scrim curve stays neutral `ease`; only the panel carries the character
|
|
340
321
|
curves. */
|
|
341
|
-
transition: opacity
|
|
322
|
+
transition-property: opacity;
|
|
323
|
+
transition-duration: var(--duration);
|
|
324
|
+
transition-timing-function: ease;
|
|
342
325
|
}
|
|
343
326
|
|
|
344
327
|
@supports (transition-behavior: allow-discrete) {
|
|
328
|
+
/* Add the discrete top-layer pair to the property list. Duration and timing
|
|
329
|
+
stay on the base rules, so [open] still only swaps the easing. */
|
|
345
330
|
:where(dialog).modal {
|
|
346
|
-
transition:
|
|
347
|
-
|
|
348
|
-
transform var(--duration) var(--ease-exit),
|
|
349
|
-
overlay var(--duration) allow-discrete,
|
|
350
|
-
display var(--duration) allow-discrete;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
:where(dialog).modal[open] {
|
|
354
|
-
transition:
|
|
355
|
-
opacity var(--duration) var(--ease-enter),
|
|
356
|
-
transform var(--duration) var(--ease-enter),
|
|
357
|
-
overlay var(--duration) allow-discrete,
|
|
358
|
-
display var(--duration) allow-discrete;
|
|
331
|
+
transition-property: opacity, transform, overlay, display;
|
|
332
|
+
transition-behavior: allow-discrete;
|
|
359
333
|
}
|
|
360
334
|
|
|
361
335
|
:where(dialog).modal::backdrop {
|
|
362
|
-
transition:
|
|
363
|
-
|
|
364
|
-
overlay var(--duration) allow-discrete,
|
|
365
|
-
display var(--duration) allow-discrete;
|
|
336
|
+
transition-property: opacity, overlay, display;
|
|
337
|
+
transition-behavior: allow-discrete;
|
|
366
338
|
}
|
|
367
339
|
}
|
|
368
340
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Docs: docs/pages/components/navbar.md
|
|
5
5
|
*
|
|
6
|
-
* .navbar is a horizontal bar; .navbar-brand is the brand link;
|
|
7
|
-
*
|
|
6
|
+
* .navbar is a horizontal bar; .navbar-brand is the brand link; the inline
|
|
7
|
+
* links are a .cluster using .nav-link items. .nav-list is the vertical nav
|
|
8
8
|
* list shared with .drawer, fed by the shared --gap token.
|
|
9
9
|
*
|
|
10
10
|
* Internal:
|
|
@@ -53,17 +53,6 @@
|
|
|
53
53
|
border-radius: var(--radius-sm);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
.navbar-nav {
|
|
57
|
-
display: flex;
|
|
58
|
-
flex-direction: row;
|
|
59
|
-
flex-wrap: wrap;
|
|
60
|
-
align-items: center;
|
|
61
|
-
gap: var(--space-10);
|
|
62
|
-
margin: 0;
|
|
63
|
-
padding: 0;
|
|
64
|
-
list-style: none;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
56
|
.nav-link {
|
|
68
57
|
display: inline-flex;
|
|
69
58
|
align-items: center;
|
|
@@ -86,19 +75,16 @@
|
|
|
86
75
|
removed everywhere else, never serialized as aria-current="false".
|
|
87
76
|
Selection never changes text metrics: the shared medium weight is kept and
|
|
88
77
|
the current link is carried by the selected accent. */
|
|
89
|
-
.nav-link[aria-current] {
|
|
90
|
-
background: var(--surface-subtle);
|
|
91
|
-
color: var(--state-selected);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
78
|
.nav-link:hover {
|
|
95
79
|
background: var(--surface-subtle);
|
|
96
80
|
color: var(--text);
|
|
97
81
|
}
|
|
98
82
|
|
|
99
|
-
/*
|
|
100
|
-
|
|
101
|
-
|
|
83
|
+
/* Current last, equal specificity to :hover (0,2,0): a hovered current link
|
|
84
|
+
keeps the selected accent instead of dropping to --text. The surface fill is
|
|
85
|
+
shared with the hover rule above. */
|
|
86
|
+
.nav-link[aria-current] {
|
|
87
|
+
background: var(--surface-subtle);
|
|
102
88
|
color: var(--state-selected);
|
|
103
89
|
}
|
|
104
90
|
|
|
@@ -118,19 +104,22 @@
|
|
|
118
104
|
color: var(--ui-fg, var(--text));
|
|
119
105
|
}
|
|
120
106
|
|
|
121
|
-
.
|
|
122
|
-
.nav-link[aria-current]
|
|
107
|
+
/* After :hover so a hovered current link keeps the selected accent. */
|
|
108
|
+
.nav-link[aria-current] {
|
|
123
109
|
color: var(--ui-fg, var(--state-selected));
|
|
124
110
|
}
|
|
125
111
|
}
|
|
126
112
|
|
|
127
113
|
/* Nav list — a vertical navigation list. Used by .drawer for stacked links.
|
|
128
114
|
Links fill each row so the whole line is an interactive target; the
|
|
129
|
-
horizontal .
|
|
115
|
+
horizontal .cluster links keep their intrinsic size. */
|
|
116
|
+
/* A vertical nav reads as one list, not a stack of separate buttons: each row
|
|
117
|
+
is already a control-height target, so the rows sit a hair apart. The grid
|
|
118
|
+
is the component's own layout; for a horizontal row use .cluster instead. */
|
|
130
119
|
.nav-list {
|
|
131
120
|
display: grid;
|
|
132
121
|
grid-template-columns: minmax(0, 1fr);
|
|
133
|
-
--gap: var(--space-
|
|
122
|
+
--gap: var(--space-10);
|
|
134
123
|
gap: var(--gap);
|
|
135
124
|
margin: 0;
|
|
136
125
|
padding: 0;
|
package/src/css/core/base.css
CHANGED
package/src/css/core/reset.css
CHANGED
package/src/css/core/theme.css
CHANGED
|
@@ -79,6 +79,10 @@
|
|
|
79
79
|
fields, a solid band), which rules out the primary itself. */
|
|
80
80
|
--focus: hsl(268 30% 55%);
|
|
81
81
|
--hover-overlay: hsl(260 20% 10% / 0.04);
|
|
82
|
+
/* Solid fills take a stronger wash than the subtle shared overlay. Black in
|
|
83
|
+
both schemes on purpose: a solid button darkens on hover even in dark mode
|
|
84
|
+
(see themes/gradient.css), unlike the neutral washes. */
|
|
85
|
+
--hover-overlay-solid: rgb(0 0 0 / 0.14);
|
|
82
86
|
|
|
83
87
|
/* Radius scale — themes may override the whole scale (e.g. [data-theme="square"]) */
|
|
84
88
|
--radius: 0.5rem;
|
package/src/css/core/tokens.css
CHANGED
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
--font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
21
21
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
22
22
|
--font-width: 100%;
|
|
23
|
-
--font-width-dense: 95%;
|
|
24
23
|
--line-height: 1.5;
|
|
25
24
|
--line-height-tight: 1.25;
|
|
26
25
|
--line-height-relaxed: 1.75;
|
|
@@ -104,8 +103,9 @@
|
|
|
104
103
|
--bar-height: 0.65rem;
|
|
105
104
|
|
|
106
105
|
/* Controls — inputs and buttons share --control-size so heights always match.
|
|
107
|
-
.compact/.spacious (variants.css) switch geometry
|
|
108
|
-
--control-font-size. Local .sm/.lg
|
|
106
|
+
.compact/.spacious (variants.css) switch geometry (--control-size,
|
|
107
|
+
--control-pad-x) without changing --control-font-size. Local .sm/.lg
|
|
108
|
+
component sizes switch all three.
|
|
109
109
|
|
|
110
110
|
Inline choices (checkbox, radio, switch) are typographic, not field-sized:
|
|
111
111
|
they sit on a line of text, so they follow --control-font-size through
|
|
@@ -116,17 +116,35 @@
|
|
|
116
116
|
|
|
117
117
|
--control-pad-x is the opposite kind of token: a resolved geometry length,
|
|
118
118
|
not an em ratio. Every part of one control must see the same horizontal
|
|
119
|
-
distance, so it is stated in
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
distance, so it is stated in lengths rather than an em whose value would
|
|
120
|
+
change on a descendant with its own font size (the input-icon glyph, made
|
|
121
|
+
larger by --input-icon-size).
|
|
122
|
+
|
|
123
|
+
Like --control-size, it is a scale plus a selection: themes set the
|
|
124
|
+
-sm/-md/-lg steps, and :root, .sm/.lg and .compact/.spacious (variants.css)
|
|
125
|
+
pick one. A theme that wrote --control-pad-x itself would be overwritten
|
|
126
|
+
by every size and density boundary — bootstrap-v6's 12px became a wider
|
|
127
|
+
13px inside .compact. The selection is rebound at each boundary because a
|
|
128
|
+
var() inside a custom property resolves where it is declared.
|
|
129
|
+
|
|
130
|
+
The boundaries also write --control-step-sm/-md/-lg: which step is
|
|
131
|
+
selected, as 0/1 weights that mean the same under every theme. A theme
|
|
132
|
+
that sets its own steps rebuilds the selection from them in its own block
|
|
133
|
+
(bootstrap-v6.css), so a nested island of it reads its steps without
|
|
134
|
+
resetting the density it sits in. Never rebind on every [data-theme]: a
|
|
135
|
+
palette-only island then became a geometry boundary that an inherited
|
|
136
|
+
--control-size or --control-pad-x no longer crossed. */
|
|
125
137
|
--control-size-sm: 2rem;
|
|
126
138
|
--control-size-md: 2.375rem;
|
|
127
139
|
--control-size-lg: 2.75rem;
|
|
128
140
|
--control-size: var(--control-size-md);
|
|
129
|
-
--control-pad-x: var(--font-size-
|
|
141
|
+
--control-pad-x-sm: var(--font-size-xs);
|
|
142
|
+
--control-pad-x-md: var(--font-size-md);
|
|
143
|
+
--control-pad-x-lg: var(--font-size-lg);
|
|
144
|
+
--control-pad-x: var(--control-pad-x-md);
|
|
145
|
+
--control-step-sm: 0;
|
|
146
|
+
--control-step-md: 1;
|
|
147
|
+
--control-step-lg: 0;
|
|
130
148
|
--control-font-size: var(--font-size-md);
|
|
131
149
|
--choice-control-size: 1.125em;
|
|
132
150
|
--disabled-opacity: 0.65;
|