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
|
@@ -0,0 +1,785 @@
|
|
|
1
|
+
# Grid Responsiveness and Balanced Subdivision
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Design note for Actual CSS 0.4.
|
|
6
|
+
|
|
7
|
+
This document explains the intended behavior of `.grid` and `.grid-N`, the alternatives considered, and the CSS constraints that prevent a single mechanism from satisfying every use case.
|
|
8
|
+
|
|
9
|
+
The goal is to preserve these decisions so future changes do not accidentally reintroduce previously rejected approaches.
|
|
10
|
+
|
|
11
|
+
## Problem
|
|
12
|
+
|
|
13
|
+
Actual CSS exposes two related but distinct grid use cases:
|
|
14
|
+
|
|
15
|
+
* a free collection that should adapt naturally to available space;
|
|
16
|
+
* a structured collection where a known column density should collapse through visually balanced subdivisions.
|
|
17
|
+
|
|
18
|
+
These goals are not equivalent.
|
|
19
|
+
|
|
20
|
+
A generic intrinsic grid can efficiently fill available space, but it does not know whether the resulting number of columns produces a balanced distribution for the number of items.
|
|
21
|
+
|
|
22
|
+
Conversely, a structurally balanced grid can preserve predictable subdivisions, but CSS cannot make it fully autonomous without introducing other layout constraints.
|
|
23
|
+
|
|
24
|
+
The distinction matters most when resizing a collection.
|
|
25
|
+
|
|
26
|
+
For six items, a purely space-driven grid may naturally move through:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
6 → 5 → 4 → 3 → 2 → 1
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This maximizes use of available space, but produces layouts such as:
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
5 + 1
|
|
36
|
+
4 + 2
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Actual's structural grid use case instead prefers:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
6 → 3 → 2 → 1
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Every state is a natural subdivision of six and every item has the same width.
|
|
46
|
+
|
|
47
|
+
## Core principle
|
|
48
|
+
|
|
49
|
+
Actual distinguishes **space-driven grids** from **count-driven grids**.
|
|
50
|
+
|
|
51
|
+
### `.grid`: space-driven collection
|
|
52
|
+
|
|
53
|
+
`.grid` is an intrinsic responsive collection.
|
|
54
|
+
|
|
55
|
+
Its behavior is driven by:
|
|
56
|
+
|
|
57
|
+
* the width of its containing block;
|
|
58
|
+
* `--grid-min`;
|
|
59
|
+
* the number of tracks that can fit.
|
|
60
|
+
|
|
61
|
+
A typical implementation uses `auto-fit`.
|
|
62
|
+
|
|
63
|
+
The browser is free to choose any number of columns that satisfies the minimum item width.
|
|
64
|
+
|
|
65
|
+
No guarantee is made about balanced final rows.
|
|
66
|
+
|
|
67
|
+
Conceptually:
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
available space
|
|
71
|
+
↓
|
|
72
|
+
how many items fit?
|
|
73
|
+
↓
|
|
74
|
+
auto-fit
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
This is appropriate for collections where filling available space is more important than preserving a particular structural rhythm.
|
|
78
|
+
|
|
79
|
+
### `.grid-N`: count-driven structural grid
|
|
80
|
+
|
|
81
|
+
`.grid-2`, `.grid-3`, `.grid-4`, and `.grid-6` express a preferred structural density.
|
|
82
|
+
|
|
83
|
+
Their responsive progression should favor natural divisors of that density.
|
|
84
|
+
|
|
85
|
+
The intended chains are:
|
|
86
|
+
|
|
87
|
+
```text
|
|
88
|
+
.grid-2 2 → 1
|
|
89
|
+
.grid-3 3 → 1
|
|
90
|
+
.grid-4 4 → 2 → 1
|
|
91
|
+
.grid-6 6 → 3 → 2 → 1
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
This deliberately skips intermediate column counts that would create visibly uneven subdivisions.
|
|
95
|
+
|
|
96
|
+
For example, `.grid-6` should not pass through five or four columns simply because five or four tracks happen to fit.
|
|
97
|
+
|
|
98
|
+
The defining property of `.grid-N` is therefore not merely:
|
|
99
|
+
|
|
100
|
+
> at most N columns
|
|
101
|
+
|
|
102
|
+
It is:
|
|
103
|
+
|
|
104
|
+
> a structural density of N columns that collapses through balanced subdivisions of N.
|
|
105
|
+
|
|
106
|
+
## Equal-width items are an invariant
|
|
107
|
+
|
|
108
|
+
Actual does not attempt to repair an incomplete row by stretching individual items, changing their span independently, or making the last card wider than its peers.
|
|
109
|
+
|
|
110
|
+
At every responsive state, all direct items in a structural grid should have the same width.
|
|
111
|
+
|
|
112
|
+
Layouts such as this are intentionally avoided:
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
[A][B][C]
|
|
116
|
+
[ D ][E]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The grid may contain an incomplete final row when its item count is incompatible with the selected density, but the items themselves remain regular.
|
|
120
|
+
|
|
121
|
+
Visual consistency of the grid is more important than artificially filling every pixel.
|
|
122
|
+
|
|
123
|
+
## Item count and structural density are different inputs
|
|
124
|
+
|
|
125
|
+
A structural density does not imply that CSS knows the actual number of items.
|
|
126
|
+
|
|
127
|
+
For example, `.grid-6` describes the sequence:
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
6 → 3 → 2 → 1
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
but it does not guarantee a balanced final row for an arbitrary number of children.
|
|
134
|
+
|
|
135
|
+
With eight items, six columns still produce:
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
6 + 2
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
and three columns produce:
|
|
142
|
+
|
|
143
|
+
```text
|
|
144
|
+
3 + 3 + 2
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
CSS is not inferring the child count and selecting an optimal divisor of that count.
|
|
148
|
+
|
|
149
|
+
The contract is narrower:
|
|
150
|
+
|
|
151
|
+
> `.grid-N` provides balanced subdivisions of the declared structural density N.
|
|
152
|
+
|
|
153
|
+
This works particularly well when collection sizes are naturally compatible with that density.
|
|
154
|
+
|
|
155
|
+
For paginated collections, the server knows the page size and should choose a page size that works well with the expected presentation densities.
|
|
156
|
+
|
|
157
|
+
A partial final page is normal. CSS should not contain pagination heuristics intended to disguise a poor full-page item count.
|
|
158
|
+
|
|
159
|
+
## Why `auto-fit` alone is insufficient
|
|
160
|
+
|
|
161
|
+
`auto-fit` answers a spatial question:
|
|
162
|
+
|
|
163
|
+
> How many tracks of this minimum size fit in the available width?
|
|
164
|
+
|
|
165
|
+
It does not answer:
|
|
166
|
+
|
|
167
|
+
> Which number of tracks produces the most balanced subdivision?
|
|
168
|
+
|
|
169
|
+
For six items, if five columns fit, `auto-fit` may use five columns.
|
|
170
|
+
|
|
171
|
+
That behavior is correct for an intrinsic collection and incorrect for the structural-grid contract.
|
|
172
|
+
|
|
173
|
+
Therefore `.grid-N` cannot simply be implemented as `.grid` with a maximum column count if balanced subdivision remains a goal.
|
|
174
|
+
|
|
175
|
+
## Why `auto-fill` does not solve subdivision
|
|
176
|
+
|
|
177
|
+
`auto-fill` differs from `auto-fit` mainly in its treatment of empty tracks.
|
|
178
|
+
|
|
179
|
+
This matters when fewer items exist than the available grid capacity.
|
|
180
|
+
|
|
181
|
+
For example, with a four-column capacity and three items:
|
|
182
|
+
|
|
183
|
+
```text
|
|
184
|
+
auto-fit
|
|
185
|
+
[A ][B ][C ]
|
|
186
|
+
|
|
187
|
+
auto-fill
|
|
188
|
+
[A][B][C][ ]
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`auto-fill` can therefore preserve card width on a partial final page instead of expanding the remaining items.
|
|
192
|
+
|
|
193
|
+
That is useful behavior for catalogue-like layouts.
|
|
194
|
+
|
|
195
|
+
However, it does not solve the subdivision problem.
|
|
196
|
+
|
|
197
|
+
With six items and enough room for four columns, both mechanisms can still produce:
|
|
198
|
+
|
|
199
|
+
```text
|
|
200
|
+
[A][B][C][D]
|
|
201
|
+
[E][F]
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Neither `auto-fit` nor `auto-fill` understands that three columns would form the more balanced `3 + 3` subdivision.
|
|
205
|
+
|
|
206
|
+
`auto-fill` is therefore not a replacement for the `.grid-N` responsive model.
|
|
207
|
+
|
|
208
|
+
## Historical implementation in 0.3.1
|
|
209
|
+
|
|
210
|
+
Actual CSS 0.3.1 already distinguished intrinsic grids from structural grids.
|
|
211
|
+
|
|
212
|
+
The structural presets changed their column count using container queries.
|
|
213
|
+
|
|
214
|
+
This allowed responsive behavior to depend on the size of the grid's local layout context rather than on the viewport.
|
|
215
|
+
|
|
216
|
+
The important architectural property was:
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
query container
|
|
220
|
+
↓
|
|
221
|
+
.grid-N
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
The grid itself remained an ordinary grid item and therefore retained its normal intrinsic sizing behavior.
|
|
225
|
+
|
|
226
|
+
The main drawback was ergonomic: responsive structural grids depended on an explicit ancestor establishing the query container.
|
|
227
|
+
|
|
228
|
+
The requirement looked like an implementation detail in markup, which motivated attempts to make `.grid-N` autonomous in 0.4.
|
|
229
|
+
|
|
230
|
+
The behavior was worth preserving; the mechanism was the part under investigation.
|
|
231
|
+
|
|
232
|
+
## Demoted, not rejected: bounded intrinsic grids
|
|
233
|
+
|
|
234
|
+
One proposed 0.4 design implemented `.grid-N` as an intrinsic `auto-fit` or
|
|
235
|
+
`auto-fill` grid capped at N columns.
|
|
236
|
+
|
|
237
|
+
This removed breakpoints and container-query requirements.
|
|
238
|
+
|
|
239
|
+
It cannot be the whole answer, because it optimizes for available space:
|
|
240
|
+
|
|
241
|
+
```text
|
|
242
|
+
6 -> 5 -> 4 -> 3 -> 2 -> 1
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
where a structural grid is expected to optimize for subdivision:
|
|
246
|
+
|
|
247
|
+
```text
|
|
248
|
+
6 -> 3 -> 2 -> 1
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
The mechanism therefore answers a different question than the one `.grid-N`
|
|
252
|
+
asks.
|
|
253
|
+
|
|
254
|
+
It is nevertheless the right **baseline**. Bounded at N, using `auto-fill`, it
|
|
255
|
+
is responsive, overflow-safe, and equal-width at every state, and it needs
|
|
256
|
+
nothing above Actual's Minimal floor. Adopting it as the unenhanced behavior of
|
|
257
|
+
`.grid-N` is what makes the actual-grid container a precision enhancement rather than
|
|
258
|
+
a prerequisite for responsiveness. This is the single most important difference
|
|
259
|
+
from 0.3.1, where a preset used without a wrapper stayed frozen at N columns
|
|
260
|
+
and was a genuine trap.
|
|
261
|
+
|
|
262
|
+
## Rejected approach: `smart-grid` item-count heuristics
|
|
263
|
+
|
|
264
|
+
Another direction attempted to choose intermediate column counts that avoided orphan rows.
|
|
265
|
+
|
|
266
|
+
For a collection containing `itemCount` items and `c` columns, a one-item final row appears when:
|
|
267
|
+
|
|
268
|
+
```text
|
|
269
|
+
itemCount mod c = 1
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
This observation is useful, but it is not a suitable basis for a generic grid class.
|
|
273
|
+
|
|
274
|
+
The undesirable column counts depend on the actual number of items, not only on the declared grid density.
|
|
275
|
+
|
|
276
|
+
A fixed `.grid-4` class cannot know whether three columns are desirable:
|
|
277
|
+
|
|
278
|
+
* for ten items, three columns produces `3 + 3 + 3 + 1`;
|
|
279
|
+
* for twelve items, three columns produces a perfect `3 + 3 + 3 + 3`.
|
|
280
|
+
|
|
281
|
+
Hard-coding `.grid-4` to skip three columns would encode the answer for one item count and penalize another.
|
|
282
|
+
|
|
283
|
+
More importantly, avoiding a singleton is weaker than balanced subdivision.
|
|
284
|
+
|
|
285
|
+
For six items, four columns gives `4 + 2`. There is no singleton, but `3 + 3` is still structurally preferable.
|
|
286
|
+
|
|
287
|
+
Actual therefore does not attempt to infer an optimal layout from item count in CSS.
|
|
288
|
+
|
|
289
|
+
## Rejected approach: make `.grid-N` its own query container
|
|
290
|
+
|
|
291
|
+
A promising prototype used:
|
|
292
|
+
|
|
293
|
+
```css
|
|
294
|
+
.grid-N {
|
|
295
|
+
container-type: inline-size;
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
and kept a fixed internal track count.
|
|
300
|
+
|
|
301
|
+
Instead of changing `grid-template-columns` on the container, container queries changed `grid-column: span ...` on its children.
|
|
302
|
+
|
|
303
|
+
For a six-track grid:
|
|
304
|
+
|
|
305
|
+
```text
|
|
306
|
+
span 1 → 6 columns
|
|
307
|
+
span 2 → 3 columns
|
|
308
|
+
span 3 → 2 columns
|
|
309
|
+
span 6 → 1 column
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
This successfully produced:
|
|
313
|
+
|
|
314
|
+
```text
|
|
315
|
+
6 → 3 → 2 → 1
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
without requiring an external query-container wrapper.
|
|
319
|
+
|
|
320
|
+
It also guaranteed equal-width items.
|
|
321
|
+
|
|
322
|
+
Technically, the mechanism is valid: descendants can query the dimensions of their ancestor query container.
|
|
323
|
+
|
|
324
|
+
However, the solution has a serious side effect.
|
|
325
|
+
|
|
326
|
+
### Inline-size containment changes intrinsic sizing
|
|
327
|
+
|
|
328
|
+
`container-type: inline-size` introduces inline-size containment.
|
|
329
|
+
|
|
330
|
+
The contents of the grid no longer contribute normally to the grid's intrinsic inline size.
|
|
331
|
+
|
|
332
|
+
Probes showed the following behavior:
|
|
333
|
+
|
|
334
|
+
| Context | Observed grid width |
|
|
335
|
+
| ------------------------------- | ------------------: |
|
|
336
|
+
| normal block | 1192px |
|
|
337
|
+
| parent grid `auto` track | 992px |
|
|
338
|
+
| parent grid `min-content` track | 48px |
|
|
339
|
+
| flex item with `flex: 0 1 auto` | 48px |
|
|
340
|
+
| `width: fit-content` | 48px |
|
|
341
|
+
| float | 48px |
|
|
342
|
+
| inline-block | 0px |
|
|
343
|
+
|
|
344
|
+
The self-container therefore works when the grid already receives a definite or stretched width, but can collapse almost completely in shrink-to-fit or intrinsic-sizing contexts.
|
|
345
|
+
|
|
346
|
+
This is not merely an artificial CSS edge case.
|
|
347
|
+
|
|
348
|
+
Actual's own composition primitives can create such contexts.
|
|
349
|
+
|
|
350
|
+
For example:
|
|
351
|
+
|
|
352
|
+
```html
|
|
353
|
+
<div class="cluster">
|
|
354
|
+
<div class="grid-4">...</div>
|
|
355
|
+
</div>
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
`.cluster` is a wrapping flex row whose children retain normal `flex: 0 1 auto` sizing.
|
|
359
|
+
|
|
360
|
+
A self-contained `.grid-4` can therefore exhibit exactly the collapse observed in the probe.
|
|
361
|
+
|
|
362
|
+
Similar intrinsic-sizing concerns exist when a structural grid participates in auto-sized tracks or other shrink-to-fit compositions.
|
|
363
|
+
|
|
364
|
+
### Why this approach is rejected
|
|
365
|
+
|
|
366
|
+
Making `.grid-N` its own query container removes an explicit wrapper at the cost of changing the fundamental sizing behavior of the primitive.
|
|
367
|
+
|
|
368
|
+
That trade-off is not acceptable for a composable layout library.
|
|
369
|
+
|
|
370
|
+
Attempts to compensate with `contain-intrinsic-size`, special rules for `.cluster`, or other context-specific fixes would add complexity and create coupling between otherwise independent primitives.
|
|
371
|
+
|
|
372
|
+
The self-container approach is therefore rejected.
|
|
373
|
+
|
|
374
|
+
## Why an external query container is not merely a workaround
|
|
375
|
+
|
|
376
|
+
A size container query cannot make an element react to its own size without applying containment to that element.
|
|
377
|
+
|
|
378
|
+
Placing the query container on an ancestor preserves the intrinsic sizing behavior of `.grid-N` while still allowing the grid to react to local available space.
|
|
379
|
+
|
|
380
|
+
The relationship is:
|
|
381
|
+
|
|
382
|
+
```text
|
|
383
|
+
query container (any ancestor, e.g. .container-query)
|
|
384
|
+
↓ observes local available width
|
|
385
|
+
.grid-N
|
|
386
|
+
↓ changes structural subdivision
|
|
387
|
+
items
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
The extra ancestor is therefore not accidental ceremony.
|
|
391
|
+
|
|
392
|
+
It represents a real boundary imposed by the CSS containment model.
|
|
393
|
+
|
|
394
|
+
Actual should not hide that boundary if hiding it requires changing the sizing semantics of the grid. Balanced subdivision keys off a container **named** `actual-grid` rather than any anonymous query container, so an incidental `container-type: inline-size` region never changes the grid by accident; `.container-query` is the convenience helper that establishes that named context.
|
|
395
|
+
|
|
396
|
+
## Out of scope: explicit per-region placement
|
|
397
|
+
|
|
398
|
+
Editorial or application layouts that need arbitrary per-region spans and starts
|
|
399
|
+
are outside the `.grid` / `.grid-N` collection contract. Both primitives lay out
|
|
400
|
+
uniform peers; neither expresses "this region occupies seven units starting at
|
|
401
|
+
the third".
|
|
402
|
+
|
|
403
|
+
That case is answered by `.column-layout`, an opt-in twelve-column coordinate
|
|
404
|
+
system with no automatic responsive behavior. See `column-layout.md`. It also
|
|
405
|
+
narrows `--grid-columns` to its intended role: track templates whose sizes carry
|
|
406
|
+
meaning, rather than a stand-in for a coordinate system.
|
|
407
|
+
|
|
408
|
+
## Progressive enhancement
|
|
409
|
+
|
|
410
|
+
Container queries are newer than Actual's Minimal browser baseline.
|
|
411
|
+
|
|
412
|
+
Structural grids must therefore have a usable fallback without them.
|
|
413
|
+
|
|
414
|
+
The fallback should remain structurally valid and readable. It does not need to reproduce the full responsive subdivision behavior.
|
|
415
|
+
|
|
416
|
+
Modern browsers may enhance the grid when it is placed inside a query container.
|
|
417
|
+
|
|
418
|
+
This follows Actual's general compatibility rule:
|
|
419
|
+
|
|
420
|
+
> Modern CSS should not raise the Minimal baseline when a progressive fallback is possible at low cost.
|
|
421
|
+
|
|
422
|
+
## Relationship with `--grid-min`
|
|
423
|
+
|
|
424
|
+
`--grid-min` belongs naturally to `.grid`.
|
|
425
|
+
|
|
426
|
+
It expresses the minimum desirable width of an item in a space-driven collection.
|
|
427
|
+
|
|
428
|
+
Structural `.grid-N` presets are different: their behavior is driven by a known count and a predefined subdivision sequence.
|
|
429
|
+
|
|
430
|
+
Trying to derive their container-query thresholds from `--grid-min` is not currently practical because size container-query conditions cannot use arbitrary custom-property values as their thresholds.
|
|
431
|
+
|
|
432
|
+
The APIs should therefore remain conceptually separate:
|
|
433
|
+
|
|
434
|
+
```text
|
|
435
|
+
.grid
|
|
436
|
+
input: --grid-min
|
|
437
|
+
strategy: available-space driven
|
|
438
|
+
|
|
439
|
+
.grid-N
|
|
440
|
+
input: structural density N
|
|
441
|
+
strategy: divisor-driven subdivision
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
The fact that both produce CSS Grid layouts does not mean they require the same responsive algorithm.
|
|
445
|
+
|
|
446
|
+
## Supported structural densities
|
|
447
|
+
|
|
448
|
+
The current useful presets are:
|
|
449
|
+
|
|
450
|
+
```text
|
|
451
|
+
2 → 1
|
|
452
|
+
3 → 1
|
|
453
|
+
4 → 2 → 1
|
|
454
|
+
6 → 3 → 2 → 1
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
Not every integer necessarily deserves a preset.
|
|
458
|
+
|
|
459
|
+
For example, five has only trivial divisors:
|
|
460
|
+
|
|
461
|
+
```text
|
|
462
|
+
5 → 1
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
Adding `.grid-5` would either produce an abrupt transition or require introducing non-divisor heuristics such as `5 → 2 → 1`.
|
|
466
|
+
|
|
467
|
+
Actual should not add structural presets merely to complete a numerical sequence.
|
|
468
|
+
|
|
469
|
+
New presets should have a clear layout use case and a defensible subdivision chain.
|
|
470
|
+
|
|
471
|
+
## Thresholds are visual design decisions
|
|
472
|
+
|
|
473
|
+
The subdivision chain can be derived from the structural density.
|
|
474
|
+
|
|
475
|
+
The width thresholds at which transitions occur cannot.
|
|
476
|
+
|
|
477
|
+
Values such as:
|
|
478
|
+
|
|
479
|
+
```text
|
|
480
|
+
28rem
|
|
481
|
+
48rem
|
|
482
|
+
64rem
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
must be validated against realistic Actual content rather than inherited mechanically from historical breakpoints.
|
|
486
|
+
|
|
487
|
+
Before changing structural-grid thresholds, probes should verify:
|
|
488
|
+
|
|
489
|
+
* realistic cards rather than empty boxes;
|
|
490
|
+
* narrow parent inside a wide viewport;
|
|
491
|
+
* every expected subdivision state;
|
|
492
|
+
* equal item widths;
|
|
493
|
+
* full and partial rows;
|
|
494
|
+
* interaction with common Actual composition primitives;
|
|
495
|
+
* fallback behavior without container-query support.
|
|
496
|
+
|
|
497
|
+
The viewport itself should not determine the result; the relevant local container should.
|
|
498
|
+
|
|
499
|
+
## Why there is no breakpoint scale
|
|
500
|
+
|
|
501
|
+
Actual exposes no `sm` / `md` / `lg` / `xl` scale, and the structural grid is the
|
|
502
|
+
clearest illustration of why. The whole framework declares two width media
|
|
503
|
+
queries:
|
|
504
|
+
|
|
505
|
+
```text
|
|
506
|
+
@media (max-width: 30rem) x2 modal padding on small screens
|
|
507
|
+
@media (min-width: 48rem) x1 .app-layout, bottom nav -> side nav
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
Everything else responsive is intrinsic or container-driven. The responsibility
|
|
511
|
+
splits cleanly:
|
|
512
|
+
|
|
513
|
+
```text
|
|
514
|
+
viewport
|
|
515
|
+
|
|
|
516
|
+
+-- the few genuinely viewport-level decisions
|
|
517
|
+
| .app-layout
|
|
518
|
+
|
|
|
519
|
+
+-- composition and chrome
|
|
520
|
+
|
|
|
521
|
+
named query container
|
|
522
|
+
|
|
|
523
|
+
actual-grid
|
|
524
|
+
|
|
|
525
|
+
2 / 3 / 4 / 6 stages
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
**Thresholds are local and content-driven.** `28rem`, `48rem` and `64rem`
|
|
529
|
+
describe how much room *this collection* needs for a given density, probed
|
|
530
|
+
against the content each preset targets. They are not device classes, and the
|
|
531
|
+
fact that two of them coincide with widely used framework breakpoints is a
|
|
532
|
+
coincidence, not the justification. A grid does not know what device it is on
|
|
533
|
+
and does not need to.
|
|
534
|
+
|
|
535
|
+
**The unit is `rem` so the thresholds follow typographic scale.** Container
|
|
536
|
+
query lengths in `rem` resolve against the root font size, so a reader who
|
|
537
|
+
scales their type up moves every threshold with it:
|
|
538
|
+
|
|
539
|
+
```text
|
|
540
|
+
root 16px 48rem = 768px 768px container -> .grid-3 = 3 columns
|
|
541
|
+
root 20px 48rem = 960px 768px container -> .grid-3 = 1 column
|
|
542
|
+
root 24px 48rem = 1152px 768px container -> .grid-3 = 1 column
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
That is the correct response, not a rounding artifact: larger type genuinely
|
|
546
|
+
needs more room per item, so the same container honestly supports fewer columns.
|
|
547
|
+
A `px` breakpoint cannot express this.
|
|
548
|
+
|
|
549
|
+
### Container width is not a monotonic function of viewport width
|
|
550
|
+
|
|
551
|
+
This is the empirical case against a global scale, and it is measurable inside
|
|
552
|
+
this repository. `demo/templates/dashboard.html` reveals its workspace sidebar
|
|
553
|
+
at `72rem`. Measuring the main region's content box across that boundary:
|
|
554
|
+
|
|
555
|
+
| Viewport | Container content box | `.grid-4` | `.grid-6` |
|
|
556
|
+
| -------- | --------------------- | --------- | --------- |
|
|
557
|
+
| 1100px | 65.8rem | 4 | 6 |
|
|
558
|
+
| 1160px | 52.6rem | 2 | 3 |
|
|
559
|
+
|
|
560
|
+
The viewport grew by 60px and the grid lost half its columns, because the
|
|
561
|
+
composition spent that growth on chrome rather than on content. A viewport-keyed
|
|
562
|
+
system reasons about this backwards by construction: it sees a wider screen and
|
|
563
|
+
infers more room, at the exact moment there is less. No amount of breakpoint
|
|
564
|
+
tuning fixes that, because there is no stable function from viewport width to
|
|
565
|
+
container width — the composition sits in between and is free to change.
|
|
566
|
+
|
|
567
|
+
A named query container asks the only question with a reliable answer: *how much
|
|
568
|
+
inline space does this collection actually have?* Everything the ladder does
|
|
569
|
+
follows from that measurement, which is why the thresholds can stay content-
|
|
570
|
+
derived instead of device-derived.
|
|
571
|
+
|
|
572
|
+
## API guidance
|
|
573
|
+
|
|
574
|
+
Use `.grid` when the number of columns should emerge naturally from available space.
|
|
575
|
+
|
|
576
|
+
Use `.grid-N` when the declared density and its balanced subdivisions are part of the intended composition.
|
|
577
|
+
|
|
578
|
+
Use structural grids for uniform peer items. They are not intended as editorial grids with arbitrary per-item spans.
|
|
579
|
+
|
|
580
|
+
Do not use CSS layout heuristics to compensate for a pagination size that produces undesirable full-page distributions when the server already knows the item count.
|
|
581
|
+
|
|
582
|
+
## Decision
|
|
583
|
+
|
|
584
|
+
Actual keeps two different grid contracts.
|
|
585
|
+
|
|
586
|
+
```text
|
|
587
|
+
.grid
|
|
588
|
+
intrinsic
|
|
589
|
+
space-driven
|
|
590
|
+
auto-fit
|
|
591
|
+
configurable through --grid-min
|
|
592
|
+
no guarantee of balanced rows
|
|
593
|
+
|
|
594
|
+
.grid-N
|
|
595
|
+
structural
|
|
596
|
+
count-driven
|
|
597
|
+
equal-width items
|
|
598
|
+
responsive through natural subdivisions of N
|
|
599
|
+
local-container driven when enhanced
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
A structural grid must not become its own inline-size query container, because doing so breaks intrinsic and shrink-to-fit composition.
|
|
603
|
+
|
|
604
|
+
If local responsive subdivision requires a size container query, that query container must remain outside the structural grid.
|
|
605
|
+
|
|
606
|
+
The explicit container boundary is preferable to hidden containment side effects.
|
|
607
|
+
|
|
608
|
+
The guiding principle is:
|
|
609
|
+
|
|
610
|
+
> `.grid` optimizes for available space. `.grid-N` optimizes for balanced subdivision.
|
|
611
|
+
|
|
612
|
+
Future grid changes should preserve that distinction unless the public contract itself is intentionally reconsidered.
|
|
613
|
+
|
|
614
|
+
## Resolution
|
|
615
|
+
|
|
616
|
+
`.grid-N` is a bounded `auto-fill` grid by default, and a divisor-chain grid
|
|
617
|
+
inside a query container.
|
|
618
|
+
|
|
619
|
+
```text
|
|
620
|
+
.grid space-driven, auto-fit, --grid-min
|
|
621
|
+
.grid-N count-driven baseline, auto-fill bounded at N,
|
|
622
|
+
may pass through non-divisor column counts
|
|
623
|
+
actual-grid .grid-N balanced subdivision, N -> divisors of N -> 1
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
The whole enhancement lives inside `@supports (container-type: inline-size)`.
|
|
627
|
+
Browsers below the floor get the baseline whether or not a wrapper is present,
|
|
628
|
+
which removes the parallel `@media` block 0.3.1 had to maintain in lockstep
|
|
629
|
+
with its container thresholds.
|
|
630
|
+
|
|
631
|
+
### `--grid-min` is not a `.grid-N` hook
|
|
632
|
+
|
|
633
|
+
`@container` thresholds cannot resolve a custom property. A hook honored in the
|
|
634
|
+
baseline would therefore fall silent the moment an author added a query
|
|
635
|
+
container — exactly the kind of hidden, position-dependent contract this
|
|
636
|
+
redesign set out to remove. The baseline hard-codes `16rem` instead.
|
|
637
|
+
|
|
638
|
+
The resulting API split is the clearer one anyway: item width is `.grid`'s
|
|
639
|
+
contract, column density is `.grid-N`'s.
|
|
640
|
+
|
|
641
|
+
### Thresholds
|
|
642
|
+
|
|
643
|
+
Three shared steps, not one calibrated per preset:
|
|
644
|
+
|
|
645
|
+
```text
|
|
646
|
+
28rem first horizontal density .grid-2, .grid-4, .grid-6 -> 2 columns
|
|
647
|
+
48rem three columns .grid-3, .grid-6 -> 3 columns
|
|
648
|
+
64rem dense densities .grid-4 -> 4, .grid-6 -> 6
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
An earlier draft tuned each final step so that every preset landed an item
|
|
652
|
+
around 240px, which put `.grid-6` at 80rem or 96rem. That was the wrong
|
|
653
|
+
optimization, and worth recording so it is not re-derived.
|
|
654
|
+
|
|
655
|
+
Calibrating every preset to the same item width erases the information N
|
|
656
|
+
carries. An author does not reach for `.grid-N` to say *I have N things*; they
|
|
657
|
+
say *my content suits this density*. `.grid-6` should reach six columns early
|
|
658
|
+
precisely because choosing it is a statement that the items are compact. At
|
|
659
|
+
96rem an item is back to `.grid`'s 16rem default, and the preset has no reason
|
|
660
|
+
to exist.
|
|
661
|
+
|
|
662
|
+
The right probe is therefore each preset against the content it targets, not
|
|
663
|
+
one specimen across all of them:
|
|
664
|
+
|
|
665
|
+
| Preset | Probed with | Item at its final step | Result |
|
|
666
|
+
| --------- | ---------------------------------- | ---------------------- | ------ |
|
|
667
|
+
| `.grid-2` | large card, summary, action | 218px @ 28rem | clean |
|
|
668
|
+
| `.grid-3` | standard card | 248px @ 48rem | clean |
|
|
669
|
+
| `.grid-4` | compact card, stat | 247px @ 64rem | clean |
|
|
670
|
+
| `.grid-6` | stat, avatar, thumbnail, icon tile | 161px @ 64rem | clean |
|
|
671
|
+
|
|
672
|
+
161px is small for a card with a heading and a paragraph — and that is the
|
|
673
|
+
point. A preset that needs 250px per item is `.grid-4`, not `.grid-6`.
|
|
674
|
+
|
|
675
|
+
The framework does not guess appropriate item size. The author states it by
|
|
676
|
+
choosing a density; the framework then guarantees a clean structural collapse
|
|
677
|
+
of that density.
|
|
678
|
+
|
|
679
|
+
### Reference container sizes
|
|
680
|
+
|
|
681
|
+
The thresholds describe the **query container's content box**, not the viewport
|
|
682
|
+
and not the grid's own border box. Padding and borders on the container come off
|
|
683
|
+
the top before the query resolves. Verified states:
|
|
684
|
+
|
|
685
|
+
| Container content box | `.grid-2` | `.grid-3` | `.grid-4` | `.grid-6` |
|
|
686
|
+
| --------------------- | --------- | --------- | --------- | --------- |
|
|
687
|
+
| `< 28rem` | 1 | 1 | 1 | 1 |
|
|
688
|
+
| `28rem` – `< 48rem` | 2 | 1 | 2 | 2 |
|
|
689
|
+
| `48rem` – `< 64rem` | 2 | 3 | 2 | 3 |
|
|
690
|
+
| `>= 64rem` | 2 | 3 | 4 | 6 |
|
|
691
|
+
|
|
692
|
+
Two consequences for anyone sizing a region that hosts a structural grid.
|
|
693
|
+
|
|
694
|
+
**Size the region to land on a threshold, not just under one.** A region whose
|
|
695
|
+
content box settles at 47rem gives `.grid-3` one column — the same result as
|
|
696
|
+
20rem. The step is a cliff, not a ramp, and `.grid-3` has no intermediate state
|
|
697
|
+
to soften it; that is the invariant, not a defect. If a region is meant to show
|
|
698
|
+
three columns, give it 48rem of content box with room to spare.
|
|
699
|
+
|
|
700
|
+
**Budget backwards from the content box.** A region that must reach a threshold
|
|
701
|
+
`T` needs `T + its own padding-inline + its own border-inline` of outer width.
|
|
702
|
+
The convenient reference sizes are therefore the thresholds plus the padding the
|
|
703
|
+
region actually carries:
|
|
704
|
+
|
|
705
|
+
```text
|
|
706
|
+
target 28rem content box + 2 x 1rem padding + 2 x 1px border ~= 30.2rem outer
|
|
707
|
+
target 48rem content box + 2 x 1rem padding + 2 x 1px border ~= 50.2rem outer
|
|
708
|
+
target 64rem content box + 2 x 1rem padding + 2 x 1px border ~= 66.2rem outer
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
Rounding **up** past that figure is deliberate. Landing on the threshold to the
|
|
712
|
+
pixel means a later padding change, measured in fractions of a rem, silently
|
|
713
|
+
drops the region a state.
|
|
714
|
+
|
|
715
|
+
### Why there is no `.grid-5`
|
|
716
|
+
|
|
717
|
+
The divisors of five are five and one, so the chain would be `5 -> 1` with no
|
|
718
|
+
intermediate state. The system exposes densities that subdivide well, not every
|
|
719
|
+
integer. Proposals to give `.grid-5` a hand-picked `5 -> 2 -> 1` chain are
|
|
720
|
+
covered by the rejected `smart-grid` section above: those steps encode an
|
|
721
|
+
assumption about item count that the class cannot verify.
|
|
722
|
+
|
|
723
|
+
## Demonstrability is part of the contract
|
|
724
|
+
|
|
725
|
+
A threshold nobody can reach is indistinguishable from a threshold that does not
|
|
726
|
+
work. This was not hypothetical: the documentation site shipped for some time
|
|
727
|
+
with an article column capped at 48rem, and the live demo previews inside it are
|
|
728
|
+
the query containers. Their content box therefore topped out at **45.9rem at
|
|
729
|
+
every viewport width** — 1280px, 1920px, identical. Every `.grid-3` demo on the
|
|
730
|
+
site rendered one column, `.grid-4` and `.grid-6` rendered two, and the page
|
|
731
|
+
teaching `6 -> 3 -> 2 -> 1` showed the reader its 2-column state as though that
|
|
732
|
+
were the top of the chain.
|
|
733
|
+
|
|
734
|
+
The bug was reported as a `.grid-3` calibration complaint. It was not one. No
|
|
735
|
+
text-based check could see it, because every class name, every threshold, and
|
|
736
|
+
every rule in the framework was correct.
|
|
737
|
+
|
|
738
|
+
Two durable rules came out of it.
|
|
739
|
+
|
|
740
|
+
**The query container must be the element whose content box is the grid's actual
|
|
741
|
+
available space.** Putting `container` on a padded box is correct; putting it on
|
|
742
|
+
an outer wrapper and letting an inner box add the padding makes the query
|
|
743
|
+
resolve against a width the grid does not have, firing every threshold early.
|
|
744
|
+
The demo would then claim a state it cannot honor — a lie in the geometry rather
|
|
745
|
+
than in the CSS.
|
|
746
|
+
|
|
747
|
+
**A surface that documents the thresholds has to be wide enough to reach them.**
|
|
748
|
+
The docs shell is sized backwards from 64rem plus the preview's own padding, and
|
|
749
|
+
that arithmetic is asserted in `tests/docs-geometry.test.js` against the
|
|
750
|
+
thresholds parsed out of `grid.css`, so raising a threshold without giving the
|
|
751
|
+
documentation room to reach it fails a test instead of quietly degrading every
|
|
752
|
+
demo. The prose keeps its own reading measure inside the wider track: demo
|
|
753
|
+
measure and prose measure are different contracts and should not be forced to
|
|
754
|
+
share a number.
|
|
755
|
+
|
|
756
|
+
### A probe that correctly changed nothing
|
|
757
|
+
|
|
758
|
+
The same investigation measured `.choice-card` in a forced 3-up, to ask whether
|
|
759
|
+
`.grid-3`'s 48rem step was simply too conservative:
|
|
760
|
+
|
|
761
|
+
| Container | px / card | Result |
|
|
762
|
+
| --------- | --------- | ------------------ |
|
|
763
|
+
| 44rem | 224 | one line, at ease |
|
|
764
|
+
| 40rem | 203 | one line, at limit |
|
|
765
|
+
| 38rem | 192 | **wraps** |
|
|
766
|
+
| 32rem | 160 | wraps badly |
|
|
767
|
+
|
|
768
|
+
So those particular cards do fit three-up at roughly 40rem, well below the
|
|
769
|
+
preset's step. That was not a reason to move the step or to add a density
|
|
770
|
+
modifier, and the temptation is worth recording.
|
|
771
|
+
|
|
772
|
+
`.grid-3` is a generic preset built around ~15.5rem per item, in agreement with
|
|
773
|
+
`.grid-4`'s ~15.4rem. Choice cards are simply smaller than the preset's target
|
|
774
|
+
content. Lowering the step to fit them would have put `.grid-3` at ~14.2rem and
|
|
775
|
+
broken the only clean agreement in the ladder — to fix a symptom whose actual
|
|
776
|
+
cause was 2rem of documentation padding.
|
|
777
|
+
|
|
778
|
+
A modifier introducing a second density ladder becomes defensible only when
|
|
779
|
+
*several unrelated* content families independently show the standard steps to be
|
|
780
|
+
too conservative while wanting identical balanced-collapse behavior. One
|
|
781
|
+
component in one miscalibrated container is not that evidence. A per-component
|
|
782
|
+
threshold would also collide with the established meaning of `.compact` in
|
|
783
|
+
Actual, which is padding density on a component (`.card.compact`,
|
|
784
|
+
`.table.compact`) and never a layout threshold.
|
|
785
|
+
|