actual-css 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/CHANGELOG.md +67 -3
  2. package/README.md +35 -30
  3. package/dist/actual.css +73 -4946
  4. package/dist/actual.full.css +7090 -0
  5. package/dist/actual.full.js +1 -0
  6. package/dist/actual.full.min.css +1 -0
  7. package/dist/actual.js +1 -1
  8. package/dist/actual.min.css +1 -1
  9. package/docs/design-notes/cascade-layer.md +34 -13
  10. package/docs/design-notes/column-layout.md +677 -0
  11. package/docs/design-notes/forced-colors.md +3 -0
  12. package/docs/design-notes/grid.md +612 -0
  13. package/docs/design-notes/naming.md +15 -0
  14. package/docs/design-notes/widget-primitives.md +1 -0
  15. package/docs/navigation.json +5 -1
  16. package/docs/pages/components/alert.md +16 -14
  17. package/docs/pages/components/aura.md +1 -1
  18. package/docs/pages/components/avatar.md +6 -6
  19. package/docs/pages/components/badge.md +7 -7
  20. package/docs/pages/components/button.md +16 -16
  21. package/docs/pages/components/card.md +13 -9
  22. package/docs/pages/components/chat.md +8 -8
  23. package/docs/pages/components/dialog.md +7 -7
  24. package/docs/pages/components/drawer.md +3 -3
  25. package/docs/pages/components/fab.md +7 -7
  26. package/docs/pages/components/navbar.md +9 -8
  27. package/docs/pages/components/pagination.md +4 -3
  28. package/docs/pages/components/skeleton.md +4 -4
  29. package/docs/pages/components/spinner.md +5 -5
  30. package/docs/pages/components/table.md +30 -5
  31. package/docs/pages/enhancements/flyout.md +13 -13
  32. package/docs/pages/enhancements/overview.md +1 -1
  33. package/docs/pages/enhancements/scrollspy.md +6 -6
  34. package/docs/pages/enhancements/status-bar.md +5 -1
  35. package/docs/pages/enhancements/tabs.md +6 -6
  36. package/docs/pages/enhancements/tooltip.md +1 -1
  37. package/docs/pages/examples/overview.md +39 -22
  38. package/docs/pages/forms/choice-cards.md +11 -6
  39. package/docs/pages/forms/controls.md +1 -1
  40. package/docs/pages/forms/detached-actions.md +1 -1
  41. package/docs/pages/forms/floating-field.md +10 -5
  42. package/docs/pages/forms/form-actions.md +1 -1
  43. package/docs/pages/forms/input-policy.md +1 -1
  44. package/docs/pages/forms/joined-controls.md +4 -4
  45. package/docs/pages/forms/labels.md +6 -6
  46. package/docs/pages/forms/otp.md +1 -1
  47. package/docs/pages/forms/overview.md +19 -19
  48. package/docs/pages/forms/password.md +1 -1
  49. package/docs/pages/forms/range.md +1 -1
  50. package/docs/pages/forms/states-and-sizes.md +41 -45
  51. package/docs/pages/forms/sticky-actions.md +3 -3
  52. package/docs/pages/forms/switch.md +110 -2
  53. package/docs/pages/forms/validation.md +9 -9
  54. package/docs/pages/foundations/philosophy.md +1 -1
  55. package/docs/pages/foundations/theming.md +17 -2
  56. package/docs/pages/foundations/tokens.md +22 -10
  57. package/docs/pages/foundations/typography.md +50 -20
  58. package/docs/pages/guides/bootstrap.md +11 -11
  59. package/docs/pages/guides/integrating-actual-css.md +186 -0
  60. package/docs/pages/guides/introduction.md +23 -26
  61. package/docs/pages/guides/modular-import.md +173 -0
  62. package/docs/pages/guides/progressive-enhancement.md +81 -78
  63. package/docs/pages/guides/tailwind.md +6 -8
  64. package/docs/pages/layout/app-shell.md +1 -1
  65. package/docs/pages/layout/center.md +1 -1
  66. package/docs/pages/layout/choosing.md +39 -0
  67. package/docs/pages/layout/column-layout.md +206 -0
  68. package/docs/pages/layout/frame.md +6 -1
  69. package/docs/pages/layout/grid.md +100 -102
  70. package/docs/pages/layout/list.md +1 -1
  71. package/docs/pages/layout/media.md +1 -1
  72. package/docs/pages/layout/overview.md +15 -2
  73. package/docs/pages/layout/scroll-snap.md +2 -2
  74. package/docs/pages/layout/scroll-target.md +45 -0
  75. package/docs/pages/layout/sidebar-layout.md +20 -19
  76. package/docs/pages/layout/stack.md +53 -1
  77. package/docs/pages/layout/switcher.md +10 -26
  78. package/docs/pages/layout/topbar.md +2 -2
  79. package/docs/pages/patterns/actions.md +1 -1
  80. package/docs/pages/patterns/code-block.md +1 -1
  81. package/docs/pages/patterns/exclusive-accordion.md +1 -1
  82. package/docs/pages/patterns/hierarchical-nav.md +1 -1
  83. package/docs/pages/patterns/icon-input.md +1 -1
  84. package/docs/pages/patterns/intent-nesting.md +1 -1
  85. package/docs/pages/patterns/navigation-list.md +1 -1
  86. package/docs/pages/patterns/overview.md +1 -1
  87. package/docs/pages/patterns/structured-lists.md +1 -1
  88. package/docs/pages/utilities/accessibility.md +3 -3
  89. package/docs/pages/utilities/flex.md +15 -15
  90. package/docs/pages/utilities/links.md +4 -4
  91. package/docs/pages/utilities/list.md +8 -8
  92. package/docs/pages/utilities/overflow.md +8 -8
  93. package/docs/pages/utilities/overview.md +1 -1
  94. package/docs/pages/utilities/shape.md +3 -3
  95. package/docs/pages/utilities/sizing-wrapping.md +23 -16
  96. package/docs/pages/utilities/spacing.md +20 -20
  97. package/docs/pages/utilities/text.md +10 -10
  98. package/llms.txt +46 -4
  99. package/package.json +42 -13
  100. package/scripts/reserved-classes.json +261 -0
  101. package/src/cli/actual-css.js +133 -0
  102. package/src/css/actual.css +1 -31
  103. package/src/css/actual.full.css +11 -2
  104. package/src/css/components/alert.css +17 -4
  105. package/src/css/components/badge.css +18 -6
  106. package/src/css/components/breadcrumb.css +1 -1
  107. package/src/css/components/button.css +19 -1
  108. package/src/css/components/card.css +27 -2
  109. package/src/css/components/drawer.css +27 -2
  110. package/src/css/components/flyout.css +5 -0
  111. package/src/css/components/index.css +32 -27
  112. package/src/css/components/join.css +8 -1
  113. package/src/css/components/key.css +2 -0
  114. package/src/css/components/menu.css +11 -1
  115. package/src/css/components/meter.css +4 -1
  116. package/src/css/components/modal.css +5 -0
  117. package/src/css/components/pagination.css +11 -0
  118. package/src/css/components/spinner.css +5 -0
  119. package/src/css/components/status-bar.css +5 -0
  120. package/src/css/components/surface.css +5 -0
  121. package/src/css/components/table.css +6 -0
  122. package/src/css/components/tooltip.css +5 -0
  123. package/src/css/core/index.css +9 -0
  124. package/src/css/core/print.css +18 -0
  125. package/src/css/{theme.css → core/theme.css} +20 -10
  126. package/src/css/{tokens.css → core/tokens.css} +15 -2
  127. package/src/css/{variants.css → core/variants.css} +3 -2
  128. package/src/css/effects/index.css +2 -0
  129. package/src/css/forms/base.css +8 -0
  130. package/src/css/forms/choice-card.css +5 -4
  131. package/src/css/forms/choice.css +49 -37
  132. package/src/css/forms/control.css +4 -1
  133. package/src/css/forms/custom-select.css +2 -0
  134. package/src/css/{optional → forms}/floating-field.css +25 -7
  135. package/src/css/forms/index.css +10 -13
  136. package/src/css/forms/native.css +15 -2
  137. package/src/css/{optional → forms}/otp.css +20 -16
  138. package/src/css/forms/switch.css +31 -29
  139. package/src/css/layout/app-shell.css +6 -0
  140. package/src/css/layout/center.css +13 -0
  141. package/src/css/layout/cluster.css +21 -0
  142. package/src/css/layout/column-layout.css +144 -0
  143. package/src/css/layout/container-query.css +13 -0
  144. package/src/css/layout/frame.css +19 -0
  145. package/src/css/layout/grid.css +141 -0
  146. package/src/css/layout/index.css +42 -0
  147. package/src/css/layout/measure.css +9 -0
  148. package/src/css/layout/media.css +10 -0
  149. package/src/css/layout/scroll-target.css +15 -0
  150. package/src/css/layout/scroller.css +41 -0
  151. package/src/css/layout/sidebar-layout.css +31 -0
  152. package/src/css/layout/stack.css +21 -0
  153. package/src/css/layout/switcher.css +23 -0
  154. package/src/css/{optional/layout-extra.css → layout/topbar.css} +0 -20
  155. package/src/css/typography/index.css +5 -0
  156. package/src/css/typography/lead.css +7 -0
  157. package/src/css/{components → typography}/overline.css +2 -2
  158. package/src/css/{prose.css → typography/prose.css} +53 -38
  159. package/src/css/{utilities.css → utilities/base.css} +2 -0
  160. package/src/css/utilities/index.css +3 -0
  161. package/src/js/escape.js +36 -0
  162. package/src/js/flyout.js +22 -12
  163. package/src/js/full.js +26 -0
  164. package/src/js/index.js +5 -21
  165. package/src/js/surface.js +1 -31
  166. package/src/js/tooltip.js +46 -18
  167. package/src/js/validation.js +1 -1
  168. package/src/tooling/css-bundle.js +308 -0
  169. package/dist/actual-themes.min.css +0 -1
  170. package/dist/optional.css +0 -1048
  171. package/dist/optional.min.css +0 -1
  172. package/docs/pages/guides/optional-components.md +0 -33
  173. package/src/css/grid.css +0 -109
  174. package/src/css/layout.css +0 -123
  175. package/src/css/optional/index.css +0 -10
  176. package/src/css/optional/scroller.css +0 -63
  177. package/src/css/print.css +0 -47
  178. package/src/css/themes/corporate.css +0 -60
  179. package/src/css/themes/cyberpunk.css +0 -60
  180. package/src/css/themes/dim.css +0 -33
  181. package/src/css/themes/edge.css +0 -117
  182. package/src/css/themes/forest.css +0 -65
  183. package/src/css/themes/gradient.css +0 -97
  184. package/src/css/themes/index.css +0 -47
  185. package/src/css/themes/indigo.css +0 -33
  186. package/src/css/themes/lavender.css +0 -66
  187. package/src/css/themes/mono.css +0 -60
  188. package/src/css/themes/neon.css +0 -66
  189. package/src/css/themes/ocean.css +0 -60
  190. package/src/css/themes/petrol.css +0 -64
  191. package/src/css/themes/spruce.css +0 -64
  192. package/src/css/themes/square.css +0 -65
  193. package/src/css/themes/sunset.css +0 -66
  194. /package/src/css/{optional → components}/chat.css +0 -0
  195. /package/src/css/{optional → components}/fab.css +0 -0
  196. /package/src/css/{base.css → core/base.css} +0 -0
  197. /package/src/css/{focus.css → core/focus.css} +0 -0
  198. /package/src/css/{intents.css → core/intents.css} +0 -0
  199. /package/src/css/{reset.css → core/reset.css} +0 -0
  200. /package/src/css/{optional → effects}/aura.css +0 -0
  201. /package/src/css/{optional → layout}/scroll-snap.css +0 -0
  202. /package/src/css/{optional/typography-fluid.css → typography/fluid.css} +0 -0
  203. /package/src/css/{optional/utilities-extra.css → utilities/extra.css} +0 -0
