actual-css 0.3.1 → 0.4.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 +67 -3
- package/README.md +35 -30
- package/dist/actual.css +73 -4946
- package/dist/actual.full.css +7090 -0
- package/dist/actual.full.js +1 -0
- package/dist/actual.full.min.css +1 -0
- package/dist/actual.js +1 -1
- package/dist/actual.min.css +1 -1
- package/docs/design-notes/cascade-layer.md +34 -13
- package/docs/design-notes/column-layout.md +677 -0
- package/docs/design-notes/forced-colors.md +3 -0
- package/docs/design-notes/grid.md +612 -0
- package/docs/design-notes/naming.md +15 -0
- package/docs/design-notes/widget-primitives.md +1 -0
- package/docs/navigation.json +5 -1
- package/docs/pages/components/alert.md +16 -14
- package/docs/pages/components/aura.md +1 -1
- package/docs/pages/components/avatar.md +6 -6
- package/docs/pages/components/badge.md +7 -7
- package/docs/pages/components/button.md +16 -16
- package/docs/pages/components/card.md +13 -9
- package/docs/pages/components/chat.md +8 -8
- package/docs/pages/components/dialog.md +7 -7
- package/docs/pages/components/drawer.md +3 -3
- package/docs/pages/components/fab.md +7 -7
- package/docs/pages/components/navbar.md +9 -8
- package/docs/pages/components/pagination.md +4 -3
- package/docs/pages/components/skeleton.md +4 -4
- package/docs/pages/components/spinner.md +5 -5
- package/docs/pages/components/table.md +30 -5
- package/docs/pages/enhancements/flyout.md +13 -13
- package/docs/pages/enhancements/overview.md +1 -1
- package/docs/pages/enhancements/scrollspy.md +6 -6
- package/docs/pages/enhancements/status-bar.md +5 -1
- package/docs/pages/enhancements/tabs.md +6 -6
- package/docs/pages/enhancements/tooltip.md +1 -1
- package/docs/pages/examples/overview.md +39 -22
- package/docs/pages/forms/choice-cards.md +11 -6
- package/docs/pages/forms/controls.md +1 -1
- package/docs/pages/forms/detached-actions.md +1 -1
- package/docs/pages/forms/floating-field.md +10 -5
- package/docs/pages/forms/form-actions.md +1 -1
- package/docs/pages/forms/input-policy.md +1 -1
- package/docs/pages/forms/joined-controls.md +4 -4
- package/docs/pages/forms/labels.md +6 -6
- package/docs/pages/forms/otp.md +1 -1
- package/docs/pages/forms/overview.md +19 -19
- package/docs/pages/forms/password.md +1 -1
- package/docs/pages/forms/range.md +1 -1
- package/docs/pages/forms/states-and-sizes.md +41 -45
- package/docs/pages/forms/sticky-actions.md +3 -3
- package/docs/pages/forms/switch.md +110 -2
- package/docs/pages/forms/validation.md +9 -9
- package/docs/pages/foundations/philosophy.md +1 -1
- package/docs/pages/foundations/theming.md +17 -2
- package/docs/pages/foundations/tokens.md +22 -10
- package/docs/pages/foundations/typography.md +50 -20
- package/docs/pages/guides/bootstrap.md +11 -11
- package/docs/pages/guides/integrating-actual-css.md +186 -0
- package/docs/pages/guides/introduction.md +23 -26
- package/docs/pages/guides/modular-import.md +173 -0
- package/docs/pages/guides/progressive-enhancement.md +81 -78
- package/docs/pages/guides/tailwind.md +6 -8
- package/docs/pages/layout/app-shell.md +1 -1
- package/docs/pages/layout/center.md +1 -1
- package/docs/pages/layout/choosing.md +39 -0
- package/docs/pages/layout/column-layout.md +206 -0
- package/docs/pages/layout/frame.md +6 -1
- package/docs/pages/layout/grid.md +100 -102
- package/docs/pages/layout/list.md +1 -1
- package/docs/pages/layout/media.md +1 -1
- package/docs/pages/layout/overview.md +15 -2
- package/docs/pages/layout/scroll-snap.md +2 -2
- package/docs/pages/layout/scroll-target.md +45 -0
- package/docs/pages/layout/sidebar-layout.md +20 -19
- package/docs/pages/layout/stack.md +53 -1
- package/docs/pages/layout/switcher.md +10 -26
- package/docs/pages/layout/topbar.md +2 -2
- package/docs/pages/patterns/actions.md +1 -1
- package/docs/pages/patterns/code-block.md +1 -1
- package/docs/pages/patterns/exclusive-accordion.md +1 -1
- package/docs/pages/patterns/hierarchical-nav.md +1 -1
- package/docs/pages/patterns/icon-input.md +1 -1
- package/docs/pages/patterns/intent-nesting.md +1 -1
- package/docs/pages/patterns/navigation-list.md +1 -1
- package/docs/pages/patterns/overview.md +1 -1
- package/docs/pages/patterns/structured-lists.md +1 -1
- package/docs/pages/utilities/accessibility.md +3 -3
- package/docs/pages/utilities/flex.md +15 -15
- package/docs/pages/utilities/links.md +4 -4
- package/docs/pages/utilities/list.md +8 -8
- package/docs/pages/utilities/overflow.md +8 -8
- package/docs/pages/utilities/overview.md +1 -1
- package/docs/pages/utilities/shape.md +3 -3
- package/docs/pages/utilities/sizing-wrapping.md +23 -16
- package/docs/pages/utilities/spacing.md +20 -20
- package/docs/pages/utilities/text.md +10 -10
- package/llms.txt +46 -4
- package/package.json +42 -13
- package/scripts/reserved-classes.json +261 -0
- package/src/cli/actual-css.js +133 -0
- package/src/css/actual.css +1 -31
- package/src/css/actual.full.css +11 -2
- package/src/css/components/alert.css +17 -4
- package/src/css/components/badge.css +18 -6
- package/src/css/components/breadcrumb.css +1 -1
- package/src/css/components/button.css +19 -1
- package/src/css/components/card.css +27 -2
- package/src/css/components/drawer.css +27 -2
- package/src/css/components/flyout.css +5 -0
- package/src/css/components/index.css +32 -27
- package/src/css/components/join.css +8 -1
- package/src/css/components/key.css +2 -0
- package/src/css/components/menu.css +11 -1
- package/src/css/components/meter.css +4 -1
- package/src/css/components/modal.css +5 -0
- package/src/css/components/pagination.css +11 -0
- package/src/css/components/spinner.css +5 -0
- package/src/css/components/status-bar.css +5 -0
- package/src/css/components/surface.css +5 -0
- package/src/css/components/table.css +6 -0
- package/src/css/components/tooltip.css +5 -0
- package/src/css/core/index.css +9 -0
- package/src/css/core/print.css +18 -0
- package/src/css/{theme.css → core/theme.css} +20 -10
- package/src/css/{tokens.css → core/tokens.css} +15 -2
- package/src/css/{variants.css → core/variants.css} +3 -2
- package/src/css/effects/index.css +2 -0
- package/src/css/forms/base.css +8 -0
- package/src/css/forms/choice-card.css +5 -4
- package/src/css/forms/choice.css +49 -37
- package/src/css/forms/control.css +4 -1
- package/src/css/forms/custom-select.css +2 -0
- package/src/css/{optional → forms}/floating-field.css +25 -7
- package/src/css/forms/index.css +10 -13
- package/src/css/forms/native.css +15 -2
- package/src/css/{optional → forms}/otp.css +20 -16
- package/src/css/forms/switch.css +31 -29
- package/src/css/layout/app-shell.css +6 -0
- package/src/css/layout/center.css +13 -0
- package/src/css/layout/cluster.css +21 -0
- package/src/css/layout/column-layout.css +144 -0
- package/src/css/layout/container-query.css +13 -0
- package/src/css/layout/frame.css +19 -0
- package/src/css/layout/grid.css +141 -0
- package/src/css/layout/index.css +42 -0
- package/src/css/layout/measure.css +9 -0
- package/src/css/layout/media.css +10 -0
- package/src/css/layout/scroll-target.css +15 -0
- package/src/css/layout/scroller.css +41 -0
- package/src/css/layout/sidebar-layout.css +31 -0
- package/src/css/layout/stack.css +21 -0
- package/src/css/layout/switcher.css +23 -0
- package/src/css/{optional/layout-extra.css → layout/topbar.css} +0 -20
- package/src/css/typography/index.css +5 -0
- package/src/css/typography/lead.css +7 -0
- package/src/css/{components → typography}/overline.css +2 -2
- package/src/css/{prose.css → typography/prose.css} +53 -38
- package/src/css/{utilities.css → utilities/base.css} +2 -0
- package/src/css/utilities/index.css +3 -0
- package/src/js/escape.js +36 -0
- package/src/js/flyout.js +22 -12
- package/src/js/full.js +26 -0
- package/src/js/index.js +5 -21
- package/src/js/surface.js +1 -31
- package/src/js/tooltip.js +46 -18
- package/src/js/validation.js +1 -1
- package/src/tooling/css-bundle.js +308 -0
- package/dist/actual-themes.min.css +0 -1
- package/dist/optional.css +0 -1048
- package/dist/optional.min.css +0 -1
- package/docs/pages/guides/optional-components.md +0 -33
- package/src/css/grid.css +0 -109
- package/src/css/layout.css +0 -123
- package/src/css/optional/index.css +0 -10
- package/src/css/optional/scroller.css +0 -63
- package/src/css/print.css +0 -47
- package/src/css/themes/corporate.css +0 -60
- package/src/css/themes/cyberpunk.css +0 -60
- package/src/css/themes/dim.css +0 -33
- package/src/css/themes/edge.css +0 -117
- package/src/css/themes/forest.css +0 -65
- package/src/css/themes/gradient.css +0 -97
- package/src/css/themes/index.css +0 -47
- package/src/css/themes/indigo.css +0 -33
- package/src/css/themes/lavender.css +0 -66
- package/src/css/themes/mono.css +0 -60
- package/src/css/themes/neon.css +0 -66
- package/src/css/themes/ocean.css +0 -60
- package/src/css/themes/petrol.css +0 -64
- package/src/css/themes/spruce.css +0 -64
- package/src/css/themes/square.css +0 -65
- package/src/css/themes/sunset.css +0 -66
- /package/src/css/{optional → components}/chat.css +0 -0
- /package/src/css/{optional → components}/fab.css +0 -0
- /package/src/css/{base.css → core/base.css} +0 -0
- /package/src/css/{focus.css → core/focus.css} +0 -0
- /package/src/css/{intents.css → core/intents.css} +0 -0
- /package/src/css/{reset.css → core/reset.css} +0 -0
- /package/src/css/{optional → effects}/aura.css +0 -0
- /package/src/css/{optional → layout}/scroll-snap.css +0 -0
- /package/src/css/{optional/typography-fluid.css → typography/fluid.css} +0 -0
- /package/src/css/{optional/utilities-extra.css → utilities/extra.css} +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Media frame with a fixed ratio.
|
|
3
|
+
*
|
|
4
|
+
* Public hooks:
|
|
5
|
+
* --frame-ratio
|
|
6
|
+
*
|
|
7
|
+
* Child contract: direct media children (img/video/iframe/canvas/svg) are
|
|
8
|
+
* sized to fill the frame.
|
|
9
|
+
*/
|
|
10
|
+
.frame {
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
aspect-ratio: var(--frame-ratio, 16 / 9);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.frame > :where(img, video, iframe, canvas, svg) {
|
|
16
|
+
inline-size: 100%;
|
|
17
|
+
block-size: 100%;
|
|
18
|
+
object-fit: cover;
|
|
19
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Responsive equal-width grids.
|
|
3
|
+
*
|
|
4
|
+
* .grid is space-driven: it fits as many tracks as --grid-min allows.
|
|
5
|
+
* .grid-N is count-driven: it declares a structural density of N and collapses
|
|
6
|
+
* through natural divisors of N (see the subdivision block below).
|
|
7
|
+
*
|
|
8
|
+
* Public hooks:
|
|
9
|
+
* --grid-min
|
|
10
|
+
* --grid-columns
|
|
11
|
+
*
|
|
12
|
+
* Internal:
|
|
13
|
+
* --grid-count
|
|
14
|
+
*
|
|
15
|
+
* Child contract: each direct child is a grid item placed in the tracks the
|
|
16
|
+
* recipe generates.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/* --grid-columns replaces the intrinsic auto-fit template: once set, the
|
|
20
|
+
grid no longer collapses on its own — the author owns narrow-container
|
|
21
|
+
collapse. Prefer .sidebar-layout for content + aside layouts; reserve
|
|
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 -> wrap it in .container-query
|
|
28
|
+
- peer regions that switch as a group -> .switcher
|
|
29
|
+
- content + aside -> .sidebar-layout
|
|
30
|
+
- exact custom track templates -> --grid-columns with an explicit collapse
|
|
31
|
+
rule of your own */
|
|
32
|
+
.grid {
|
|
33
|
+
display: grid;
|
|
34
|
+
gap: var(--gap);
|
|
35
|
+
grid-template-columns: var(
|
|
36
|
+
--grid-columns,
|
|
37
|
+
repeat(auto-fit, minmax(min(100%, var(--grid-min, 16rem)), 1fr))
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Structural density presets. The baseline is an auto-fill grid bounded at N
|
|
42
|
+
tracks: responsive and overflow-safe on its own, so a preset used without a
|
|
43
|
+
query container is never a trap — it simply reaches its balanced states less
|
|
44
|
+
precisely. auto-fill rather than auto-fit keeps item width stable when a
|
|
45
|
+
partial final page holds fewer items than the grid's capacity.
|
|
46
|
+
|
|
47
|
+
--grid-min is deliberately NOT read here. The subdivision below runs on
|
|
48
|
+
@container thresholds, which cannot resolve a custom property, so a hook
|
|
49
|
+
honored in the baseline would fall silent the moment a query container is
|
|
50
|
+
added. Item width is .grid's contract; column density is .grid-N's. */
|
|
51
|
+
:where(.grid-2, .grid-3, .grid-4, .grid-6) {
|
|
52
|
+
display: grid;
|
|
53
|
+
gap: var(--gap);
|
|
54
|
+
grid-template-columns: repeat(
|
|
55
|
+
auto-fill,
|
|
56
|
+
minmax(
|
|
57
|
+
min(
|
|
58
|
+
100%,
|
|
59
|
+
max(16rem, calc((100% - (var(--grid-count) - 1) * var(--gap)) / var(--grid-count)))
|
|
60
|
+
),
|
|
61
|
+
1fr
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.grid-2 {
|
|
67
|
+
--grid-count: 2;
|
|
68
|
+
}
|
|
69
|
+
.grid-3 {
|
|
70
|
+
--grid-count: 3;
|
|
71
|
+
}
|
|
72
|
+
.grid-4 {
|
|
73
|
+
--grid-count: 4;
|
|
74
|
+
}
|
|
75
|
+
.grid-6 {
|
|
76
|
+
--grid-count: 6;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Grid items keep an automatic minimum inline size (min-content), which can
|
|
80
|
+
overflow a track sized minmax(0, 1fr). Layout primitives remove that
|
|
81
|
+
intrinsic floor so their overflow-safe track sizing holds; authors only
|
|
82
|
+
need .min-inline-size-0 outside primitives that do not already remove this
|
|
83
|
+
floor. */
|
|
84
|
+
:where(.grid, .grid-2, .grid-3, .grid-4, .grid-6) > * {
|
|
85
|
+
min-inline-size: 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Balanced subdivision, opted into by an ancestor query container.
|
|
89
|
+
*
|
|
90
|
+
* .grid-2 2 -> 1
|
|
91
|
+
* .grid-3 3 -> 1
|
|
92
|
+
* .grid-4 4 -> 2 -> 1
|
|
93
|
+
* .grid-6 6 -> 3 -> 2 -> 1
|
|
94
|
+
*
|
|
95
|
+
* Every state is a divisor of N, so all items keep the same width and no
|
|
96
|
+
* state produces a visibly uneven split such as 5 + 1 or 4 + 2. Intermediate
|
|
97
|
+
* counts are skipped on purpose: do not "complete" these chains with the
|
|
98
|
+
* missing integers, and do not replace them with an unbounded auto-fit.
|
|
99
|
+
*
|
|
100
|
+
* The query container must be an ancestor. An element cannot query its own
|
|
101
|
+
* size without taking inline-size containment, which destroys its intrinsic
|
|
102
|
+
* inline contribution and collapses it inside .cluster, .topbar, floats, and
|
|
103
|
+
* any shrink-to-fit box. See docs/design-notes/grid.md.
|
|
104
|
+
*
|
|
105
|
+
* Three shared thresholds, not one calibrated per preset: 28rem opens the
|
|
106
|
+
* first horizontal density, 48rem opens three columns, 64rem opens the dense
|
|
107
|
+
* 4 and 6. N already carries the author's statement about item size, so
|
|
108
|
+
* tuning every step to land the same item width would erase that signal —
|
|
109
|
+
* .grid-6 at 64rem gives ~161px per tile, which is the point of choosing it.
|
|
110
|
+
* Probed with the content each preset targets: large cards for .grid-2 down
|
|
111
|
+
* to stats, avatars, and thumbnails for .grid-6.
|
|
112
|
+
*
|
|
113
|
+
* Ascending min-width only: the last matching step wins by cascade order.
|
|
114
|
+
*/
|
|
115
|
+
@supports (container-type: inline-size) {
|
|
116
|
+
.container-query :where(.grid-2, .grid-3, .grid-4, .grid-6) {
|
|
117
|
+
grid-template-columns: 1fr;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@container (min-width: 28rem) {
|
|
121
|
+
.container-query :where(.grid-2, .grid-4, .grid-6) {
|
|
122
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@container (min-width: 48rem) {
|
|
127
|
+
.container-query :where(.grid-3, .grid-6) {
|
|
128
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@container (min-width: 64rem) {
|
|
133
|
+
.container-query :where(.grid-4) {
|
|
134
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.container-query :where(.grid-6) {
|
|
138
|
+
grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Layout — composition primitives. Readable cascade order: container-query
|
|
3
|
+
* first (a local opt-in, unrelated to the primitives), then the primitives.
|
|
4
|
+
*
|
|
5
|
+
* Selection:
|
|
6
|
+
* .stack vertical flow
|
|
7
|
+
* .cluster wrapping inline group
|
|
8
|
+
* .center centered content column
|
|
9
|
+
* .frame fixed-ratio media frame
|
|
10
|
+
* .app-shell full-height application shell
|
|
11
|
+
* .media fixed media + flexible content
|
|
12
|
+
* .switcher peer regions: all horizontal or all stacked
|
|
13
|
+
* .sidebar-layout main content + secondary aside
|
|
14
|
+
* .grid responsive collection; item width is the contract
|
|
15
|
+
* .grid-N structural density of N; wrap in .container-query for
|
|
16
|
+
* balanced subdivision (in grid.css)
|
|
17
|
+
* .column-layout explicit 12-column region composition; the author owns
|
|
18
|
+
* narrow-container recomposition
|
|
19
|
+
* .scroller theme-aware scrollbar density and colour
|
|
20
|
+
* .scroll-snap snap rails
|
|
21
|
+
* .scroll-target anchor scroll offset for sticky headers
|
|
22
|
+
* .topbar sticky app-shell top bar
|
|
23
|
+
* .measure readable content measure cap
|
|
24
|
+
*
|
|
25
|
+
* Prefer these compositions over viewport breakpoint utilities.
|
|
26
|
+
*/
|
|
27
|
+
@import "./container-query.css";
|
|
28
|
+
@import "./stack.css";
|
|
29
|
+
@import "./cluster.css";
|
|
30
|
+
@import "./center.css";
|
|
31
|
+
@import "./frame.css";
|
|
32
|
+
@import "./app-shell.css";
|
|
33
|
+
@import "./media.css";
|
|
34
|
+
@import "./switcher.css";
|
|
35
|
+
@import "./sidebar-layout.css";
|
|
36
|
+
@import "./grid.css";
|
|
37
|
+
@import "./column-layout.css";
|
|
38
|
+
@import "./scroller.css";
|
|
39
|
+
@import "./scroll-snap.css";
|
|
40
|
+
@import "./scroll-target.css";
|
|
41
|
+
@import "./topbar.css";
|
|
42
|
+
@import "./measure.css";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* Media object: fixed-width leading element (avatar, icon, image) + flexible
|
|
2
|
+
trailing content. Replaces the repeated "auto minmax(0, 1fr)" pattern in
|
|
3
|
+
author cards, comments, meta rows, and similar compositions.
|
|
4
|
+
Child contract: exactly two direct children (leading media + content). */
|
|
5
|
+
.media {
|
|
6
|
+
display: grid;
|
|
7
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
8
|
+
gap: var(--gap);
|
|
9
|
+
align-items: start;
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* Anchor scroll offset. A #hash link or scrollIntoView() places the target
|
|
2
|
+
* flush at the scroll container's start edge, which hides it behind a sticky
|
|
3
|
+
* header (.topbar) or underlaps it; scroll-margin reserves space on the
|
|
4
|
+
* target itself so the browser stops early.
|
|
5
|
+
*
|
|
6
|
+
* Apply to the elements that are link targets: <section id="...">, headings,
|
|
7
|
+
* etc. The scrollspy enhancer only reads ids — this offset is what actually
|
|
8
|
+
* shapes where the page lands, and the two are independent.
|
|
9
|
+
*
|
|
10
|
+
* Public hooks:
|
|
11
|
+
* --scroll-target-offset
|
|
12
|
+
*/
|
|
13
|
+
.scroll-target {
|
|
14
|
+
scroll-margin-block-start: var(--scroll-target-offset, var(--space-60));
|
|
15
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Scroller — optional scrollbar treatment for overflow regions.
|
|
3
|
+
*
|
|
4
|
+
* Apply to any scroll container (.overflow-auto, sidebars, long tables). Only
|
|
5
|
+
* the standard properties are used: the engine keeps drawing the scrollbar,
|
|
6
|
+
* this hands it the theme's density and colour. No ::-webkit-scrollbar chrome —
|
|
7
|
+
* rebuilding a thumb by hand means re-implementing the hover, the corner and
|
|
8
|
+
* the light/dark adaptation that color-scheme already gives for free, and the
|
|
9
|
+
* two never match anyway.
|
|
10
|
+
*
|
|
11
|
+
* Above the capability floor: scrollbar-color lands in Chromium 121 and
|
|
12
|
+
* Safari 18.2, the floor is Chromium 99 / Safari 15.4. Older engines ignore
|
|
13
|
+
* both declarations and draw their native scrollbar — the intended fallback,
|
|
14
|
+
* not a broken state.
|
|
15
|
+
*
|
|
16
|
+
* Styles the scrollbar only; it does not create the overflow. Pair it with
|
|
17
|
+
* .overflow-auto or a component that already scrolls.
|
|
18
|
+
*
|
|
19
|
+
* Public hooks:
|
|
20
|
+
* --scroller-track
|
|
21
|
+
* --scroller-thumb
|
|
22
|
+
*/
|
|
23
|
+
.scroller {
|
|
24
|
+
--scroller-track: transparent;
|
|
25
|
+
--scroller-thumb: var(--border);
|
|
26
|
+
|
|
27
|
+
scrollbar-color: var(--scroller-thumb) var(--scroller-track);
|
|
28
|
+
scrollbar-width: thin;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Opt-in gutter: reserves space so the layout does not shift when
|
|
32
|
+
the scrollbar appears. Use alongside a known-overflow container. */
|
|
33
|
+
.scroller.stable-gutter {
|
|
34
|
+
scrollbar-gutter: stable;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@supports (color: color-mix(in oklch, red, white)) {
|
|
38
|
+
.scroller {
|
|
39
|
+
--scroller-thumb: color-mix(in oklch, var(--text-muted), transparent 55%);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Main content + secondary region.
|
|
3
|
+
*
|
|
4
|
+
* The children wrap according to the space allocated to this layout, without
|
|
5
|
+
* an ancestor opt-in or viewport threshold. The large growth ratio lets the
|
|
6
|
+
* main region reclaim the full row when the aside wraps instead of remaining
|
|
7
|
+
* at its minimum viable width.
|
|
8
|
+
*
|
|
9
|
+
* Public hooks:
|
|
10
|
+
* --sidebar-content-min
|
|
11
|
+
* --sidebar-layout-size
|
|
12
|
+
*
|
|
13
|
+
* Child contract: the first and last direct children carry the sizing roles.
|
|
14
|
+
*/
|
|
15
|
+
.sidebar-layout {
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-wrap: wrap;
|
|
18
|
+
gap: var(--gap);
|
|
19
|
+
align-items: flex-start;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.sidebar-layout > :first-child {
|
|
23
|
+
flex-basis: 0;
|
|
24
|
+
flex-grow: 999;
|
|
25
|
+
min-inline-size: min(100%, var(--sidebar-content-min, 30rem));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.sidebar-layout > :last-child {
|
|
29
|
+
flex-basis: var(--sidebar-layout-size, 18rem);
|
|
30
|
+
flex-grow: 1;
|
|
31
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* Vertical flow. .stack owns the block axis only: block margins reset, inline
|
|
2
|
+
margins stay free (self-centering children via margin-inline: auto keep
|
|
3
|
+
working).
|
|
4
|
+
Child contract: direct children become flex items; margins reset, spacing
|
|
5
|
+
comes from --gap.
|
|
6
|
+
|
|
7
|
+
--gap is inherited, so a container that composes other primitives sets its
|
|
8
|
+
own rhythm on the `gap` property, not on the token: setting --gap on a
|
|
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 .sm/.lg set it
|
|
11
|
+
deliberately so a density context reaches nested layouts. See
|
|
12
|
+
docs/pages/layout/stack.md. */
|
|
13
|
+
.stack {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
gap: var(--gap);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.stack > * {
|
|
20
|
+
margin-block: 0;
|
|
21
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Peer regions that switch as a group.
|
|
3
|
+
*
|
|
4
|
+
* All children sit side by side when they all fit; otherwise every child
|
|
5
|
+
* stacks. Unlike .grid, this recipe cannot create a partial row such as two
|
|
6
|
+
* items plus one orphan.
|
|
7
|
+
*
|
|
8
|
+
* Public hooks:
|
|
9
|
+
* --switcher-threshold
|
|
10
|
+
*
|
|
11
|
+
* Child contract: every direct child is a peer; the flex-basis applies to all.
|
|
12
|
+
*/
|
|
13
|
+
.switcher {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-wrap: wrap;
|
|
16
|
+
gap: var(--gap);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.switcher > * {
|
|
20
|
+
flex-basis: calc((var(--switcher-threshold, 40rem) - 100%) * 999);
|
|
21
|
+
flex-grow: 1;
|
|
22
|
+
min-inline-size: 0;
|
|
23
|
+
}
|
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Additional layout primitives not included in the core bundle.
|
|
3
|
-
*
|
|
4
|
-
* Import this file explicitly when a project needs these patterns.
|
|
5
|
-
*
|
|
6
|
-
* Public hooks:
|
|
7
|
-
* --switcher-threshold
|
|
8
|
-
*/
|
|
9
|
-
.switcher {
|
|
10
|
-
display: flex;
|
|
11
|
-
flex-wrap: wrap;
|
|
12
|
-
gap: var(--gap);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.switcher > * {
|
|
16
|
-
flex-basis: calc((var(--switcher-threshold, 40rem) - 100%) * 999);
|
|
17
|
-
flex-grow: 1;
|
|
18
|
-
min-inline-size: 0;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
1
|
/*
|
|
22
2
|
* Sticky, frosted top bar shell for app/dashboard shells (persistent
|
|
23
3
|
* sidebar + topbar layouts). Owns positioning, stacking, and the frosted
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/* Lead paragraph: a slightly larger, more relaxed reading-introduction text.
|
|
2
|
+
Non-fluid and always available — use .text-lead from the optional fluid
|
|
3
|
+
module for marketing/display surfaces. */
|
|
4
|
+
.lead {
|
|
5
|
+
font-size: 1.125em;
|
|
6
|
+
line-height: var(--line-height-relaxed);
|
|
7
|
+
}
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
keeps a readable bordered pill everywhere else. */
|
|
43
43
|
@supports (color: color-mix(in oklch, red, white)) {
|
|
44
44
|
.overline.pill {
|
|
45
|
-
border-color: color-mix(in
|
|
46
|
-
background: color-mix(in
|
|
45
|
+
border-color: color-mix(in oklab, var(--intent, var(--border)) 35%, var(--border));
|
|
46
|
+
background: color-mix(in oklab, var(--intent, transparent) 6%, var(--surface));
|
|
47
47
|
}
|
|
48
48
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* --prose-h2
|
|
15
15
|
* --prose-h3
|
|
16
16
|
* --prose-h4
|
|
17
|
+
* --link
|
|
17
18
|
*/
|
|
18
19
|
|
|
19
20
|
.prose {
|
|
@@ -29,19 +30,23 @@
|
|
|
29
30
|
--prose-h4: 1.125rem;
|
|
30
31
|
|
|
31
32
|
max-inline-size: var(--prose-measure);
|
|
32
|
-
color: var(--text);
|
|
33
33
|
font-size: var(--prose-size);
|
|
34
34
|
line-height: var(--prose-line-height);
|
|
35
35
|
overflow-wrap: break-word;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
/* Vertical rhythm belongs to the direct block that participates in the
|
|
39
|
+
document, so it must not be injected into the internals of a nested
|
|
40
|
+
component (e.g. a .table-wrap around a .table would trap the margin). */
|
|
41
|
+
.prose > * + * {
|
|
42
|
+
margin-block-start: var(--prose-flow);
|
|
40
43
|
}
|
|
41
44
|
|
|
45
|
+
/* Heading hierarchy remains low-specificity and contextual: a surface may
|
|
46
|
+
remap --heading without prose repainting it with the root text color. */
|
|
42
47
|
.prose :where(h1, h2, h3, h4, h5, h6) {
|
|
43
48
|
margin-block: 1.5em 0;
|
|
44
|
-
color: var(--text);
|
|
49
|
+
color: var(--heading, var(--text));
|
|
45
50
|
font-weight: var(--font-weight-strong);
|
|
46
51
|
line-height: var(--prose-heading-line-height);
|
|
47
52
|
}
|
|
@@ -54,48 +59,71 @@
|
|
|
54
59
|
margin-block-start: 0;
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
.prose
|
|
62
|
+
/* Element recipes are wrapped in :where() so every .prose rule is 0-1-0.
|
|
63
|
+
An explicit component/util class can therefore override prose defaults
|
|
64
|
+
regardless of the underlying element — a .btn or .alert link must not be
|
|
65
|
+
reshaped just because it lives inside authored content. */
|
|
66
|
+
.prose :where(h1) {
|
|
58
67
|
font-size: var(--prose-h1);
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
.prose h2 {
|
|
70
|
+
.prose :where(h2) {
|
|
62
71
|
font-size: var(--prose-h2);
|
|
63
72
|
}
|
|
64
73
|
|
|
65
|
-
.prose h3 {
|
|
74
|
+
.prose :where(h3) {
|
|
66
75
|
font-size: var(--prose-h3);
|
|
67
76
|
}
|
|
68
77
|
|
|
69
|
-
.prose h4 {
|
|
78
|
+
.prose :where(h4) {
|
|
70
79
|
font-size: var(--prose-h4);
|
|
71
80
|
}
|
|
72
81
|
|
|
73
|
-
.
|
|
74
|
-
|
|
82
|
+
/* Link ink defaults to --primary. --link is an optional escape hatch: a
|
|
83
|
+
theme whose primary is too light or too vivid to read as inline text sets
|
|
84
|
+
it without having to restate the rule. */
|
|
85
|
+
.prose :where(a) {
|
|
86
|
+
color: var(--link, var(--primary));
|
|
75
87
|
text-decoration-line: underline;
|
|
76
88
|
text-decoration-thickness: 0.08em;
|
|
77
89
|
text-underline-offset: 0.18em;
|
|
78
90
|
}
|
|
79
91
|
|
|
80
|
-
.prose a:hover {
|
|
92
|
+
.prose :where(a:hover) {
|
|
81
93
|
text-decoration-thickness: 0.12em;
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
@supports (color: color-mix(in oklch, red, white)) {
|
|
85
|
-
.prose a:hover {
|
|
86
|
-
color: color-mix(in
|
|
97
|
+
.prose :where(a:hover) {
|
|
98
|
+
color: color-mix(in oklab, var(--link, var(--primary)), var(--text) 15%);
|
|
87
99
|
}
|
|
88
100
|
}
|
|
89
101
|
|
|
102
|
+
/* Rich-text descendants are intentional here. Components and application UI
|
|
103
|
+
belong outside the prose boundary. */
|
|
90
104
|
.prose :where(ul, ol) {
|
|
91
105
|
padding-inline-start: 1.5em;
|
|
92
106
|
}
|
|
93
107
|
|
|
94
|
-
.prose li + li {
|
|
108
|
+
.prose :where(li + li) {
|
|
95
109
|
margin-block-start: 0.35em;
|
|
96
110
|
}
|
|
97
111
|
|
|
98
|
-
|
|
112
|
+
/* Description lists keep document flow: a valid <dl> may pair several <dt>
|
|
113
|
+
with one <dd> or the reverse, so no grid layout is imposed here. */
|
|
114
|
+
.prose :where(dt) {
|
|
115
|
+
font-weight: var(--font-weight-strong);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.prose :where(dd) {
|
|
119
|
+
margin-inline-start: 1.5em;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.prose :where(dd + dt) {
|
|
123
|
+
margin-block-start: 0.75em;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.prose :where(code) {
|
|
99
127
|
padding: 0.15em 0.3em;
|
|
100
128
|
border-radius: var(--radius-sm);
|
|
101
129
|
background: var(--surface-subtle);
|
|
@@ -104,7 +132,8 @@
|
|
|
104
132
|
}
|
|
105
133
|
|
|
106
134
|
/* Visual twin of .key (components/key.css) — keep the two recipes in sync. */
|
|
107
|
-
|
|
135
|
+
/* @sync key-recipe */
|
|
136
|
+
.prose :where(kbd) {
|
|
108
137
|
display: inline-flex;
|
|
109
138
|
align-items: center;
|
|
110
139
|
justify-content: center;
|
|
@@ -123,8 +152,9 @@
|
|
|
123
152
|
white-space: nowrap;
|
|
124
153
|
vertical-align: 0.08em;
|
|
125
154
|
}
|
|
155
|
+
/* @sync key-recipe */
|
|
126
156
|
|
|
127
|
-
.prose pre {
|
|
157
|
+
.prose :where(pre) {
|
|
128
158
|
overflow-x: auto;
|
|
129
159
|
padding: 1em;
|
|
130
160
|
border: var(--border-width) solid var(--border);
|
|
@@ -132,53 +162,38 @@
|
|
|
132
162
|
background: var(--surface-subtle);
|
|
133
163
|
}
|
|
134
164
|
|
|
135
|
-
.prose pre code {
|
|
165
|
+
.prose :where(pre) :where(code) {
|
|
136
166
|
padding: 0;
|
|
137
167
|
background: transparent;
|
|
138
168
|
}
|
|
139
169
|
|
|
140
|
-
.prose blockquote {
|
|
170
|
+
.prose :where(blockquote) {
|
|
141
171
|
padding-inline-start: 1em;
|
|
142
172
|
border-inline-start: var(--border-width) solid var(--border);
|
|
143
173
|
color: var(--text-muted);
|
|
144
174
|
}
|
|
145
175
|
|
|
146
|
-
.prose figure {
|
|
176
|
+
.prose :where(figure) {
|
|
147
177
|
margin-inline: 0;
|
|
148
178
|
}
|
|
149
179
|
|
|
150
|
-
.prose figcaption {
|
|
180
|
+
.prose :where(figcaption) {
|
|
151
181
|
margin-block-start: 0.5em;
|
|
152
182
|
color: var(--text-muted);
|
|
153
183
|
font-size: 0.875em;
|
|
154
184
|
}
|
|
155
185
|
|
|
156
|
-
.prose table {
|
|
186
|
+
.prose :where(table) {
|
|
157
187
|
inline-size: 100%;
|
|
158
188
|
border-collapse: collapse;
|
|
159
189
|
}
|
|
160
190
|
|
|
161
|
-
.prose th,
|
|
162
|
-
.prose td {
|
|
191
|
+
.prose :where(th, td) {
|
|
163
192
|
padding: 0.5em;
|
|
164
193
|
border-block-end: var(--border-width) solid var(--border);
|
|
165
194
|
text-align: start;
|
|
166
195
|
}
|
|
167
196
|
|
|
168
|
-
/* Lead paragraph: a slightly larger, more relaxed reading-introduction text.
|
|
169
|
-
Non-fluid and always available — use .text-lead from the optional fluid
|
|
170
|
-
module for marketing/display surfaces. */
|
|
171
|
-
.lead {
|
|
172
|
-
font-size: 1.125em;
|
|
173
|
-
line-height: var(--line-height-relaxed);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/* Readable content measure. Composes with .center for centering or with
|
|
177
|
-
margin-inline: auto for a one-off centered block. */
|
|
178
|
-
.measure {
|
|
179
|
-
max-inline-size: var(--prose-measure, 70ch);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
197
|
.prose.sm {
|
|
183
198
|
--prose-size: 0.9375rem;
|
|
184
199
|
--prose-h1: 1.75rem;
|
package/src/js/escape.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Per-document LIFO dismissal for transient UI that closes on Escape.
|
|
3
|
+
* Consumers register only while visible, so the topmost dismissable UI owns
|
|
4
|
+
* one key press and pinned or hidden UI never consumes it.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const dismissableStacks = new WeakMap();
|
|
8
|
+
|
|
9
|
+
export function registerEscapeDismissal(element, dismiss) {
|
|
10
|
+
const doc = element.ownerDocument;
|
|
11
|
+
let stack = dismissableStacks.get(doc);
|
|
12
|
+
if (!stack) {
|
|
13
|
+
stack = [];
|
|
14
|
+
dismissableStacks.set(doc, stack);
|
|
15
|
+
doc.addEventListener("keydown", onDocumentEscape);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const entry = { element, dismiss };
|
|
19
|
+
stack.push(entry);
|
|
20
|
+
|
|
21
|
+
return () => {
|
|
22
|
+
const index = stack.indexOf(entry);
|
|
23
|
+
if (index >= 0) stack.splice(index, 1);
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function onDocumentEscape(event) {
|
|
28
|
+
if (event.key !== "Escape" || event.ctrlKey || event.altKey || event.shiftKey) return;
|
|
29
|
+
|
|
30
|
+
const stack = dismissableStacks.get(event.currentTarget);
|
|
31
|
+
const entry = stack.at(-1);
|
|
32
|
+
if (!entry) return;
|
|
33
|
+
|
|
34
|
+
event.preventDefault();
|
|
35
|
+
entry.dismiss({ restoreFocus: true });
|
|
36
|
+
}
|