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
@@ -42,7 +42,7 @@
42
42
  border-radius: var(--radius-sm);
43
43
  }
44
44
 
45
- .breadcrumb :where(a, span):where([aria-current]:not([aria-current="false"])) {
45
+ .breadcrumb :where(li, a, span):where([aria-current]:not([aria-current="false"])) {
46
46
  color: var(--text);
47
47
  font-weight: var(--font-weight-medium);
48
48
  }
@@ -133,7 +133,7 @@
133
133
 
134
134
  .btn.soft {
135
135
  --ui-hover-bg: color-mix(
136
- in oklch,
136
+ in oklab,
137
137
  var(--surface) calc(var(--soft-bg-mix) - var(--soft-hover-alpha)),
138
138
  var(--intent, var(--neutral))
139
139
  );
@@ -149,6 +149,9 @@
149
149
  the overlay darkening for solid/soft is .btn-specific. */
150
150
  .btn:not(:where(:disabled, [aria-disabled="true"])):hover {
151
151
  background: var(--ui-hover-bg, var(--btn-default-hover-bg));
152
+ /* Huge inset spread paints the overlay over any border-radius without a
153
+ pseudo-element; a plain background would replace the fill instead of
154
+ darkening it. */
152
155
  box-shadow: inset 0 0 0 999px var(--btn-hover-overlay);
153
156
  }
154
157
 
@@ -249,6 +252,11 @@ a.btn[aria-disabled="true"] {
249
252
  forced-color-adjust: none;
250
253
  }
251
254
 
255
+ .btn:is([aria-pressed="true"], [aria-current="page"]):not(.link):focus-visible {
256
+ outline: var(--focus-outline);
257
+ box-shadow: none;
258
+ }
259
+
252
260
  .btn:hover:not(:where(:disabled, [aria-disabled="true"], .link)) {
253
261
  outline: var(--border-width) solid ButtonText;
254
262
  outline-offset: 2px;
@@ -262,3 +270,13 @@ a.btn[aria-disabled="true"] {
262
270
  forced-color-adjust: auto;
263
271
  }
264
272
  }
273
+ @media print {
274
+ :is(button, input[type="button"], input[type="submit"], input[type="reset"]).btn {
275
+ display: none !important;
276
+ }
277
+
278
+ a.btn[href^="http"]::after {
279
+ content: " (" attr(href) ")";
280
+ font-size: 0.875em;
281
+ }
282
+ }
@@ -2,8 +2,10 @@
2
2
  * Cards provide a neutral raised surface for grouped content.
3
3
  *
4
4
  * Surface, border, radius, padding, density, and elevation stay local here.
5
- * Direct header/footer children are structural slots. Page layout remains the
6
- * job of markup and layout primitives outside the card.
5
+ * Direct header/footer children are structural slots; a direct footer is
6
+ * anchored to the card's bottom edge when extra block space is available (so
7
+ * price/action rows align across equal-height cards in a grid). Page layout
8
+ * remains the job of markup and layout primitives outside the card.
7
9
  *
8
10
  * Public hooks:
9
11
  * --card-pad
@@ -47,6 +49,17 @@
47
49
  border-radius: var(--card-radius);
48
50
  }
49
51
 
52
+ /* The box lives at zero specificity so a layout primitive applied to the same
53
+ element owns it. components/ is imported after layout/, so a plain .card
54
+ declaration would beat .media (and any other non-column primitive) on source
55
+ order alone and silently flatten `class="card media"` into a column. The
56
+ column is still the default a bare .card gets, and > footer's margin-auto
57
+ bottom-anchoring depends on it. */
58
+ :where(.card) {
59
+ display: flex;
60
+ flex-direction: column;
61
+ }
62
+
50
63
  .card > * {
51
64
  min-inline-size: 0;
52
65
  }
@@ -69,6 +82,12 @@
69
82
  align-items: var(--items-align, flex-start);
70
83
  }
71
84
 
