actual-css 0.11.1 → 0.13.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 (123) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +25 -6
  3. package/components.json +12 -18
  4. package/dist/actual.full.css +780 -479
  5. package/dist/actual.full.js +1 -1
  6. package/dist/actual.full.min.css +1 -1
  7. package/docs/brand/favicon.svg +5 -0
  8. package/docs/brand/lockup.svg +12 -0
  9. package/docs/brand/logo-dark.svg +9 -0
  10. package/docs/brand/logo-light.svg +9 -0
  11. package/docs/brand/logo.svg +11 -0
  12. package/docs/brand/mark.svg +11 -0
  13. package/docs/design-notes/architecture.md +67 -0
  14. package/docs/design-notes/steps.md +45 -28
  15. package/docs/navigation.json +3 -2
  16. package/docs/pages/components/alert.md +2 -10
  17. package/docs/pages/components/avatar.md +17 -11
  18. package/docs/pages/components/badge.md +3 -9
  19. package/docs/pages/components/breadcrumb.md +16 -2
  20. package/docs/pages/components/card.md +31 -2
  21. package/docs/pages/components/dialog.md +121 -33
  22. package/docs/pages/components/drawer.md +2 -2
  23. package/docs/pages/components/list.md +9 -13
  24. package/docs/pages/components/navbar.md +49 -30
  25. package/docs/pages/components/pagination.md +19 -0
  26. package/docs/pages/components/steps.md +24 -20
  27. package/docs/pages/components/table.md +3 -3
  28. package/docs/pages/enhancements/flyout.md +2 -2
  29. package/docs/pages/enhancements/scrollspy.md +7 -7
  30. package/docs/pages/enhancements/status-bar.md +2 -2
  31. package/docs/pages/enhancements/tabs.md +74 -3
  32. package/docs/pages/examples/overview.md +2 -1
  33. package/docs/pages/forms/date-picker.md +90 -0
  34. package/docs/pages/forms/detached-actions.md +37 -30
  35. package/docs/pages/forms/floating-field.md +21 -7
  36. package/docs/pages/forms/form-actions.md +58 -28
  37. package/docs/pages/forms/input-policy.md +1 -1
  38. package/docs/pages/forms/overview.md +6 -5
  39. package/docs/pages/forms/states-and-sizes.md +15 -15
  40. package/docs/pages/forms/sticky-actions.md +85 -83
  41. package/docs/pages/forms/validation.md +5 -5
  42. package/docs/pages/foundations/philosophy.md +102 -4
  43. package/docs/pages/foundations/theming.md +15 -4
  44. package/docs/pages/foundations/tokens.md +89 -36
  45. package/docs/pages/foundations/typography.md +4 -7
  46. package/docs/pages/guides/bootstrap.md +2 -2
  47. package/docs/pages/guides/design-tools.md +2 -2
  48. package/docs/pages/guides/integrating-actual-css.md +79 -9
  49. package/docs/pages/guides/introduction.md +5 -5
  50. package/docs/pages/guides/modular-import.md +3 -0
  51. package/docs/pages/guides/progressive-enhancement.md +27 -9
  52. package/docs/pages/guides/tailwind.md +70 -0
  53. package/docs/pages/guides/upgrading.md +151 -0
  54. package/docs/pages/layout/center.md +2 -1
  55. package/docs/pages/layout/cluster.md +37 -4
  56. package/docs/pages/layout/grid.md +2 -0
  57. package/docs/pages/layout/overview.md +1 -1
  58. package/docs/pages/layout/sidebar-layout.md +3 -0
  59. package/docs/pages/layout/stack.md +21 -16
  60. package/docs/pages/layout/switcher.md +2 -0
  61. package/docs/pages/layout/topbar.md +7 -5
  62. package/docs/pages/patterns/icon-slot.md +17 -1
  63. package/docs/pages/patterns/navigation-list.md +16 -46
  64. package/docs/pages/patterns/overview.md +3 -4
  65. package/docs/pages/utilities/flex.md +30 -20
  66. package/docs/pages/utilities/sizing-wrapping.md +52 -7
  67. package/docs/pages/utilities/spacing.md +4 -3
  68. package/package.json +6 -6
  69. package/reserved-classes.json +3 -3
  70. package/skills/actual-css/references/enhancements.md +10 -7
  71. package/src/cli/actual-css.js +39 -13
  72. package/src/css/components/alert.css +34 -34
  73. package/src/css/components/avatar.css +8 -17
  74. package/src/css/components/badge.css +1 -6
  75. package/src/css/components/breadcrumb.css +74 -8
  76. package/src/css/components/button.css +3 -9
  77. package/src/css/components/card.css +18 -10
  78. package/src/css/components/chat.css +4 -5
  79. package/src/css/components/drawer.css +42 -45
  80. package/src/css/components/flyout.css +3 -1
  81. package/src/css/components/index.css +0 -1
  82. package/src/css/components/join.css +2 -2
  83. package/src/css/components/list.css +10 -10
  84. package/src/css/components/modal.css +113 -134
  85. package/src/css/components/navbar.css +22 -31
  86. package/src/css/components/pagination.css +5 -0
  87. package/src/css/components/scrollspy.css +1 -1
  88. package/src/css/components/steps.css +66 -12
  89. package/src/css/components/tab.css +134 -4
  90. package/src/css/core/base.css +1 -1
  91. package/src/css/core/focus.css +8 -6
  92. package/src/css/core/reset.css +2 -1
  93. package/src/css/core/theme.css +10 -1
  94. package/src/css/core/tokens.css +32 -12
  95. package/src/css/core/variants.css +41 -14
  96. package/src/css/forms/choice-card.css +1 -1
  97. package/src/css/forms/choice.css +1 -1
  98. package/src/css/forms/control.css +1 -1
  99. package/src/css/forms/floating-field.css +20 -18
  100. package/src/css/forms/form-actions.css +14 -12
  101. package/src/css/forms/native.css +2 -2
  102. package/src/css/forms/otp.css +4 -5
  103. package/src/css/forms/switch.css +1 -1
  104. package/src/css/forms/validation.css +23 -30
  105. package/src/css/layout/center.css +2 -0
  106. package/src/css/layout/cluster.css +25 -0
  107. package/src/css/layout/grid.css +7 -0
  108. package/src/css/layout/scroller.css +19 -3
  109. package/src/css/layout/sidebar-layout.css +3 -0
  110. package/src/css/layout/switcher.css +3 -0
  111. package/src/css/layout/topbar.css +12 -6
  112. package/src/css/utilities/base.css +5 -0
  113. package/src/css/utilities/extra.css +3 -7
  114. package/src/js/full.js +4 -0
  115. package/src/js/inline-visible.js +54 -0
  116. package/src/js/parsed.js +21 -0
  117. package/src/js/tab.js +33 -7
  118. package/src/tooling/color.js +65 -0
  119. package/src/tooling/design-export.js +1 -1
  120. package/src/tooling/design-tokens.js +53 -34
  121. package/src/tooling/theme-contrast.js +217 -0
  122. package/docs/pages/patterns/actions.md +0 -52
  123. package/src/css/components/actions.css +0 -27