@@ -0,0 +1,612 @@
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 query container an opt-in for precision 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
+ .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.
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
+ ## API guidance
500
+
501
+ Use `.grid` when the number of columns should emerge naturally from available space.
502
+
503
+ Use `.grid-N` when the declared density and its balanced subdivisions are part of the intended composition.
504
+
505
+ Use structural grids for uniform peer items. They are not intended as editorial grids with arbitrary per-item spans.
506
+
507
+ 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.
508
+
509
+ ## Decision
510
+
511
+ Actual keeps two different grid contracts.
512
+
513
+ ```text
514
+ .grid
515
+ intrinsic
516
+ space-driven
517
+ auto-fit
518
+ configurable through --grid-min
519
+ no guarantee of balanced rows
520
+
521
+ .grid-N
522
+ structural
523
+ count-driven
524
+ equal-width items
525
+ responsive through natural subdivisions of N
526
+ local-container driven when enhanced
527
+ ```
528
+
529
+ A structural grid must not become its own inline-size query container, because doing so breaks intrinsic and shrink-to-fit composition.
530
+
531
+ If local responsive subdivision requires a size container query, that query container must remain outside the structural grid.
532
+
533
+ The explicit container boundary is preferable to hidden containment side effects.
534
+
535
+ The guiding principle is:
536
+
537
+ > `.grid` optimizes for available space. `.grid-N` optimizes for balanced subdivision.
538
+
539
+ Future grid changes should preserve that distinction unless the public contract itself is intentionally reconsidered.
540
+
541
+ ## Resolution
542
+
543
+ `.grid-N` is a bounded `auto-fill` grid by default, and a divisor-chain grid
544
+ inside a query container.
545
+
546
+ ```text
547
+ .grid space-driven, auto-fit, --grid-min
548
+ .grid-N count-driven baseline, auto-fill bounded at N,
549
+ may pass through non-divisor column counts
550
+ .container-query .grid-N balanced subdivision, N -> divisors of N -> 1
551
+ ```
552
+
553
+ The whole enhancement lives inside `@supports (container-type: inline-size)`.
554
+ Browsers below the floor get the baseline whether or not a wrapper is present,
555
+ which removes the parallel `@media` block 0.3.1 had to maintain in lockstep
556
+ with its container thresholds.
557
+
558
+ ### `--grid-min` is not a `.grid-N` hook
559
+
560
+ `@container` thresholds cannot resolve a custom property. A hook honored in the
561
+ baseline would therefore fall silent the moment an author added a query
562
+ container — exactly the kind of hidden, position-dependent contract this
563
+ redesign set out to remove. The baseline hard-codes `16rem` instead.
564
+
565
+ The resulting API split is the clearer one anyway: item width is `.grid`'s
566
+ contract, column density is `.grid-N`'s.
567
+
568
+ ### Thresholds
569
+
570
+ Three shared steps, not one calibrated per preset:
571
+
572
+ ```text
573
+ 28rem first horizontal density .grid-2, .grid-4, .grid-6 -> 2 columns
574
+ 48rem three columns .grid-3, .grid-6 -> 3 columns
575
+ 64rem dense densities .grid-4 -> 4, .grid-6 -> 6
576
+ ```
577
+
578
+ An earlier draft tuned each final step so that every preset landed an item
579
+ around 240px, which put `.grid-6` at 80rem or 96rem. That was the wrong
580
+ optimization, and worth recording so it is not re-derived.
581
+
582
+ Calibrating every preset to the same item width erases the information N
583
+ carries. An author does not reach for `.grid-N` to say *I have N things*; they
584
+ say *my content suits this density*. `.grid-6` should reach six columns early
585
+ precisely because choosing it is a statement that the items are compact. At
586
+ 96rem an item is back to `.grid`'s 16rem default, and the preset has no reason
587
+ to exist.
588
+
589
+ The right probe is therefore each preset against the content it targets, not
590
+ one specimen across all of them:
591
+
592
+ | Preset | Probed with | Item at its final step | Result |
593
+ | --------- | ---------------------------------- | ---------------------- | ------ |
594
+ | `.grid-2` | large card, summary, action | 218px @ 28rem | clean |
595
+ | `.grid-3` | standard card | 248px @ 48rem | clean |
596
+ | `.grid-4` | compact card, stat | 247px @ 64rem | clean |
597
+ | `.grid-6` | stat, avatar, thumbnail, icon tile | 161px @ 64rem | clean |
598
+
599
+ 161px is small for a card with a heading and a paragraph — and that is the
600
+ point. A preset that needs 250px per item is `.grid-4`, not `.grid-6`.
601
+
602
+ The framework does not guess appropriate item size. The author states it by
603
+ choosing a density; the framework then guarantees a clean structural collapse
604
+ of that density.
605
+
606
+ ### Why there is no `.grid-5`
607
+
608
+ The divisors of five are five and one, so the chain would be `5 -> 1` with no
609
+ intermediate state. The system exposes densities that subdivide well, not every
610
+ integer. Proposals to give `.grid-5` a hand-picked `5 -> 2 -> 1` chain are
611
+ covered by the rejected `smart-grid` section above: those steps encode an
612
+ assumption about item count that the class cannot verify.
@@ -37,3 +37,18 @@ Actual CSS is intended for new projects. Existing projects with class collisions
37
37
  - Shared runtime primitives may have public JavaScript APIs without becoming author-facing CSS or HTML vocabulary. Do not introduce `.surface` or `data-surface-*`; components keep their feature-specific author APIs (`data-flyout-*`, `data-context-menu-*`, CSS `.flyout`, `.menu`, …).
