actual-css 0.7.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 +166 -0
- package/README.md +13 -11
- package/components.json +668 -0
- package/dist/actual.css +166 -37
- package/dist/actual.full.css +963 -603
- 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 +62 -30
- 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 +53 -7
- package/docs/design-notes/popover-manual-poc.md +105 -25
- package/docs/design-notes/surface-reparenting.md +56 -47
- package/docs/design-notes/tooltip-coordinate-space.md +132 -0
- package/docs/design-notes/widget-primitives.md +7 -4
- 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 +66 -36
- package/docs/pages/enhancements/overview.md +25 -0
- package/docs/pages/enhancements/status-bar.md +1 -1
- package/docs/pages/enhancements/tabs.md +45 -37
- package/docs/pages/enhancements/tooltip.md +36 -10
- 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 +57 -26
- package/docs/pages/foundations/theming.md +2 -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 +115 -30
- 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 +17 -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 +35 -98
- package/src/css/components/index.css +0 -1
- package/src/css/components/indicator.css +21 -0
- package/src/css/components/key.css +10 -0
- package/src/css/components/list.css +40 -4
- package/src/css/components/menu.css +2 -1
- 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 +36 -40
- 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 +6 -5
- package/src/js/surface.js +97 -139
- package/src/js/tab.js +6 -1
- package/src/js/tooltip.js +150 -51
- package/src/js/validation.js +1 -1
- package/src/css/components/surface.css +0 -49
|
@@ -62,8 +62,18 @@ dialog.drawer[open] {
|
|
|
62
62
|
transform var(--drawer-motion-duration) var(--ease-enter);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
/*
|
|
66
|
-
|
|
65
|
+
/* A form is the drawer's content wrapper when actions need method="dialog".
|
|
66
|
+
Span the drawer tracks so the form can keep the header at the top, let the
|
|
67
|
+
body absorb the available height, and keep the footer at the bottom. */
|
|
68
|
+
dialog.drawer > form {
|
|
69
|
+
display: grid;
|
|
70
|
+
grid-row: 1 / -1;
|
|
71
|
+
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
72
|
+
gap: var(--space-40);
|
|
73
|
+
min-block-size: 100%;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* The shared backdrop token keeps drawer and modal scrims consistent. */
|
|
67
77
|
dialog.drawer::backdrop {
|
|
68
78
|
background: var(--backdrop-fill);
|
|
69
79
|
opacity: 0;
|
|
@@ -128,33 +138,33 @@ dialog.drawer nav {
|
|
|
128
138
|
}
|
|
129
139
|
|
|
130
140
|
/* Safari before 16.4 does not support :dir(); keep the ancestor-attribute
|
|
131
|
-
path as the
|
|
141
|
+
path as the Degraded-tier fallback. The :dir() rules are dropped on their
|
|
142
|
+
own there, so they need no wrapper. :dir() follows inherited HTML
|
|
143
|
+
directionality and handles nested direction changes; the explicit ltr rules
|
|
144
|
+
neutralize an outer RTL fallback selector. Each specificity group keeps the
|
|
145
|
+
fallback before the :dir() rules. */
|
|
132
146
|
[dir="rtl"] dialog.drawer {
|
|
133
147
|
--drawer-closed-translate: 100%;
|
|
134
148
|
}
|
|
135
149
|
|
|
136
|
-
|
|
150
|
+
dialog.drawer:dir(ltr) {
|
|
137
151
|
--drawer-closed-translate: -100%;
|
|
138
152
|
}
|
|
139
153
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
dialog.drawer:dir(ltr) {
|
|
144
|
-
--drawer-closed-translate: -100%;
|
|
145
|
-
}
|
|
154
|
+
dialog.drawer:dir(rtl) {
|
|
155
|
+
--drawer-closed-translate: 100%;
|
|
156
|
+
}
|
|
146
157
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
158
|
+
[dir="rtl"] dialog.drawer[data-side="end"] {
|
|
159
|
+
--drawer-closed-translate: -100%;
|
|
160
|
+
}
|
|
150
161
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
162
|
+
dialog.drawer[data-side="end"]:dir(ltr) {
|
|
163
|
+
--drawer-closed-translate: 100%;
|
|
164
|
+
}
|
|
154
165
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
166
|
+
dialog.drawer[data-side="end"]:dir(rtl) {
|
|
167
|
+
--drawer-closed-translate: -100%;
|
|
158
168
|
}
|
|
159
169
|
|
|
160
170
|
@media (prefers-reduced-motion: no-preference) {
|
|
@@ -163,15 +173,59 @@ dialog.drawer nav {
|
|
|
163
173
|
}
|
|
164
174
|
}
|
|
165
175
|
|
|
166
|
-
/* The
|
|
167
|
-
|
|
168
|
-
inline room so the title never runs under it. */
|
|
176
|
+
/* The header reserves inline room so the title never runs under the close
|
|
177
|
+
control. */
|
|
169
178
|
dialog.drawer > header,
|
|
170
179
|
dialog.drawer > form > header {
|
|
171
180
|
padding-inline-end: calc(var(--control-size) + var(--space-30));
|
|
172
181
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
182
|
+
|
|
183
|
+
/* Close control, owned here so a drawer-only build does not depend on
|
|
184
|
+
modal.css. Mirrors the modal's .dialog-close placement: absolute at the
|
|
185
|
+
panel's top inline-end, out of the content flow. */
|
|
186
|
+
.drawer-close {
|
|
187
|
+
--drawer-close-icon-size: 1rem;
|
|
188
|
+
|
|
189
|
+
display: inline-grid;
|
|
190
|
+
place-items: center;
|
|
191
|
+
padding: 0;
|
|
192
|
+
border: 0;
|
|
193
|
+
border-radius: var(--radius);
|
|
194
|
+
color: var(--text-muted);
|
|
195
|
+
cursor: pointer;
|
|
196
|
+
|
|
197
|
+
position: absolute;
|
|
198
|
+
z-index: 1;
|
|
199
|
+
inset-block-start: var(--space-20);
|
|
200
|
+
inset-inline-end: var(--space-20);
|
|
201
|
+
|
|
202
|
+
inline-size: var(--control-size);
|
|
203
|
+
block-size: var(--control-size);
|
|
204
|
+
|
|
205
|
+
background: var(--surface-subtle);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.drawer-close::before {
|
|
209
|
+
content: "";
|
|
210
|
+
inline-size: var(--drawer-close-icon-size);
|
|
211
|
+
block-size: var(--drawer-close-icon-size);
|
|
212
|
+
background: currentColor;
|
|
213
|
+
-webkit-mask: var(--icon-close) center / contain no-repeat;
|
|
214
|
+
mask: var(--icon-close) center / contain no-repeat;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
@media (hover: hover) {
|
|
218
|
+
.drawer-close:hover {
|
|
219
|
+
box-shadow: inset 0 0 0 999px var(--hover-overlay);
|
|
220
|
+
color: var(--text);
|
|
176
221
|
}
|
|
177
222
|
}
|
|
223
|
+
|
|
224
|
+
.drawer-close:active {
|
|
225
|
+
box-shadow: inset 0 0 0 999px var(--hover-overlay);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.drawer-close:focus-visible {
|
|
229
|
+
outline: 2px solid transparent;
|
|
230
|
+
box-shadow: var(--focus-ring-shadow);
|
|
231
|
+
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Flyout — floating panel for actions and links.
|
|
3
3
|
*
|
|
4
|
-
* JavaScript
|
|
5
|
-
*
|
|
6
|
-
* panel appearance, item styles,
|
|
4
|
+
* JavaScript promotes the panel to the top layer and chooses viewport or
|
|
5
|
+
* document coordinates from its anchor. It also owns state (.is-open), focus,
|
|
6
|
+
* and keyboard behavior. CSS owns the surfaced panel appearance, item styles,
|
|
7
|
+
* and animation.
|
|
7
8
|
*
|
|
8
|
-
* Runtime
|
|
9
|
-
* .is-sheet marks "auto" resolved to bottom-sheet presentation on coarse
|
|
10
|
-
* pointers. Authors opt into mobile behavior with
|
|
11
|
-
* [data-flyout-mobile="sheet|auto|none|anchored"].
|
|
9
|
+
* Runtime marker (managed by surface.js): .is-open marks an open flyout.
|
|
12
10
|
* --surface-anchor-width is set by surface.js while an open flyout is positioned.
|
|
13
11
|
*
|
|
14
12
|
* Public hooks:
|
|
@@ -17,7 +15,7 @@
|
|
|
17
15
|
*
|
|
18
16
|
* Internal:
|
|
19
17
|
* --menu-item-size (derived from --control-size for menu rows)
|
|
20
|
-
* --available-height (measured by
|
|
18
|
+
* --available-height (measured by the positioner)
|
|
21
19
|
* --surface-anchor-width (measured by surface.js)
|
|
22
20
|
*/
|
|
23
21
|
|
|
@@ -44,8 +42,8 @@
|
|
|
44
42
|
--menu-item-size: var(--control-size);
|
|
45
43
|
z-index: var(--z-menu);
|
|
46
44
|
/* No-JS fallback: below the trigger inside the relative .flyout-trigger.
|
|
47
|
-
surface.js
|
|
48
|
-
|
|
45
|
+
surface.js replaces this with the anchor's coupled position/coordinate
|
|
46
|
+
mode; this only covers a flyout revealed without the runtime. */
|
|
49
47
|
position: absolute;
|
|
50
48
|
inset-block-start: calc(100% + var(--space-10));
|
|
51
49
|
inset-inline-start: 0;
|
|
@@ -73,101 +71,53 @@
|
|
|
73
71
|
box-shadow: var(--shadow-popout);
|
|
74
72
|
list-style: none;
|
|
75
73
|
opacity: 1;
|
|
76
|
-
/*
|
|
77
|
-
|
|
78
|
-
below adds its own translate motion. */
|
|
74
|
+
/* Flyouts fade in and out; scaling around the center would detach the
|
|
75
|
+
anchored edge from its trigger on wide panels. */
|
|
79
76
|
transition: opacity var(--duration-fast) ease;
|
|
80
77
|
}
|
|
81
78
|
|
|
82
|
-
/*
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
reset would strand a permanently visible panel at inset: auto. Popover is
|
|
87
|
-
not part of Actual's Minimal contract — engines inside the Minimal range
|
|
88
|
-
may lack it — so the gate is about the capability, not the floor. Kept
|
|
89
|
-
scoped to Actual surfaces so bare [popover] elements retain their platform
|
|
79
|
+
/* A popover enters the top layer with UA geometry that conflicts with anchored
|
|
80
|
+
positioning. surface.js sets popover="manual" on every surface it manages,
|
|
81
|
+
so this is the transport's own geometry rather than an opt-in. Kept scoped to
|
|
82
|
+
Actual surfaces so bare [popover] elements retain their platform
|
|
90
83
|
presentation. */
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
84
|
+
.flyout[popover] {
|
|
85
|
+
position: fixed;
|
|
86
|
+
inset: auto;
|
|
87
|
+
margin: 0;
|
|
88
|
+
}
|
|
97
89
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
display: none;
|
|
109
|
-
opacity: 0;
|
|
110
|
-
}
|
|
90
|
+
/* A presentation class may own its *visible* display type, but must not
|
|
91
|
+
override the platform's hidden state. The UA closes a popover with
|
|
92
|
+
display: none, and an author declaration beats the UA origin whatever the
|
|
93
|
+
specificity — so .flyout's own display: grid would leave a closed popover
|
|
94
|
+
painted at its static position. The opacity is the exit target: with nothing
|
|
95
|
+
to animate, the overlay/display allow-discrete transition below would hold a
|
|
96
|
+
fully opaque panel in the top layer and then snap it away. */
|
|
97
|
+
.flyout[popover]:not(:popover-open) {
|
|
98
|
+
display: none;
|
|
99
|
+
opacity: 0;
|
|
111
100
|
}
|
|
112
101
|
|
|
113
|
-
/*
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
a plain selector list an unsupported member would invalidate the whole rule.
|
|
117
|
-
Keep it inside :is()/:where() or gate it. */
|
|
118
|
-
.flyout:is(.is-open, :popover-open):not(.is-sheet) {
|
|
102
|
+
/* Both lifecycles in one rule: .is-open for a surface.js panel, :popover-open
|
|
103
|
+
for one an author drives natively with popovertarget. */
|
|
104
|
+
.flyout:is(.is-open, :popover-open) {
|
|
119
105
|
inset: auto;
|
|
120
106
|
}
|
|
121
107
|
|
|
122
|
-
/*
|
|
123
|
-
|
|
108
|
+
/* @starting-style is above the floor (Safari 17.5), so where it is missing the
|
|
109
|
+
entry simply has no animated origin and the panel appears at full opacity. */
|
|
124
110
|
@starting-style {
|
|
125
111
|
.flyout:is(.is-open, :popover-open) {
|
|
126
112
|
opacity: 0;
|
|
127
113
|
}
|
|
128
114
|
}
|
|
129
115
|
|
|
130
|
-
/* Bottom-sheet presentation. surface.js toggles .is-sheet when the flyout is
|
|
131
|
-
opened on coarse pointer / narrow viewports. */
|
|
132
|
-
.flyout.is-sheet {
|
|
133
|
-
--menu-item-size: var(--control-size-lg);
|
|
134
|
-
position: fixed;
|
|
135
|
-
inset-inline: 0;
|
|
136
|
-
inset-block-start: auto;
|
|
137
|
-
inset-block-end: 0;
|
|
138
|
-
inline-size: auto;
|
|
139
|
-
min-inline-size: 0;
|
|
140
|
-
max-inline-size: none;
|
|
141
|
-
max-block-size: min(calc(var(--viewport-block) * 0.75), 32rem);
|
|
142
|
-
overflow: auto;
|
|
143
|
-
grid-template-columns: 1fr;
|
|
144
|
-
padding: max(var(--space-30), env(safe-area-inset-top)) var(--space-30)
|
|
145
|
-
max(var(--space-30), env(safe-area-inset-bottom));
|
|
146
|
-
border-end-start-radius: 0;
|
|
147
|
-
border-end-end-radius: 0;
|
|
148
|
-
/* Sheet motion is a directional slide from the viewport edge; it replaces
|
|
149
|
-
the base fade-only recipe. */
|
|
150
|
-
transition:
|
|
151
|
-
opacity var(--duration-fast) ease,
|
|
152
|
-
translate var(--duration-fast) ease;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
116
|
.flyout[hidden] {
|
|
156
117
|
display: none;
|
|
157
118
|
opacity: 0;
|
|
158
119
|
}
|
|
159
120
|
|
|
160
|
-
.flyout.is-sheet[hidden] {
|
|
161
|
-
translate: 0 var(--space-30);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/* compat-ok: forgiving :is() inside @starting-style, as above. */
|
|
165
|
-
@starting-style {
|
|
166
|
-
.flyout.is-sheet:is(.is-open, :popover-open) {
|
|
167
|
-
translate: 0 var(--space-30);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
121
|
/* display keeps the exit transition alive for the [hidden] path; overlay does
|
|
172
122
|
the same for the native popover path, where hidePopover() would otherwise
|
|
173
123
|
drop the panel out of the top layer on the first frame and let a parent
|
|
@@ -180,14 +130,6 @@
|
|
|
180
130
|
overlay var(--duration-fast) allow-discrete,
|
|
181
131
|
display var(--duration-fast) allow-discrete;
|
|
182
132
|
}
|
|
183
|
-
|
|
184
|
-
.flyout.is-sheet {
|
|
185
|
-
transition:
|
|
186
|
-
opacity var(--duration-fast) ease,
|
|
187
|
-
translate var(--duration-fast) ease,
|
|
188
|
-
overlay var(--duration-fast) allow-discrete,
|
|
189
|
-
display var(--duration-fast) allow-discrete;
|
|
190
|
-
}
|
|
191
133
|
}
|
|
192
134
|
|
|
193
135
|
.flyout > li {
|
|
@@ -203,7 +145,7 @@
|
|
|
203
145
|
margin: 0;
|
|
204
146
|
color: var(--text-muted);
|
|
205
147
|
font-size: var(--font-size-xs);
|
|
206
|
-
font-weight: var(--font-weight-
|
|
148
|
+
font-weight: var(--font-weight-semibold);
|
|
207
149
|
}
|
|
208
150
|
|
|
209
151
|
.flyout > section > ul {
|
|
@@ -221,8 +163,3 @@
|
|
|
221
163
|
/* Vertical dividers — .menu-separator (menu.css) is the canonical way to
|
|
222
164
|
separate menu items. Flyout panels that are not menus use auto grid gap
|
|
223
165
|
declared on .flyout itself. */
|
|
224
|
-
@media print {
|
|
225
|
-
.flyout {
|
|
226
|
-
display: none !important;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
@@ -38,10 +38,31 @@
|
|
|
38
38
|
inset-block-end: 0;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/* Safari before 16.4 has no :dir(); the ancestor-attribute path is the
|
|
42
|
+
Degraded-tier fallback and the :dir() rules are dropped on their own there.
|
|
43
|
+
:dir() follows inherited directionality, so the explicit ltr rules
|
|
44
|
+
neutralize an outer RTL fallback for a nested direction change. The rules
|
|
45
|
+
stay grouped by specificity so the fallback never follows the :dir() rules. */
|
|
46
|
+
[dir="rtl"] .indicator-item {
|
|
47
|
+
--indicator-translate-x: -50%;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.indicator-item:dir(ltr) {
|
|
51
|
+
--indicator-translate-x: 50%;
|
|
52
|
+
}
|
|
53
|
+
|
|
41
54
|
.indicator-item:dir(rtl) {
|
|
42
55
|
--indicator-translate-x: -50%;
|
|
43
56
|
}
|
|
44
57
|
|
|
58
|
+
[dir="rtl"] .indicator-item.start {
|
|
59
|
+
--indicator-translate-x: 50%;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.indicator-item.start:dir(ltr) {
|
|
63
|
+
--indicator-translate-x: -50%;
|
|
64
|
+
}
|
|
65
|
+
|
|
45
66
|
.indicator-item.start:dir(rtl) {
|
|
46
67
|
--indicator-translate-x: 50%;
|
|
47
68
|
}
|
|
@@ -27,3 +27,13 @@
|
|
|
27
27
|
vertical-align: 0.08em;
|
|
28
28
|
}
|
|
29
29
|
/* @sync key-recipe */
|
|
30
|
+
|
|
31
|
+
/* .key has a compact typographic default of its own, so its local size roles
|
|
32
|
+
step around that default instead of inheriting the generic mapping. */
|
|
33
|
+
.key.sm {
|
|
34
|
+
font-size: var(--font-size-xs);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.key.lg {
|
|
38
|
+
font-size: var(--font-size-md);
|
|
39
|
+
}
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
--list-divider: var(--border-width) solid var(--border);
|
|
19
19
|
|
|
20
20
|
display: grid;
|
|
21
|
+
grid-template-columns: minmax(0, 1fr);
|
|
21
22
|
gap: 0;
|
|
22
23
|
margin: 0;
|
|
23
24
|
padding: 0;
|
|
@@ -29,10 +30,17 @@
|
|
|
29
30
|
margin: 0;
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
/*
|
|
34
|
+
* Flex, not a three-track grid: the leading and trailing regions are optional,
|
|
35
|
+
* and a fixed `auto minmax(0, 1fr) auto` template gives an absent region a
|
|
36
|
+
* track anyway — the content of a row with no leading region lands in the
|
|
37
|
+
* leading track and the trailing control inherits the flexible one, which is
|
|
38
|
+
* the opposite of the intent. Flex drops both the track and its gap when a
|
|
39
|
+
* region is not there, so all four shapes work off one rule.
|
|
40
|
+
*/
|
|
32
41
|
.list > :where(.list-item),
|
|
33
42
|
.list > li > .list-item {
|
|
34
|
-
display:
|
|
35
|
-
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
43
|
+
display: flex;
|
|
36
44
|
/* Regions align to the first line, so leading, title, and trailing controls
|
|
37
45
|
share one horizontal line while supporting text wraps beneath. */
|
|
38
46
|
align-items: start;
|
|
@@ -48,6 +56,7 @@
|
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
a.list-item {
|
|
59
|
+
position: relative;
|
|
51
60
|
color: inherit;
|
|
52
61
|
text-decoration: none;
|
|
53
62
|
}
|
|
@@ -56,7 +65,31 @@ a.list-item:hover {
|
|
|
56
65
|
background: var(--surface-subtle);
|
|
57
66
|
}
|
|
58
67
|
|
|
68
|
+
/* Current row in a navigational list (master/detail). Generic by design: the
|
|
69
|
+
visual treatment only cares that the row is current, so presence is the
|
|
70
|
+
contract — the attribute is set on the current row and removed everywhere
|
|
71
|
+
else, never serialized as aria-current="false". The value (page, location,
|
|
72
|
+
…) stays free for assistive technology.
|
|
73
|
+
Calm by design: the row keeps its normal text metrics and color on a subtle
|
|
74
|
+
surface, and the selected accent lives on an inset inline-start trait only.
|
|
75
|
+
The trait is a real border, so it survives forced colors without a repaint
|
|
76
|
+
block. The positioning context lives on every row link, so becoming current
|
|
77
|
+
never moves an app-positioned decoration to a new containing block. */
|
|
78
|
+
a.list-item[aria-current] {
|
|
79
|
+
background: var(--surface-subtle);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
a.list-item[aria-current]::before {
|
|
83
|
+
content: "";
|
|
84
|
+
position: absolute;
|
|
85
|
+
inset-block: var(--list-item-pad-block);
|
|
86
|
+
inset-inline-start: 0;
|
|
87
|
+
border-inline-start: 2px solid var(--state-selected);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* The content region is the one that flexes, whichever position it holds. */
|
|
59
91
|
.list-item-content {
|
|
92
|
+
flex: 1 1 0;
|
|
60
93
|
min-inline-size: 0;
|
|
61
94
|
}
|
|
62
95
|
|
|
@@ -64,16 +97,19 @@ a.list-item:hover {
|
|
|
64
97
|
.list-item-text {
|
|
65
98
|
display: block;
|
|
66
99
|
margin: 0;
|
|
100
|
+
overflow-wrap: anywhere;
|
|
67
101
|
}
|
|
68
102
|
|
|
69
103
|
.list-item-text {
|
|
70
104
|
color: var(--text-muted);
|
|
71
105
|
font-size: var(--font-size-sm);
|
|
72
|
-
overflow-wrap: anywhere;
|
|
73
106
|
}
|
|
74
107
|
|
|
108
|
+
/* The auto margin keeps the trailing region at the row's end even in a row
|
|
109
|
+
with no content region to absorb the free space. */
|
|
75
110
|
.list-item-trailing {
|
|
76
|
-
|
|
111
|
+
flex: none;
|
|
112
|
+
margin-inline-start: auto;
|
|
77
113
|
min-inline-size: 0;
|
|
78
114
|
}
|
|
79
115
|
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
background: transparent;
|
|
45
45
|
color: var(--intent, var(--text));
|
|
46
46
|
font: inherit;
|
|
47
|
+
font-size: var(--control-font-size);
|
|
47
48
|
text-align: start;
|
|
48
49
|
text-decoration: none;
|
|
49
50
|
white-space: nowrap;
|
|
@@ -124,7 +125,7 @@
|
|
|
124
125
|
padding: var(--space-20);
|
|
125
126
|
color: var(--text-muted);
|
|
126
127
|
font-size: var(--font-size-xs);
|
|
127
|
-
font-weight: var(--font-weight-
|
|
128
|
+
font-weight: var(--font-weight-semibold);
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
.menu-separator {
|
|
@@ -12,21 +12,40 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Internal:
|
|
14
14
|
* --dialog-close-icon-size
|
|
15
|
+
* --dialog-viewport-block
|
|
16
|
+
* --dialog-viewport-gap-default
|
|
15
17
|
* --dialog-viewport-gap-2x
|
|
16
18
|
*/
|
|
17
19
|
dialog.modal {
|
|
18
|
-
--
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
/* --dialog-viewport-gap stays fallback-only for the same reason as
|
|
21
|
+
--modal-size below: declaring the public hook here would give it
|
|
22
|
+
type+class specificity that a single author class cannot outrank. The
|
|
23
|
+
responsive default lives in an internal token instead, so a media query
|
|
24
|
+
can still move it without closing the hook.
|
|
25
|
+
|
|
26
|
+
calc() multiplication is not in the supported browser range, so the
|
|
21
27
|
doubled gap is spelled out with an additive calc. */
|
|
22
|
-
--dialog-viewport-gap-
|
|
28
|
+
--dialog-viewport-gap-default: var(--space-40);
|
|
29
|
+
--dialog-viewport-gap-2x: calc(
|
|
30
|
+
var(--dialog-viewport-gap, var(--dialog-viewport-gap-default)) +
|
|
31
|
+
var(--dialog-viewport-gap, var(--dialog-viewport-gap-default))
|
|
32
|
+
);
|
|
23
33
|
|
|
24
34
|
/* Keep the surface anchored to the viewport in the top layer. This also
|
|
25
35
|
provides the containing block for the absolutely positioned close button. */
|
|
26
36
|
position: fixed;
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
/* --modal-size stays fallback-only: declaring it on dialog.modal gives it
|
|
39
|
+
type+class specificity, which a single author class cannot outrank, so the
|
|
40
|
+
public hook would silently keep this default. Same reason choice.css never
|
|
41
|
+
declares --choice-control-offset. */
|
|
42
|
+
max-inline-size: min(calc(100% - var(--dialog-viewport-gap-2x)), var(--modal-size, 32rem));
|
|
43
|
+
|
|
44
|
+
/* Stable viewport cap for the panel: dvh tracks the dynamic browser toolbar
|
|
45
|
+
and makes the dialog reflow on mobile, svh does not. App shells keep the
|
|
46
|
+
dynamic token; only the modal pins the small viewport. */
|
|
47
|
+
--dialog-viewport-block: var(--viewport-block);
|
|
48
|
+
max-block-size: calc(var(--dialog-viewport-block) - var(--dialog-viewport-gap-2x));
|
|
30
49
|
border: var(--border-width) solid var(--border);
|
|
31
50
|
border-radius: var(--radius-lg);
|
|
32
51
|
background: var(--surface-raised);
|
|
@@ -36,7 +55,7 @@ dialog.modal {
|
|
|
36
55
|
overflow: visible;
|
|
37
56
|
opacity: 0;
|
|
38
57
|
/*
|
|
39
|
-
* Keep the open dialog root untransformed. Fixed overlays
|
|
58
|
+
* Keep the open dialog root untransformed. Fixed overlays authored inside a
|
|
40
59
|
* modal dialog use viewport coordinates; transform/translate/scale on this
|
|
41
60
|
* element would make them position against the dialog instead. The closed
|
|
42
61
|
* state can still carry a small transform for enter/exit motion. The lift
|
|
@@ -53,16 +72,23 @@ dialog.modal > form,
|
|
|
53
72
|
dialog.modal > .stack {
|
|
54
73
|
display: grid;
|
|
55
74
|
gap: var(--space-40);
|
|
56
|
-
max-block-size: calc(var(--viewport-block) - var(--dialog-viewport-gap-2x));
|
|
75
|
+
max-block-size: calc(var(--dialog-viewport-block) - var(--dialog-viewport-gap-2x));
|
|
57
76
|
overflow: auto;
|
|
58
77
|
overscroll-behavior: contain;
|
|
59
78
|
padding: var(--space-40);
|
|
60
79
|
}
|
|
61
80
|
|
|
62
|
-
|
|
81
|
+
@supports (block-size: 100svh) {
|
|
82
|
+
dialog.modal {
|
|
83
|
+
--dialog-viewport-block: 100svh;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Keep the dialog off the viewport edges on small screens. Moves the internal
|
|
88
|
+
default, not the public hook, so an author override still wins here too. */
|
|
63
89
|
@media (max-width: 30rem) {
|
|
64
90
|
dialog.modal {
|
|
65
|
-
--dialog-viewport-gap: var(--space-30);
|
|
91
|
+
--dialog-viewport-gap-default: var(--space-30);
|
|
66
92
|
}
|
|
67
93
|
}
|
|
68
94
|
|
|
@@ -72,8 +98,8 @@ dialog.modal.scrollable > .stack {
|
|
|
72
98
|
overflow: hidden;
|
|
73
99
|
}
|
|
74
100
|
|
|
75
|
-
dialog.modal.scrollable > form > :not(header, footer),
|
|
76
|
-
dialog.modal.scrollable > .stack > :not(header, footer) {
|
|
101
|
+
dialog.modal.scrollable > form > :not(:is(header, footer)),
|
|
102
|
+
dialog.modal.scrollable > .stack > :not(:is(header, footer)) {
|
|
77
103
|
min-block-size: 0;
|
|
78
104
|
overflow: auto;
|
|
79
105
|
overscroll-behavior: contain;
|
|
@@ -128,6 +154,7 @@ dialog.modal.dialog-confirmation > form > footer {
|
|
|
128
154
|
:where(.dialog-icon) {
|
|
129
155
|
--intent: initial;
|
|
130
156
|
--intent-fg: initial;
|
|
157
|
+
--intent-soft-fg: initial;
|
|
131
158
|
--ui-bg: initial;
|
|
132
159
|
--ui-fg: initial;
|
|
133
160
|
}
|
|
@@ -256,14 +283,11 @@ html.has-modal-open.had-scrollbar {
|
|
|
256
283
|
}
|
|
257
284
|
}
|
|
258
285
|
|
|
259
|
-
/* Close
|
|
260
|
-
never participates in the content flow
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
required in the markup. */
|
|
265
|
-
.dialog-close,
|
|
266
|
-
.drawer-close {
|
|
286
|
+
/* Close control. A close button is an action attached to a surface, so it
|
|
287
|
+
never participates in the content flow — absolute at the panel's top
|
|
288
|
+
inline-end, inside the surface. The glyph comes from --icon-close, masked to
|
|
289
|
+
currentColor, so no icon font or text glyph is required in the markup. */
|
|
290
|
+
.dialog-close {
|
|
267
291
|
--dialog-close-icon-size: 1rem;
|
|
268
292
|
|
|
269
293
|
display: inline-grid;
|
|
@@ -287,8 +311,7 @@ html.has-modal-open.had-scrollbar {
|
|
|
287
311
|
background: var(--surface-subtle);
|
|
288
312
|
}
|
|
289
313
|
|
|
290
|
-
.dialog-close::before
|
|
291
|
-
.drawer-close::before {
|
|
314
|
+
.dialog-close::before {
|
|
292
315
|
content: "";
|
|
293
316
|
inline-size: var(--dialog-close-icon-size);
|
|
294
317
|
block-size: var(--dialog-close-icon-size);
|
|
@@ -298,25 +321,17 @@ html.has-modal-open.had-scrollbar {
|
|
|
298
321
|
}
|
|
299
322
|
|
|
300
323
|
@media (hover: hover) {
|
|
301
|
-
.dialog-close:hover
|
|
302
|
-
.drawer-close:hover {
|
|
324
|
+
.dialog-close:hover {
|
|
303
325
|
box-shadow: inset 0 0 0 999px var(--hover-overlay);
|
|
304
326
|
color: var(--text);
|
|
305
327
|
}
|
|
306
328
|
}
|
|
307
329
|
|
|
308
|
-
.dialog-close:active
|
|
309
|
-
.drawer-close:active {
|
|
330
|
+
.dialog-close:active {
|
|
310
331
|
box-shadow: inset 0 0 0 999px var(--hover-overlay);
|
|
311
332
|
}
|
|
312
333
|
|
|
313
|
-
.dialog-close:focus-visible
|
|
314
|
-
.drawer-close:focus-visible {
|
|
334
|
+
.dialog-close:focus-visible {
|
|
315
335
|
outline: 2px solid transparent;
|
|
316
336
|
box-shadow: var(--focus-ring-shadow);
|
|
317
337
|
}
|
|
318
|
-
@media print {
|
|
319
|
-
dialog.modal[open] {
|
|
320
|
-
display: none !important;
|
|
321
|
-
}
|
|
322
|
-
}
|