@@ -39,19 +39,24 @@ content, keep them in normal block flow instead:
39
39
  Use `.stack` for the vertical rhythm *between* related items inside one region,
40
40
  not as the wrapper that lays out independent full-width regions of a page.
41
41
 
42
- ## Choosing a density
42
+ ## Choosing a rhythm
43
43
 
44
- `--gap` is the primitive's channel, and the density context variants set it at
45
- the point of use. Apply the density class on the stack itself to size one
46
- instance, or on an ancestor to reach nested layouts. Pick the one that matches
47
- how tightly the children belong together:
44
+ `--gap` is the primitive's channel. `.tight` and `.loose` set it and nothing
45
+ else; apply one on the stack to space one instance, or on an ancestor to reach
46
+ nested layouts. Pick the one that matches how tightly the children belong
47
+ together:
48
48
 
49
49
  | Markup | Gap | Use for |
50
50
  | ----------------- | ---- | ------------------------------------------ |
51
51
  | `.stack gap-none` | 0 | Lines forming one typographic unit |
52
- | `.stack compact` | 8px | A homogeneous series of controls |
52
+ | `.stack tight` | 8px | A homogeneous series of controls |
53
53
  | `.stack` | 12px | Normal flow between distinct elements |
54
- | `.stack spacious` | 24px | Sections, or blocks that read as separated |
54
+ | `.stack loose` | 24px | Sections, or blocks that read as separated |
55
+
56
+ **Size** changes a component's scale. **Density** (`.compact`, `.spacious`)
57
+ changes UI geometry and rhythm together: gap, padding, control size. **Rhythm**
58
+ (`.tight`, `.loose`) changes the gap only — closer or farther items, tighter
59
+ spacing without smaller buttons. Use one rhythm class per element.
55
60
 
56
61
  One typographic unit means a name and a job title, or a figure and its caption.
57
62
  A homogeneous series means a radio list, a checkbox list, or a compact vertical
@@ -60,13 +65,13 @@ menu.
60
65
  The distinction between the first two matters. `42` above `Open issues` is
61
66
  almost a single block of text, so it takes `gap-none`. Three `.choice` labels
62
67
  stay three separate controls that happen to form one group, so they take
63
- `compact` — tighter than the default, but still spaced.
68
+ `tight` — tighter than the default, but still spaced.
64
69
 