85
+ /* A direct footer is pinned to the bottom when the card has free space.
86
+ Specificity beats .stack > * so a .card.stack keeps this contract. */
87
+ .card > footer {
88
+ margin-block-start: auto;
89
+ }
90
+
72
91
  .card > :first-child {
73
92
  margin-block-start: 0;
74
93
  }
@@ -126,3 +145,9 @@
126
145
  .card > :where(header, footer, figure).bleed {
127
146
  padding: var(--card-pad);
128
147
  }
148
+ @media print {
149
+ .card {
150
+ border: var(--border-width) solid CanvasText;
151
+ break-inside: avoid;
152
+ }
153
+ }
@@ -108,8 +108,8 @@ dialog.drawer nav {
108
108
  }
109
109
  }
110
110
 
111
- /* RTL: swap closed transform directions so the drawer slides from the correct
112
- inline side. */
111
+ /* Safari before 16.4 does not support :dir(); keep the ancestor-attribute
112
+ path as the Minimal-tier fallback. */
113
113
  [dir="rtl"] dialog.drawer {
114
114
  --drawer-closed-translate: 100%;
115
115
  }
@@ -118,6 +118,26 @@ dialog.drawer nav {
118
118
  --drawer-closed-translate: -100%;
119
119
  }
120
120
 