38
38
  - `data-enhance` is the one un-namespaced attribute — it carries *opt-in* for any feature, not *configuration* for one feature. Feature-namespaced attributes carry configuration.
39
39
  - Custom events use the `actual:*` namespace.
40
+
41
+ ## Utilities
42
+
43
+ Utilities ship in two layers that follow the same class grammar.
44
+
45
+ - **Base utilities** are a curated set of common ergonomic shortcuts
46
+ (`.overflow-auto`, `.gap-none`, `.text-nowrap`, `.px`, `.py`).
47
+ - **Optional utilities** use explicit property/value names
48
+ (`.overflow-hidden`, `.gap-sm`, `.white-space-normal`).
49
+
50
+ Base utilities may use compact names for a small set of frequent operations
51
+ (`.px`, `.py`, `.mbs`, `.mbe`). Extra utilities use explicit property/value
52
+ names and must not duplicate an operation already provided by base. The two
53
+ sets may cover the same CSS property, but must not expose competing names for
54
+ the same declaration.
@@ -7,6 +7,7 @@ The JS runtime doubles as a primitive kit for building custom widgets.
7
7
  | Need | Primitive | Import |
8
8
  |------|-----------|--------|
9
9
  | Opt-in discovery + lifecycle + cleanup | `enhance`, `registerEnhancement` | `actual-css/js/enhance` |
