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.
Files changed (223) hide show
  1. package/CHANGELOG.md +109 -3
  2. package/README.md +35 -30
  3. package/dist/actual.css +66 -4958
  4. package/dist/actual.full.css +7548 -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/app-ui.md +33 -0
  10. package/docs/design-notes/cascade-layer.md +34 -13
  11. package/docs/design-notes/column-layout.md +677 -0
  12. package/docs/design-notes/enhancement-contract.md +25 -0
  13. package/docs/design-notes/forced-colors.md +3 -0
  14. package/docs/design-notes/grid.md +785 -0
  15. package/docs/design-notes/naming.md +15 -0
  16. package/docs/design-notes/references.md +1 -1
  17. package/docs/design-notes/widget-primitives.md +1 -0
  18. package/docs/navigation.json +12 -2
  19. package/docs/pages/components/alert.md +21 -14
  20. package/docs/pages/components/app-navigation.md +34 -0
  21. package/docs/pages/components/aura.md +1 -1
  22. package/docs/pages/components/avatar.md +16 -6
  23. package/docs/pages/components/badge.md +9 -7
  24. package/docs/pages/components/button.md +46 -16
  25. package/docs/pages/components/card.md +17 -9
  26. package/docs/pages/components/chat.md +8 -8
  27. package/docs/pages/components/dialog.md +7 -7
  28. package/docs/pages/components/drawer.md +3 -3
  29. package/docs/pages/components/fab.md +9 -7
  30. package/docs/pages/components/indicator.md +52 -0
  31. package/docs/pages/components/list.md +62 -0
  32. package/docs/pages/components/navbar.md +15 -8
  33. package/docs/pages/components/pagination.md +4 -3
  34. package/docs/pages/components/rating.md +68 -0
  35. package/docs/pages/components/skeleton.md +4 -4
  36. package/docs/pages/components/spinner.md +5 -5
  37. package/docs/pages/components/steps.md +57 -0
  38. package/docs/pages/components/table.md +30 -5
  39. package/docs/pages/enhancements/flyout.md +15 -15
  40. package/docs/pages/enhancements/overview.md +1 -1
  41. package/docs/pages/enhancements/scrollspy.md +6 -6
  42. package/docs/pages/enhancements/status-bar.md +5 -1
  43. package/docs/pages/enhancements/tabs.md +7 -8
  44. package/docs/pages/enhancements/tooltip.md +1 -1
  45. package/docs/pages/examples/overview.md +47 -22
  46. package/docs/pages/forms/choice-cards.md +11 -6
  47. package/docs/pages/forms/controls.md +1 -1
  48. package/docs/pages/forms/detached-actions.md +1 -1
  49. package/docs/pages/forms/floating-field.md +10 -5
  50. package/docs/pages/forms/form-actions.md +1 -1
  51. package/docs/pages/forms/input-policy.md +1 -1
  52. package/docs/pages/forms/joined-controls.md +4 -4
  53. package/docs/pages/forms/labels.md +6 -6
  54. package/docs/pages/forms/otp.md +33 -1
  55. package/docs/pages/forms/overview.md +19 -19
  56. package/docs/pages/forms/password.md +1 -1
  57. package/docs/pages/forms/range.md +1 -1
  58. package/docs/pages/forms/states-and-sizes.md +44 -48
  59. package/docs/pages/forms/sticky-actions.md +3 -3
  60. package/docs/pages/forms/switch.md +110 -2
  61. package/docs/pages/forms/validation.md +9 -9
  62. package/docs/pages/foundations/philosophy.md +1 -1
  63. package/docs/pages/foundations/theming.md +17 -2
  64. package/docs/pages/foundations/tokens.md +22 -10
  65. package/docs/pages/foundations/typography.md +50 -20
  66. package/docs/pages/guides/bootstrap.md +11 -11
  67. package/docs/pages/guides/integrating-actual-css.md +192 -0
  68. package/docs/pages/guides/introduction.md +23 -26
  69. package/docs/pages/guides/modular-import.md +173 -0
  70. package/docs/pages/guides/progressive-enhancement.md +81 -78
  71. package/docs/pages/guides/tailwind.md +6 -8
  72. package/docs/pages/layout/app-layout.md +57 -0
  73. package/docs/pages/layout/app-shell.md +28 -7
  74. package/docs/pages/layout/center.md +12 -1
  75. package/docs/pages/layout/choosing.md +54 -0
  76. package/docs/pages/layout/column-layout.md +206 -0
  77. package/docs/pages/layout/frame.md +6 -1
  78. package/docs/pages/layout/grid.md +133 -102
  79. package/docs/pages/layout/media.md +1 -1
  80. package/docs/pages/layout/overview.md +15 -2
  81. package/docs/pages/layout/scroll-snap.md +2 -2
  82. package/docs/pages/layout/scroll-target.md +45 -0
  83. package/docs/pages/layout/sidebar-layout.md +38 -18
  84. package/docs/pages/layout/stack.md +70 -1
  85. package/docs/pages/layout/switcher.md +10 -26
  86. package/docs/pages/layout/topbar.md +2 -2
  87. package/docs/pages/patterns/actions.md +1 -1
  88. package/docs/pages/patterns/code-block.md +1 -1
  89. package/docs/pages/patterns/exclusive-accordion.md +1 -1
  90. package/docs/pages/patterns/hierarchical-nav.md +1 -1
  91. package/docs/pages/patterns/icon-input.md +1 -1
  92. package/docs/pages/patterns/icon-slot.md +61 -0
  93. package/docs/pages/patterns/intent-nesting.md +1 -1
  94. package/docs/pages/patterns/navigation-list.md +1 -1
  95. package/docs/pages/patterns/overview.md +1 -1
  96. package/docs/pages/patterns/structured-lists.md +38 -40
  97. package/docs/pages/utilities/accessibility.md +3 -3
  98. package/docs/pages/utilities/flex.md +15 -15
  99. package/docs/pages/utilities/links.md +4 -4
  100. package/docs/pages/utilities/list.md +8 -8
  101. package/docs/pages/utilities/overflow.md +8 -8
  102. package/docs/pages/utilities/overview.md +1 -1
  103. package/docs/pages/utilities/shape.md +3 -3
  104. package/docs/pages/utilities/sizing-wrapping.md +23 -16
  105. package/docs/pages/utilities/spacing.md +20 -20
  106. package/docs/pages/utilities/text.md +10 -10
  107. package/llms.txt +46 -4
  108. package/package.json +43 -13
  109. package/scripts/reserved-classes.json +278 -0
  110. package/src/cli/actual-css.js +133 -0
  111. package/src/css/actual.css +1 -31
  112. package/src/css/actual.full.css +11 -2
  113. package/src/css/components/alert.css +17 -4
  114. package/src/css/components/app-nav.css +76 -0
  115. package/src/css/components/avatar.css +4 -0
  116. package/src/css/components/badge.css +19 -19
  117. package/src/css/components/breadcrumb.css +1 -1
  118. package/src/css/components/button.css +20 -32
  119. package/src/css/components/card.css +34 -9
  120. package/src/css/{optional → components}/chat.css +0 -6
  121. package/src/css/components/drawer.css +24 -5
  122. package/src/css/{optional → components}/fab.css +1 -7
  123. package/src/css/components/flyout.css +5 -0
  124. package/src/css/components/index.css +37 -27
  125. package/src/css/components/indicator.css +47 -0
  126. package/src/css/components/join.css +8 -1
  127. package/src/css/components/key.css +2 -0
  128. package/src/css/components/list.css +83 -0
  129. package/src/css/components/menu.css +11 -8
  130. package/src/css/components/meter.css +4 -1
  131. package/src/css/components/modal.css +5 -17
  132. package/src/css/components/navbar.css +6 -15
  133. package/src/css/components/pagination.css +11 -0
  134. package/src/css/components/rating.css +101 -0
  135. package/src/css/components/spinner.css +5 -0
  136. package/src/css/components/status-bar.css +5 -0
  137. package/src/css/components/steps.css +122 -0
  138. package/src/css/components/surface.css +5 -0
  139. package/src/css/components/tab.css +20 -22
  140. package/src/css/components/table.css +6 -0
  141. package/src/css/components/tooltip.css +3 -7
  142. package/src/css/{focus.css → core/focus.css} +0 -8
  143. package/src/css/core/index.css +9 -0
  144. package/src/css/core/print.css +18 -0
  145. package/src/css/{theme.css → core/theme.css} +19 -19
  146. package/src/css/{tokens.css → core/tokens.css} +16 -4
  147. package/src/css/{variants.css → core/variants.css} +3 -2
  148. package/src/css/{optional → effects}/aura.css +1 -10
  149. package/src/css/effects/index.css +2 -0
  150. package/src/css/forms/base.css +8 -0
  151. package/src/css/forms/choice-card.css +7 -15
  152. package/src/css/forms/choice.css +49 -37
  153. package/src/css/forms/control.css +4 -1
  154. package/src/css/forms/custom-select.css +2 -0
  155. package/src/css/{optional → forms}/floating-field.css +25 -7
  156. package/src/css/forms/index.css +10 -13
  157. package/src/css/forms/native.css +15 -2
  158. package/src/css/{optional → forms}/otp.css +25 -16
  159. package/src/css/forms/range.css +14 -4
  160. package/src/css/forms/switch.css +31 -29
  161. package/src/css/layout/app-layout.css +102 -0
  162. package/src/css/layout/app-shell.css +6 -0
  163. package/src/css/layout/center.css +14 -0
  164. package/src/css/layout/cluster.css +21 -0
  165. package/src/css/layout/column-layout.css +144 -0
  166. package/src/css/layout/container-query.css +15 -0
  167. package/src/css/layout/frame.css +19 -0
  168. package/src/css/layout/grid.css +155 -0
  169. package/src/css/layout/index.css +44 -0
  170. package/src/css/layout/measure.css +9 -0
  171. package/src/css/layout/media.css +10 -0
  172. package/src/css/layout/scroll-target.css +15 -0
  173. package/src/css/layout/scroller.css +41 -0
  174. package/src/css/layout/sidebar-layout.css +52 -0
  175. package/src/css/layout/stack.css +21 -0
  176. package/src/css/layout/switcher.css +23 -0
  177. package/src/css/{optional/layout-extra.css → layout/topbar.css} +0 -20
  178. package/src/css/typography/index.css +5 -0
  179. package/src/css/typography/lead.css +7 -0
  180. package/src/css/{components → typography}/overline.css +2 -2
  181. package/src/css/{prose.css → typography/prose.css} +57 -38
  182. package/src/css/{utilities.css → utilities/base.css} +2 -0
  183. package/src/css/utilities/index.css +3 -0
  184. package/src/js/escape.js +36 -0
  185. package/src/js/flyout.js +22 -12
  186. package/src/js/full.js +26 -0
  187. package/src/js/index.js +5 -21
  188. package/src/js/surface.js +1 -31
  189. package/src/js/tooltip.js +48 -19
  190. package/src/js/validation.js +1 -1
  191. package/src/tooling/css-bundle.js +308 -0
  192. package/dist/actual-themes.min.css +0 -1
  193. package/dist/optional.css +0 -1048
  194. package/dist/optional.min.css +0 -1
  195. package/docs/pages/guides/optional-components.md +0 -33
  196. package/docs/pages/layout/list.md +0 -41
  197. package/src/css/grid.css +0 -109
  198. package/src/css/layout.css +0 -123
  199. package/src/css/optional/index.css +0 -10
  200. package/src/css/optional/scroller.css +0 -63
  201. package/src/css/print.css +0 -47
  202. package/src/css/themes/corporate.css +0 -60
  203. package/src/css/themes/cyberpunk.css +0 -60
  204. package/src/css/themes/dim.css +0 -33
  205. package/src/css/themes/edge.css +0 -117
  206. package/src/css/themes/forest.css +0 -65
  207. package/src/css/themes/gradient.css +0 -97
  208. package/src/css/themes/index.css +0 -47
  209. package/src/css/themes/indigo.css +0 -33
  210. package/src/css/themes/lavender.css +0 -66
  211. package/src/css/themes/mono.css +0 -60
  212. package/src/css/themes/neon.css +0 -66
  213. package/src/css/themes/ocean.css +0 -60
  214. package/src/css/themes/petrol.css +0 -64
  215. package/src/css/themes/spruce.css +0 -64
  216. package/src/css/themes/square.css +0 -65
  217. package/src/css/themes/sunset.css +0 -66
  218. /package/src/css/{base.css → core/base.css} +0 -0
  219. /package/src/css/{intents.css → core/intents.css} +0 -0
  220. /package/src/css/{reset.css → core/reset.css} +0 -0
  221. /package/src/css/{optional → layout}/scroll-snap.css +0 -0
  222. /package/src/css/{optional/typography-fluid.css → typography/fluid.css} +0 -0
  223. /package/src/css/{optional/utilities-extra.css → utilities/extra.css} +0 -0