65
- `.compact` and `.spacious` rebind `--gap` to `--space-20` and `--space-50`.
66
- Because the token is inherited, the same two classes also tighten the inner gap
67
- of any nested stack, cluster, and grid. For a one-off gap that must *not* follow
68
- density, set the `gap` property directly on the instance (the next section
69
- explains why).
70
+ `.tight` and `.loose` rebind `--gap` to `--space-20` and `--space-50`, the same
71
+ steps `.compact` and `.spacious` use. Because the token is inherited, they also
72
+ tighten the inner gap of any nested stack, cluster, and grid. For a one-off gap
73
+ that must *not* reach nested layouts, set the `gap` property directly on the
74
+ instance (the next section explains why).
70
75
 
71
76
  A stack that mixes kinds keeps the default: five `.field` wrappers and one lone
72
77
  `.choice` is a form that contains a checkbox, not a list of options, and
@@ -92,7 +97,7 @@ depth:
92
97
  ```
93
98
 
94
99
  Both give the region the same spacing; only the second stops there. This is what
95
- makes a nested `.stack compact` a real density choice — 12px down to 8px — rather
100
+ makes a nested `.stack tight` a real rhythm choice — 12px down to 8px — rather
96
101
  than a patch cancelling a rhythm inherited from three levels up.
97
102
 
98
103
  Setting `--gap` is still the right move on the element that *consumes* it: a
@@ -100,8 +105,8 @@ one-off `--gap` on a card body or an actions row tunes that primitive and
100
105
  nothing below it, because the setter is the consumer. The leak only appears when
101
106
  a container sets the token and its children read it.
102
107
 
103
- `.compact` and `.spacious` are the deliberate exception: they set `--gap`
104
- precisely so a density context reaches nested layouts.
108
+ The rhythm and density classes are the deliberate exception: they set `--gap`
109
+ precisely so the context reaches nested layouts.
105
110
 
106
111
  ## Grouping items tightly
107
112
 
@@ -18,3 +18,5 @@ independently.
18
18
  ### Hooks
19
19
 
20
20
  - `--switcher-threshold` controls the space needed before all peers share a row.
21
+ The children read it, so it also reaches the children of a nested
22
+ switcher; set it on the nested switcher to give it its own threshold.
@@ -4,17 +4,19 @@ Sticky, frosted top bar shell for app/dashboard layouts with a persistent sideba
4
4
 
5
5
  > **Module** — import `actual-css/css/layout/topbar` or `actual-css/css/layout`.
6
6
 
7
- Use `.topbar` for the structural shell of an app-shell header: sticky positioning, stacking, and a frosted background. Row content — search, breadcrumb, actions — stays local to the page.
7
+ Use `.topbar` for the structural shell of an app-shell header: sticky positioning, stacking, and a frosted background. Its children sit in a centered row by default; a layout primitive on the same element, such as `topbar cluster`, replaces that row. Row content — search, breadcrumb, actions — stays local to the page.
8
8
 
9
9
  ```html demo
10
10
  <header class="topbar">
11
11
  <button class="btn neutral ghost" type="button" aria-label="Open navigation">
12
12
  <i class="ti ti-menu-2" aria-hidden="true"></i>
13
13
  </button>
14
- <ol class="breadcrumb" aria-label="Breadcrumb">
15
- <li><a href="#">Overview</a></li>
16
- <li aria-current="page">Accounts</li>
17
- </ol>
14
+ <nav aria-label="Breadcrumb">
15
+ <ol class="breadcrumb">
16
+ <li><a href="#">Overview</a></li>
17
+ <li aria-current="page">Accounts</li>
18
+ </ol>
19
+ </nav>
18
20
  <div class="grow"></div>
19
21
  <span class="avatar sm"><abbr>LW</abbr></span>
20
22
  </header>
@@ -42,6 +42,22 @@ For a round disc, add the `.circle` utility — it supplies the `border-radius`:
42
42
  }
43
43
  ```
44
44
 
45
+ ## Webfont glyphs
46
+
47
+ An icon font draws nothing measurable until it loads, so a button or nav link
48
+ shifts when the glyph arrives. Reserve a `1em` square on the icon library's
49
+ class in application CSS; it follows the surrounding font size:
50
+
51
+ ```css
52
+ .ti {
53
+ display: inline-grid;
54
+ flex: none;
55
+ place-items: center;
56
+ inline-size: 1em;
57
+ block-size: 1em;
58
+ }
59
+ ```
60
+
45
61
  ## Reuse the existing icon slots
46
62
 
47
63
  Several components already embody this slot — reach for them before composing
@@ -55,7 +71,7 @@ your own:
55
71
  ```html demo
56
72
  <div class="media">
57
73
  <span class="avatar primary"><abbr>AM</abbr></span>
58
- <div class="stack">
74
+ <div class="stack gap-none">
59
75
  <strong>Ada Meridian</strong>
60
76
  <span class="muted">Senior designer.</span>
61
77
  </div>
@@ -1,14 +1,15 @@
1
1
  # Navigation List
2
2
 
3
- Use `.nav-list` for a semantic list of navigation links.
4
-
5
- It removes native list chrome but does not force a direction. Combine it with layout primitives depending on context.
3
+ Use `.nav-list` for a vertical list of navigation links: it removes native list
4
+ chrome, stacks the rows and stretches each `.nav-link` across the row. For a
5
+ horizontal row of links, use `.cluster` on the list, which removes the list
6
+ chrome too.
6
7
 
