actual-css 0.9.2 → 0.10.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 +96 -0
- package/README.md +65 -44
- package/components.json +33 -13
- package/dist/actual.full.css +585 -499
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/docs/design-notes/app-ui.md +1 -1
- package/docs/design-notes/column-layout.md +103 -520
- package/docs/design-notes/forced-colors.md +12 -1
- package/docs/design-notes/naming.md +1 -1
- package/docs/design-notes/surface-reparenting.md +8 -9
- package/docs/navigation.json +2 -0
- package/docs/pages/components/accordion.md +55 -1
- package/docs/pages/components/alert.md +9 -15
- package/docs/pages/components/badge.md +6 -7
- package/docs/pages/components/bleed.md +42 -0
- package/docs/pages/components/button.md +30 -4
- package/docs/pages/components/card.md +21 -86
- package/docs/pages/components/close.md +48 -0
- package/docs/pages/components/data-list.md +26 -1
- package/docs/pages/components/dialog.md +16 -16
- package/docs/pages/components/drawer.md +9 -9
- package/docs/pages/components/navbar.md +5 -5
- package/docs/pages/components/spinner.md +3 -3
- package/docs/pages/enhancements/overview.md +5 -0
- package/docs/pages/enhancements/tooltip.md +3 -1
- package/docs/pages/examples/overview.md +2 -1
- package/docs/pages/forms/choice-cards.md +39 -0
- package/docs/pages/forms/combobox.md +23 -11
- package/docs/pages/forms/joined-controls.md +2 -5
- package/docs/pages/forms/overview.md +6 -0
- package/docs/pages/foundations/theming.md +64 -18
- package/docs/pages/foundations/tokens.md +28 -10
- package/docs/pages/guides/introduction.md +15 -23
- package/docs/pages/guides/modular-import.md +53 -6
- package/docs/pages/guides/progressive-enhancement.md +6 -3
- package/docs/pages/guides/tailwind.md +17 -5
- package/docs/pages/layout/column-layout.md +92 -54
- package/docs/pages/layout/media.md +1 -1
- package/docs/pages/layout/overview.md +1 -1
- package/docs/pages/utilities/overview.md +1 -1
- package/docs/pages/utilities/sizing-wrapping.md +5 -3
- package/llms.txt +24 -4
- package/package.json +6 -6
- package/reserved-classes.json +5 -30
- package/skills/actual-css/PROJECT.md.example +65 -0
- package/skills/actual-css/README.md +64 -0
- package/skills/actual-css/SKILL.md +234 -0
- package/skills/actual-css/references/accessibility.md +44 -0
- package/skills/actual-css/references/api-and-vocabulary.md +94 -0
- package/skills/actual-css/references/enhancements.md +59 -0
- package/skills/actual-css/references/forms.md +49 -0
- package/skills/actual-css/references/layout-and-theming.md +82 -0
- package/skills/actual-css/references/review-checklist.md +51 -0
- package/skills/actual-css/scripts/inspect-actual-css.mjs +171 -0
- package/src/css/components/accordion.css +44 -7
- package/src/css/components/alert.css +23 -43
- package/src/css/components/app-nav.css +9 -0
- package/src/css/components/badge.css +11 -39
- package/src/css/components/bleed.css +79 -0
- package/src/css/components/button.css +6 -17
- package/src/css/components/card.css +7 -40
- package/src/css/components/close.css +91 -0
- package/src/css/components/data-list.css +18 -0
- package/src/css/components/drawer.css +15 -56
- package/src/css/components/fab.css +10 -0
- package/src/css/components/flyout.css +1 -0
- package/src/css/components/index.css +2 -0
- package/src/css/components/join.css +36 -20
- package/src/css/components/menu.css +12 -0
- package/src/css/components/modal.css +10 -57
- package/src/css/components/navbar.css +27 -0
- package/src/css/core/focus.css +9 -3
- package/src/css/core/theme.css +25 -43
- package/src/css/core/tokens.css +1 -1
- package/src/css/core/variants.css +3 -33
- package/src/css/forms/choice-card.css +74 -4
- package/src/css/forms/choice.css +2 -3
- package/src/css/forms/control.css +15 -8
- package/src/css/forms/custom-select.css +4 -4
- package/src/css/forms/native.css +4 -4
- package/src/css/forms/range.css +23 -8
- package/src/css/forms/switch.css +2 -3
- package/src/css/layout/app-layout.css +1 -1
- package/src/css/layout/column-layout.css +26 -111
- package/src/css/utilities/extra.css +7 -6
- package/src/js/dialog.js +95 -24
- package/src/js/enhance.js +7 -3
- package/src/js/escape.js +1 -0
- package/src/js/flyout.js +10 -1
- package/src/js/focus-group.js +2 -1
- package/src/js/keys.js +14 -0
- package/src/js/menu.js +2 -1
- package/src/js/status.js +19 -0
- package/src/js/surface.js +12 -8
- package/src/js/tab.js +2 -1
- package/src/js/tooltip.js +18 -1
- package/src/js/validation.js +5 -6
- package/dist/actual.css +0 -876
- package/dist/actual.js +0 -1
- package/dist/actual.min.css +0 -1
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
# Explicit Column Layout
|
|
2
2
|
|
|
3
|
-
## Status
|
|
4
|
-
|
|
5
|
-
Proposed for Actual CSS 0.4.
|
|
6
|
-
|
|
7
3
|
## Problem
|
|
8
4
|
|
|
9
5
|
Actual CSS already provides several layout primitives with distinct responsive contracts:
|
|
@@ -25,464 +21,176 @@ Examples include:
|
|
|
25
21
|
* a 3 / 6 / 3 application shell;
|
|
26
22
|
* editorial or dashboard compositions where regions must align to a shared column canvas.
|
|
27
23
|
|
|
28
|
-
|
|
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.
|
|
24
|
+
These layouts can be expressed with `--grid-columns` and application-specific `grid-column` rules, but that gives no shared canvas and makes every application redefine the same conventions — including the auto-placement trap described below.
|
|
47
25
|
|
|
48
26
|
## Decision
|
|
49
27
|
|
|
50
|
-
|
|
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:
|
|
28
|
+
An opt-in 12-column horizontal composition primitive, placed through two hooks on each direct child:
|
|
139
29
|
|
|
140
30
|
```css
|
|
141
31
|
.column-layout {
|
|
142
32
|
display: grid;
|
|
143
|
-
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
144
33
|
gap: var(--gap);
|
|
34
|
+
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
145
35
|
}
|
|
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 is not coupled to the track count
|
|
157
|
-
|
|
158
|
-
A grid that sizes its tracks from `--gap` couples spacing and density: setting the `gap` property on its own can make the two disagree. Actual removed its `.gap-sm` / `.gap-md` / `.gap-lg` utilities before 0.8 for exactly that reason — a local gap is now a direct `gap` declaration, and `--gap` is reserved for rebinding an inherited rhythm.
|
|
159
|
-
|
|
160
|
-
`.column-layout` never had that coupling. Its track template is `repeat(12, minmax(0, 1fr))` and never reads `--gap` for sizing, so `gap` and the track count are independent at every width.
|
|
161
36
|
|
|
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
37
|
:where(.column-layout) > * {
|
|
170
|
-
|
|
38
|
+
--column-start: initial;
|
|
39
|
+
--column-span: initial;
|
|
40
|
+
|
|
41
|
+
grid-column-start: var(--column-start, auto);
|
|
42
|
+
grid-column-end: span var(--column-span, calc(13 - var(--column-start, 1)));
|
|
171
43
|
min-inline-size: 0;
|
|
172
44
|
}
|
|
173
45
|
```
|
|
174
46
|
|
|
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
47
|
```css
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
grid-column: 1 / -1;
|
|
48
|
+
.main {
|
|
49
|
+
--column-span: 8;
|
|
185
50
|
}
|
|
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
51
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
|
52
|
+
.aside {
|
|
53
|
+
--column-span: 4;
|
|
54
|
+
}
|
|
205
55
|
```
|
|
206
56
|
|
|
207
|
-
|
|
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.
|
|
57
|
+
The public API is one class and two hooks. No responsive behavior is implied. No row placement, ordering, named areas, breakpoint variants, offsets, or configurable column counts are part of the primitive.
|
|
211
58
|
|
|
212
|
-
|
|
59
|
+
## Why hooks rather than placement classes
|
|
213
60
|
|
|
214
|
-
|
|
61
|
+
A span and a start are two numbers. As classes they need twenty-four adapters (`.column-span-1` … `.column-start-12`) to encode CSS coordinates, and those adapters interact: a start class must also set the end line so it can stand alone, a span class must refine that end, and the only thing separating them at zero specificity is source order. The primitive then depended on a file order no browser reports when it breaks, guarded by a test whose only job was that order.
|
|
215
62
|
|
|
216
|
-
|
|
63
|
+
As custom properties the two inputs are independent. One rule reads both, and there is nothing to order.
|
|
217
64
|
|
|
218
|
-
|
|
65
|
+
Recomposition also stays inside the primitive's API. With classes, an application's container query had to leave the vocabulary and write `grid-column` itself. With hooks, it sets the same two properties the region was placed with.
|
|
219
66
|
|
|
220
|
-
|
|
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
|
-
}
|
|
67
|
+
The API does not require inline styles. The documented form puts the hooks on an application class; `style="--column-span: 8"` is a shortcut for a composition that never changes. An inline value outranks every rule, so coordinates that a container query recomposes belong on a class.
|
|
228
68
|
|
|
229
|
-
|
|
69
|
+
## One rule, four placements
|
|
230
70
|
|
|
231
|
-
|
|
232
|
-
|
|
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>
|
|
71
|
+
```text
|
|
72
|
+
neither set start auto, span 12 auto-placed, full canvas
|
|
73
|
+
span only start auto, span N auto-placed over N units
|
|
74
|
+
start only start S, span 13 - S from line S to the canvas end
|
|
75
|
+
start and span start S, span N explicit
|
|
243
76
|
```
|
|
244
77
|
|
|
245
|
-
|
|
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.
|
|
78
|
+
### The start defaults to `auto`
|
|
248
79
|
|
|
249
|
-
|
|
80
|
+
The obvious way to write "full canvas" is `grid-column: 1 / -1`. It 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:
|
|
250
81
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
grid-column: 1 / -1;
|
|
256
|
-
}
|
|
82
|
+
```text
|
|
83
|
+
<section style="--column-span: 8"> row 1, columns 1-8
|
|
84
|
+
<aside style="--column-span: 4"> row 2, columns 1-4 ← not 9-12
|
|
85
|
+
```
|
|
257
86
|
|
|
258
|
-
:
|
|
259
|
-
grid-column: 2 / -1;
|
|
260
|
-
}
|
|
87
|
+
Measured, not reasoned: the pair renders on two rows with a definite default start and on one row with an `auto` start, which `tests/browser/column-layout.test.js` asserts. Keeping the start `auto` until an author sets it lets auto-placement flow peers across the canvas.
|
|
261
88
|
|
|
262
|
-
|
|
89
|
+
### A start alone runs to the edge
|
|
263
90
|
|
|
264
|
-
:
|
|
265
|
-
grid-column: 12 / -1;
|
|
266
|
-
}
|
|
267
|
-
```
|
|
91
|
+
With a fixed `span 12` default, `--column-start: 9` would place a region across lines 9 to 21 and add nine implicit columns. The span fallback is therefore derived from the start, `calc(13 - start)`, so a start alone always ends on the canvas edge. With no start, the same expression resolves to `span 12`.
|
|
268
92
|
|
|
269
|
-
|
|
93
|
+
`calc()` in an integer position resolves to a whole number once `var()` is substituted. Support for `calc()` in number contexts predates the CSS floor by years (Chrome 31, Firefox 48, Safari 6); the browser tests prove the four placements in Chromium.
|
|
270
94
|
|
|
271
|
-
|
|
95
|
+
### Coordinates are local to the grid item
|
|
272
96
|
|
|
273
|
-
|
|
274
|
-
<aside class="column-start-9">...</aside>
|
|
275
|
-
```
|
|
97
|
+
Custom properties inherit. A nested `.column-layout` placed with `--column-start: 3; --column-span: 8` would hand those values to each of its own children, which would all land on line 3.
|
|
276
98
|
|
|
277
|
-
|
|
99
|
+
The reset is declared on each child, not on the canvas: the nested canvas needs its coordinates for its own position in the parent, and its children must start from nothing. `--column-start: initial` makes the property guaranteed-invalid, so `var()` takes its fallback. The rule has zero specificity, so any author rule on the child wins.
|
|
278
100
|
|
|
279
|
-
|
|
101
|
+
`@property { inherits: false }` would express the same thing, but it is above the CSS floor and two declarations already do the job.
|
|
280
102
|
|
|
281
|
-
|
|
103
|
+
## Why this is separate from `.grid`
|
|
282
104
|
|
|
283
|
-
|
|
284
|
-
<div class="column-layout">
|
|
285
|
-
<article class="column-span-10 column-start-2">
|
|
286
|
-
...
|
|
287
|
-
</article>
|
|
288
|
-
</div>
|
|
289
|
-
```
|
|
105
|
+
`.grid` and `.grid-N` are collection primitives. They answer:
|
|
290
106
|
|
|
291
|
-
|
|
107
|
+
> How many peer items should fit here?
|
|
292
108
|
|
|
293
|
-
|
|
109
|
+
`.column-layout` answers a different question:
|
|
294
110
|
|
|
295
|
-
|
|
296
|
-
S + N - 1 <= 12
|
|
297
|
-
```
|
|
111
|
+
> Where should these distinct regions sit on a shared composition canvas?
|
|
298
112
|
|
|
299
|
-
|
|
113
|
+
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.
|
|
300
114
|
|
|
301
|
-
|
|
302
|
-
start 9 + span 4 → valid
|
|
303
|
-
start 10 + span 4 → exceeds the canvas
|
|
304
|
-
```
|
|
115
|
+
Use `.column-layout` because **placement is part of the design**, not merely because the design contains columns.
|
|
305
116
|
|
|
306
|
-
|
|
117
|
+
| Need | Primitive |
|
|
118
|
+
| ------------------------------------------------------ | ----------------- |
|
|
119
|
+
| Independent repeated items with intrinsic reflow | `.grid` |
|
|
120
|
+
| Equal-width peers with a known structural density | `.grid-N` |
|
|
121
|
+
| Peer regions that must switch together | `.switcher` |
|
|
122
|
+
| Main content with a secondary region | `.sidebar-layout` |
|
|
123
|
+
| Media paired with flexible content | `.media` |
|
|
124
|
+
| Explicit region placement on a shared 12-column canvas | `.column-layout` |
|
|
307
125
|
|
|
308
|
-
|
|
126
|
+
> If local CSS mainly makes one layout primitive behave like another, reconsider the primitive before adding more CSS.
|
|
309
127
|
|
|
310
|
-
##
|
|
128
|
+
## Why twelve columns
|
|
311
129
|
|
|
312
|
-
|
|
130
|
+
Twelve provides useful subdivisions without requiring a configurable grid system: `6 + 6`, `4 + 4 + 4`, `3 + 3 + 3 + 3`, `8 + 4`, `9 + 3`, `2 + 8 + 2`.
|
|
313
131
|
|
|
314
|
-
|
|
132
|
+
A configurable `--column-count` would weaken the shared canvas: a span of 8 would mean different widths in different layouts, and the start-only fallback would need the count too. Applications that genuinely require a different track system should use `--grid-columns`.
|
|
315
133
|
|
|
316
|
-
|
|
134
|
+
## Base recipe
|
|
317
135
|
|
|
318
|
-
|
|
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
|
-
```
|
|
136
|
+
`minmax(0, 1fr)` is an invariant, not responsive behavior. It prevents the track definition itself from creating a min-content floor that forces the layout wider than its container. It does **not** guarantee that arbitrary content remains usable inside a very narrow span; that is an application composition problem.
|
|
326
137
|
|
|
327
|
-
The
|
|
138
|
+
The track template never reads `--gap`, so `gap` and the track count are independent at every width. This is worth stating because an author who has read the grid page will reasonably assume the opposite.
|
|
328
139
|
|
|
329
|
-
|
|
330
|
-
@container (width < 48rem) {
|
|
331
|
-
.profile-main {
|
|
332
|
-
grid-column: 1 / span 6;
|
|
333
|
-
}
|
|
140
|
+
## Exceeding the canvas
|
|
334
141
|
|
|
335
|
-
|
|
336
|
-
grid-column: 7 / span 6;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
```
|
|
142
|
+
When combining a start and a span, the author keeps `start + span - 1 <= 12`. Actual CSS does not guard against invalid combinations, and the failure is worth stating precisely because it is not local.
|
|
340
143
|
|
|
341
|
-
|
|
144
|
+
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 shifts, not just the one carrying the invalid placement.
|
|
342
145
|
|
|
343
|
-
|
|
146
|
+
A pair that does not fit *without* explicit starts wraps instead: two eight-unit regions occupy sixteen units, so the second moves to the next row. That is correct grid behavior, not a canvas violation.
|
|
344
147
|
|
|
345
148
|
## Responsive contract
|
|
346
149
|
|
|
347
|
-
`.column-layout` has **no automatic responsive collapse**.
|
|
348
|
-
|
|
349
|
-
This is deliberate.
|
|
350
|
-
|
|
351
|
-
Different compositions have different useful breakpoints:
|
|
150
|
+
`.column-layout` has **no automatic responsive collapse**. Different compositions have different useful breakpoints:
|
|
352
151
|
|
|
353
152
|
* `8 + 4` may remain useful longer than `3 + 9`;
|
|
354
153
|
* a centered article may only need reduced side gutters;
|
|
355
154
|
* a dashboard may recombine from three regions into two before eventually stacking;
|
|
356
155
|
* an editorial layout may switch to a completely different representation on narrow screens.
|
|
357
156
|
|
|
358
|
-
A single framework threshold would
|
|
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:
|
|
157
|
+
A single framework threshold would be arbitrary, and a hidden one would force authors to undo it before writing their own. The author owns narrow-container behavior, and writes it with the same hooks:
|
|
400
158
|
|
|
401
159
|
```css
|
|
402
160
|
@container (width < 48rem) {
|
|
403
161
|
.profile-main {
|
|
404
|
-
|
|
162
|
+
--column-start: 1;
|
|
163
|
+
--column-span: 6;
|
|
405
164
|
}
|
|
406
165
|
|
|
407
166
|
.profile-aside {
|
|
408
|
-
|
|
167
|
+
--column-start: 7;
|
|
168
|
+
--column-span: 6;
|
|
409
169
|
}
|
|
410
170
|
}
|
|
411
171
|
|
|
412
172
|
@container (width < 32rem) {
|
|
413
173
|
.profile-main,
|
|
414
174
|
.profile-aside {
|
|
415
|
-
|
|
175
|
+
--column-start: 1;
|
|
176
|
+
--column-span: 12;
|
|
416
177
|
}
|
|
417
178
|
}
|
|
418
179
|
```
|
|
419
180
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
The framework should not reduce every narrow layout to a single automatic stack.
|
|
423
|
-
|
|
424
|
-
## Relationship with `.container-query`
|
|
181
|
+
The ability to recompose through intermediate states is part of the value of a coordinate system; the framework should not reduce every narrow layout to a single automatic stack.
|
|
425
182
|
|
|
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.
|
|
183
|
+
`.container-query` is not required by `.column-layout`. An author uses it because their own rules use `@container` — the author uses a query container because the author wrote a container query.
|
|
445
184
|
|
|
446
185
|
## Narrow containers
|
|
447
186
|
|
|
448
|
-
|
|
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.
|
|
187
|
+
With the default `--gap: 0.75rem` and a container measuring 279px at a 360px viewport, the eleven gaps take 132px. The twelve tracks divide the remaining 147px, roughly **12px per unit**: a four-unit region is 84px wide.
|
|
451
188
|
|
|
452
|
-
|
|
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`.
|
|
189
|
+
That number is the argument. There is no useful composition to guess at that width, which is precisely why the primitive does not try. For intrinsic responsive behavior, prefer `.grid`, `.switcher`, or `.sidebar-layout`.
|
|
469
190
|
|
|
470
191
|
## One-dimensional scope
|
|
471
192
|
|
|
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:
|
|
193
|
+
Despite being implemented with CSS Grid, `.column-layout` exposes only horizontal column placement. There are no row, area, end-line, or order hooks. Applications remain free to use native CSS Grid where a genuinely two-dimensional composition requires it:
|
|
486
194
|
|
|
487
195
|
```css
|
|
488
196
|
.dashboard-summary {
|
|
@@ -490,188 +198,63 @@ Applications remain free to use native CSS Grid features where a genuinely two-d
|
|
|
490
198
|
}
|
|
491
199
|
```
|
|
492
200
|
|
|
493
|
-
Keeping the
|
|
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.
|
|
201
|
+
Keeping the API one-dimensional prevents the primitive from growing into a general-purpose grid DSL.
|
|
496
202
|
|
|
497
|
-
|
|
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.
|
|
203
|
+
Visual reordering has consequences for reading order, keyboard navigation, and accessibility, and is not specific to this primitive. Authors should prefer DOM order that stays sensible when the composition changes.
|
|
506
204
|
|
|
507
205
|
## Direct-child box requirement
|
|
508
206
|
|
|
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.
|
|
207
|
+
Placement applies to grid items. A direct child carrying coordinates must generate a box: with `display: contents`, its own box is removed, its descendants become grid items, and the coordinates no longer control them.
|
|
546
208
|
|
|
547
209
|
## Why this is opt-in
|
|
548
210
|
|
|
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.
|
|
211
|
+
A general coordinate system is easy to misuse. Given `main + aside`, it is tempting to write an 8 + 4 composition even when `.sidebar-layout` already expresses the relationship with intrinsic responsive behavior. Likewise, three peer panels should not become three four-unit regions merely because the canvas makes that possible.
|
|
569
212
|
|
|
570
|
-
The primitive
|
|
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.
|
|
213
|
+
The primitive belongs to the Layout family but is not part of the core baseline, and its documentation makes the selection boundary explicit.
|
|
575
214
|
|
|
576
215
|
## Rejected alternatives
|
|
577
216
|
|
|
578
|
-
###
|
|
579
|
-
|
|
580
|
-
Rejected.
|
|
217
|
+
### Placement classes (`.column-span-N`, `.column-start-N`)
|
|
581
218
|
|
|
582
|
-
|
|
219
|
+
Rejected. Twenty-four classes encode two numbers, the start and span classes must be ordered in the source for a start alone to end on the canvas edge, and application recomposition has to leave the vocabulary to write `grid-column`. See [Why hooks rather than placement classes](#why-hooks-rather-than-placement-classes).
|
|
583
220
|
|
|
584
|
-
###
|
|
221
|
+
### Reset the hooks on the canvas
|
|
585
222
|
|
|
586
|
-
|
|
223
|
+
Rejected. A nested canvas is also a grid item of its parent; resetting on `.column-layout` would erase the coordinates it needs for its own placement.
|
|
587
224
|
|
|
588
|
-
|
|
225
|
+
### `@property` with `inherits: false`
|
|
589
226
|
|
|
590
|
-
|
|
227
|
+
Rejected for the base contract: above the CSS floor, and the per-child reset already keeps coordinates local.
|
|
591
228
|
|
|
592
|
-
|
|
229
|
+
### Make `.grid-N` support arbitrary spans
|
|
593
230
|
|
|
594
|
-
|
|
231
|
+
Rejected. `.grid-N` represents structural peer density. Arbitrary child placement would blur the distinction between a collection and a composition canvas.
|
|
595
232
|
|
|
596
|
-
|
|
233
|
+
### Use `.layout-grid` or `.columns`
|
|
597
234
|
|
|
598
|
-
|
|
235
|
+
Rejected. `.layout-grid` is confusing beside `.grid` and `.grid-N`; `.columns` is too generic and reads as CSS multi-column layout.
|
|
599
236
|
|
|
600
237
|
### Add `--column-count`
|
|
601
238
|
|
|
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.
|
|
239
|
+
Rejected. Twelve columns are part of the primitive's contract.
|
|
625
240
|
|
|
626
|
-
|
|
241
|
+
### Add automatic container-query collapse, breakpoint variants, or offsets
|
|
627
242
|
|
|
628
|
-
|
|
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
|
|
243
|
+
Rejected. There is no universally correct threshold; breakpoint variants would move responsive strategy back into markup; `--column-start` already expresses native grid placement without a second, margin-based model.
|
|
644
244
|
|
|
645
|
-
|
|
245
|
+
### Add row placement, named areas, or ordering
|
|
646
246
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
### Add ordering utilities
|
|
650
|
-
|
|
651
|
-
Rejected as unrelated to the primitive and carrying separate accessibility concerns.
|
|
247
|
+
Rejected for this scope. Row placement and areas belong to native Grid on an application class; ordering carries separate accessibility concerns.
|
|
652
248
|
|
|
653
249
|
## Decision summary
|
|
654
250
|
|
|
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
251
|
The primitive:
|
|
664
252
|
|
|
665
253
|
* uses twelve equal `minmax(0, 1fr)` tracks;
|
|
666
|
-
*
|
|
667
|
-
*
|
|
668
|
-
*
|
|
669
|
-
*
|
|
254
|
+
* places each direct child from `--column-start` and `--column-span`, in one rule;
|
|
255
|
+
* keeps the start `auto` until set, so auto-placement composes spans;
|
|
256
|
+
* derives the default span from the start, so a start alone ends on the canvas edge;
|
|
257
|
+
* resets both hooks on each child, so coordinates never leak into a nested canvas;
|
|
258
|
+
* provides no automatic responsive behavior and leaves recomposition to the application, through the same hooks;
|
|
670
259
|
* 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
260
|
* 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.
|