121
+ /* :dir() follows inherited HTML directionality and handles nested direction
122
+ changes. Explicit ltr rules neutralize an outer RTL fallback selector. */
123
+ @supports selector(:dir(rtl)) {
124
+ dialog.drawer:dir(ltr) {
125
+ --drawer-closed-translate: -100%;
126
+ }
127
+
128
+ dialog.drawer[data-side="end"]:dir(ltr) {
129
+ --drawer-closed-translate: 100%;
130
+ }
131
+
132
+ dialog.drawer:dir(rtl) {
133
+ --drawer-closed-translate: 100%;
134
+ }
135
+
136
+ dialog.drawer[data-side="end"]:dir(rtl) {
137
+ --drawer-closed-translate: -100%;
138
+ }
139
+ }
140
+
121
141
  @media (forced-colors: active) {
122
142
  dialog.drawer::backdrop {
123
143
  background: Canvas;
@@ -137,3 +157,8 @@ dialog.drawer > header,
137
157
  dialog.drawer > form > header {
138
158
  padding-inline-end: calc(var(--control-size) + var(--space-30));
139
159
  }
160
+ @media print {
161
+ dialog.drawer[open] {
162
+ display: none !important;
163
+ }
164
+ }
@@ -170,3 +170,8 @@
170
170
  /* Vertical dividers — .menu-separator (menu.css) is the canonical way to
171
171
  separate menu items. Flyout panels that are not menus use auto grid gap
172
172
  declared on .flyout itself. */
173
+ @media print {
174
+ .flyout {
175
+ display: none !important;
176
+ }
177
+ }
@@ -1,42 +1,47 @@
1
1
  /* Controls */
2
- @import "button.css";
3
- @import "badge.css";
4
- @import "alert.css";
5
- @import "actions.css";
2
+ @import "./button.css";
3
+ @import "./badge.css";
4
+ @import "./alert.css";
5
+ @import "./actions.css";
6
6
 
7
7
  /* Surfaces */
8
- @import "card.css";
9
- @import "surface.css";
10
- @import "modal.css";
11
- @import "drawer.css";
8
+ @import "./card.css";
9
+ @import "./surface.css";
10
+ @import "./modal.css";
11
+ @import "./drawer.css";
12
12
 
13
13
  /* Overlays */
14
- @import "flyout.css";
15
- @import "menu.css";
16
- @import "tooltip.css";
14
+ @import "./flyout.css";
15
+ @import "./menu.css";
16
+ @import "./tooltip.css";
17
17
 
18
18
  /* Disclosure */
19
- @import "accordion.css";
19
+ @import "./accordion.css";
20
20
 
21
21
  /* Feedback */
22
- @import "spinner.css";
23
- @import "skeleton.css";
24
- @import "busy.css";
25
- @import "progress.css";
26
- @import "status-bar.css";
22
+ @import "./spinner.css";
23
+ @import "./skeleton.css";
24
+ @import "./busy.css";
25
+ @import "./progress.css";
26
+ @import "./status-bar.css";
27
27
 
28
28
  /* Display */
29
- @import "avatar.css";
30
- @import "key.css";
31
- @import "overline.css";
29
+ @import "./avatar.css";
30
+ @import "./key.css";
31
+ @import "./chat.css";
32
+ @import "./fab.css";
32
33
 
33
34
  /* Navigation */
34
- @import "navbar.css";
35
- @import "breadcrumb.css";
36
- @import "pagination.css";
37
- @import "tab.css";
38
- @import "scrollspy.css";
35
+ @import "./navbar.css";
36
+ @import "./breadcrumb.css";
37
+ @import "./pagination.css";
38
+ @import "./tab.css";
39
+ @import "./scrollspy.css";
39
40
 
40
41
  /* Data */
41
- @import "table.css";
42
- @import "meter.css";
42
+ @import "./table.css";
43
+ @import "./meter.css";
44
+
45
+ /* Composed controls — imported last so equal-specificity corner rules remain
46
+ later than the controls they group. */
47
+ @import "./join.css";
@@ -91,7 +91,7 @@
91
91
  /* Keep an active focus ring above an adjacent hovered control. This matters
92
92
  on platforms where clicking a button does not move focus from the input. */
93
93
  .join > :is(:focus, :focus-within) {
94
- z-index: 2;
94
+ z-index: 4;
95
95
  }
96
96
 
97
97
  /* An input's border must read as the "primary" edge against an attached
@@ -115,3 +115,10 @@
115
115
  outline: none;
116
116
  box-shadow: none;
117
117
  }
118
+
119
+ /* A default outline action belongs to the composite field border. An explicit
120
+ intent still provides its own outline color — --intent is guaranteed-invalid
121
+ for a neutral button (the :where(.btn) boundary), so the fallback applies. */
122
+ .join:has(> :is(.input, .textarea, .select)) > .btn.outline {
123
+ --btn-border-color: var(--intent, var(--border));
124
+ }
@@ -6,6 +6,7 @@
6
6
  *
7
7
  * Visual twin of .prose kbd (prose.css) — keep the two recipes in sync.
8
8
  */
9
+ /* @sync key-recipe */
9
10
  .key {
10
11
  display: inline-flex;
11
12
  align-items: center;
@@ -25,3 +26,4 @@
25
26
  white-space: nowrap;
26
27
  vertical-align: 0.08em;
27
28
  }
29
+ /* @sync key-recipe */
@@ -6,6 +6,9 @@
6
6
  *
7
7
  * Public hooks:
8
8
  * --menu-item-icon-size
9
+ *
10
+ * Intent: .menu-item consumes --intent as its text color, so intent classes
11
+ * (.danger, .success, …) tint an item without a per-intent variant.
9
12
  */
10
13
 
11
14
  .menu {
@@ -20,6 +23,13 @@
20
23
  margin: 0;
21
24
  }
22
25
 
26
+ /* Intent boundary — inherited intents do not tint this component.
27
+ Local intent classes still win because :where() has zero specificity. */
28
+ :where(.menu-item) {
29
+ --intent: initial;
30
+ --intent-fg: initial;
31
+ }
32
+
23
33
  .menu-item {
24
34
  --menu-item-icon-size: 1.25em;
25
35
 
@@ -32,7 +42,7 @@
32
42
  border: 0;
33
43
  border-radius: var(--radius-sm);
34
44
  background: transparent;
35
- color: var(--text);
45
+ color: var(--intent, var(--text));
36
46
  font: inherit;
37
47
  text-align: start;
38
48
  text-decoration: none;
@@ -51,7 +51,10 @@
51
51
  @media (forced-colors: active) {
52
52
  .meter::-webkit-meter-optimum-value,
53
53
  .meter::-webkit-meter-suboptimum-value,
54
- .meter::-webkit-meter-even-less-good-value,
54
+ .meter::-webkit-meter-even-less-good-value {
55
+ background: Highlight;
56
+ }
57
+
55
58
  .meter::-moz-meter-bar {
56
59
  background: Highlight;
57
60
  }
@@ -315,3 +315,8 @@ html.has-modal-open.had-scrollbar {
315
315
  outline: 2px solid transparent;
316
316
  box-shadow: var(--focus-ring-shadow);
317
317
  }
318
+ @media print {
319
+ dialog.modal[open] {
320
+ display: none !important;
321
+ }
322
+ }
@@ -16,9 +16,20 @@
16
16
  margin: 0;
17
17
  padding: 0;
18
18
  list-style: none;
19
+
20
+ /* Arrows, page numbers, and truncation ellipses share one geometric
21
+ system: equal digit widths keep the row from re-flowing as numbers grow
22
+ (98 → 99 → 100). */
23
+ font-variant-numeric: tabular-nums;
19
24
  }
20
25
 
21
26
  .pagination .btn {
27
+ /* Every item gets the same min footprint; wider content (100, 1000,
28
+ text labels) grows past it naturally. The default 1em control padding
29
+ would let an icon-only arrow exceed the numbered items' footprint, so
30
+ the horizontal padding tightens here and the min size does the work. */
31
+ --btn-pad-x: var(--space-20);
32
+
22
33
  min-inline-size: var(--control-size);
23
34
  }
24
35
 
@@ -57,3 +57,8 @@
57
57
  transform: rotate(360deg);
58
58
  }
59
59
  }
60
+ @media print {
61
+ .spinner {
62
+ display: none !important;
63
+ }
64
+ }
@@ -70,3 +70,8 @@
70
70
  translate: 0 var(--space-20);
71
71
  pointer-events: none;
72
72
  }