7
8
  ```html demo
8
9
  <div class="stack">
9
10
  <h2>Navigation</h2>
10
11
  <nav aria-label="Main navigation">
11
- <ul class="nav-list cluster">
12
+ <ul class="cluster">
12
13
  <li><a href="/docs">Docs</a></li>
13
14
  <li><a href="/components">Components</a></li>
14
15
  <li><a href="/examples">Examples</a></li>
@@ -16,7 +17,7 @@ It removes native list chrome but does not force a direction. Combine it with la
16
17
  </nav>
17
18
  <h2>Documentation</h2>
18
19
  <nav aria-label="Documentation">
19
- <ul class="nav-list stack">
20
+ <ul class="nav-list">
20
21
  <li><a href="/docs/getting-started" aria-current="page">Getting started</a></li>
21
22
  <li><a href="/docs/tokens">Tokens</a></li>
22
23
  <li><a href="/docs/layout">Layout</a></li>
@@ -26,14 +27,6 @@ It removes native list chrome but does not force a direction. Combine it with la
26
27
  </div>
27
28
  ```
28
29
 
29
- Use `.nav-list` for:
30
-
31
- - header navigation
32
- - side navigation
33
- - footer navigation
34
- - tab-like navigation lists
35
- - documentation navigation
36
-
37
30
  Do not reset every list inside `nav`.
38
31
 
39
32
  ```css
@@ -51,7 +44,7 @@ Prefer an explicit pattern class.
51
44
 
52
45
  ```html demo
53
46
  <nav aria-label="Footer">
54
- <ul class="nav-list cluster">
47
+ <ul class="cluster">
55
48
  <li><a href="/privacy">Privacy</a></li>
56
49
  <li><a href="/terms">Terms</a></li>
57
50
  <li><a href="/contact">Contact</a></li>
@@ -69,7 +62,7 @@ Header navigation is usually a composition of landmarks, layout primitives, and
69
62
  <a class="brand" href="/">Actual CSS</a>
70
63
 
71
64
  <nav aria-label="Main navigation">
72
- <ul class="nav-list cluster">
65
+ <ul class="cluster">
73
66
  <li><a href="/docs">Docs</a></li>
74
67
  <li><a href="/components">Components</a></li>
75
68
  <li><a href="/examples">Examples</a></li>
@@ -85,8 +78,7 @@ In this example:
85
78
  - `.center` constrains the content.
86
79
  - `.cluster` arranges the brand and navigation.
87
80
  - `nav` provides navigation semantics.
88
- - `.nav-list` normalizes the navigation list.
89
- - the inner `.cluster` arranges navigation items horizontally.
81
+ - the inner `.cluster` removes the list chrome and arranges the links horizontally.
90
82
 
91
83
  A project may add `.site-header` when it needs a visual shell.
92
84
 
@@ -106,7 +98,7 @@ A project may add `.site-header` when it needs a visual shell.
106
98
  <aside class="shell-sidebar-nav" aria-label="Primary">
107
99
  <a class="navbar-brand" href="#"><span class="avatar primary"><abbr>AC</abbr></span> Product</a>
108
100
  <nav aria-label="Primary">
109
- <ul class="nav-list stack">
101
+ <ul class="nav-list">
110
102
  <li><a class="nav-link" href="#" aria-current="page">Overview</a></li>
111
103
  <li><a class="nav-link" href="#">Settings</a></li>
112
104
  </ul>
@@ -167,7 +159,7 @@ Side navigation is usually a `.nav-list` composed with `.stack`.
167
159
  ```html demo
168
160
  <aside>
169
161
  <nav aria-label="Documentation">
170
- <ul class="nav-list stack">
162
+ <ul class="nav-list">
171
163
  <li><a href="/docs/getting-started" aria-current="page">Getting started</a></li>
172
164
  <li><a href="/docs/tokens">Tokens</a></li>
173
165
  <li><a href="/docs/layout">Layout</a></li>
@@ -182,7 +174,7 @@ A project may add `.side-nav` only when it needs specific visual behavior such a
182
174
  ```html demo
183
175
  <aside class="side-nav">
184
176
  <nav aria-label="Documentation">
185
- <ul class="nav-list stack">
177
+ <ul class="nav-list">
186
178
  <li><a href="/docs/getting-started" aria-current="page">Getting started</a></li>
187
179
  <li><a href="/docs/tokens">Tokens</a></li>
188
180
  <li><a href="/docs/layout">Layout</a></li>
@@ -206,30 +198,8 @@ A project may add `.side-nav` only when it needs specific visual behavior such a
206
198
  }