package/dist/optional.css DELETED
@@ -1,1048 +0,0 @@
1
- /*
2
- * One-time code input with visual cells and one native input.
3
- *
4
- * Add one direct span per accepted character. The cells determine the
5
- * component width; the overlaid input remains the only interactive control.
6
- *
7
- * Public hooks:
8
- * --otp-cell-size
9
- * --otp-gap
10
- * --otp-font-size
11
- *
12
- * Internal:
13
- * --otp-cell-border
14
- * --otp-cell-bg
15
- */
16
- .otp {
17
- --otp-cell-size: var(--control-size);
18
- --otp-gap: var(--space-10);
19
- --otp-font-size: 1.5rem;
20
- --otp-cell-border: var(--border);
21
- --otp-cell-bg: var(--surface);
22
-
23
- position: relative;
24
- display: inline-grid;
25
- grid-auto-flow: column;
26
- grid-auto-columns: var(--otp-cell-size);
27
- inline-size: fit-content;
28
- max-inline-size: 100%;
29
- gap: var(--otp-gap);
30
- border-radius: var(--radius);
31
- direction: ltr;
32
- }
33
-
34
- .otp > span {
35
- inline-size: var(--otp-cell-size);
36
- block-size: var(--otp-cell-size);
37
- border: var(--border-width) solid var(--otp-cell-border);
38
- border-radius: var(--radius);
39
- background: var(--otp-cell-bg);
40
- transition:
41
- border-color var(--duration),
42
- background-color var(--duration),
43
- box-shadow var(--duration);
44
- }
45
-
46
- /* The native input owns focus, selection, autofill, paste, and one-time-code
47
- integration. Its spacing aligns monospace glyphs with the visual cells. */
48
- .otp > input {
49
- position: absolute;
50
- z-index: 1;
51
- inset: 0;
52
- inline-size: 100%;
53
- block-size: 100%;
54
- margin: 0;
55
- padding: 0 calc((var(--otp-cell-size) - 1ch) / 2);
56
- border: 0;
57
- outline: 2px solid transparent;
58
- box-shadow: none;
59
- background: transparent;
60
- color: var(--text);
61
- font-family: var(--font-mono);
62
- font-size: var(--otp-font-size);
63
- font-variant-numeric: tabular-nums;
64
- line-height: var(--otp-cell-size);
65
- letter-spacing: calc(var(--otp-cell-size) + var(--otp-gap) - 1ch);
66
- text-align: start;
67
- appearance: none;
68
- }
69
-
70
- .otp:focus-within {
71
- box-shadow: var(--focus-ring-shadow);
72
- }
73
-
74
- .otp > input:focus ~ span {
75
- --otp-cell-border: var(--focus);
76
- }
77
-
78
- .otp > input[aria-invalid="true"] ~ span,
79
- .needs-validation.was-validated .otp > input:invalid ~ span {
80
- --otp-cell-border: var(--danger);
81
- }
82
-
83
- @supports selector(:user-invalid) {
84
- .needs-validation .otp > input:user-invalid ~ span {
85
- --otp-cell-border: var(--danger);
86
- }
87
- }
88
-
89
- .otp > input:disabled {
90
- color: var(--state-disabled);
91
- cursor: not-allowed;
92
- }
93
-
94
- .otp > input:disabled ~ span {
95
- --otp-cell-bg: var(--surface-subtle);
96
-
97
- opacity: var(--disabled-opacity);
98
- }
99
-
100
- @media (forced-colors: active) {
101
- .otp:focus-within {
102
- outline: var(--focus-outline);
103
- outline-offset: var(--focus-outline-offset);
104
- box-shadow: none;
105
- }
106
-
107
- .otp > input:focus ~ span {
108
- --otp-cell-border: Highlight;
109
- }
110
-
111
- .otp > input[aria-invalid="true"] ~ span,
112
- .needs-validation.was-validated .otp > input:invalid ~ span {
113
- --otp-cell-border: Mark;
114
- }
115
- }
116
-
117
- /*
118
- * Message rows composed from an optional avatar, metadata, and bubble.
119
- *
120
- * Public hooks:
121
- * --chat-gap
122
- * --chat-max-size
123
- *
124
- * Internal:
125
- * --chat-bubble-bg
126
- * --chat-bubble-fg
127
- * --chat-bubble-border
128
- */
129
- .chat {
130
- --chat-gap: var(--space-30);
131
- --chat-max-size: min(85%, 40rem);
132
-
133
- display: grid;
134
- grid-auto-rows: min-content;
135
- column-gap: var(--chat-gap);
136
- }
137
-
138
- .chat-start {
139
- grid-template-columns: auto minmax(0, 1fr);
140
- justify-items: start;
141
- }
142
-
143
- .chat-end {
144
- grid-template-columns: minmax(0, 1fr) auto;
145
- justify-items: end;
146
- }
147
-
148
- .chat-avatar {
149
- grid-row: 1 / span 3;
150
- align-self: end;
151
- }
152
-
153
- .chat-start > .chat-avatar {
154
- grid-column: 1;
155
- }
156
-
157
- .chat-end > .chat-avatar {
158
- grid-column: 2;
159
- }
160
-
161
- .chat-header,
162
- .chat-footer {
163
- display: flex;
164
- gap: var(--space-10);
165
- color: var(--text-muted);
166
- font-size: var(--font-size-xs);
167
- }
168
-
169
- .chat-start > :is(.chat-header, .chat-bubble, .chat-footer) {
170
- grid-column: 2;
171
- }
172
-
173
- .chat-end > :is(.chat-header, .chat-bubble, .chat-footer) {
174
- grid-column: 1;
175
- }
176
-
177
- /* Intent boundary — an outer intent does not tint nested messages. */
178
- :where(.chat-bubble) {
179
- --intent: initial;
180
- --intent-fg: initial;
181
- --ui-bg: initial;
182
- --ui-fg: initial;
183
- --ui-border: initial;
184
- }
185
-
186
- .chat-bubble {
187
- --chat-bubble-bg: var(--ui-bg, var(--intent, var(--surface-subtle)));
188
- --chat-bubble-fg: var(--ui-fg, var(--intent-fg, var(--text)));
189
- --chat-bubble-border: var(--ui-border, transparent);
190
-
191
- inline-size: fit-content;
192
- max-inline-size: var(--chat-max-size);
193
- padding: var(--space-20) var(--space-30);
194
- border: var(--border-width) solid var(--chat-bubble-border);
195
- border-radius: var(--radius-lg);
196
- background: var(--chat-bubble-bg);
197
- color: var(--chat-bubble-fg);
198
- overflow-wrap: anywhere;
199
- }
200
-
201
- .chat-start > .chat-bubble {
202
- border-end-start-radius: var(--radius-sm);
203
- }
204
-
205
- .chat-end > .chat-bubble {
206
- border-end-end-radius: var(--radius-sm);
207
- }
208
-
209
- /* Remove the reserved avatar gutter when a message has no avatar. This is a
210
- cosmetic enhancement; the two-column baseline remains fully readable. */
211
- @supports selector(:has(*)) {
212
- .chat:not(:has(> .chat-avatar)) {
213
- column-gap: 0;
214
- }
215
- }
216
-
217
- @media (forced-colors: active) {
218
- .chat-bubble {
219
- border-color: CanvasText;
220
- }
221
- }
222
-
223
- /*
224
- * Decorative intent-colored frame for one direct child.
225
- *
226
- * Public hooks:
227
- * --aura-color
228
- * --aura-width
229
- * --aura-blur
230
- * --aura-radius
231
- * --aura-duration
232
- */
233
- @property --aura-angle {
234
- syntax: "<angle>";
235
- inherits: false;
236
- initial-value: 0deg;
237
- }
238
-
239
- /* Intent boundary — an outer intent does not tint nested aura frames. */
240
- :where(.aura) {
241
- --intent: initial;
242
- --intent-fg: initial;
243
- }
244
-
245
- .aura {
246
- --aura-color: var(--intent, var(--primary));
247
- --aura-width: 2px;
248
- --aura-blur: 0.75rem;
249
- --aura-radius: var(--radius-lg);
250
- --aura-duration: 6s;
251
-
252
- position: relative;
253
- isolation: isolate;
254
- display: inline-grid;
255
- padding: var(--aura-width);
256
- border-radius: calc(var(--aura-radius) + var(--aura-width));
257
- background: var(--aura-color);
258
- background: conic-gradient(from var(--aura-angle), transparent 0 65%, var(--aura-color));
259
- }
260
-
261
- .aura::before {
262
- position: absolute;
263
- z-index: -1;
264
- inset: 0;
265
- border-radius: inherit;
266
- background: inherit;
267
- filter: blur(var(--aura-blur));
268
- opacity: 0.45;
269
- pointer-events: none;
270
- content: "";
271
- }
272
-
273
- .aura > * {
274
- position: relative;
275
- }
276
-
277
- .aura-glow {
278
- background: var(--aura-color);
279
- }
280
-
281
- @media (prefers-reduced-motion: no-preference) {
282
- .aura:not(.aura-glow) {
283
- animation: aura-rotate var(--aura-duration) linear infinite;
284
- }
285
- }
286
-
287
- @keyframes aura-rotate {
288
- to {
289
- --aura-angle: 360deg;
290
- }
291
- }
292
-
293
- @supports selector(:has(*)) {
294
- .aura:has(> .btn) {
295
- --aura-radius: var(--radius);
296
- }
297
-
298
- .aura:has(> :is(.card, .alert)) {
299
- --aura-radius: var(--radius-lg);
300
- }
301
-
302
- .aura:has(> .badge) {
303
- --aura-radius: var(--radius-full);
304
- }
305
- }
306
-
307
- @media (forced-colors: active) {
308
- .aura {
309
- background: CanvasText;
310
- }
311
-
312
- .aura::before {
313
- display: none;
314
- }
315
- }
316
-
317
- /*
318
- * Fixed floating action placement and a native-details speed dial.
319
- * Button owns the action's size, shape, and intent; FAB only owns placement.
320
- *
321
- * Public hooks:
322
- * --fab-offset
323
- * --fab-gap
324
- */
325
- .fab {
326
- --fab-offset: var(--space-40);
327
- --fab-gap: var(--space-20);
328
-
329
- position: fixed;
330
- z-index: var(--z-menu);
331
- inset-block-end: max(var(--fab-offset), env(safe-area-inset-bottom));
332
- inset-inline-end: var(--fab-offset);
333
- }
334
-
335
- .fab > summary {
336
- list-style: none;
337
- cursor: pointer;
338
- }
339
-
340
- .fab > summary::-webkit-details-marker {
341
- display: none;
342
- }
343
-
344
- /* A lone decorative plus can communicate the open/close state without
345
- requiring a second icon or JavaScript-managed content. */
346
- .fab > summary > [aria-hidden="true"]:only-child {
347
- display: inline-block;
348
- transition: transform var(--duration);
349
- }
350
-
351
- .fab-actions {
352
- position: absolute;
353
- inset-inline-end: 0;
354
- inset-block-end: calc(100% + var(--fab-gap));
355
- display: flex;
356
- flex-direction: column;
357
- align-items: flex-end;
358
- gap: var(--fab-gap);
359
- opacity: 0;
360
- visibility: hidden;
361
- pointer-events: none;
362
- transform: translateY(var(--space-10));
363
- transition:
364
- opacity var(--duration),
365
- transform var(--duration),
366
- visibility 0s linear var(--duration);
367
- }
368
-
369
- /* One action is one compact row. Keeping this relationship inside FAB avoids
370
- wrapping and prevents labels from becoming visually associated with the
371
- preceding or following button. */
372
- .fab-action {
373
- display: flex;
374
- align-items: center;
375
- justify-content: flex-end;
376
- gap: var(--fab-gap);
377
- inline-size: max-content;
378
- max-inline-size: calc(var(--viewport-inline) - var(--fab-offset) - var(--fab-offset));
379
- }
380
-
381
- .fab-label {
382
- flex: 0 1 auto;
383
- overflow: hidden;
384
- max-inline-size: 16rem;
385
- padding: var(--space-10) var(--space-20);
386
- border-radius: var(--radius);
387
- background: var(--surface);
388
- color: var(--text);
389
- box-shadow: var(--shadow);
390
- font-size: var(--font-size-xs);
391
- font-weight: var(--font-weight-medium);
392
- line-height: var(--line-height-tight);
393
- text-overflow: ellipsis;
394
- white-space: nowrap;
395
- }
396
-
397
- .fab[open] > .fab-actions {
398
- opacity: 1;
399
- visibility: visible;
400
- pointer-events: auto;
401
- transform: translateY(0);
402
- transition-delay: 0s;
403
- }
404
-
405
- .fab[open] > summary > [aria-hidden="true"]:only-child {
406
- transform: rotate(45deg);
407
- }
408
-
409
- @media (forced-colors: active) {
410
- .fab-label {
411
- border: var(--border-width) solid CanvasText;
412
- box-shadow: none;
413
- }
414
- }
415
-
416
- @media print {
417
- .fab {
418
- display: none;
419
- }
420
- }
421
-
422
- /*
423
- * Floating labels — optional composition over the text controls
424
- * (.input, .textarea, .select) and .field-label.
425
- *
426
- * The cell owns a control plus its label; the label overlays the control and
427
- * floats to the top when the control is focused or non-empty. Help and error
428
- * messages stay in the surrounding .field, not inside the cell, so the
429
- * resting label can center on the control alone.
430
- *
431
- * The control must precede the label: the float is driven by the sibling
432
- * combinator and :placeholder-shown. Every .input/.textarea needs
433
- * placeholder=" " to expose a reliable empty state — without it the label
434
- * only reacts to focus.
435
- *
436
- * Density flows through --control-size: .sm/.lg shrink the control geometry
437
- * and the reserved headroom with it, leaving typography untouched.
438
- *
439
- * Public hooks:
440
- * --floating-pad-block-start
441
- */
442
- .floating-field {
443
- --floating-pad-block-start: calc(var(--control-size) / 2 + var(--space-20));
444
-
445
- /* Grid blockifies the control, removing the inline descender gap a textarea
446
- would otherwise leave under itself. */
447
- position: relative;
448
- display: grid;
449
- }
450
-
451
- .floating-field > :is(.input, .textarea, .select) {
452
- /* Headroom for the floated label is always reserved, so the value never
453
- jumps when the label floats. Half the control centers the resting label;
454
- the extra step clears the floated label's bottom edge. */
455
- padding-block-start: var(--floating-pad-block-start);
456
- padding-block-end: var(--space-10);
457
- }
458
-
459
- .floating-field > .field-label {
460
- position: absolute;
461
- inset-block-start: 50%;
462
- inset-inline-start: calc(var(--space-30) + var(--border-width));
463
- transform: translateY(-50%);
464
- color: var(--text-muted);
465
- font-size: var(--control-font-size);
466
- font-weight: var(--font-weight);
467
- pointer-events: none;
468
- transition:
469
- inset-block-start var(--duration),
470
- transform var(--duration),
471
- font-size var(--duration),
472
- color var(--duration);
473
- }
474
-
475
- /* A textarea's resting label aligns with the first text line instead of
476
- centering in the (tall) control. */
477
- .floating-field > .textarea + .field-label {
478
- inset-block-start: var(--floating-pad-block-start);
479
- transform: none;
480
- }
481
-
482
- /* :autofill covers prefilled values before interaction, when the browser may
483
- not have propagated the value to the placeholder state yet. */
484
- .floating-field
485
- > :is(.input, .textarea):is(:focus, :autofill, :not(:placeholder-shown))
486
- + .field-label {
487
- inset-block-start: var(--space-10);
488
- transform: none;
489
- font-size: var(--font-size-xs);
490
- }
491
-
492
- /* Controls without a dependable :placeholder-shown empty state stay floated:
493
- select always, and date/time inputs whose placeholder rendering is
494
- inconsistent across engines (Firefox shows none). */
495
- .floating-field
496
- > :is(
497
- .select,
498
- .input[type="date"],
499
- .input[type="time"],
500
- .input[type="datetime-local"],
501
- .input[type="month"],
502
- .input[type="week"]
503
- )
504
- + .field-label {
505
- inset-block-start: var(--space-10);
506
- transform: none;
507
- font-size: var(--font-size-xs);
508
- }
509
-
510
- /*
511
- * Scroller — optional custom scrollbar styling for overflow regions.
512
- *
513
- * Apply to any scroll container (.overflow-auto, sidebars, long tables). Both
514
- * scrollbar-color (Firefox) and ::-webkit-scrollbar (Safari/Chromium) are
515
- * styled; the thumb lands as a thin, theme-aware affordance with a hover
516
- * state.
517
- *
518
- * Public hooks:
519
- * --scroller-size
520
- * --scroller-padding
521
- * --scroller-track
522
- * --scroller-thumb
523
- * --scroller-thumb-hover
524
- */
525
- .scroller {
526
- --scroller-size: 0.625rem;
527
- --scroller-padding: 0.125rem;
528
- --scroller-track: transparent;
529
- --scroller-thumb: var(--border);
530
- --scroller-thumb-hover: var(--text-muted);
531
-
532
- scrollbar-color: var(--scroller-thumb) var(--scroller-track);
533
- scrollbar-width: thin;
534
- }
535
-
536
- /* Opt-in gutter: reserves space so the layout does not shift when
537
- the scrollbar appears. Use alongside a known-overflow container. */
538
- .scroller.stable-gutter {
539
- scrollbar-gutter: stable;
540
- }
541
-
542
- .scroller::-webkit-scrollbar {
543
- width: var(--scroller-size);
544
- height: var(--scroller-size);
545
- }
546
-
547
- .scroller::-webkit-scrollbar-track {
548
- background: var(--scroller-track);
549
- }
550
-
551
- .scroller::-webkit-scrollbar-thumb {
552
- border: var(--scroller-padding) solid transparent;
553
- border-radius: var(--radius-full);
554
- background: var(--scroller-thumb);
555
- background-clip: content-box;
556
- }
557
-
558
- .scroller::-webkit-scrollbar-thumb:hover {
559
- background: var(--scroller-thumb-hover);
560
- background-clip: content-box;
561
- }
562
-
563
- .scroller::-webkit-scrollbar-corner {
564
- background: var(--scroller-track);
565
- }
566
-
567
- @supports (color: color-mix(in oklch, red, white)) {
568
- .scroller {
569
- --scroller-thumb: color-mix(in oklch, var(--text-muted), transparent 55%);
570
- --scroller-thumb-hover: color-mix(in oklch, var(--text-muted), transparent 30%);
571
- }
572
- }
573
-
574
- /*
575
- * Scroll snap — optional horizontal rails for niche card strips and simple
576
- * touch-friendly overflow areas.
577
- *
578
- * This is not a full carousel. For previous/next controls, pagination,
579
- * active state, drag behavior, looping, or robust desktop mouse interaction,
580
- * use a dedicated carousel library.
581
- *
582
- * Public hooks:
583
- * --scroll-snap-gap
584
- * --scroll-snap-align
585
- * --scroll-snap-item-size
586
- * --scroll-snap-padding
587
- */
588
- .scroll-snap {
589
- --scroll-snap-gap: var(--gap);
590
- --scroll-snap-align: start;
591
- --scroll-snap-item-size: auto;
592
-
593
- display: flex;
594
- gap: var(--scroll-snap-gap);
595
- overflow-x: auto;
596
- }
597
-
598
- .scroll-snap > * {
599
- flex: 0 0 var(--scroll-snap-item-size);
600
- }
601
-
602
- @supports (scroll-snap-type: x mandatory) {
603
- .scroll-snap {
604
- overscroll-behavior-inline: contain;
605
- scroll-padding-inline: var(--scroll-snap-padding, var(--scroll-snap-gap));
606
- scroll-snap-type: x mandatory;
607
- }
608
-
609
- .scroll-snap > * {
610
- scroll-snap-align: var(--scroll-snap-align);
611
- }
612
-
613
- .scroll-snap[data-snap="center"] {
614
- --scroll-snap-align: center;
615
- }
616
- }
617
-
618
- @supports (scroll-snap-type: inline mandatory) {
619
- .scroll-snap {
620
- scroll-snap-type: inline mandatory;
621
- }
622
- }
623
-
624
- .scroll-snap[data-scrollbar="hidden"] {
625
- scrollbar-width: none;
626
- }
627
-
628
- .scroll-snap[data-scrollbar="hidden"]::-webkit-scrollbar {
629
- display: none;
630
- }
631
-
632
- /*
633
- * Optional fluid typography module.
634
- *
635
- * Not imported by actual.css. Import this file manually after actual.css when
636
- * you need display, title, and lead sizes that scale with the viewport. Typical
637
- * use cases: marketing pages, landing pages, hero sections, documentation
638
- * homepages.
639
- *
640
- * Do not use this for body text, app UI, or the default prose scale. Fluid
641
- * sizing is not the foundation of the typography system — it is a small opt-in
642
- * surface for display text on content-heavy pages.
643
- *
644
- * Public API:
645
- * --fluid-display, --fluid-title, --fluid-lead (size tokens, clamp())
646
- * .text-display, .text-title, .text-lead (composed classes)
647
- *
648
- * Public hooks:
649
- * --fluid-display
650
- * --fluid-title
651
- * --fluid-lead
652
- *
653
- * Values are generated from scripts/rfs.ts over the 360px -> 1440px range.
654
- */
655
- :root {
656
- --fluid-display: clamp(2.75rem, calc(2.25rem + 2.222vw), 4.25rem);
657
- --fluid-title: clamp(2rem, calc(1.75rem + 1.111vw), 2.75rem);
658
- --fluid-lead: clamp(1.125rem, calc(1.0833rem + 0.185vw), 1.25rem);
659
- }
660
-
661
- @supports (inline-size: 100vi) {
662
- :root {
663
- --fluid-display: clamp(2.75rem, calc(2.25rem + 2.222vi), 4.25rem);
664
- --fluid-title: clamp(2rem, calc(1.75rem + 1.111vi), 2.75rem);
665
- --fluid-lead: clamp(1.125rem, calc(1.0833rem + 0.185vi), 1.25rem);
666
- }
667
- }
668
-
669
- .text-display {
670
- font-size: var(--fluid-display);
671
- line-height: var(--line-height-tight);
672
- text-wrap: balance;
673
- }
674
-
675
- .text-title {
676
- font-size: var(--fluid-title);
677
- line-height: var(--line-height-tight);
678
- text-wrap: balance;
679
- }
680
-
681
- .text-lead {
682
- font-size: var(--fluid-lead);
683
- line-height: var(--line-height-relaxed);
684
- }
685
-
686
- /*
687
- * Optional utility helpers.
688
- *
689
- * These classes are intentionally not part of the core API.
690
- * They are escape hatches for layout nudges, not a full atomic scale.
691
- *
692
- * Core principle:
693
- * - .sm / .lg provide local rhythm through --gap and --density-space.
694
- * - utilities consume the current context instead of defining their own -sm/-lg scale.
695
- * - optional utilities extend core utilities, they do not re-express them.
696
- * - follow verbose .{property}-{value} naming.
697
- *
698
- * Internal:
699
- * --cluster-justify (local relay consumed by .cluster)
700
- */
701
-
702
- /* Display */
703
- .display-block {
704
- display: block;
705
- }
706
-
707
- .display-inline {
708
- display: inline;
709
- }
710
-
711
- .display-inline-block {
712
- display: inline-block;
713
- }
714
-
715
- .display-flex {
716
- display: flex;
717
- }
718
-
719
- .display-inline-flex {
720
- display: inline-flex;
721
- }
722
-
723
- .display-grid {
724
- display: grid;
725
- }
726
-
727
- /* Position */
728
- .position-relative {
729
- position: relative;
730
- }
731
-
732
- .position-absolute {
733
- position: absolute;
734
- }
735
-
736
- .position-fixed {
737
- position: fixed;
738
- }
739
-
740
- .position-sticky {
741
- position: sticky;
742
- }
743
-
744
- /* Sizing */
745
- .inline-size-full {
746
- inline-size: 100%;
747
- }
748
-
749
- .block-size-full {
750
- block-size: 100%;
751
- }
752
-
753
- .min-inline-size-0 {
754
- min-inline-size: 0;
755
- }
756
-
757
- .min-block-size-0 {
758
- min-block-size: 0;
759
- }
760
-
761
- .max-inline-size-none {
762
- max-inline-size: none;
763
- }
764
-
765
- /* Flex / alignment */
766
- .flex-wrap {
767
- flex-wrap: wrap;
768
- }
769
-
770
- .flex-nowrap {
771
- flex-wrap: nowrap;
772
- }
773
-
774
- .gap-sm {
775
- gap: var(--space-20);
776
- }
777
-
778
- .gap-md {
779
- gap: var(--space-40);
780
- }
781
-
782
- .gap-lg {
783
- gap: var(--space-50);
784
- }
785
-
786
- .justify-content-start {
787
- --cluster-justify: flex-start;
788
- justify-content: flex-start;
789
- }
790
-
791
- .justify-content-center {
792
- --cluster-justify: center;
793
- justify-content: center;
794
- }
795
-
796
- .justify-content-end {
797
- --cluster-justify: flex-end;
798
- justify-content: flex-end;
799
- }
800
-
801
- .justify-content-space-between {
802
- --cluster-justify: space-between;
803
- justify-content: space-between;
804
- }
805
-
806
- .align-self-start {
807
- align-self: start;
808
- }
809
-
810
- .align-self-center {
811
- align-self: center;
812
- }
813
-
814
- .align-self-end {
815
- align-self: end;
816
- }
817
-
818
- /* Spacing */
819
- .padding-context {
820
- padding: var(--density-space);
821
- }
822
-
823
- .margin-inline-auto {
824
- margin-inline: auto;
825
- }
826
-
827
- .margin-inline-start-auto {
828
- margin-inline-start: auto;
829
- }
830
-
831
- .margin-inline-end-auto {
832
- margin-inline-end: auto;
833
- }
834
-
835
- .margin-block-start-auto {
836
- margin-block-start: auto;
837
- }
838
-
839
- .margin-block-end-auto {
840
- margin-block-end: auto;
841
- }
842
-
843
- .gap-context {
844
- gap: var(--gap);
845
- }
846
-
847
- .row-gap-context {
848
- row-gap: var(--gap);
849
- }
850
-
851
- .column-gap-context {
852
- column-gap: var(--gap);
853
- }
854
-
855
- /* Overflow */
856
- .overflow-hidden {
857
- overflow: hidden;
858
- }
859
-
860
- .overflow-clip {
861
- overflow: clip;
862
- }
863
-
864
- .overflow-x-auto {
865
- overflow-x: auto;
866
- }
867
-
868
- .overflow-y-auto {
869
- overflow-y: auto;
870
- }
871
-
872
- /* Text */
873
- .white-space-normal {
874
- white-space: normal;
875
- }
876
-
877
- .text-break {
878
- overflow-wrap: break-word;
879
- }
880
-
881
- .text-uppercase {
882
- text-transform: uppercase;
883
- }
884
-
885
- .text-lowercase {
886
- text-transform: lowercase;
887
- }
888
-
889
- .text-capitalize {
890
- text-transform: capitalize;
891
- }
892
-
893
- .font-weight-normal {
894
- font-weight: var(--font-weight);
895
- }
896
-
897
- .font-weight-medium {
898
- font-weight: var(--font-weight-medium);
899
- }
900
-
901
- .font-weight-strong {
902
- font-weight: var(--font-weight-strong);
903
- }
904
-
905
- .font-weight-bold {
906
- font-weight: var(--font-weight-bold);
907
- }
908
-
909
- /* Radius */
910
- .radius-sm-context {
911
- border-radius: var(--radius-sm);
912
- }
913
-
914
- .radius-context {
915
- border-radius: var(--radius);
916
- }
917
-
918
- .radius-lg-context {
919
- border-radius: var(--radius-lg);
920
- }
921
-
922
- /* Border */
923
- .border-context {
924
- border: var(--border-width) solid var(--border);
925
- }
926
-
927
- .border-block-start {
928
- border-block-start: var(--border-width) solid var(--border);
929
- }
930
-
931
- .border-block-end {
932
- border-block-end: var(--border-width) solid var(--border);
933
- }
934
-
935
- .border-inline-start {
936
- border-inline-start: var(--border-width) solid var(--border);
937
- }
938
-
939
- .border-inline-end {
940
- border-inline-end: var(--border-width) solid var(--border);
941
- }
942
-
943
- /* Surface */
944
- .background-surface {
945
- background: var(--surface);
946
- color: var(--text);
947
- }
948
-
949
- .background-raised {
950
- background: var(--surface-raised);
951
- color: var(--text);
952
- }
953
-
954
- .background-subtle {
955
- background: var(--surface-subtle);
956
- color: var(--text);
957
- }
958
-
959
- /* Shadow */
960
- .shadow-context {
961
- box-shadow: var(--shadow);
962
- }
963
-
964
- .shadow-popout-context {
965
- box-shadow: var(--shadow-popout);
966
- }
967
-
968
- /* Interaction */
969
- .cursor-pointer {
970
- cursor: pointer;
971
- }
972
-
973
- .pointer-events-none {
974
- pointer-events: none;
975
- }
976
-
977
- .user-select-none {
978
- -webkit-user-select: none;
979
- user-select: none;
980
- }
981
-
982
- /* Visibility */
983
- .display-none {
984
- display: none;
985
- }
986
-
987
- .visibility-hidden {
988
- visibility: hidden;
989
- }
990
-
991
- /*
992
- * Additional layout primitives not included in the core bundle.
993
- *
994
- * Import this file explicitly when a project needs these patterns.
995
- *
996
- * Public hooks:
997
- * --switcher-threshold
998
- */
999
- .switcher {
1000
- display: flex;
1001
- flex-wrap: wrap;
1002
- gap: var(--gap);
1003
- }
1004
-
1005
- .switcher > * {
1006
- flex-basis: calc((var(--switcher-threshold, 40rem) - 100%) * 999);
1007
- flex-grow: 1;
1008
- min-inline-size: 0;
1009
- }
1010
-
1011
- /*
1012
- * Sticky, frosted top bar shell for app/dashboard shells (persistent
1013
- * sidebar + topbar layouts). Owns positioning, stacking, and the frosted
1014
- * background only — row content (search, breadcrumb, actions) stays local
1015
- * to the page.
1016
- *
1017
- * Not for marketing/editorial site headers — those are one-off visual
1018
- * identity per page; see docs/pages/patterns/overview.md's Header Navigation
1019
- * section. This is for the structural app-shell topbar, which recurs unchanged
1020
- * across app UIs regardless of brand.
1021
- */
1022
- .topbar {
1023
- position: sticky;
1024
- inset-block-start: 0;
1025
- z-index: var(--z-sticky);
1026
- isolation: isolate;
1027
- display: flex;
1028
- align-items: center;
1029
- gap: var(--gap);
1030
- padding-block: var(--space-30);
1031
- padding-inline: var(--space-40);
1032
- border-block-end: var(--border-width) solid var(--border);
1033
- background: var(--surface);
1034
- }
1035
-
1036
- /* Both conditions are required: the translucent background contains var(),
1037
- so a browser with backdrop-filter but not color-mix (e.g. Safari < 16.2)
1038
- would accept the declaration at parse time, fail it at computed-value
1039
- time, and lose the background entirely instead of keeping the opaque
1040
- fallback above. */
1041
- @supports ((backdrop-filter: blur(1rem)) or (-webkit-backdrop-filter: blur(1rem))) and
1042
- (background: color-mix(in oklch, red 50%, transparent)) {
1043
- .topbar {
1044
- background: color-mix(in oklch, var(--surface) 88%, transparent);
1045
- backdrop-filter: blur(0.75rem);
1046
- -webkit-backdrop-filter: blur(0.75rem);
1047
- }
1048
- }