73
+ @media print {
74
+ .status-bar {
75
+ display: none !important;
76
+ }
77
+ }
@@ -40,3 +40,8 @@
40
40
  display var(--duration) allow-discrete;
41
41
  }
42
42
  }
43
+ @media print {
44
+ .surface-backdrop {
45
+ display: none !important;
46
+ }
47
+ }
@@ -64,3 +64,9 @@
64
64
  .table > :last-child > tr:last-child > * {
65
65
  border-block-end: 0;
66
66
  }
67
+
68
+ /* Same compact vocabulary as .card.compact: one hook change, no selector
69
+ duplication. Density is padding only — typography never changes. */
70
+ .table.compact {
71
+ --table-cell-pad: var(--space-20);
72
+ }
@@ -112,3 +112,8 @@
112
112
  display: none;
113
113
  }
114
114
  }
115
+ @media print {
116
+ .tooltip {
117
+ display: none !important;
118
+ }
119
+ }
@@ -0,0 +1,9 @@
1
+ /* Core — the minimal Actual language. Every module depends on this order. */
2
+ @import "./reset.css";
3
+ @import "./tokens.css";
4
+ @import "./theme.css";
5
+ @import "./base.css";
6
+ @import "./intents.css";
7
+ @import "./variants.css";
8
+ @import "./focus.css";
9
+ @import "./print.css";
@@ -0,0 +1,18 @@
1
+ @media print {
2
+ :root,
3
+ body {
4
+ /* Save some ink in dark mode */
5
+ color-scheme: light;
6
+ background: #fff;
7
+ color: #000;
8
+ }
9
+
10
+ h1,
11
+ h2,
12
+ h3,
13
+ h4,
14
+ h5,
15
+ h6 {
16
+ break-after: avoid;
17
+ }
18
+ }
@@ -101,6 +101,16 @@
101
101
  --focus-ring-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring);
102
102
  }
103
103
 