207
199
  ```
208
200
 
209
- ### Optional Navigation Link Styling
210
-
211
- `.nav-list` can stay purely structural. If the framework wants navigation links to have a default hit area and active state, this can be added as a lightweight visual pattern.
212
-
213
- ```css
214
- .nav-list a {
215
- display: flex;
216
- align-items: center;
217
- gap: var(--space-20);
218
- min-block-size: 2rem;
219
- padding-inline: var(--space-20);
220
- border-radius: var(--radius-sm);
221
- color: inherit;
222
- text-decoration: none;
223
- }
224
-
225
- .nav-list a:hover {
226
- background: var(--surface-subtle);
227
- }
228
-
229
- .nav-list a[aria-current="page"] {
230
- background: var(--surface-subtle);
231
- font-weight: 600;
232
- }
233
- ```
201
+ ### Link Styling
234
202
 
235
- Keep this separate from layout primitives. `.cluster` and `.stack` should not style links.
203
+ `.nav-link` gives a link its hit area, hover fill and `aria-current` state, in a
204
+ `.cluster` row or a `.nav-list` (see [Navbar](../components/navbar.md)). Set
205
+ `--gap` on a `.nav-list` to space its rows differently.
@@ -22,9 +22,8 @@ Use patterns when a semantic element needs a small amount of normalization befor
22
22
  Patterns often compose with layout primitives.
23
23
 
24
24
  ```html
25
- <menu class="actions cluster">...</menu>
26
- <ul class="nav-list cluster">...</ul>
27
- <ul class="nav-list stack">...</ul>
25
+ <menu class="cluster">...</menu>
26
+ <ul class="nav-list">...</ul>
28
27
  <section class="grid">...</section>
29
28
  ```
30
29
 
@@ -51,7 +50,7 @@ Avoid putting semantic resets into layout primitives.
51
50
  If list chrome should be removed, use an explicit pattern.
52
51
 
53
52
  ```html demo
54
- <ul class="nav-list stack">
53
+ <ul class="nav-list">
55
54
  <li><a href="/docs">Docs</a></li>
56
55
  <li><a href="/components">Components</a></li>
57
56
  </ul>
@@ -6,24 +6,25 @@ Small flex helpers for grow behavior and other one-line needs.
6
6
 
7
7
  Base utilities:
8
8
 
9
- | Class | Description |
10
- | --------------- | -------------------------------------------------------------- |
11
- | `.grow` | Makes an item take available flex space without overflowing. |
12
- | `.items-start` | Aligns children to the block-start edge; sets `--items-align`. |
13
- | `.items-center` | Centers children on the cross axis; sets `--items-align`. |
14
- | `.items-end` | Aligns children to the block-end edge; sets `--items-align`. |
15
- | `.dot` | Inline separator dot for metadata rows. |
9
+ | Class | Description |
10
+ | ----------------- | ------------------------------------------------------------------- |
11
+ | `.grow` | Makes an item take available flex space without overflowing. |
12
+ | `.items-start` | Aligns children to the block-start edge; sets `--items-align`. |
13
+ | `.items-center` | Centers children on the cross axis; sets `--items-align`. |
14
+ | `.items-end` | Aligns children to the block-end edge; sets `--items-align`. |
15
+ | `.items-baseline` | Aligns children on their first text baseline; sets `--items-align`. |
16
+ | `.dot` | Inline separator dot for metadata rows. |
16
17
 
17
18
  Extra utilities:
18
19
 
19
- | Class | Description |
20
- | -------------------------------- | --------------------------------------------------------- |
21
- | `.justify-content-start` | `justify-content: flex-start` and `--cluster-justify`. |
22
- | `.justify-content-center` | `justify-content: center` and `--cluster-justify`. |
23
- | `.justify-content-end` | `justify-content: flex-end` and `--cluster-justify`. |
24
- | `.justify-content-space-between` | `justify-content: space-between` and `--cluster-justify`. |
25
- | `.flex-wrap` | `flex-wrap: wrap` on any flex container. |
26
- | `.flex-nowrap` | `flex-wrap: nowrap` on any flex container. |
20
+ | Class | Description |
21
+ | -------------------------------- | ------------------------------------------------------- |
22
+ | `.justify-content-start` | `justify-content: flex-start` on the element itself. |
23
+ | `.justify-content-center` | `justify-content: center` on the element itself. |
24
+ | `.justify-content-end` | `justify-content: flex-end` on the element itself. |
25
+ | `.justify-content-space-between` | `justify-content: space-between` on the element itself. |
26
+ | `.flex-wrap` | `flex-wrap: wrap` on any flex container. |
27
+ | `.flex-nowrap` | `flex-wrap: nowrap` on any flex container. |
27
28
 
28
29
  ## Grow
29
30
 
@@ -66,14 +67,24 @@ Use `.items-start` on flex or grid layouts when children should keep their natur
66
67
 
67
68
  Use `.items-center` when a flex or grid layout should center its children on the cross axis. Use `.items-end` when children should align to the block-end edge.
68
69
 
