actual-css 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +214 -1
- package/README.md +13 -11
- package/components.json +668 -0
- package/dist/actual.css +166 -37
- package/dist/actual.full.css +1005 -550
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/dist/actual.js +1 -1
- package/dist/actual.min.css +1 -1
- package/docs/design-notes/accessibility-decisions.md +33 -0
- package/docs/design-notes/browser-support.md +82 -25
- package/docs/design-notes/column-layout.md +3 -3
- package/docs/design-notes/css-anchor.md +75 -21
- package/docs/design-notes/enhancement-contract.md +14 -6
- package/docs/design-notes/grid.md +4 -4
- package/docs/design-notes/naming.md +6 -1
- package/docs/design-notes/platform-alignment.md +99 -2
- package/docs/design-notes/popover-manual-poc.md +328 -0
- package/docs/design-notes/surface-reparenting.md +131 -0
- package/docs/design-notes/tooltip-coordinate-space.md +132 -0
- package/docs/design-notes/widget-primitives.md +9 -6
- package/docs/pages/components/accordion.md +41 -2
- package/docs/pages/components/alert.md +80 -30
- package/docs/pages/components/app-navigation.md +6 -0
- package/docs/pages/components/avatar.md +6 -6
- package/docs/pages/components/badge.md +101 -16
- package/docs/pages/components/button.md +16 -8
- package/docs/pages/components/card.md +10 -10
- package/docs/pages/components/dialog.md +16 -6
- package/docs/pages/components/fab.md +4 -3
- package/docs/pages/components/list.md +42 -4
- package/docs/pages/components/navbar.md +17 -1
- package/docs/pages/components/pagination.md +2 -2
- package/docs/pages/components/steps.md +19 -0
- package/docs/pages/components/table.md +3 -2
- package/docs/pages/enhancements/flyout.md +144 -24
- package/docs/pages/enhancements/overview.md +25 -0
- package/docs/pages/enhancements/status-bar.md +20 -0
- package/docs/pages/enhancements/tabs.md +45 -37
- package/docs/pages/enhancements/tooltip.md +40 -2
- package/docs/pages/examples/overview.md +38 -5
- package/docs/pages/forms/choice-cards.md +6 -2
- package/docs/pages/forms/combobox.md +1 -1
- package/docs/pages/forms/floating-field.md +4 -3
- package/docs/pages/forms/joined-controls.md +22 -0
- package/docs/pages/forms/otp.md +2 -1
- package/docs/pages/forms/overview.md +1 -1
- package/docs/pages/forms/states-and-sizes.md +16 -18
- package/docs/pages/forms/sticky-actions.md +2 -2
- package/docs/pages/forms/switch.md +1 -1
- package/docs/pages/foundations/philosophy.md +67 -22
- package/docs/pages/foundations/theming.md +10 -2
- package/docs/pages/foundations/tokens.md +118 -21
- package/docs/pages/foundations/typography.md +35 -14
- package/docs/pages/guides/introduction.md +11 -9
- package/docs/pages/guides/progressive-enhancement.md +136 -49
- package/docs/pages/guides/tailwind.md +3 -2
- package/docs/pages/layout/media.md +1 -1
- package/docs/pages/layout/overview.md +11 -1
- package/docs/pages/layout/scroll-snap.md +3 -3
- package/docs/pages/layout/stack.md +17 -9
- package/docs/pages/patterns/icon-input.md +4 -2
- package/docs/pages/patterns/icon-slot.md +2 -0
- package/docs/pages/patterns/intent-nesting.md +1 -1
- package/docs/pages/utilities/overview.md +23 -2
- package/docs/pages/utilities/sizing-wrapping.md +1 -1
- package/docs/pages/utilities/spacing.md +10 -8
- package/llms.txt +2 -1
- package/package.json +19 -8
- package/{scripts/reserved-classes.json → reserved-classes.json} +4 -6
- package/src/css/components/accordion.css +42 -3
- package/src/css/components/alert.css +25 -13
- package/src/css/components/app-nav.css +18 -8
- package/src/css/components/avatar.css +13 -1
- package/src/css/components/badge.css +78 -12
- package/src/css/components/breadcrumb.css +2 -3
- package/src/css/components/button.css +9 -16
- package/src/css/components/card.css +9 -2
- package/src/css/components/chat.css +1 -0
- package/src/css/components/data-list.css +1 -1
- package/src/css/components/drawer.css +79 -25
- package/src/css/components/fab.css +0 -6
- package/src/css/components/flyout.css +46 -63
- package/src/css/components/index.css +0 -1
- package/src/css/components/indicator.css +21 -0
- package/src/css/components/join.css +7 -0
- package/src/css/components/key.css +10 -0
- package/src/css/components/list.css +40 -4
- package/src/css/components/menu.css +18 -11
- package/src/css/components/modal.css +48 -33
- package/src/css/components/navbar.css +37 -4
- package/src/css/components/pagination.css +1 -0
- package/src/css/components/spinner.css +6 -6
- package/src/css/components/status-bar.css +0 -5
- package/src/css/components/steps.css +45 -59
- package/src/css/components/tab.css +29 -9
- package/src/css/components/table.css +5 -5
- package/src/css/components/tooltip.css +45 -13
- package/src/css/core/base.css +23 -1
- package/src/css/core/intents.css +17 -6
- package/src/css/core/theme.css +27 -0
- package/src/css/core/tokens.css +35 -15
- package/src/css/core/variants.css +64 -15
- package/src/css/forms/choice-card.css +4 -4
- package/src/css/forms/choice.css +6 -3
- package/src/css/forms/control.css +2 -1
- package/src/css/forms/floating-field.css +3 -3
- package/src/css/forms/form.css +4 -3
- package/src/css/forms/input-icon.css +20 -14
- package/src/css/forms/native.css +6 -1
- package/src/css/forms/otp.css +9 -1
- package/src/css/forms/range.css +2 -0
- package/src/css/forms/select.css +29 -9
- package/src/css/forms/switch.css +19 -5
- package/src/css/layout/app-layout.css +1 -1
- package/src/css/layout/grid.css +50 -76
- package/src/css/layout/scroll-snap.css +11 -7
- package/src/css/layout/scroller.css +3 -4
- package/src/css/layout/stack.css +2 -2
- package/src/css/typography/fluid.css +4 -3
- package/src/css/typography/prose.css +6 -5
- package/src/css/utilities/base.css +2 -1
- package/src/css/utilities/extra.css +20 -17
- package/src/js/context-menu.js +27 -26
- package/src/js/dialog.js +2 -1
- package/src/js/dismiss.js +5 -0
- package/src/js/enhance.js +34 -2
- package/src/js/enhancement-loader.js +36 -43
- package/src/js/events.js +0 -3
- package/src/js/filter.js +0 -1
- package/src/js/floating-space.js +42 -0
- package/src/js/flyout.js +9 -44
- package/src/js/focus-group.js +7 -0
- package/src/js/focus.js +6 -0
- package/src/js/menu.js +10 -1
- package/src/js/selectors.js +5 -9
- package/src/js/status.js +44 -7
- package/src/js/surface.js +106 -132
- package/src/js/tab.js +6 -1
- package/src/js/tooltip.js +152 -53
- package/src/js/validation.js +1 -1
- package/src/css/components/surface.css +0 -49
- package/src/js/floating.js +0 -432
package/src/css/forms/select.css
CHANGED
|
@@ -7,20 +7,40 @@
|
|
|
7
7
|
* the CSS chevron, so the picker (appearance: base-select) enhancement in
|
|
8
8
|
* custom-select.css can be opted out independently. Add .native to a specific
|
|
9
9
|
* select to opt out of the enhancement.
|
|
10
|
+
*
|
|
11
|
+
* Public hooks:
|
|
12
|
+
* --select-icon-size
|
|
10
13
|
*/
|
|
11
|
-
|
|
14
|
+
/* --select-icon-size is typographic (em), so the reserved slot and the
|
|
15
|
+
pictogram scale with the control's font size, not a fixed rem sliver. */
|
|
16
|
+
.select:not(:is([multiple], [size]:not([size="1"]))) {
|
|
17
|
+
--select-icon-size: 1em;
|
|
18
|
+
|
|
12
19
|
appearance: none;
|
|
13
|
-
|
|
20
|
+
-webkit-appearance: none;
|
|
21
|
+
padding-inline-end: calc(var(--control-pad-x) * 2 + var(--select-icon-size));
|
|
14
22
|
background-image: var(--icon-chevron);
|
|
15
|
-
background-position: center right var(--
|
|
16
|
-
background-size:
|
|
23
|
+
background-position: center right var(--control-pad-x);
|
|
24
|
+
background-size: var(--select-icon-size) auto;
|
|
17
25
|
background-repeat: no-repeat;
|
|
18
26
|
}
|
|
19
27
|
|
|
20
28
|
/* The chevron sits on the inline-end side; flip it in RTL so it follows
|
|
21
|
-
the padding-inline-end space reserved for it.
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
the padding-inline-end space reserved for it. Safari before 16.4 has no
|
|
30
|
+
:dir(); the ancestor-attribute path is the Degraded-tier fallback and the
|
|
31
|
+
:dir() rules are dropped on their own there. :dir() follows inherited
|
|
32
|
+
directionality, so the explicit ltr rule neutralizes an outer RTL fallback
|
|
33
|
+
for a nested direction change. */
|
|
34
|
+
[dir="rtl"] .select:not(:is([multiple], [size]:not([size="1"]))) {
|
|
35
|
+
background-position: center left var(--control-pad-x);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.select:not(:is([multiple], [size]:not([size="1"]))):dir(ltr) {
|
|
39
|
+
background-position: center right var(--control-pad-x);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.select:not(:is([multiple], [size]:not([size="1"]))):dir(rtl) {
|
|
43
|
+
background-position: center left var(--control-pad-x);
|
|
24
44
|
}
|
|
25
45
|
|
|
26
46
|
/* Both forced-colors and prefers-contrast hand the arrow back to the
|
|
@@ -28,9 +48,9 @@
|
|
|
28
48
|
simpler than maintaining contrast variants, and native rendering already
|
|
29
49
|
matches whatever contrast mode the user picked. */
|
|
30
50
|
@media (forced-colors: active), (prefers-contrast: more) {
|
|
31
|
-
.select:not([multiple], [size]:not([size="1"])) {
|
|
51
|
+
.select:not(:is([multiple], [size]:not([size="1"]))) {
|
|
32
52
|
appearance: auto;
|
|
33
53
|
background-image: none;
|
|
34
|
-
padding-inline-end: var(--
|
|
54
|
+
padding-inline-end: var(--control-pad-x);
|
|
35
55
|
}
|
|
36
56
|
}
|
package/src/css/forms/switch.css
CHANGED
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
border-radius: var(--radius-full);
|
|
66
66
|
background: var(--switch-bg);
|
|
67
67
|
appearance: none;
|
|
68
|
+
-webkit-appearance: none;
|
|
68
69
|
cursor: pointer;
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -85,14 +86,17 @@
|
|
|
85
86
|
background-color var(--duration);
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
|
|
89
|
+
/* Hover is not selected: an unchecked switch keeps the brand fallback on hover,
|
|
90
|
+
so a theme that separates --primary from --state-selected never tints an
|
|
91
|
+
OFF control with the selection accent before it is checked. */
|
|
92
|
+
.switch:not(:checked):hover:not(:disabled) {
|
|
89
93
|
--switch-border: var(--intent, var(--primary));
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
.switch:checked {
|
|
93
|
-
--switch-bg: var(--intent, var(--
|
|
94
|
-
--switch-border: var(--intent, var(--
|
|
95
|
-
--switch-knob: var(--intent-fg, var(--
|
|
97
|
+
--switch-bg: var(--intent, var(--state-selected));
|
|
98
|
+
--switch-border: var(--intent, var(--state-selected));
|
|
99
|
+
--switch-knob: var(--intent-fg, var(--state-selected-fg));
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
.switch:checked::before {
|
|
@@ -100,7 +104,17 @@
|
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
/* The knob travels toward inline-end; in RTL that is leftward, so the
|
|
103
|
-
translateX sign flips. translateX is physical, not logical.
|
|
107
|
+
translateX sign flips. translateX is physical, not logical. Safari before
|
|
108
|
+
16.4 has no :dir(); the ancestor-attribute path is the Degraded-tier
|
|
109
|
+
fallback and the :dir() rules are dropped on their own there. */
|
|
110
|
+
[dir="rtl"] .switch:checked::before {
|
|
111
|
+
transform: translateX(calc(var(--switch-travel) * -1));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.switch:checked:dir(ltr)::before {
|
|
115
|
+
transform: translateX(var(--switch-travel));
|
|
116
|
+
}
|
|
117
|
+
|
|
104
118
|
.switch:checked:dir(rtl)::before {
|
|
105
119
|
transform: translateX(calc(var(--switch-travel) * -1));
|
|
106
120
|
}
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
text-align: start;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
/*
|
|
86
|
+
/* In the side layout the FAB owns the viewport's bottom safe-area inset. */
|
|
87
87
|
.app-layout > .fab {
|
|
88
88
|
margin-block-end: max(var(--fab-offset), env(safe-area-inset-bottom));
|
|
89
89
|
}
|
package/src/css/layout/grid.css
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* .grid is space-driven: it fits as many tracks as --grid-min allows.
|
|
5
5
|
* .grid-N is count-driven: it declares a structural density of N and collapses
|
|
6
|
-
* through natural divisors of N
|
|
6
|
+
* through natural divisors of N.
|
|
7
7
|
*
|
|
8
8
|
* Public hooks:
|
|
9
9
|
* --grid-min
|
|
@@ -14,22 +14,15 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Child contract: each direct child is a grid item placed in the tracks the
|
|
16
16
|
* recipe generates.
|
|
17
|
+
*
|
|
18
|
+
* Choosing between these and the other layout primitives:
|
|
19
|
+
* docs/pages/layout/grid.md. Why divisors, why a *named* container, and how
|
|
20
|
+
* the thresholds were arrived at: docs/design-notes/grid.md.
|
|
17
21
|
*/
|
|
18
22
|
|
|
19
|
-
/* --grid-columns replaces the intrinsic auto-fit template: once set, the
|
|
20
|
-
|
|
21
|
-
collapse.
|
|
22
|
-
--grid-columns for exact custom track templates. */
|
|
23
|
-
|
|
24
|
-
/* Recipe:
|
|
25
|
-
- collection whose item width is the contract -> .grid, tune --grid-min
|
|
26
|
-
- collection whose column density is the contract -> .grid-2/3/4/6
|
|
27
|
-
- balanced subdivision of that density -> place it inside any size query
|
|
28
|
-
container (.container-query is the convenience helper for that context)
|
|
29
|
-
- peer regions that switch as a group -> .switcher
|
|
30
|
-
- content + aside -> .sidebar-layout
|
|
31
|
-
- exact custom track templates -> --grid-columns with an explicit collapse
|
|
32
|
-
rule of your own */
|
|
23
|
+
/* --grid-columns replaces the intrinsic auto-fit template: once set, the grid
|
|
24
|
+
no longer collapses on its own and the author owns narrow-container
|
|
25
|
+
collapse. */
|
|
33
26
|
.grid {
|
|
34
27
|
display: grid;
|
|
35
28
|
gap: var(--gap);
|
|
@@ -39,16 +32,14 @@
|
|
|
39
32
|
);
|
|
40
33
|
}
|
|
41
34
|
|
|
42
|
-
/*
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
partial final page holds fewer items than the grid's capacity.
|
|
35
|
+
/* Baseline: auto-fill bounded at N, so a preset is responsive and
|
|
36
|
+
overflow-safe with no wrapper at all. auto-fill rather than auto-fit keeps
|
|
37
|
+
item width stable when a partial final page holds fewer items than the
|
|
38
|
+
grid's capacity.
|
|
47
39
|
|
|
48
|
-
--grid-min is deliberately NOT read here
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
added. Item width is .grid's contract; column density is .grid-N's. */
|
|
40
|
+
--grid-min is deliberately NOT read here: @container thresholds cannot
|
|
41
|
+
resolve a custom property, so a hook honored in the baseline would fall
|
|
42
|
+
silent the moment a query container is added. */
|
|
52
43
|
:where(.grid-2, .grid-3, .grid-4, .grid-6) {
|
|
53
44
|
display: grid;
|
|
54
45
|
gap: var(--gap);
|
|
@@ -77,79 +68,62 @@
|
|
|
77
68
|
--grid-count: 6;
|
|
78
69
|
}
|
|
79
70
|
|
|
80
|
-
/* Grid items keep an automatic
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
need .min-inline-size-0 outside primitives that do not already remove this
|
|
84
|
-
floor. */
|
|
71
|
+
/* Grid items keep an automatic min-content floor that can overflow a
|
|
72
|
+
minmax(0, 1fr) track. Layout primitives remove it so their overflow-safe
|
|
73
|
+
track sizing holds; .min-inline-size-0 is for the ones that do not. */
|
|
85
74
|
:where(.grid, .grid-2, .grid-3, .grid-4, .grid-6) > * {
|
|
86
75
|
min-inline-size: 0;
|
|
87
76
|
}
|
|
88
77
|
|
|
89
|
-
/* Balanced subdivision,
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* or a region that sets container-type: inline-size for an unrelated reason
|
|
93
|
-
* would silently change the grid. The author opts the grid in by naming the
|
|
94
|
-
* context — .container-query is the official helper, or declare
|
|
95
|
-
* container: actual-container / inline-size on your own region.
|
|
78
|
+
/* Balanced subdivision, keyed on an ancestor container *named*
|
|
79
|
+
* actual-container — never an anonymous one, so a region that sets
|
|
80
|
+
* container-type: inline-size for an unrelated reason cannot change a grid.
|
|
96
81
|
*
|
|
97
82
|
* .grid-2 2 -> 1
|
|
98
83
|
* .grid-3 3 -> 1
|
|
99
84
|
* .grid-4 4 -> 2 -> 1
|
|
100
85
|
* .grid-6 6 -> 3 -> 2 -> 1
|
|
101
86
|
*
|
|
102
|
-
* Every state is a divisor of N
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* missing integers, and do not replace them with an unbounded auto-fit.
|
|
87
|
+
* Every state is a divisor of N. Do not complete these chains with the
|
|
88
|
+
* missing integers and do not replace them with an unbounded auto-fit: both
|
|
89
|
+
* bring back uneven splits such as 5 + 1.
|
|
106
90
|
*
|
|
107
|
-
* The
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
91
|
+
* The container must be an ANCESTOR. An element cannot query its own size
|
|
92
|
+
* without inline-size containment, which destroys its intrinsic inline
|
|
93
|
+
* contribution and collapses it inside .cluster, .topbar, floats and any
|
|
94
|
+
* shrink-to-fit box.
|
|
111
95
|
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* Three shared thresholds, not one calibrated per preset: 28rem opens the
|
|
118
|
-
* first horizontal density, 48rem opens three columns, 64rem opens the dense
|
|
119
|
-
* 4 and 6. N already carries the author's statement about item size, so
|
|
120
|
-
* tuning every step to land the same item width would erase that signal —
|
|
121
|
-
* .grid-6 at 64rem gives ~161px per tile, which is the point of choosing it.
|
|
122
|
-
* Probed with the content each preset targets: large cards for .grid-2 down
|
|
123
|
-
* to stats, avatars, and thumbnails for .grid-6.
|
|
96
|
+
* With no such ancestor none of these resolve and the baseline above stays.
|
|
97
|
+
* min-width: 0 is the lowest step: it pins the grid to one column before any
|
|
98
|
+
* larger threshold fires, which a bare grid would otherwise show as a 2 + 1
|
|
99
|
+
* orphan between 28 and 48rem.
|
|
124
100
|
*
|
|
125
101
|
* Ascending min-width only: the last matching step wins by cascade order.
|
|
126
102
|
*/
|
|
127
|
-
@
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
grid-template-columns: 1fr;
|
|
131
|
-
}
|
|
103
|
+
@container actual-container (min-width: 0px) {
|
|
104
|
+
:where(.grid-2, .grid-3, .grid-4, .grid-6) {
|
|
105
|
+
grid-template-columns: 1fr;
|
|
132
106
|
}
|
|
107
|
+
}
|
|
133
108
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
109
|
+
@container actual-container (min-width: 28rem) {
|
|
110
|
+
:where(.grid-2, .grid-4, .grid-6) {
|
|
111
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
138
112
|
}
|
|
113
|
+
}
|
|
139
114
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
115
|
+
@container actual-container (min-width: 48rem) {
|
|
116
|
+
:where(.grid-3, .grid-6) {
|
|
117
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
144
118
|
}
|
|
119
|
+
}
|
|
145
120
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
121
|
+
@container actual-container (min-width: 64rem) {
|
|
122
|
+
:where(.grid-4) {
|
|
123
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
124
|
+
}
|
|
150
125
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
126
|
+
:where(.grid-6) {
|
|
127
|
+
grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
154
128
|
}
|
|
155
129
|
}
|
|
@@ -31,24 +31,28 @@
|
|
|
31
31
|
.scroll-snap {
|
|
32
32
|
overscroll-behavior-inline: contain;
|
|
33
33
|
scroll-padding-inline: var(--scroll-snap-padding, var(--scroll-snap-gap));
|
|
34
|
+
/* x then inline: an engine that lacks the logical keyword keeps x.
|
|
35
|
+
proximity is the default, so a short rail cannot trap the pointer. */
|
|
36
|
+
scroll-snap-type: x proximity;
|
|
37
|
+
scroll-snap-type: inline proximity;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Strict snapping is opt-in; proximity keeps a rail from fighting the
|
|
41
|
+
pointer on a short strip. */
|
|
42
|
+
.scroll-snap[data-snap="mandatory"] {
|
|
34
43
|
scroll-snap-type: x mandatory;
|
|
44
|
+
scroll-snap-type: inline mandatory;
|
|
35
45
|
}
|
|
36
46
|
|
|
37
47
|
.scroll-snap > * {
|
|
38
48
|
scroll-snap-align: var(--scroll-snap-align);
|
|
39
49
|
}
|
|
40
50
|
|
|
41
|
-
.scroll-snap[data-snap="center"] {
|
|
51
|
+
.scroll-snap[data-snap-align="center"] {
|
|
42
52
|
--scroll-snap-align: center;
|
|
43
53
|
}
|
|
44
54
|
}
|
|
45
55
|
|
|
46
|
-
@supports (scroll-snap-type: inline mandatory) {
|
|
47
|
-
.scroll-snap {
|
|
48
|
-
scroll-snap-type: inline mandatory;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
56
|
.scroll-snap[data-scrollbar="hidden"] {
|
|
53
57
|
scrollbar-width: none;
|
|
54
58
|
}
|
|
@@ -8,10 +8,9 @@
|
|
|
8
8
|
* the light/dark adaptation that color-scheme already gives for free, and the
|
|
9
9
|
* two never match anyway.
|
|
10
10
|
*
|
|
11
|
-
* Above
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* not a broken state.
|
|
11
|
+
* Above every tier: scrollbar-color lands in Chromium 121 and Safari 18.2, so
|
|
12
|
+
* even a Recommended engine may lack it. Engines that ignore both declarations
|
|
13
|
+
* draw their native scrollbar — the intended fallback, not a broken state.
|
|
15
14
|
*
|
|
16
15
|
* Styles the scrollbar only; it does not create the overflow. Pair it with
|
|
17
16
|
* .overflow-auto or a component that already scrolls.
|
package/src/css/layout/stack.css
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
--gap is inherited, so a container that composes other primitives sets its
|
|
8
8
|
own rhythm on the `gap` property, not on the token: setting --gap on a
|
|
9
9
|
region re-spaces every nested stack, cluster and grid at any depth. Setting
|
|
10
|
-
the token stays correct on the element that consumes it, and
|
|
11
|
-
deliberately so a density context reaches nested layouts. See
|
|
10
|
+
the token stays correct on the element that consumes it, and
|
|
11
|
+
.compact/.spacious set it deliberately so a density context reaches nested layouts. See
|
|
12
12
|
docs/pages/layout/stack.md. */
|
|
13
13
|
.stack {
|
|
14
14
|
display: flex;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Optional fluid typography module.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Part of the typography family (typography/index.css) and the full bundle; it
|
|
5
|
+
* is not in the minimal core, so a core-only import adds it explicitly when
|
|
6
|
+
* display, title, and lead sizes must scale with the viewport. Typical use
|
|
7
|
+
* cases: marketing pages, landing pages, hero sections, documentation
|
|
7
8
|
* homepages.
|
|
8
9
|
*
|
|
9
10
|
* Do not use this for body text, app UI, or the default prose scale. Fluid
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
.prose :where(h1, h2, h3, h4, h5, h6) {
|
|
52
52
|
margin-block: 1.5em 0;
|
|
53
53
|
color: var(--heading, var(--text));
|
|
54
|
-
font-weight: var(--font-weight-
|
|
54
|
+
font-weight: var(--font-weight-semibold);
|
|
55
55
|
line-height: var(--prose-heading-line-height);
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -92,12 +92,12 @@
|
|
|
92
92
|
text-underline-offset: 0.18em;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
.prose :where(a:hover) {
|
|
95
|
+
.prose :where(a:hover, a:focus-visible) {
|
|
96
96
|
text-decoration-thickness: 0.12em;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
@supports (color: color-mix(in oklch, red, white)) {
|
|
100
|
-
.prose :where(a:hover) {
|
|
100
|
+
.prose :where(a:hover, a:focus-visible) {
|
|
101
101
|
color: color-mix(in oklab, var(--link, var(--primary)), var(--text) 15%);
|
|
102
102
|
}
|
|
103
103
|
}
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
/* Description lists keep document flow: a valid <dl> may pair several <dt>
|
|
116
116
|
with one <dd> or the reverse, so no grid layout is imposed here. */
|
|
117
117
|
.prose :where(dt) {
|
|
118
|
-
font-weight: var(--font-weight-
|
|
118
|
+
font-weight: var(--font-weight-semibold);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
.prose :where(dd) {
|
|
@@ -157,8 +157,8 @@
|
|
|
157
157
|
}
|
|
158
158
|
/* @sync key-recipe */
|
|
159
159
|
|
|
160
|
+
/* Overflow containment is global on `pre` (base.css); only the box is prose's. */
|
|
160
161
|
.prose :where(pre) {
|
|
161
|
-
overflow-x: auto;
|
|
162
162
|
padding: 1em;
|
|
163
163
|
border: var(--border-width) solid var(--border);
|
|
164
164
|
border-radius: var(--radius);
|
|
@@ -171,6 +171,7 @@
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
.prose :where(blockquote) {
|
|
174
|
+
margin-inline: 0;
|
|
174
175
|
padding-inline-start: 1em;
|
|
175
176
|
border-inline-start: var(--border-width) solid var(--border);
|
|
176
177
|
color: var(--text-muted);
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* They are escape hatches for layout nudges, not a full atomic scale.
|
|
6
6
|
*
|
|
7
7
|
* Core principle:
|
|
8
|
-
* - .
|
|
8
|
+
* - .compact / .spacious provide inherited rhythm through --gap and
|
|
9
|
+
* --density-space.
|
|
9
10
|
* - utilities consume the current context instead of defining their own -sm/-lg scale.
|
|
10
11
|
* - optional utilities extend core utilities, they do not re-express them.
|
|
11
12
|
* - follow verbose .{property}-{value} naming.
|
|
@@ -86,18 +87,6 @@
|
|
|
86
87
|
flex-wrap: nowrap;
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
.gap-sm {
|
|
90
|
-
gap: var(--space-20);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
.gap-md {
|
|
94
|
-
gap: var(--space-40);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
.gap-lg {
|
|
98
|
-
gap: var(--space-50);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
90
|
.justify-content-start {
|
|
102
91
|
--cluster-justify: flex-start;
|
|
103
92
|
justify-content: flex-start;
|
|
@@ -155,6 +144,10 @@
|
|
|
155
144
|
margin-block-end: auto;
|
|
156
145
|
}
|
|
157
146
|
|
|
147
|
+
/* Consume the inherited --gap rhythm on an arbitrary layout container.
|
|
148
|
+
Setting --gap alone only defines the custom property; it has no visual
|
|
149
|
+
effect unless the element already uses gap: var(--gap) (as .stack,
|
|
150
|
+
.cluster, .grid, etc. do). */
|
|
158
151
|
.gap-context {
|
|
159
152
|
gap: var(--gap);
|
|
160
153
|
}
|
|
@@ -206,15 +199,15 @@
|
|
|
206
199
|
}
|
|
207
200
|
|
|
208
201
|
.font-weight-normal {
|
|
209
|
-
font-weight: var(--font-weight);
|
|
202
|
+
font-weight: var(--font-weight-normal);
|
|
210
203
|
}
|
|
211
204
|
|
|
212
205
|
.font-weight-medium {
|
|
213
206
|
font-weight: var(--font-weight-medium);
|
|
214
207
|
}
|
|
215
208
|
|
|
216
|
-
.font-weight-
|
|
217
|
-
font-weight: var(--font-weight-
|
|
209
|
+
.font-weight-semibold {
|
|
210
|
+
font-weight: var(--font-weight-semibold);
|
|
218
211
|
}
|
|
219
212
|
|
|
220
213
|
.font-weight-bold {
|
|
@@ -255,18 +248,28 @@
|
|
|
255
248
|
border-inline-end: var(--border-width) solid var(--border);
|
|
256
249
|
}
|
|
257
250
|
|
|
258
|
-
/* Surface
|
|
251
|
+
/* Surface. These are semantic surfaces, not arbitrary paint: each owns its
|
|
252
|
+
matching ink and rebinds --heading to that ink, so it stays readable when it
|
|
253
|
+
recreates a light surface inside a context that installed a dark one
|
|
254
|
+
(.inverted). Intent ink is not inherited through a surface utility; use
|
|
255
|
+
.intent-color for that. */
|
|
259
256
|
.background-surface {
|
|
257
|
+
--heading: currentColor;
|
|
258
|
+
|
|
260
259
|
background: var(--surface);
|
|
261
260
|
color: var(--text);
|
|
262
261
|
}
|
|
263
262
|
|
|
264
263
|
.background-raised {
|
|
264
|
+
--heading: currentColor;
|
|
265
|
+
|
|
265
266
|
background: var(--surface-raised);
|
|
266
267
|
color: var(--text);
|
|
267
268
|
}
|
|
268
269
|
|
|
269
270
|
.background-subtle {
|
|
271
|
+
--heading: currentColor;
|
|
272
|
+
|
|
270
273
|
background: var(--surface-subtle);
|
|
271
274
|
color: var(--text);
|
|
272
275
|
}
|
package/src/js/context-menu.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Context menu — `data-context-menu` targets opening a shared menu surface at
|
|
3
|
+
* a pointer, keyboard, or button origin. Reuses surface for the lifecycle and
|
|
4
|
+
* menu for item navigation; the panel gets no token.
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import enhance from "./enhance.js";
|
|
2
8
|
import { EVENTS } from "./events.js";
|
|
3
9
|
import { connectMenu, focusFirstMenuItem, hasMenuItems } from "./menu.js";
|
|
@@ -53,7 +59,6 @@ function getLongPressDelay(target, menu) {
|
|
|
53
59
|
function readPanelOptions(menu) {
|
|
54
60
|
const opts = { autoClose: "true" };
|
|
55
61
|
const ds = menu.dataset;
|
|
56
|
-
if (ds.flyoutMobile) opts.mobile = ds.flyoutMobile;
|
|
57
62
|
if (ds.flyoutAutoClose) opts.autoClose = ds.flyoutAutoClose;
|
|
58
63
|
return opts;
|
|
59
64
|
}
|
|
@@ -62,8 +67,10 @@ function focusMenuContainer(menu) {
|
|
|
62
67
|
if (!menu.hasAttribute("tabindex")) {
|
|
63
68
|
menu.tabIndex = -1;
|
|
64
69
|
}
|
|
65
|
-
//
|
|
66
|
-
//
|
|
70
|
+
// The menu is promoted where it stands rather than moved, so there is no
|
|
71
|
+
// relocation left to compensate for. preventScroll stays for its own sake:
|
|
72
|
+
// focusing a panel must not move the page under the user. Genuine user
|
|
73
|
+
// scrolling is handled separately by the surface policy.
|
|
67
74
|
menu.focus({ preventScroll: true });
|
|
68
75
|
}
|
|
69
76
|
|
|
@@ -95,15 +102,23 @@ function requestContextMenu(context, menu, opts = {}) {
|
|
|
95
102
|
function openContextMenu(context, menu, opts = {}) {
|
|
96
103
|
if (!requestContextMenu(context, menu, opts)) return;
|
|
97
104
|
if (isSurfaceOpen(menu)) closeSurface(menu);
|
|
105
|
+
|
|
106
|
+
// Measured after the cancelable event: a handler may have moved the anchor.
|
|
107
|
+
let { x, y } = opts;
|
|
108
|
+
if (x == null || y == null) {
|
|
109
|
+
const rect = (opts.origin || context).getBoundingClientRect();
|
|
110
|
+
x ??= rect.left;
|
|
111
|
+
y ??= rect.bottom;
|
|
112
|
+
}
|
|
113
|
+
|
|
98
114
|
if (
|
|
99
115
|
openSurface(menu, {
|
|
100
116
|
source: context,
|
|
101
|
-
x
|
|
102
|
-
y
|
|
117
|
+
x,
|
|
118
|
+
y,
|
|
103
119
|
placement: opts.placement || "bottom-start",
|
|
104
120
|
distance: opts.distance ?? 2,
|
|
105
121
|
dismissOnScroll: true,
|
|
106
|
-
mobile: opts.mobile || "auto",
|
|
107
122
|
scope: opts.scope || getContextScope(context),
|
|
108
123
|
restoreFocusTo: opts.restoreFocusTo,
|
|
109
124
|
...readPanelOptions(menu),
|
|
@@ -114,10 +129,7 @@ function openContextMenu(context, menu, opts = {}) {
|
|
|
114
129
|
}
|
|
115
130
|
|
|
116
131
|
function openFromKeyboard(context, menu, origin = context, restoreFocusTo) {
|
|
117
|
-
const rect = origin.getBoundingClientRect();
|
|
118
132
|
openContextMenu(context, menu, {
|
|
119
|
-
x: rect.left,
|
|
120
|
-
y: rect.bottom,
|
|
121
133
|
placement: "bottom-start",
|
|
122
134
|
distance: 4,
|
|
123
135
|
focus: "first-item",
|
|
@@ -264,23 +276,12 @@ function connectContextTarget(target) {
|
|
|
264
276
|
return;
|
|
265
277
|
}
|
|
266
278
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
x: rect.left,
|
|
274
|
-
y: rect.bottom,
|
|
275
|
-
placement: "bottom-start",
|
|
276
|
-
distance: 4,
|
|
277
|
-
dismissOnScroll: true,
|
|
278
|
-
restoreFocusTo: trigger,
|
|
279
|
-
...readPanelOptions(menu),
|
|
280
|
-
})
|
|
281
|
-
) {
|
|
282
|
-
focusMenuContainer(menu);
|
|
283
|
-
}
|
|
279
|
+
openContextMenu(target, menu, {
|
|
280
|
+
distance: 4,
|
|
281
|
+
origin: trigger,
|
|
282
|
+
trigger: "button",
|
|
283
|
+
restoreFocusTo: trigger,
|
|
284
|
+
});
|
|
284
285
|
},
|
|
285
286
|
{ signal: controller.signal },
|
|
286
287
|
);
|
package/src/js/dialog.js
CHANGED
|
@@ -37,6 +37,7 @@ const wiredDialogs = new Set();
|
|
|
37
37
|
const DIALOG_COMMANDS = ["show-modal", "show", "request-close", "close"];
|
|
38
38
|
const DIALOG_SELECTOR = "dialog";
|
|
39
39
|
const DIALOG_TITLE_SELECTOR = "[data-title], h1, h2, h3, h4, h5, h6";
|
|
40
|
+
const STATIC_SHAKE_MS = 250;
|
|
40
41
|
let uid = 0;
|
|
41
42
|
|
|
42
43
|
function isDialogElement(el) {
|
|
@@ -213,7 +214,7 @@ function flashStatic(dialog) {
|
|
|
213
214
|
state.staticTimer = win?.setTimeout(() => {
|
|
214
215
|
dialog.classList.remove(CLASSES.static);
|
|
215
216
|
state.staticTimer = null;
|
|
216
|
-
},
|
|
217
|
+
}, STATIC_SHAKE_MS);
|
|
217
218
|
}
|
|
218
219
|
|
|
219
220
|
function finishClose(dialog, returnValue = "") {
|
package/src/js/dismiss.js
CHANGED