104
+ /* Native platform controls (checkbox, radio, range, progress) that are not
105
+ restyled by a component class still follow the theme accent. Declared on
106
+ every island: accent-color inherits as a computed value, so islands must
107
+ recompute it from their own --primary (same resolution rule as above).
108
+ Unsupported browsers ignore it; forced-colors keeps browser control. */
109
+ :root,
110
+ [data-theme] {
111
+ accent-color: var(--primary);
112
+ }
113
+
104
114
  /* Tone subsystem — fixed-backdrop foreground for components placed over media
105
115
  or other surfaces outside the theme token stack. Use data-theme islands for
106
116
  full dark/light subtree inversion. */
@@ -115,13 +125,13 @@
115
125
  /* Focus ring derivation — color-mix tier. Runs whether or not light-dark()
116
126
  is also supported, so it covers the color-mix-without-light-dark browser
117
127
  tier on its own; the light-dark block below refines it further with the
118
- light/dark alpha split once both features are available. Deriving from
119
- --focus (not a literal) means overriding --primary/--focus, in the
120
- default theme or any custom one, keeps the ring in sync automatically. */
128
+ light/dark alpha split once both features are available. Declared on every
129
+ [data-theme] island (not just the built-in light/dark pair) so custom
130
+ islands recolor the ring for free; a preset that re-declares --focus-ring
131
+ later in source order intentionally pins its own ring instead. */
121
132
  @supports (color: color-mix(in oklch, red, white)) {
122
133
  :root,
123
- [data-theme="light"],
124
- [data-theme="dark"] {
134
+ [data-theme] {
125
135
  --focus-ring: color-mix(in oklch, var(--focus) 30%, transparent);
126
136
  }
127
137
  }
@@ -170,7 +180,7 @@
170
180
 
171
181
  @supports (color: color-mix(in oklch, red, white)) and (color: light-dark(red, blue)) {
172
182
  :root,
173
- [data-theme="light"] {
183
+ [data-theme] {
174
184
  --focus-ring: light-dark(
175
185
  color-mix(in oklch, var(--focus) 30%, transparent),
176
186
  color-mix(in oklch, var(--focus) 28%, transparent)
@@ -180,8 +190,7 @@
180
190
 
181
191
  @media (forced-colors: active) {
182
192
  :root,
183
- [data-theme="light"],
184
- [data-theme="dark"] {
193
+ [data-theme] {
185
194
  --focus-outline-color: Highlight;
186
195
  --focus-ring-shadow: none;
187
196
 
@@ -191,8 +200,9 @@
191
200
 
192
201
  @media (prefers-contrast: more) {
193
202
  :root,
194
- [data-theme="light"],
195
- [data-theme="dark"] {
203
+ /* The repeated attribute intentionally outranks single-attribute preset
204
+ palettes, including a preset stylesheet loaded after the core. */
205
+ [data-theme][data-theme] {
196
206
  --border-width: 2px;
197
207
  --focus-outline-offset: 3px;
198
208
  --focus-ring-width: 4px;
@@ -83,13 +83,21 @@
83
83
 
84
84
  /* Controls — inputs and buttons share --control-size so heights always match.
85
85
  .sm/.lg (variants.css) switch --control-size; --control-font-size stays
86
- at its baseline because density does not change typography. */
86
+ at its baseline because density does not change typography.
87
+
88
+ Inline choices (checkbox, radio, switch) are typographic, not field-sized:
89
+ they sit on a line of text, so they follow --control-font-size through
90
+ --choice-control-size instead of --control-size. Being an em value in
91
+ :root is deliberate — an unregistered custom property resolves its units
92
+ at the element that consumes it, so one declaration tracks whatever font
93
+ size the choice actually renders at. */
87
94
  --control-size-sm: 2rem;
88
95
  --control-size-md: 2.375rem;
89
96
  --control-size-lg: 2.75rem;
90
97
  --control-size: var(--control-size-md);
91
98
  --control-pad-x: 1em;
92
99
  --control-font-size: var(--font-size-md);
100
+ --choice-control-size: 1.125em;
93
101
  --disabled-opacity: 0.65;
94
102
  }
95
103
 
@@ -118,6 +126,10 @@
118
126
  --soft-bg-mix: 88%;
119
127
  --soft-border-mix: 65%;
120
128
  --soft-hover-alpha: 12%;
129
+ /* Soft foreground: the share of raw intent in soft text. 100% keeps the
130
+ pure intent color; lower values rebate it toward --text, which vivid or
131
+ light intent palettes need to stay legible on their own soft surface. */
132
+ --soft-fg-mix: 100%;
121
133
 
122
134
  /* Shared icon assets. Override these URLs when a strict CSP disallows data:. */
123
135
  --icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%238a8594' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
@@ -142,7 +154,8 @@
142
154
  }
143
155
 
144
156
  @media (forced-colors: active) {
145
- :root {
157
+ :root,
158
+ [data-theme] {
146
159
  --state-selected: Highlight;
147
160
  --state-selected-fg: HighlightText;
148
161
  --state-disabled: GrayText;
@@ -85,12 +85,13 @@
85
85
  @supports (color: color-mix(in oklch, red, white)) {
86
86
  .soft {
87
87
  /* @sync soft-recipe */
88
- --ui-bg: color-mix(in oklch, var(--surface) var(--soft-bg-mix), var(--intent, var(--neutral)));
88
+ --ui-bg: color-mix(in oklab, var(--surface) var(--soft-bg-mix), var(--intent, var(--neutral)));
89
89
  --ui-border: color-mix(
90
- in oklch,
90
+ in oklab,
91
91
  var(--surface) var(--soft-border-mix),
92
92
  var(--intent, var(--neutral))
93
93
  );
94
+ --ui-fg: color-mix(in oklab, var(--text), var(--intent, var(--text)) var(--soft-fg-mix));
94
95
  /* @sync soft-recipe */
95
96
  }
96
97
 
@@ -0,0 +1,2 @@
1
+ /* Effects — purely visual enrichments. */
2
+ @import "./aura.css";
@@ -0,0 +1,8 @@
1
+ /* Forms base — the ordinary native controls bundle. */
2
+ @import "./form.css";
3
+ @import "./control.css";
4
+ @import "./choice.css";
5
+ @import "./select.css";
6
+ @import "./native.css";
7
+ @import "./validation.css";
8
+ @import "./form-actions.css";
@@ -85,7 +85,7 @@
85
85
  /* Subtle background tint on checked — modern browsers only */
86
86
  @supports (color: color-mix(in oklch, red, white)) {
87
87
  .choice-card:has(:checked) {
88
- --choice-card-bg: color-mix(in oklch, var(--surface) 96%, var(--intent, var(--primary)));
88
+ --choice-card-bg: color-mix(in oklab, var(--surface) 96%, var(--intent, var(--primary)));
89
89
  }
90
90
  }
91
91
 
@@ -139,18 +139,19 @@
139
139
 
140
140
  .choice-card:has(:checked) {
141
141
  --choice-card-border: Highlight;
142
- /* Pin the background: with forced-color-adjust: none the color-mix tint
143
- above would actually render, uncontrolled in high-contrast themes. */
142
+ /* Pin the selected surface to the system canvas: only the essential
143
+ marker pseudos opt out of remapping, never the interactive card host. */
144
144
  --choice-card-bg: Canvas;
145
- forced-color-adjust: none;
146
145
  }
147
146
 
148
147
  .choice-card:has(:checked)::before {
149
148
  background: Highlight;
150
149
  box-shadow: 0 0 0 2px Canvas;
150
+ forced-color-adjust: none;
151
151
  }
152
152
 
153
153
  .choice-card:has(:checked)::after {
154
154
  background: HighlightText;
155
+ forced-color-adjust: none;
155
156
  }
156
157
  }