70
+ Use `.items-baseline` when a row mixes text sizes — a price and its period, a name and a timestamp — so the words share one line instead of their boxes sharing a center.
71
+
72
+ ```html demo
73
+ <p class="cluster items-baseline">
74
+ <strong style="font-size: 2rem">$29</strong>
75
+ <span class="muted">/ month</span>
76
+ </p>
77
+ ```
78
+
69
79
  These helpers work by setting a custom property that participating components read,
70
80
  so they also apply inside a component's own grid anatomy rather than only on plain
71
81
  flex containers:
72
82
 
73
- - `.items-start` / `.items-center` / `.items-end` set `--items-align`, read by `.alert` and `.card`.
83
+ - `.items-start` / `.items-center` / `.items-end` / `.items-baseline` set `--items-align`, read by `.alert`, `.card` and `.list-item`.
74
84
  - `.text-start` / `.text-center` / `.text-end` set `--text-align`, read by `.table` cells.
75
- - `.justify-content-*` (utilities/extra) set `--cluster-justify`, read by `.cluster`.
76
- - `.flex-wrap` / `.flex-nowrap` (utilities/extra) set `flex-wrap` directly on any flex container.
85
+
86
+ `.justify-content-*` and `.flex-wrap` / `.flex-nowrap` (utilities/extra) set the property
87
+ directly, so they never reach a nested `.cluster`.
77
88
 
78
89
  Set the property directly when you need a value the helpers do not ship.
79
90
 
@@ -107,6 +118,5 @@ Always mark it `aria-hidden="true"` — it is a visual separator, not content.
107
118
 
108
119
  ## CSS hooks
109
120
 
110
- - `--items-align` — cross-axis alignment, set by `.items-start` / `.items-center` / `.items-end`.
121
+ - `--items-align` — cross-axis alignment, set by the `.items-*` helpers.
111
122
  - `--text-align` — text alignment, set by `.text-start` / `.text-center` / `.text-end`.
112
- - `--cluster-justify` — main-axis distribution, set by the optional `.justify-content-*` helpers and read by `.cluster`.
@@ -29,7 +29,7 @@ one line (`.badge`). To stop a layout primitive from wrapping — for example to
29
29
  force a `.cluster` onto a single row — set `--cluster-wrap: nowrap`.
30
30
 
31
31
  ```html demo
32
- <div class="cluster compact" style="--cluster-wrap: nowrap;">
32
+ <div class="cluster tight" style="--cluster-wrap: nowrap;">
33
33
  <select class="select sm fit" aria-label="Segment">
34
34
  <option>All segments</option>
35
35
  </select>
@@ -66,11 +66,10 @@ Use `.truncate` on the flexible item that should ellipsize inside a constrained
66
66
 
67
67
  ## Scroller
68
68
 
69
- Use `.scroller` to apply optional framework scrollbar treatment to a scroll container. It styles the scrollbar only; pair it with `.overflow-auto` or a component that already creates overflow.
69
+ Use `.scroller` to apply optional framework overflow treatment to a scroll container: a quieter scrollbar. It does not create overflow; pair it with `.overflow-auto` or a component that already creates overflow.
70
70
 
71
- The utility is two standard declarations and nothing else — the engine keeps
72
- drawing the scrollbar, `.scroller` only hands it a density and a colour that
73
- follow the theme. There is no `::-webkit-scrollbar` chrome: rebuilding a thumb
71
+ The engine keeps drawing the scrollbar; `.scroller` only hands it a density
72
+ and a colour that follow the theme. There is no `::-webkit-scrollbar` chrome: rebuilding a thumb
74
73
  by hand means re-implementing the hover, the corner and the light/dark
75
74
  adaptation that `color-scheme` already provides, and the hand-built version
76
75
  never quite matches the native one anyway.
@@ -80,6 +79,9 @@ above the capability floor. An engine that knows neither ignores both
80
79
  declarations and draws its native scrollbar — the intended fallback, not a
81
80
  broken state.
82
81
 
82
+ `.scroller` does not apply to touch screens, where the native overlay scrollbar
83
+ stays: it hides at rest, where a styled bar would stay visible.
84
+
83
85
  `.scroller` is a layout primitive, so it comes with `actual.full.css` and with
84
86
  `actual-css/css/layout` like `.stack` or `.cluster`. A project importing module
85
87
  by module reaches it directly:
@@ -102,9 +104,13 @@ by module reaches it directly:
102
104
  .scroller {
103
105
  --scroller-track: transparent;
104
106
  --scroller-thumb: var(--border);
107
+ }
105
108
 
106
- scrollbar-color: var(--scroller-thumb) var(--scroller-track);
107
- scrollbar-width: thin;
109
+ @media not all and (pointer: coarse) {
110
+ .scroller {
111
+ scrollbar-color: var(--scroller-thumb) var(--scroller-track);
112
+ scrollbar-width: thin;
113
+ }
108
114
  }
109
115
  ```
110
116
 
