actual-css 0.3.1 → 0.4.1
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 +109 -3
- package/README.md +35 -30
- package/dist/actual.css +66 -4958
- package/dist/actual.full.css +7548 -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/app-ui.md +33 -0
- package/docs/design-notes/cascade-layer.md +34 -13
- package/docs/design-notes/column-layout.md +677 -0
- package/docs/design-notes/enhancement-contract.md +25 -0
- package/docs/design-notes/forced-colors.md +3 -0
- package/docs/design-notes/grid.md +785 -0
- package/docs/design-notes/naming.md +15 -0
- package/docs/design-notes/references.md +1 -1
- package/docs/design-notes/widget-primitives.md +1 -0
- package/docs/navigation.json +12 -2
- package/docs/pages/components/alert.md +21 -14
- package/docs/pages/components/app-navigation.md +34 -0
- package/docs/pages/components/aura.md +1 -1
- package/docs/pages/components/avatar.md +16 -6
- package/docs/pages/components/badge.md +9 -7
- package/docs/pages/components/button.md +46 -16
- package/docs/pages/components/card.md +17 -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 +9 -7
- package/docs/pages/components/indicator.md +52 -0
- package/docs/pages/components/list.md +62 -0
- package/docs/pages/components/navbar.md +15 -8
- package/docs/pages/components/pagination.md +4 -3
- package/docs/pages/components/rating.md +68 -0
- package/docs/pages/components/skeleton.md +4 -4
- package/docs/pages/components/spinner.md +5 -5
- package/docs/pages/components/steps.md +57 -0
- package/docs/pages/components/table.md +30 -5
- package/docs/pages/enhancements/flyout.md +15 -15
- 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 +7 -8
- package/docs/pages/enhancements/tooltip.md +1 -1
- package/docs/pages/examples/overview.md +47 -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 +33 -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 +44 -48
- 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 +192 -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-layout.md +57 -0
- package/docs/pages/layout/app-shell.md +28 -7
- package/docs/pages/layout/center.md +12 -1
- package/docs/pages/layout/choosing.md +54 -0
- package/docs/pages/layout/column-layout.md +206 -0
- package/docs/pages/layout/frame.md +6 -1
- package/docs/pages/layout/grid.md +133 -102
- 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 +38 -18
- package/docs/pages/layout/stack.md +70 -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/icon-slot.md +61 -0
- 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 +38 -40
- 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 +43 -13
- package/scripts/reserved-classes.json +278 -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/app-nav.css +76 -0
- package/src/css/components/avatar.css +4 -0
- package/src/css/components/badge.css +19 -19
- package/src/css/components/breadcrumb.css +1 -1
- package/src/css/components/button.css +20 -32
- package/src/css/components/card.css +34 -9
- package/src/css/{optional → components}/chat.css +0 -6
- package/src/css/components/drawer.css +24 -5
- package/src/css/{optional → components}/fab.css +1 -7
- package/src/css/components/flyout.css +5 -0
- package/src/css/components/index.css +37 -27
- package/src/css/components/indicator.css +47 -0
- package/src/css/components/join.css +8 -1
- package/src/css/components/key.css +2 -0
- package/src/css/components/list.css +83 -0
- package/src/css/components/menu.css +11 -8
- package/src/css/components/meter.css +4 -1
- package/src/css/components/modal.css +5 -17
- package/src/css/components/navbar.css +6 -15
- package/src/css/components/pagination.css +11 -0
- package/src/css/components/rating.css +101 -0
- package/src/css/components/spinner.css +5 -0
- package/src/css/components/status-bar.css +5 -0
- package/src/css/components/steps.css +122 -0
- package/src/css/components/surface.css +5 -0
- package/src/css/components/tab.css +20 -22
- package/src/css/components/table.css +6 -0
- package/src/css/components/tooltip.css +3 -7
- package/src/css/{focus.css → core/focus.css} +0 -8
- package/src/css/core/index.css +9 -0
- package/src/css/core/print.css +18 -0
- package/src/css/{theme.css → core/theme.css} +19 -19
- package/src/css/{tokens.css → core/tokens.css} +16 -4
- package/src/css/{variants.css → core/variants.css} +3 -2
- package/src/css/{optional → effects}/aura.css +1 -10
- package/src/css/effects/index.css +2 -0
- package/src/css/forms/base.css +8 -0
- package/src/css/forms/choice-card.css +7 -15
- 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 +25 -16
- package/src/css/forms/range.css +14 -4
- package/src/css/forms/switch.css +31 -29
- package/src/css/layout/app-layout.css +102 -0
- package/src/css/layout/app-shell.css +6 -0
- package/src/css/layout/center.css +14 -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 +15 -0
- package/src/css/layout/frame.css +19 -0
- package/src/css/layout/grid.css +155 -0
- package/src/css/layout/index.css +44 -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 +52 -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} +57 -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 +48 -19
- 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/docs/pages/layout/list.md +0 -41
- 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/{base.css → core/base.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 → 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
package/src/css/forms/switch.css
CHANGED
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
* --switch-knob-margin
|
|
14
14
|
* --switch-knob-size
|
|
15
15
|
* --switch-travel
|
|
16
|
+
*
|
|
17
|
+
* The track is sized from --choice-control-size (tokens.css), not
|
|
18
|
+
* --control-size: a switch sits on a line of text next to a checkbox, so it
|
|
19
|
+
* follows the control typography rather than field density.
|
|
16
20
|
*/
|
|
17
21
|
/* Intent boundary — inherited intents do not tint this component.
|
|
18
22
|
Local intent classes still win because :where() has zero specificity. */
|
|
@@ -25,10 +29,22 @@
|
|
|
25
29
|
--switch-bg: var(--border);
|
|
26
30
|
--switch-border: var(--border);
|
|
27
31
|
--switch-knob: var(--surface-raised);
|
|
28
|
-
--switch-
|
|
29
|
-
--switch-
|
|
30
|
-
--switch-knob-margin: calc(var(--switch-block-size) * 0.
|
|
31
|
-
|
|
32
|
+
--switch-block-size: calc(var(--choice-control-size) + 0.125em);
|
|
33
|
+
--switch-width: calc(var(--switch-block-size) * 1.8);
|
|
34
|
+
--switch-knob-margin: calc(var(--switch-block-size) * 0.08);
|
|
35
|
+
|
|
36
|
+
/* The knob is concentric in the track: --switch-block-size is a border-box
|
|
37
|
+
height, so the borders have to come out before the inset is applied on
|
|
38
|
+
both axes. Deriving from the outer height instead leaves the knob
|
|
39
|
+
--border-width closer to the rails than to the ends — a 1px error on a
|
|
40
|
+
~1.6px inset, which reads as a knob squeezed against the track. */
|
|
41
|
+
--switch-knob-size: calc(
|
|
42
|
+
var(--switch-block-size) -
|
|
43
|
+
2 *
|
|
44
|
+
var(--border-width) -
|
|
45
|
+
2 *
|
|
46
|
+
var(--switch-knob-margin)
|
|
47
|
+
);
|
|
32
48
|
--switch-travel: calc(
|
|
33
49
|
var(--switch-width) -
|
|
34
50
|
var(--switch-knob-size) -
|
|
@@ -52,6 +68,11 @@
|
|
|
52
68
|
cursor: pointer;
|
|
53
69
|
}
|
|
54
70
|
|
|
71
|
+
/* No elevation on the knob: --shadow is a surface elevation (card, modal,
|
|
72
|
+
drawer, fab), and at this size its 12px blur reads as a smudge that spills
|
|
73
|
+
past the rail while carrying no state. The range thumb — the same kind of
|
|
74
|
+
small moving part — stays flat for the same reason. The concentric inset
|
|
75
|
+
against the track is what makes the knob legible. */
|
|
55
76
|
.switch::before {
|
|
56
77
|
content: "";
|
|
57
78
|
inline-size: var(--switch-knob-size);
|
|
@@ -59,11 +80,9 @@
|
|
|
59
80
|
margin-inline: var(--switch-knob-margin);
|
|
60
81
|
border-radius: var(--radius-full);
|
|
61
82
|
background: var(--switch-knob);
|
|
62
|
-
box-shadow: var(--shadow);
|
|
63
83
|
transition:
|
|
64
84
|
transform var(--duration),
|
|
65
|
-
background-color var(--duration)
|
|
66
|
-
box-shadow var(--duration);
|
|
85
|
+
background-color var(--duration);
|
|
67
86
|
}
|
|
68
87
|
|
|
69
88
|
.switch:hover:not(:disabled) {
|
|
@@ -101,28 +120,11 @@
|
|
|
101
120
|
}
|
|
102
121
|
|
|
103
122
|
@media (forced-colors: active) {
|
|
104
|
-
/*
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
--switch-knob: ButtonText;
|
|
109
|
-
}
|
|
110
|
-
|
|
123
|
+
/* The host intentionally stays under UA control for platform contrast and
|
|
124
|
+
focus. The custom knob has no native edge, so this system-color border
|
|
125
|
+
keeps it distinct from the remapped track. If real HCM testing ever needs
|
|
126
|
+
an opt-out, scope it to ::before—never to the interactive input host. */
|
|
111
127
|
.switch::before {
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
.switch:checked {
|
|
116
|
-
--switch-bg: Highlight;
|
|
117
|
-
--switch-border: Highlight;
|
|
118
|
-
--switch-knob: HighlightText;
|
|
119
|
-
forced-color-adjust: none;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
.switch:disabled {
|
|
123
|
-
--switch-bg: Canvas;
|
|
124
|
-
--switch-border: GrayText;
|
|
125
|
-
--switch-knob: GrayText;
|
|
126
|
-
forced-color-adjust: auto;
|
|
128
|
+
border: var(--border-width) solid ButtonText;
|
|
127
129
|
}
|
|
128
130
|
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Specialized application shell recipe: one exact DOM structure uses a
|
|
3
|
+
* bottom navigation bar when narrow and a labelled side navigation when space
|
|
4
|
+
* permits. The main region owns scrolling, keeping both pieces of application
|
|
5
|
+
* chrome available without fixed offsets. This is not a generic page wrapper
|
|
6
|
+
* or a public-site layout; all three direct regions are part of the contract.
|
|
7
|
+
*
|
|
8
|
+
* Public hook:
|
|
9
|
+
* --app-nav-side-size
|
|
10
|
+
*/
|
|
11
|
+
.app-layout {
|
|
12
|
+
--app-nav-side-size: 12rem;
|
|
13
|
+
|
|
14
|
+
display: grid;
|
|
15
|
+
grid-template:
|
|
16
|
+
"topbar" auto
|
|
17
|
+
"main" minmax(0, 1fr)
|
|
18
|
+
"nav" auto
|
|
19
|
+
/ minmax(0, 1fr);
|
|
20
|
+
block-size: var(--viewport-block);
|
|
21
|
+
min-inline-size: 0;
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.app-layout > .topbar {
|
|
26
|
+
grid-area: topbar;
|
|
27
|
+
padding-block-start: calc(var(--space-30) + env(safe-area-inset-top));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.app-layout > .app-main {
|
|
31
|
+
grid-area: main;
|
|
32
|
+
min-inline-size: 0;
|
|
33
|
+
min-block-size: 0;
|
|
34
|
+
overflow: auto;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.app-layout > .app-nav {
|
|
38
|
+
grid-area: nav;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* The main grid area keeps the FAB clear of bottom navigation without making
|
|
42
|
+
either component copy the other's geometry. */
|
|
43
|
+
.app-layout > .fab {
|
|
44
|
+
position: relative;
|
|
45
|
+
grid-area: main;
|
|
46
|
+
place-self: end;
|
|
47
|
+
inset: auto;
|
|
48
|
+
margin: var(--fab-offset);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@media (min-width: 48rem) {
|
|
52
|
+
.app-layout {
|
|
53
|
+
grid-template:
|
|
54
|
+
"nav topbar" auto
|
|
55
|
+
"nav main" minmax(0, 1fr)
|
|
56
|
+
/ var(--app-nav-side-size) minmax(0, 1fr);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.app-layout > .app-nav {
|
|
60
|
+
position: sticky;
|
|
61
|
+
inset-block: 0;
|
|
62
|
+
grid-auto-flow: row;
|
|
63
|
+
grid-auto-rows: minmax(var(--control-size-lg), auto);
|
|
64
|
+
grid-auto-columns: auto;
|
|
65
|
+
align-content: start;
|
|
66
|
+
gap: var(--space-20);
|
|
67
|
+
min-block-size: 0;
|
|
68
|
+
padding-block: max(var(--space-20), env(safe-area-inset-top))
|
|
69
|
+
max(var(--space-20), env(safe-area-inset-bottom));
|
|
70
|
+
padding-inline: var(--space-20);
|
|
71
|
+
border-block-start: 0;
|
|
72
|
+
border-inline-end: var(--border-width) solid var(--ui-border, var(--border));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.app-layout > .app-nav > a {
|
|
76
|
+
grid-template-rows: auto;
|
|
77
|
+
grid-template-columns: 1.5rem minmax(0, 1fr);
|
|
78
|
+
place-content: center start;
|
|
79
|
+
align-items: center;
|
|
80
|
+
justify-items: start;
|
|
81
|
+
gap: var(--space-30);
|
|
82
|
+
padding-inline: var(--space-30);
|
|
83
|
+
text-align: start;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Side navigation no longer occupies the viewport's bottom safe area. */
|
|
87
|
+
.app-layout > .fab {
|
|
88
|
+
margin-block-end: max(var(--fab-offset), env(safe-area-inset-bottom));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@media print {
|
|
93
|
+
.app-layout {
|
|
94
|
+
display: block;
|
|
95
|
+
block-size: auto;
|
|
96
|
+
overflow: visible;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.app-layout > .app-main {
|
|
100
|
+
overflow: visible;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Centered content column with a maximum measure.
|
|
3
|
+
*
|
|
4
|
+
* Public hooks:
|
|
5
|
+
* --center-size
|
|
6
|
+
* --center-pad
|
|
7
|
+
*/
|
|
8
|
+
.center {
|
|
9
|
+
box-sizing: content-box;
|
|
10
|
+
inline-size: calc(100% - var(--center-pad, 1rem) * 2);
|
|
11
|
+
max-inline-size: var(--center-size, 72rem);
|
|
12
|
+
margin-inline: auto;
|
|
13
|
+
padding-inline: var(--center-pad, 1rem);
|
|
14
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Inline group that wraps when space runs out.
|
|
3
|
+
*
|
|
4
|
+
* Public hooks:
|
|
5
|
+
* --cluster-align
|
|
6
|
+
* --cluster-justify
|
|
7
|
+
* --cluster-wrap
|
|
8
|
+
*
|
|
9
|
+
* Child contract: direct-child margins are reset, spacing comes from --gap.
|
|
10
|
+
*/
|
|
11
|
+
.cluster {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-wrap: var(--cluster-wrap, wrap);
|
|
14
|
+
align-items: var(--cluster-align, center);
|
|
15
|
+
justify-content: var(--cluster-justify, flex-start);
|
|
16
|
+
gap: var(--gap);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.cluster > * {
|
|
20
|
+
margin: 0;
|
|
21
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Explicit 12-column composition canvas.
|
|
3
|
+
*
|
|
4
|
+
* Neither space-driven like .grid nor count-driven like .grid-N: this is a
|
|
5
|
+
* coordinate system. The author places regions with .column-span-N /
|
|
6
|
+
* .column-start-N and owns narrow-container recomposition. There is
|
|
7
|
+
* deliberately no automatic collapse — no threshold, no hidden
|
|
8
|
+
* .container-query behavior, no breakpoint variants. A single framework
|
|
9
|
+
* threshold cannot be right for arbitrary per-region compositions, and a
|
|
10
|
+
* hidden default would have to be undone rather than extended.
|
|
11
|
+
*
|
|
12
|
+
* See docs/design-notes/column-layout.md.
|
|
13
|
+
*
|
|
14
|
+
* Selection — reach for this only when placement is part of the design:
|
|
15
|
+
* independent repeated items -> .grid
|
|
16
|
+
* a known peer density -> .grid-N
|
|
17
|
+
* peers switching together -> .switcher
|
|
18
|
+
* content + secondary region -> .sidebar-layout
|
|
19
|
+
* media + flexible content -> .media
|
|
20
|
+
* exact custom track templates -> --grid-columns on .grid
|
|
21
|
+
*
|
|
22
|
+
* Child contract: each direct child is placed on the canvas via its own
|
|
23
|
+
* .column-span-N / .column-start-N.
|
|
24
|
+
*
|
|
25
|
+
* Unlike .grid-N, the track template does not read --gap, so the gap
|
|
26
|
+
* utilities are exact here at every width.
|
|
27
|
+
*/
|
|
28
|
+
.column-layout {
|
|
29
|
+
display: grid;
|
|
30
|
+
gap: var(--gap);
|
|
31
|
+
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Three zero-specificity tiers, applied in this order and only ever separated
|
|
35
|
+
* by source order. ORDER IS LOAD-BEARING:
|
|
36
|
+
*
|
|
37
|
+
* 1. the child reset auto-placed, spanning the whole canvas
|
|
38
|
+
* 2. .column-start-* a definite start line, running to the canvas end
|
|
39
|
+
* 3. .column-span-* the end only, so it refines either of the above
|
|
40
|
+
*
|
|
41
|
+
* Moving tier 3 above tier 2 makes every start+span pair run to column 12;
|
|
42
|
+
* moving either above tier 1 makes every placement full-span. Both failures
|
|
43
|
+
* are silent. tests/css-audit.test.js locks the order and
|
|
44
|
+
* tests/browser/column-layout.test.js locks the resulting geometry.
|
|
45
|
+
*
|
|
46
|
+
* Zero specificity is the point: an application's own container-query rules
|
|
47
|
+
* override placement additively, with a single-class selector and without
|
|
48
|
+
* having to undo framework specificity first.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/* Tier 1. A child with no placement class stays a readable full-width region
|
|
52
|
+
* instead of collapsing to one twelfth. Expressed as a span, not as
|
|
53
|
+
* `1 / -1`: a definite start line would give every child a definite column
|
|
54
|
+
* position and disable auto-placement, so `.column-span-8` next to
|
|
55
|
+
* `.column-span-4` would stack instead of composing 8 + 4.
|
|
56
|
+
*
|
|
57
|
+
* min-inline-size: 0 keeps the item box inside its track so the canvas never
|
|
58
|
+
* grows past its container. It does not stop content from painting outside a
|
|
59
|
+
* narrow track — that distinction is the primitive's contract, not a bug. */
|
|
60
|
+
:where(.column-layout) > * {
|
|
61
|
+
grid-column-end: span 12;
|
|
62
|
+
min-inline-size: 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Tier 2. A definite start line, running to the end of the canvas unless a
|
|
66
|
+
* .column-span-* refines the end. `column-start-9` alone is therefore the
|
|
67
|
+
* last four columns rather than an implicit 21-line overflow. */
|
|
68
|
+
:where(.column-start-1) {
|
|
69
|
+
grid-column: 1 / -1;
|
|
70
|
+
}
|
|
71
|
+
:where(.column-start-2) {
|
|
72
|
+
grid-column: 2 / -1;
|
|
73
|
+
}
|
|
74
|
+
:where(.column-start-3) {
|
|
75
|
+
grid-column: 3 / -1;
|
|
76
|
+
}
|
|
77
|
+
:where(.column-start-4) {
|
|
78
|
+
grid-column: 4 / -1;
|
|
79
|
+
}
|
|
80
|
+
:where(.column-start-5) {
|
|
81
|
+
grid-column: 5 / -1;
|
|
82
|
+
}
|
|
83
|
+
:where(.column-start-6) {
|
|
84
|
+
grid-column: 6 / -1;
|
|
85
|
+
}
|
|
86
|
+
:where(.column-start-7) {
|
|
87
|
+
grid-column: 7 / -1;
|
|
88
|
+
}
|
|
89
|
+
:where(.column-start-8) {
|
|
90
|
+
grid-column: 8 / -1;
|
|
91
|
+
}
|
|
92
|
+
:where(.column-start-9) {
|
|
93
|
+
grid-column: 9 / -1;
|
|
94
|
+
}
|
|
95
|
+
:where(.column-start-10) {
|
|
96
|
+
grid-column: 10 / -1;
|
|
97
|
+
}
|
|
98
|
+
:where(.column-start-11) {
|
|
99
|
+
grid-column: 11 / -1;
|
|
100
|
+
}
|
|
101
|
+
:where(.column-start-12) {
|
|
102
|
+
grid-column: 12 / -1;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Tier 3. The end only. Combined with a start, keep the placement inside the
|
|
106
|
+
* canvas: S + N - 1 <= 12. Exceeding it adds an auto-sized implicit column,
|
|
107
|
+
* which reshapes every 1fr track — the whole canvas loses its alignment, not
|
|
108
|
+
* just the offending region. */
|
|
109
|
+
:where(.column-span-1) {
|
|
110
|
+
grid-column-end: span 1;
|
|
111
|
+
}
|
|
112
|
+
:where(.column-span-2) {
|
|
113
|
+
grid-column-end: span 2;
|
|
114
|
+
}
|
|
115
|
+
:where(.column-span-3) {
|
|
116
|
+
grid-column-end: span 3;
|
|
117
|
+
}
|
|
118
|
+
:where(.column-span-4) {
|
|
119
|
+
grid-column-end: span 4;
|
|
120
|
+
}
|
|
121
|
+
:where(.column-span-5) {
|
|
122
|
+
grid-column-end: span 5;
|
|
123
|
+
}
|
|
124
|
+
:where(.column-span-6) {
|
|
125
|
+
grid-column-end: span 6;
|
|
126
|
+
}
|
|
127
|
+
:where(.column-span-7) {
|
|
128
|
+
grid-column-end: span 7;
|
|
129
|
+
}
|
|
130
|
+
:where(.column-span-8) {
|
|
131
|
+
grid-column-end: span 8;
|
|
132
|
+
}
|
|
133
|
+
:where(.column-span-9) {
|
|
134
|
+
grid-column-end: span 9;
|
|
135
|
+
}
|
|
136
|
+
:where(.column-span-10) {
|
|
137
|
+
grid-column-end: span 10;
|
|
138
|
+
}
|
|
139
|
+
:where(.column-span-11) {
|
|
140
|
+
grid-column-end: span 11;
|
|
141
|
+
}
|
|
142
|
+
:where(.column-span-12) {
|
|
143
|
+
grid-column-end: span 12;
|
|
144
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* Convenience helper: establishes a query container named actual-grid for
|
|
2
|
+
descendants. Use it for a component that changes representation according
|
|
3
|
+
to its allocated inline size, and to give .grid-N the named context it needs
|
|
4
|
+
for balanced subdivision (see grid.css). The name matters: grids only respond
|
|
5
|
+
to an explicitly actual-grid-named container, never to an incidental
|
|
6
|
+
query container. Layout primitives are responsive without it; this adds
|
|
7
|
+
precision, it is never a prerequisite.
|
|
8
|
+
|
|
9
|
+
The element takes inline-size containment, so put it on a normal
|
|
10
|
+
block-level wrapper, never on a shrink-to-fit item (a .cluster child, a
|
|
11
|
+
float, an inline-block): its contents would stop contributing to its own
|
|
12
|
+
inline size and it would collapse. */
|
|
13
|
+
.container-query {
|
|
14
|
+
container: actual-grid / inline-size;
|
|
15
|
+
}
|
|
@@ -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,155 @@
|
|
|
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 -> 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 */
|
|
33
|
+
.grid {
|
|
34
|
+
display: grid;
|
|
35
|
+
gap: var(--gap);
|
|
36
|
+
grid-template-columns: var(
|
|
37
|
+
--grid-columns,
|
|
38
|
+
repeat(auto-fit, minmax(min(100%, var(--grid-min, 16rem)), 1fr))
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Structural density presets. The baseline is an auto-fill grid bounded at N
|
|
43
|
+
tracks: responsive and overflow-safe on its own, so a preset used without a
|
|
44
|
+
query container is never a trap — it simply reaches its balanced states less
|
|
45
|
+
precisely. auto-fill rather than auto-fit keeps item width stable when a
|
|
46
|
+
partial final page holds fewer items than the grid's capacity.
|
|
47
|
+
|
|
48
|
+
--grid-min is deliberately NOT read here. The subdivision below runs on
|
|
49
|
+
@container thresholds, which cannot resolve a custom property, so a hook
|
|
50
|
+
honored in the baseline would fall silent the moment a query container is
|
|
51
|
+
added. Item width is .grid's contract; column density is .grid-N's. */
|
|
52
|
+
:where(.grid-2, .grid-3, .grid-4, .grid-6) {
|
|
53
|
+
display: grid;
|
|
54
|
+
gap: var(--gap);
|
|
55
|
+
grid-template-columns: repeat(
|
|
56
|
+
auto-fill,
|
|
57
|
+
minmax(
|
|
58
|
+
min(
|
|
59
|
+
100%,
|
|
60
|
+
max(16rem, calc((100% - (var(--grid-count) - 1) * var(--gap)) / var(--grid-count)))
|
|
61
|
+
),
|
|
62
|
+
1fr
|
|
63
|
+
)
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.grid-2 {
|
|
68
|
+
--grid-count: 2;
|
|
69
|
+
}
|
|
70
|
+
.grid-3 {
|
|
71
|
+
--grid-count: 3;
|
|
72
|
+
}
|
|
73
|
+
.grid-4 {
|
|
74
|
+
--grid-count: 4;
|
|
75
|
+
}
|
|
76
|
+
.grid-6 {
|
|
77
|
+
--grid-count: 6;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Grid items keep an automatic minimum inline size (min-content), which can
|
|
81
|
+
overflow a track sized minmax(0, 1fr). Layout primitives remove that
|
|
82
|
+
intrinsic floor so their overflow-safe track sizing holds; authors only
|
|
83
|
+
need .min-inline-size-0 outside primitives that do not already remove this
|
|
84
|
+
floor. */
|
|
85
|
+
:where(.grid, .grid-2, .grid-3, .grid-4, .grid-6) > * {
|
|
86
|
+
min-inline-size: 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Balanced subdivision, activated by an ancestor container named actual-grid.
|
|
90
|
+
* A .grid-N inside such a container enters only divisor states of N. The name
|
|
91
|
+
* is deliberate: a grid must not key off the nearest anonymous query container,
|
|
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-grid / inline-size on your own region.
|
|
96
|
+
*
|
|
97
|
+
* .grid-2 2 -> 1
|
|
98
|
+
* .grid-3 3 -> 1
|
|
99
|
+
* .grid-4 4 -> 2 -> 1
|
|
100
|
+
* .grid-6 6 -> 3 -> 2 -> 1
|
|
101
|
+
*
|
|
102
|
+
* Every state is a divisor of N, so all items keep the same width and no
|
|
103
|
+
* state produces a visibly uneven split such as 5 + 1 or 4 + 2. Intermediate
|
|
104
|
+
* counts are skipped on purpose: do not "complete" these chains with the
|
|
105
|
+
* missing integers, and do not replace them with an unbounded auto-fit.
|
|
106
|
+
*
|
|
107
|
+
* The query container must be an ancestor. An element cannot query its own
|
|
108
|
+
* size without taking inline-size containment, which destroys its intrinsic
|
|
109
|
+
* inline contribution and collapses it inside .cluster, .topbar, floats, and
|
|
110
|
+
* any shrink-to-fit box. See docs/design-notes/grid.md.
|
|
111
|
+
*
|
|
112
|
+
* Without an actual-grid ancestor none of these @container rules resolve, so
|
|
113
|
+
* the auto-fill baseline above stays. The min-width: 0 rule is the lowest
|
|
114
|
+
* step: it pins the grid to one column inside a container before any larger
|
|
115
|
+
* threshold fires (a bare grid would otherwise show a 2 + 1 orphan at 28-48rem).
|
|
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.
|
|
124
|
+
*
|
|
125
|
+
* Ascending min-width only: the last matching step wins by cascade order.
|
|
126
|
+
*/
|
|
127
|
+
@supports (container-type: inline-size) {
|
|
128
|
+
@container actual-grid (min-width: 0px) {
|
|
129
|
+
:where(.grid-2, .grid-3, .grid-4, .grid-6) {
|
|
130
|
+
grid-template-columns: 1fr;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@container actual-grid (min-width: 28rem) {
|
|
135
|
+
:where(.grid-2, .grid-4, .grid-6) {
|
|
136
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@container actual-grid (min-width: 48rem) {
|
|
141
|
+
:where(.grid-3, .grid-6) {
|
|
142
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@container actual-grid (min-width: 64rem) {
|
|
147
|
+
:where(.grid-4) {
|
|
148
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
:where(.grid-6) {
|
|
152
|
+
grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
* .app-layout topbar + scrolling main + bottom/side app navigation
|
|
12
|
+
* .media fixed media + flexible content
|
|
13
|
+
* .switcher peer regions: all horizontal or all stacked
|
|
14
|
+
* .sidebar-layout main content + secondary aside
|
|
15
|
+
* .grid responsive collection; item width is the contract
|
|
16
|
+
* .grid-N structural density of N; balanced inside a container named
|
|
17
|
+
* actual-grid (in grid.css)
|
|
18
|
+
* .column-layout explicit 12-column region composition; the author owns
|
|
19
|
+
* narrow-container recomposition
|
|
20
|
+
* .scroller theme-aware scrollbar density and colour
|
|
21
|
+
* .scroll-snap snap rails
|
|
22
|
+
* .scroll-target anchor scroll offset for sticky headers
|
|
23
|
+
* .topbar sticky app-shell top bar
|
|
24
|
+
* .measure readable content measure cap
|
|
25
|
+
*
|
|
26
|
+
* Prefer these compositions over viewport breakpoint utilities.
|
|
27
|
+
*/
|
|
28
|
+
@import "./container-query.css";
|
|
29
|
+
@import "./stack.css";
|
|
30
|
+
@import "./cluster.css";
|
|
31
|
+
@import "./center.css";
|
|
32
|
+
@import "./frame.css";
|
|
33
|
+
@import "./app-shell.css";
|
|
34
|
+
@import "./app-layout.css";
|
|
35
|
+
@import "./media.css";
|
|
36
|
+
@import "./switcher.css";
|
|
37
|
+
@import "./sidebar-layout.css";
|
|
38
|
+
@import "./grid.css";
|
|
39
|
+
@import "./column-layout.css";
|
|
40
|
+
@import "./scroller.css";
|
|
41
|
+
@import "./scroll-snap.css";
|
|
42
|
+
@import "./scroll-target.css";
|
|
43
|
+
@import "./topbar.css";
|
|
44
|
+
@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
|
+
}
|