actual-css 0.3.0 → 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 +78 -3
- package/README.md +35 -30
- package/dist/actual.css +98 -4882
- 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 +35 -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 +22 -17
- package/docs/pages/components/card.md +29 -13
- 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 +41 -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 +29 -7
- 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/cluster.md +4 -2
- 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 +10 -3
- 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 +90 -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 -7
- 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 +261 -0
- package/src/cli/actual-css.js +133 -0
- package/src/css/actual.css +1 -28
- package/src/css/actual.full.css +11 -2
- package/src/css/components/alert.css +33 -10
- package/src/css/components/badge.css +38 -19
- package/src/css/components/breadcrumb.css +1 -1
- package/src/css/components/button.css +35 -13
- package/src/css/components/card.css +37 -4
- 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/navbar.css +13 -5
- 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} +27 -13
- 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 +6 -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 +10 -4
- 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 +4 -32
- package/src/js/tooltip.js +65 -24
- 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 -96
- package/src/css/layout.css +0 -108
- 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 -43
- 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,677 @@
|
|
|
1
|
+
# Explicit Column Layout
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Proposed for Actual CSS 0.4.
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
Actual CSS already provides several layout primitives with distinct responsive contracts:
|
|
10
|
+
|
|
11
|
+
* `.grid` for intrinsic collections whose item width drives reflow;
|
|
12
|
+
* `.grid-N` for equal-width structural collections with a known peer density;
|
|
13
|
+
* `.switcher` for peer regions that must switch together between horizontal and stacked layouts;
|
|
14
|
+
* `.sidebar-layout` for main content paired with a secondary region;
|
|
15
|
+
* `.media` for media paired with flexible content.
|
|
16
|
+
|
|
17
|
+
These primitives intentionally encode common layout relationships rather than exposing a general coordinate system.
|
|
18
|
+
|
|
19
|
+
There is still one legitimate case they do not cover well: **explicit composition where the placement of individual regions is itself part of the design**.
|
|
20
|
+
|
|
21
|
+
Examples include:
|
|
22
|
+
|
|
23
|
+
* an 8-column main region beside a 4-column supporting region;
|
|
24
|
+
* a centered 10-column article starting on column 2;
|
|
25
|
+
* a 3 / 6 / 3 application shell;
|
|
26
|
+
* editorial or dashboard compositions where regions must align to a shared column canvas.
|
|
27
|
+
|
|
28
|
+
Today these layouts can be expressed with `--grid-columns` and application-specific `grid-column` rules:
|
|
29
|
+
|
|
30
|
+
```css
|
|
31
|
+
.my-layout {
|
|
32
|
+
--grid-columns: repeat(12, minmax(0, 1fr));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.my-main {
|
|
36
|
+
grid-column: span 8;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.my-aside {
|
|
40
|
+
grid-column: span 4;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This is already possible, but it provides no shared vocabulary and makes every application redefine the same placement conventions.
|
|
45
|
+
|
|
46
|
+
The missing feature is therefore not new CSS capability. It is a small, explicit composition vocabulary.
|
|
47
|
+
|
|
48
|
+
## Decision
|
|
49
|
+
|
|
50
|
+
Add an opt-in 12-column horizontal composition primitive:
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<div class="column-layout">
|
|
54
|
+
<main class="column-span-8">
|
|
55
|
+
...
|
|
56
|
+
</main>
|
|
57
|
+
|
|
58
|
+
<aside class="column-span-4 column-start-9">
|
|
59
|
+
...
|
|
60
|
+
</aside>
|
|
61
|
+
</div>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The public API is intentionally limited to:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
.column-layout
|
|
68
|
+
.column-span-1 … .column-span-12
|
|
69
|
+
.column-start-1 … .column-start-12
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
No responsive behavior is implied.
|
|
73
|
+
|
|
74
|
+
No row placement, ordering, named areas, breakpoint variants, offsets, or configurable column counts are part of the primitive.
|
|
75
|
+
|
|
76
|
+
## Why this is separate from `.grid`
|
|
77
|
+
|
|
78
|
+
`.grid` and `.grid-N` are collection primitives.
|
|
79
|
+
|
|
80
|
+
They answer questions such as:
|
|
81
|
+
|
|
82
|
+
> How many peer items should fit here?
|
|
83
|
+
|
|
84
|
+
or:
|
|
85
|
+
|
|
86
|
+
> Should these repeated items preserve a known structural density?
|
|
87
|
+
|
|
88
|
+
`.column-layout` answers a different question:
|
|
89
|
+
|
|
90
|
+
> Where should these distinct regions sit on a shared composition canvas?
|
|
91
|
+
|
|
92
|
+
The distinction is intentional.
|
|
93
|
+
|
|
94
|
+
A layout that happens to contain three regions is not automatically a column layout. If those regions are peers that should switch together, `.switcher` is the correct primitive. If one region is secondary to another, `.sidebar-layout` is usually the better choice.
|
|
95
|
+
|
|
96
|
+
Use `.column-layout` because **placement is part of the design**, not merely because the design contains columns.
|
|
97
|
+
|
|
98
|
+
## Choosing the primitive
|
|
99
|
+
|
|
100
|
+
Use the relationship between regions rather than their visual count.
|
|
101
|
+
|
|
102
|
+
| Need | Primitive |
|
|
103
|
+
| ------------------------------------------------------ | ----------------- |
|
|
104
|
+
| Independent repeated items with intrinsic reflow | `.grid` |
|
|
105
|
+
| Equal-width peers with a known structural density | `.grid-N` |
|
|
106
|
+
| Peer regions that must switch together | `.switcher` |
|
|
107
|
+
| Main content with a secondary region | `.sidebar-layout` |
|
|
108
|
+
| Media paired with flexible content | `.media` |
|
|
109
|
+
| Explicit region placement on a shared 12-column canvas | `.column-layout` |
|
|
110
|
+
|
|
111
|
+
A useful diagnostic is:
|
|
112
|
+
|
|
113
|
+
> If local CSS mainly makes one layout primitive behave like another, reconsider the primitive before adding more CSS.
|
|
114
|
+
|
|
115
|
+
## Why twelve columns
|
|
116
|
+
|
|
117
|
+
The column count is deliberately fixed at 12.
|
|
118
|
+
|
|
119
|
+
Twelve provides useful subdivisions without requiring a configurable grid system:
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
6 + 6
|
|
123
|
+
4 + 4 + 4
|
|
124
|
+
3 + 3 + 3 + 3
|
|
125
|
+
8 + 4
|
|
126
|
+
9 + 3
|
|
127
|
+
2 + 8 + 2
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The purpose of `.column-layout` is to provide a shared and predictable composition vocabulary.
|
|
131
|
+
|
|
132
|
+
A configurable `--column-count` would weaken that contract. It would also make utilities such as `.column-span-8` ambiguous once the underlying canvas no longer had twelve units.
|
|
133
|
+
|
|
134
|
+
Applications that genuinely require a different track system should use custom CSS or `--grid-columns`.
|
|
135
|
+
|
|
136
|
+
## Base recipe
|
|
137
|
+
|
|
138
|
+
The primitive uses twelve equal tracks with zero intrinsic minimum:
|
|
139
|
+
|
|
140
|
+
```css
|
|
141
|
+
.column-layout {
|
|
142
|
+
display: grid;
|
|
143
|
+
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
144
|
+
gap: var(--gap);
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Using `minmax(0, 1fr)` is an invariant, not responsive behavior.
|
|
149
|
+
|
|
150
|
+
It prevents the track definition itself from creating a min-content floor that forces the layout wider than its container.
|
|
151
|
+
|
|
152
|
+
It does **not** guarantee that arbitrary content will remain usable inside a very narrow span.
|
|
153
|
+
|
|
154
|
+
A two-column span in a narrow container may still be too small for its content. That is an application composition problem, not something the primitive should guess how to solve.
|
|
155
|
+
|
|
156
|
+
## Gap utilities are exact here
|
|
157
|
+
|
|
158
|
+
`grid.css` documents a pitfall: `.gap-sm` and `.gap-lg` set the `gap` property without updating `--gap`, and the unwrapped `.grid-N` baseline sizes its tracks from `--gap`, so the two disagree and a bare `.grid-4.gap-lg` stops one column short.
|
|
159
|
+
|
|
160
|
+
`.column-layout` has no such coupling. Its track template is `repeat(12, minmax(0, 1fr))` and never reads `--gap` for sizing, so `gap` and the track count are independent. Every gap utility is exact at every width.
|
|
161
|
+
|
|
162
|
+
This is worth stating explicitly because an author who has read the grid page will reasonably assume the opposite.
|
|
163
|
+
|
|
164
|
+
## Default child behavior
|
|
165
|
+
|
|
166
|
+
Unplaced direct children span the full canvas:
|
|
167
|
+
|
|
168
|
+
```css
|
|
169
|
+
:where(.column-layout) > * {
|
|
170
|
+
grid-column-end: span 12;
|
|
171
|
+
min-inline-size: 0;
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
This gives the primitive a safe failure mode. Without an explicit placement class, an element remains a normal full-width region instead of implicitly occupying one of twelve very narrow tracks.
|
|
176
|
+
|
|
177
|
+
Placement is therefore opt-in per child, and a heading or a full-width intro can sit in the same canvas as placed regions without carrying a class of its own.
|
|
178
|
+
|
|
179
|
+
The span form is load-bearing. The obvious alternative is:
|
|
180
|
+
|
|
181
|
+
```css
|
|
182
|
+
/* Rejected. */
|
|
183
|
+
:where(.column-layout) > * {
|
|
184
|
+
grid-column: 1 / -1;
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
That gives every child a **definite** column start of line 1, which removes it from column auto-placement. Two placed peers then both resolve to column 1 and the second is pushed to the next row:
|
|
189
|
+
|
|
190
|
+
```text
|
|
191
|
+
<section class="column-span-8"> row 1, columns 1-8
|
|
192
|
+
<aside class="column-span-4"> row 2, columns 1-4 ← not 9-12
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Measured, not reasoned: the pair renders at `top=8` and `top=26` with the shorthand reset, and at `top=8`/`top=8` with the span reset. Expressing the default as a span keeps the start line `auto`, so auto-placement continues to flow peers across the canvas.
|
|
196
|
+
|
|
197
|
+
## Placement tiers and source order
|
|
198
|
+
|
|
199
|
+
The primitive is three zero-specificity tiers, and **only source order separates them**:
|
|
200
|
+
|
|
201
|
+
```text
|
|
202
|
+
1. :where(.column-layout) > * auto-placed, spanning the whole canvas
|
|
203
|
+
2. :where(.column-start-N) a definite start line, running to the canvas end
|
|
204
|
+
3. :where(.column-span-N) the end only, refining either of the above
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Because every tier resolves to specificity `(0,0,0)`, reordering the file changes behavior silently:
|
|
208
|
+
|
|
209
|
+
* moving tier 3 above tier 2 makes every `start + span` pair run to column 12;
|
|
210
|
+
* moving either above tier 1 makes every placement full-span.
|
|
211
|
+
|
|
212
|
+
Neither failure produces an error, an override warning, or a visibly broken canvas — both look like a plausible layout. The order is therefore asserted twice: `tests/css-audit.test.js` locks the order of the blocks in the source file, and `tests/browser/column-layout.test.js` locks the geometry the order produces.
|
|
213
|
+
|
|
214
|
+
This is the cost of the zero-specificity design, and it is worth paying: the alternative is placement classes at normal class specificity, which would put every application-owned recomposition rule back into a source-order tie with the framework.
|
|
215
|
+
|
|
216
|
+
## Span utilities
|
|
217
|
+
|
|
218
|
+
Span utilities set only the column span:
|
|
219
|
+
|
|
220
|
+
```css
|
|
221
|
+
:where(.column-span-1) {
|
|
222
|
+
grid-column-end: span 1;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
:where(.column-span-2) {
|
|
226
|
+
grid-column-end: span 2;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* … */
|
|
230
|
+
|
|
231
|
+
:where(.column-span-12) {
|
|
232
|
+
grid-column-end: span 12;
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
For example:
|
|
237
|
+
|
|
238
|
+
```html
|
|
239
|
+
<div class="column-layout">
|
|
240
|
+
<section class="column-span-8">...</section>
|
|
241
|
+
<aside class="column-span-4">...</aside>
|
|
242
|
+
</div>
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
With normal grid auto-placement, this produces an 8 / 4 composition: neither child has a definite start line, so the aside flows into columns 9-12.
|
|
246
|
+
|
|
247
|
+
Auto-placement also means a pair that does not fit wraps rather than overflowing. Two `column-span-8` regions occupy sixteen units, so the second moves to the next row. That is correct grid behavior, not a canvas violation.
|
|
248
|
+
|
|
249
|
+
## Start utilities
|
|
250
|
+
|
|
251
|
+
Start utilities set the explicit starting grid line:
|
|
252
|
+
|
|
253
|
+
```css
|
|
254
|
+
:where(.column-start-1) {
|
|
255
|
+
grid-column: 1 / -1;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
:where(.column-start-2) {
|
|
259
|
+
grid-column: 2 / -1;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* … */
|
|
263
|
+
|
|
264
|
+
:where(.column-start-12) {
|
|
265
|
+
grid-column: 12 / -1;
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
This allows deliberate offsets without introducing a separate margin-based `offset-*` abstraction.
|
|
270
|
+
|
|
271
|
+
Each start utility also sets the end line to `-1`, so a start used **without** a span runs to the edge of the canvas:
|
|
272
|
+
|
|
273
|
+
```html
|
|
274
|
+
<aside class="column-start-9">...</aside>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
occupies columns 9 through 12. The alternative — setting `grid-column-start` alone — would leave the tier 1 default of `span 12` in place and place the region across lines 9 to 21, adding nine implicit columns. A single class must not be able to do that.
|
|
278
|
+
|
|
279
|
+
A `column-span-*` on the same element then refines the end, because tier 3 follows tier 2 in source order.
|
|
280
|
+
|
|
281
|
+
For example:
|
|
282
|
+
|
|
283
|
+
```html
|
|
284
|
+
<div class="column-layout">
|
|
285
|
+
<article class="column-span-10 column-start-2">
|
|
286
|
+
...
|
|
287
|
+
</article>
|
|
288
|
+
</div>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
The article occupies columns 2 through 11.
|
|
292
|
+
|
|
293
|
+
When combining `column-start-S` and `column-span-N`, the author must keep the placement inside the twelve-column canvas:
|
|
294
|
+
|
|
295
|
+
```text
|
|
296
|
+
S + N - 1 <= 12
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
For example:
|
|
300
|
+
|
|
301
|
+
```text
|
|
302
|
+
start 9 + span 4 → valid
|
|
303
|
+
start 10 + span 4 → exceeds the canvas
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Actual CSS does not attempt to guard against invalid combinations, and the failure is worth stating precisely because it is not local.
|
|
307
|
+
|
|
308
|
+
When a placement exceeds the explicit canvas, CSS Grid adds implicit columns at the end. `grid-auto-columns` defaults to `auto`, so an implicit column is sized by its content while the twelve `1fr` tracks divide whatever inline size is left. Every region in the canvas therefore shifts, not just the region carrying the invalid placement — a thirteenth column silently re-scales the coordinate system for everything else.
|
|
309
|
+
|
|
310
|
+
## Low specificity is intentional
|
|
311
|
+
|
|
312
|
+
Placement utilities use `:where()` so their specificity remains zero.
|
|
313
|
+
|
|
314
|
+
This makes application-owned recomposition additive rather than corrective.
|
|
315
|
+
|
|
316
|
+
For example:
|
|
317
|
+
|
|
318
|
+
```html
|
|
319
|
+
<div class="container-query">
|
|
320
|
+
<div class="column-layout profile-layout">
|
|
321
|
+
<main class="column-span-8 profile-main">...</main>
|
|
322
|
+
<aside class="column-span-4 column-start-9 profile-aside">...</aside>
|
|
323
|
+
</div>
|
|
324
|
+
</div>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
The application can later redefine placement with a normal class selector:
|
|
328
|
+
|
|
329
|
+
```css
|
|
330
|
+
@container (width < 48rem) {
|
|
331
|
+
.profile-main {
|
|
332
|
+
grid-column: 1 / span 6;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.profile-aside {
|
|
336
|
+
grid-column: 7 / span 6;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
The author does not need to undo framework specificity before applying a new composition.
|
|
342
|
+
|
|
343
|
+
Normal cascade-layer rules still apply. If application CSS is placed in an earlier layer than Actual CSS, layer order can still take precedence over selector specificity.
|
|
344
|
+
|
|
345
|
+
## Responsive contract
|
|
346
|
+
|
|
347
|
+
`.column-layout` has **no automatic responsive collapse**.
|
|
348
|
+
|
|
349
|
+
This is deliberate.
|
|
350
|
+
|
|
351
|
+
Different compositions have different useful breakpoints:
|
|
352
|
+
|
|
353
|
+
* `8 + 4` may remain useful longer than `3 + 9`;
|
|
354
|
+
* a centered article may only need reduced side gutters;
|
|
355
|
+
* a dashboard may recombine from three regions into two before eventually stacking;
|
|
356
|
+
* an editorial layout may switch to a completely different representation on narrow screens.
|
|
357
|
+
|
|
358
|
+
A single framework threshold would therefore be arbitrary.
|
|
359
|
+
|
|
360
|
+
It would also recreate a hidden two-part contract where a layout class silently changes behavior only when combined with a wrapper or breakpoint rule.
|
|
361
|
+
|
|
362
|
+
Actual CSS does not make that decision.
|
|
363
|
+
|
|
364
|
+
The author owns narrow-container behavior.
|
|
365
|
+
|
|
366
|
+
## Author-owned recomposition
|
|
367
|
+
|
|
368
|
+
A simple composition may stack completely:
|
|
369
|
+
|
|
370
|
+
```css
|
|
371
|
+
@container (width < 40rem) {
|
|
372
|
+
.profile-layout > * {
|
|
373
|
+
grid-column: 1 / -1;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
A more complex composition can preserve the coordinate system through intermediate states.
|
|
379
|
+
|
|
380
|
+
For example, desktop:
|
|
381
|
+
|
|
382
|
+
```text
|
|
383
|
+
[ MAIN 8 ][ ASIDE 4 ]
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Intermediate:
|
|
387
|
+
|
|
388
|
+
```text
|
|
389
|
+
[ MAIN 6 ][ ASIDE 6 ]
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
Narrow:
|
|
393
|
+
|
|
394
|
+
```text
|
|
395
|
+
[ MAIN ]
|
|
396
|
+
[ ASIDE ]
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Implementation:
|
|
400
|
+
|
|
401
|
+
```css
|
|
402
|
+
@container (width < 48rem) {
|
|
403
|
+
.profile-main {
|
|
404
|
+
grid-column: 1 / span 6;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.profile-aside {
|
|
408
|
+
grid-column: 7 / span 6;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
@container (width < 32rem) {
|
|
413
|
+
.profile-main,
|
|
414
|
+
.profile-aside {
|
|
415
|
+
grid-column: 1 / -1;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
This ability to deliberately recompose regions is part of the value of a coordinate system.
|
|
421
|
+
|
|
422
|
+
The framework should not reduce every narrow layout to a single automatic stack.
|
|
423
|
+
|
|
424
|
+
## Relationship with `.container-query`
|
|
425
|
+
|
|
426
|
+
`.container-query` is not required by `.column-layout`.
|
|
427
|
+
|
|
428
|
+
The primitive does not inspect container width and does not contain hidden container-query behavior.
|
|
429
|
+
|
|
430
|
+
An author may choose to use `.container-query` because their own responsive rules use `@container`:
|
|
431
|
+
|
|
432
|
+
```html
|
|
433
|
+
<div class="container-query">
|
|
434
|
+
<div class="column-layout profile-layout">
|
|
435
|
+
...
|
|
436
|
+
</div>
|
|
437
|
+
</div>
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
In that case, the dependency is explicit:
|
|
441
|
+
|
|
442
|
+
> the author uses a query container because the author wrote a container query.
|
|
443
|
+
|
|
444
|
+
This is intentionally different from a framework primitive whose responsive contract depends on an unrelated wrapper being present.
|
|
445
|
+
|
|
446
|
+
## Narrow containers
|
|
447
|
+
|
|
448
|
+
A twelve-column coordinate system is primarily a wide-context composition tool.
|
|
449
|
+
|
|
450
|
+
At narrow widths, individual tracks can become extremely small, and eleven gaps consume a large share of the available inline size rather than a marginal one.
|
|
451
|
+
|
|
452
|
+
With the default `--gap: 0.75rem` and a `.center` container measuring 279px at a 360px viewport, the eleven gaps take 132px. The twelve tracks divide the remaining 147px, which is roughly **12px per unit**: a `column-span-4` region is 84px wide.
|
|
453
|
+
|
|
454
|
+
That number is the argument. There is no useful composition to guess at that width, which is precisely why the primitive does not try.
|
|
455
|
+
|
|
456
|
+
The framework guarantees that the explicit track definition can shrink:
|
|
457
|
+
|
|
458
|
+
```css
|
|
459
|
+
repeat(12, minmax(0, 1fr))
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
It does not guarantee that every arbitrary span remains a useful content width.
|
|
463
|
+
|
|
464
|
+
Applications should define an appropriate narrow representation where necessary.
|
|
465
|
+
|
|
466
|
+
This is not considered a failure of the primitive. The purpose of `.column-layout` is explicit composition, not intrinsic reflow.
|
|
467
|
+
|
|
468
|
+
For intrinsic responsive behavior, prefer `.grid`, `.switcher`, or `.sidebar-layout`.
|
|
469
|
+
|
|
470
|
+
## One-dimensional scope
|
|
471
|
+
|
|
472
|
+
Despite being implemented with CSS Grid, `.column-layout` exposes only horizontal column placement.
|
|
473
|
+
|
|
474
|
+
Actual CSS does not provide:
|
|
475
|
+
|
|
476
|
+
```text
|
|
477
|
+
.row-span-*
|
|
478
|
+
.row-start-*
|
|
479
|
+
.area-*
|
|
480
|
+
.column-end-*
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
as part of this primitive.
|
|
484
|
+
|
|
485
|
+
Applications remain free to use native CSS Grid features where a genuinely two-dimensional composition requires them:
|
|
486
|
+
|
|
487
|
+
```css
|
|
488
|
+
.dashboard-summary {
|
|
489
|
+
grid-row: 1 / span 2;
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
Keeping the framework API one-dimensional prevents the primitive from growing into a general-purpose grid DSL.
|
|
494
|
+
|
|
495
|
+
The `column-*` prefix is reserved for placement on this canvas. If Actual CSS later needs CSS multi-column utilities, they must not be named `column-count-*` or `column-width-*` under the same prefix — the two concepts would be indistinguishable in markup. A separate name such as `text-columns-*` keeps the coordinate domain unambiguous.
|
|
496
|
+
|
|
497
|
+
## Source order and `order`
|
|
498
|
+
|
|
499
|
+
`column-layout` does not provide `order-*` utilities.
|
|
500
|
+
|
|
501
|
+
Visual reordering has consequences for reading order, keyboard navigation, and accessibility. It is also not specific to the column-layout primitive.
|
|
502
|
+
|
|
503
|
+
If Actual CSS introduces ordering utilities in the future, they should be designed and documented independently.
|
|
504
|
+
|
|
505
|
+
Authors should prefer DOM order that remains sensible when the composition changes.
|
|
506
|
+
|
|
507
|
+
## Direct-child box requirement
|
|
508
|
+
|
|
509
|
+
Placement applies to grid items.
|
|
510
|
+
|
|
511
|
+
A direct child carrying `column-span-*` or `column-start-*` must therefore generate a box.
|
|
512
|
+
|
|
513
|
+
Avoid:
|
|
514
|
+
|
|
515
|
+
```css
|
|
516
|
+
.region {
|
|
517
|
+
display: contents;
|
|
518
|
+
}
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
when `.region` is the element carrying column placement.
|
|
522
|
+
|
|
523
|
+
With `display: contents`, the element's own box is removed and its descendants become grid items instead. The `grid-column-*` values assigned to the boxless element no longer control those descendants.
|
|
524
|
+
|
|
525
|
+
This is a general limitation of box-dependent layout primitives, not something `.column-layout` attempts to compensate for.
|
|
526
|
+
|
|
527
|
+
## Relationship with `--grid-columns`
|
|
528
|
+
|
|
529
|
+
Before `.column-layout`, custom `--grid-columns` was the only built-in escape hatch for arbitrary asymmetric track systems.
|
|
530
|
+
|
|
531
|
+
After introducing `.column-layout`, the responsibilities become clearer:
|
|
532
|
+
|
|
533
|
+
Use `.column-layout` when distinct regions need explicit spans or starts on a standard twelve-column canvas.
|
|
534
|
+
|
|
535
|
+
Use `--grid-columns` for genuinely custom track templates that do not fit existing primitives or the twelve-column composition model.
|
|
536
|
+
|
|
537
|
+
For example:
|
|
538
|
+
|
|
539
|
+
```css
|
|
540
|
+
.custom-layout {
|
|
541
|
+
--grid-columns: minmax(12rem, 18rem) 1fr minmax(10rem, 14rem);
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
is still a legitimate custom grid because its tracks encode meaningful sizing rather than twelve interchangeable coordinate units.
|
|
546
|
+
|
|
547
|
+
## Why this is opt-in
|
|
548
|
+
|
|
549
|
+
A general coordinate system is easy to misuse.
|
|
550
|
+
|
|
551
|
+
Given:
|
|
552
|
+
|
|
553
|
+
```text
|
|
554
|
+
main + aside
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
it is tempting to write:
|
|
558
|
+
|
|
559
|
+
```html
|
|
560
|
+
<div class="column-layout">
|
|
561
|
+
<main class="column-span-8">...</main>
|
|
562
|
+
<aside class="column-span-4">...</aside>
|
|
563
|
+
</div>
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
even when `.sidebar-layout` already expresses the relationship and provides a more appropriate intrinsic responsive behavior.
|
|
567
|
+
|
|
568
|
+
Likewise, three peer panels should not become three `column-span-4` regions merely because the coordinate system makes that possible.
|
|
569
|
+
|
|
570
|
+
The primitive therefore belongs to the Layout family but is not part of the core baseline.
|
|
571
|
+
|
|
572
|
+
Its documentation must make the selection boundary explicit.
|
|
573
|
+
|
|
574
|
+
The convenience of `column-layout` should not turn every layout problem into coordinate arithmetic.
|
|
575
|
+
|
|
576
|
+
## Rejected alternatives
|
|
577
|
+
|
|
578
|
+
### Make `.grid-N` support arbitrary spans
|
|
579
|
+
|
|
580
|
+
Rejected.
|
|
581
|
+
|
|
582
|
+
`.grid-N` represents structural peer density. Adding arbitrary child placement would blur the distinction between a collection and an explicit composition canvas.
|
|
583
|
+
|
|
584
|
+
### Use `.layout-grid`
|
|
585
|
+
|
|
586
|
+
Technically accurate, but potentially confusing beside `.grid` and `.grid-N`.
|
|
587
|
+
|
|
588
|
+
The public concept exposed to authors is a column composition system. CSS Grid is its implementation mechanism.
|
|
589
|
+
|
|
590
|
+
### Use `.columns`
|
|
591
|
+
|
|
592
|
+
Rejected as too generic and potentially confused with CSS multi-column layout.
|
|
593
|
+
|
|
594
|
+
### Use `.span-N` and `.start-N`
|
|
595
|
+
|
|
596
|
+
Rejected because the class names are overly broad and would reserve generic application vocabulary.
|
|
597
|
+
|
|
598
|
+
`column-span-N` and `column-start-N` make the coordinate domain explicit.
|
|
599
|
+
|
|
600
|
+
### Add `--column-count`
|
|
601
|
+
|
|
602
|
+
Rejected.
|
|
603
|
+
|
|
604
|
+
Twelve columns are part of the primitive's contract. A configurable track count would make the placement vocabulary inconsistent and weaken interoperability between layouts.
|
|
605
|
+
|
|
606
|
+
### Give unplaced children `grid-column: 1 / -1`
|
|
607
|
+
|
|
608
|
+
Rejected, after measurement.
|
|
609
|
+
|
|
610
|
+
It reads as the natural way to express "spans the whole canvas", but the shorthand sets a definite `grid-column-start: 1` on every child, which removes all of them from column auto-placement. `.column-span-8` beside `.column-span-4` then stacks instead of composing 8 + 4, because both children resolve to column 1.
|
|
611
|
+
|
|
612
|
+
Every placed region would need an explicit `column-start-*`, turning the most common two-region composition into coordinate arithmetic. `grid-column-end: span 12` gives the same default width while leaving the start line `auto`.
|
|
613
|
+
|
|
614
|
+
### Add automatic container-query collapse
|
|
615
|
+
|
|
616
|
+
Rejected.
|
|
617
|
+
|
|
618
|
+
There is no universally correct threshold for arbitrary per-region compositions.
|
|
619
|
+
|
|
620
|
+
A hidden default would also force authors with different requirements to undo framework behavior before implementing their intended layout.
|
|
621
|
+
|
|
622
|
+
### Add breakpoint variants
|
|
623
|
+
|
|
624
|
+
Rejected.
|
|
625
|
+
|
|
626
|
+
Classes such as:
|
|
627
|
+
|
|
628
|
+
```text
|
|
629
|
+
md-column-span-6
|
|
630
|
+
lg-column-span-4
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
would recreate a breakpoint-driven layout DSL and move responsive strategy back into markup.
|
|
634
|
+
|
|
635
|
+
Author-owned CSS and container queries are preferred.
|
|
636
|
+
|
|
637
|
+
### Add offset utilities
|
|
638
|
+
|
|
639
|
+
Rejected.
|
|
640
|
+
|
|
641
|
+
`column-start-N` directly expresses native grid placement and avoids introducing a second, margin-based placement model.
|
|
642
|
+
|
|
643
|
+
### Add row placement and named areas
|
|
644
|
+
|
|
645
|
+
Rejected for the initial scope.
|
|
646
|
+
|
|
647
|
+
The primitive intentionally provides only horizontal coordinate placement.
|
|
648
|
+
|
|
649
|
+
### Add ordering utilities
|
|
650
|
+
|
|
651
|
+
Rejected as unrelated to the primitive and carrying separate accessibility concerns.
|
|
652
|
+
|
|
653
|
+
## Decision summary
|
|
654
|
+
|
|
655
|
+
Actual CSS will provide an explicit, opt-in twelve-column composition vocabulary:
|
|
656
|
+
|
|
657
|
+
```text
|
|
658
|
+
.column-layout
|
|
659
|
+
.column-span-1 … .column-span-12
|
|
660
|
+
.column-start-1 … .column-start-12
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
The primitive:
|
|
664
|
+
|
|
665
|
+
* uses twelve equal `minmax(0, 1fr)` tracks;
|
|
666
|
+
* gives unplaced direct children full-width placement, expressed as a span so auto-placement keeps working;
|
|
667
|
+
* uses low-specificity placement utilities in three source-order-dependent tiers;
|
|
668
|
+
* provides no automatic responsive behavior;
|
|
669
|
+
* leaves narrow-container recomposition to the application;
|
|
670
|
+
* remains horizontal-only;
|
|
671
|
+
* provides no order, row, area, breakpoint, offset, or configurable-count API;
|
|
672
|
+
* requires placed children to generate grid-item boxes;
|
|
673
|
+
* complements rather than replaces `.grid`, `.grid-N`, `.switcher`, `.sidebar-layout`, and `.media`.
|
|
674
|
+
|
|
675
|
+
The purpose is not to add layout capability that CSS Grid does not already provide.
|
|
676
|
+
|
|
677
|
+
The purpose is to give explicit application and editorial compositions a small, shared, readable coordinate vocabulary without weakening the intrinsic layout primitives used everywhere else.
|
|
@@ -72,6 +72,9 @@ Only use when the color **must not** be remapped — e.g., a `background-color`
|
|
|
72
72
|
whose value is `currentColor` (the system would turn it into `Canvas` and make
|
|
73
73
|
the element invisible). Every `forced-color-adjust: none` blocks the browser
|
|
74
74
|
from fixing contrast issues automatically. Most components need zero instances.
|
|
75
|
+
When only a pseudo-element carries essential color information, put the
|
|
76
|
+
declaration on that pseudo-element so the interactive host keeps native color
|
|
77
|
+
and focus adaptation.
|
|
75
78
|
|
|
76
79
|
### Redundant rules
|
|
77
80
|
```css
|