@@ -120,6 +126,45 @@ Thickness is not a hook: `scrollbar-width` takes `thin` or `auto`, not a length.
120
126
  Hover is left to the engine — it varies by platform, and matching it by hand was
121
127
  the part of a custom scrollbar that never held up.
122
128
 
129
+ ### One-row strips
130
+
131
+ Components that never wrap scroll horizontally when they do not fit: `.tabs`,
132
+ `.steps-horizontal`, and `.table-wrap`. (`.breadcrumb` truncates its labels
133
+ instead; see [Breadcrumb](../components/breadcrumb.md). `.pagination` wraps.)
134
+ In supporting browsers, a strip that loads on a current link tab or step starts
135
+ scrolled to it; elsewhere it starts at its first item.
136
+
137
+ `.tabs` hides its scrollbar and fades its edges instead: selecting the clipped
138
+ tab scrolls it into view. The others leave the scrollbar to the engine, so an
139
+ OS-default horizontal scrollbar can sit under a short strip, and on touch
140
+ screens it overlays the strip and hides at rest, leaving a clipped item as the
141
+ cue that the strip continues. Put `.scroller` on the strip itself — the element
142
+ that scrolls — for a thin, theme-coloured bar under a mouse; touch keeps the
143
+ overlay. These strips have no edge fade (overflow shadow, scroll hint): the
144
+ clipped item and the scrollbar already say there is more. Keyboard focus stops
145
+ an item with its focus line inside the strip.
146
+
147
+ ```html
148
+ <ol class="steps steps-horizontal scroller">
149
+ ```
150
+
151
+ There is no class that hides the scrollbar: on a strip whose items do not
152
+ scroll it themselves, it is the only visible scroll control for a mouse without
153
+ a horizontal wheel. Apply `.scroller` to every such strip of a page, or to
154
+ none, so the bars stay consistent.
155
+
156
+ A strip keeps scrolling inside a flex or grid row, such as a label and link
157
+ tabs on one `.cluster`: a `<nav>` directly around `.tabs` shrinks with the row
158
+ (and around `.breadcrumb`, which then truncates). Any other wrapper needs
159
+ `min-inline-size: 0`, or the row pushes the whole page into scrolling.
160
+
161
+ ```html
162
+ <div class="cluster">
163
+ <span>Sections</span>
164
+ <nav aria-label="Account sections"><ul class="tabs">…</ul></nav>
165
+ </div>
166
+ ```
167
+
123
168
  ## CSS hooks
124
169
 
125
170
  - `--scroller-track` — scrollbar track color.
@@ -48,9 +48,10 @@ Override the default gap from layout primitives (`.stack`, `.cluster`, `.grid`):
48
48
  - `.gap-none` → `gap: 0`
49
49
 
50
50
  `.gap-none` sets the element's own gap directly and does not change `--gap`
51
- for nested layouts. For a denser or roomier rhythm, use the `.compact` /
52
- `.spacious` density contexts, or set the `gap` property on the instance when the
53
- change must stop there.
51
+ for nested layouts. For a tighter or looser rhythm alone — closer items, without
52
+ smaller controls — use `.tight` / `.loose`; `.compact` / `.spacious` also change
53
+ padding and control size. Set the `gap` property on the instance when the change
54
+ must stop there.
54
55
 
55
56
  `--gap` defines the contextual rhythm; `.gap-context` applies that rhythm to a
56
57
  container that does not already consume `--gap`. The layout primitives
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "actual-css",
3
- "version": "0.11.1",
3
+ "version": "0.13.0",
4
4
  "description": "Plain CSS component framework with semantic classes, universal variants, small tokens, theme hooks, and progressive enhancements.",
5
5
  "type": "module",
6
6
  "packageManager": "bun@1.4.2",
@@ -10,7 +10,7 @@
10
10
  "type": "git",
11
11
  "url": "https://github.com/lekoala/actual-css.git"
12
12
  },
13
- "homepage": "https://github.com/lekoala/actual-css",
13
+ "homepage": "https://lekoala.github.io/actual-css/",
14
14
  "bugs": {
15
15
  "url": "https://github.com/lekoala/actual-css/issues"
16
16
  },
@@ -112,7 +112,8 @@
112
112
  "node": ">=18"
113
113
  },