10
+ | Per-document LIFO Escape dismissal | `escape` (`registerEscapeDismissal`) | `actual-css/js/escape` |
10
11
  | Positioning, flip/shift, `--available-height`, `data-placement` | `floating` (`track`, `reposition`, `repositionAt`) | `actual-css/js/floating` |
11
12
  | Open/close, sheet on mobile, backdrop, outside-click, Escape, focus restore | `surface` | `actual-css/js/surface` |
12
13
  | Directional item lookup, edges, wrapping, RTL | `keys` (`itemForKey`, `firstItem`, `lastItem`, `nextItem`) | `actual-css/js/keys` |
@@ -5,7 +5,8 @@
5
5
  "slug": "guides",
6
6
  "pages": [
7
7
  "introduction",
8
- "optional-components",
8
+ "integrating-actual-css",
9
+ "modular-import",
9
10
  "progressive-enhancement",
10
11
  "bootstrap",
11
12
  "tailwind"
@@ -21,15 +22,18 @@
21
22
  "slug": "layout",
22
23
  "pages": [
23
24
  "overview",
25
+ "choosing",
24
26
  "stack",
25
27
  "cluster",
26
28
  "grid",
29
+ "column-layout",
27
30
  "center",
28
31
  "sidebar-layout",
29
32
  "media",
30
33
  "switcher",
31
34
  "topbar",
32
35
  "scroll-snap",
36
+ "scroll-target",
33
37
  "frame",
34
38
  "app-shell",
35
39
  "list"