114
114
  "scripts": {
115
- "lint": "biome check",
115
+ "hooks:install": "bun scripts/install-hooks.js",
116
+ "lint": "biome check --reporter concise",
116
117
  "lint:fix": "biome check --write",
117
118
  "build:docs": "bun run build:themes && bun scripts/build-docs.js",
118
119
  "watch:docs": "bun run build:themes && bun scripts/build-docs.js --watch",
@@ -139,7 +140,6 @@
139
140
  "check:catalog": "bun scripts/check-catalog.js",
140
141
  "check:links": "bun scripts/check-doc-links.js",
141
142
  "check:templates": "bun scripts/check-templates.js",
142
- "check:markup": "bun scripts/check-markup.js",
143
143
  "shot:crop": "bun scripts/shot-crop.js",
144
144
  "shot:forced": "bun scripts/forced-colors-shot.js",
145
145
  "shot:multi": "bun scripts/multi-shot.js",
@@ -152,11 +152,11 @@
152
152
  "release:notes": "bun scripts/release-notes.js",
153
153
  "test": "bun test",
154
154
  "test:changed": "bun test --changed=master",
155
- "build:all": "bun run lint && bun run build:dist && bun run build:js && bun run build:size && bun run format:docs && bun run build:docs && bun run check:docs && bun run check:doc-classes && bun run check:compat && bun run check:css-api && bun run check:color-space && bun run check:architecture && bun run check:enhance && bun run generate:reserved && bun run check:reserved && bun run generate:catalog && bun run check:catalog && bun run check:sync && bun run check:links && bun run check:templates && bun run check:markup && bun run test",
155
+ "build:all": "bun run lint && bun run build:dist && bun run build:js && bun run build:size && bun run format:docs && bun run build:docs && bun run check:docs && bun run check:doc-classes && bun run check:compat && bun run check:css-api && bun run check:color-space && bun run check:architecture && bun run check:enhance && bun run generate:reserved && bun run check:reserved && bun run generate:catalog && bun run check:catalog && bun run check:sync && bun run check:links && bun run check:templates && bun run test",
156
156
  "prepublishOnly": "bun run build:all"
157
157
  },
158
158
  "devDependencies": {
159
- "@biomejs/biome": "^2.5.12",
159
+ "@biomejs/biome": "^2.5.15",
160
160
  "happy-dom": "^20.14.0"
161
161
  },
162
162
  "dependencies": {
@@ -1,6 +1,5 @@
1
1
  [
2
2
  "accordion",
3
- "actions",
4
3
  "admonition",
5
4
  "alert",
6
5
  "alert-body",
@@ -56,7 +55,6 @@
56
55
  "cursor-pointer",
57
56
  "danger",
58
57
  "data-list",
59
- "dialog-confirmation",
60
58
  "dialog-icon",
61
59
  "display-block",
62
60
  "display-flex",
@@ -110,6 +108,7 @@
110
108
  "intent-color",
111
109
  "is-open",
112
110
  "is-static",
111
+ "items-baseline",
113
112
  "items-center",
114
113
  "items-end",
115
114
  "items-start",
@@ -132,6 +131,7 @@
132
131
  "list-item-title",
133
132
  "list-item-trailing",
134
133
  "list-reset",
134
+ "loose",
135
135
  "margin-block-end-auto",
136
136
  "margin-block-start-auto",
137
137
  "margin-inline-auto",
@@ -159,7 +159,6 @@
159
159
  "nav-list",
160
160
  "navbar",
161
161
  "navbar-brand",
162
- "navbar-nav",
163
162
  "needs-validation",
164
163
  "neutral",
165
164
  "otp",
@@ -248,6 +247,7 @@
248
247
  "text-uppercase",
249
248
  "text-wrap",
250
249
  "textarea",
250
+ "tight",
251
251
  "tooltip",
252
252
  "topbar",
253
253
  "touch-target",
@@ -40,16 +40,19 @@ the title in the content wrapper's direct `<header>`. The close button is then
40
40
  anchored to the surface, and the header reserves room so the title cannot run
41
41
  under it. A `.close` that is a direct child of the chrome header is that same
42
42
  corner overlay, and that header reserves room for it too. Put actions in the
43
- wrapper's `<footer>`.
43
+ wrapper's `<footer>` and compose its row in markup
44
+ (`<footer class="cluster justify-content-end">`); the modal and drawer footers
45
+ have no layout of their own.
44
46
 
45
47
  Use `.bleed` on that direct header when it needs a contrasting full-width band;
46
48
  the wrapper relays the modal padding so the band reaches the surface edges while
47
- its contents keep the normal inset. In `.dialog-confirmation`, keep the icon and
48
- the text header as the media object's two children; the component centers the
49
- header's first heading on the icon while following copy stays in that text
50
- column. For a trailing icon, use the confirmation's direct header as a
51
- non-wrapping split `.cluster`, with `.grow` on the text group. Do not compensate
52
- heading alignment with a literal margin.
49
+ its contents keep the normal inset. A confirmation is composition, not a
50
+ variant: a `.media` row with a leading `.dialog-icon` and the text column (its
51
+ first heading centers on the icon, following copy stays in that column), then a
52
+ `<footer class="bleed background-subtle cluster justify-content-space-between">` action
53
+ band with the cancel-style action first. For a trailing icon, use the wrapper's
54
+ direct header as a non-wrapping split `.cluster`, with `.grow` on the text
55
+ group. Do not compensate heading alignment with a literal margin.
53
56
 
54
57
  Do not turn the dialog heading into an `.alert` to obtain icon or surface
55
58
  styling. Compose `.dialog-icon` with `.media` for a leading title icon; reserve