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
@@ -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 */
@@ -0,0 +1,83 @@
1
+ /*
2
+ * Application list — repeated rows with optional leading and trailing regions.
3
+ * Content determines the number of lines; the component only owns the row
4
+ * geometry, dividers, and supporting-text treatment.
5
+ *
6
+ * Public hooks:
7
+ * --list-item-min-size
8
+ * --list-item-pad-block
9
+ * --list-item-pad-inline
10
+ * --list-item-gap
11
+ * --list-divider
12
+ */
13
+ .list {
14
+ --list-item-min-size: var(--control-size-lg);
15
+ --list-item-pad-block: var(--space-30);
16
+ --list-item-pad-inline: 0;
17
+ --list-item-gap: var(--gap);
18
+ --list-divider: var(--border-width) solid var(--border);
19
+
20
+ display: grid;
21
+ gap: 0;
22
+ margin: 0;
23
+ padding: 0;
24
+ border-block-start: var(--list-divider);
25
+ list-style: none;
26
+ }
27
+
28
+ .list > li {
29
+ margin: 0;
30
+ }
31
+
32
+ .list > :where(.list-item),
33
+ .list > li > .list-item {
34
+ display: grid;
35
+ grid-template-columns: auto minmax(0, 1fr) auto;
36
+ align-items: center;
37
+ gap: var(--list-item-gap);
38
+ min-block-size: var(--list-item-min-size);
39
+ padding-block: var(--list-item-pad-block);
40
+ padding-inline: var(--list-item-pad-inline);
41
+ }
42
+
43
+ .list > .list-item + .list-item,
44
+ .list > li + li {
45
+ border-block-start: var(--list-divider);
46
+ }
47
+
48
+ a.list-item {
49
+ color: inherit;
50
+ text-decoration: none;
51
+ }
52
+
53
+ a.list-item:hover {
54
+ background: var(--surface-subtle);
55
+ }
56
+
57
+ .list-item-content {
58
+ min-inline-size: 0;
59
+ }
60
+
61
+ .list-item-title,
62
+ .list-item-text {
63
+ display: block;
64
+ margin: 0;
65
+ }
66
+
67
+ .list-item-text {
68
+ color: var(--text-muted);
69
+ font-size: var(--font-size-sm);
70
+ overflow-wrap: anywhere;
71
+ }
72
+
73
+ .list-item-trailing {
74
+ justify-self: end;
75
+ min-inline-size: 0;
76
+ }
77
+
78
+ @media print {
79
+ .list > :where(.list-item),
80
+ .list > li > .list-item {
81
+ break-inside: avoid;
82
+ }
83
+ }
@@ -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;
@@ -116,10 +126,3 @@
116
126
  border: 0;
117
127
  border-block-start: var(--border-width) solid var(--border);
118
128
  }
119
-
120
- @media (forced-colors: active) {
121
- .menu-item:focus-visible {
122
- outline: var(--border-width) solid ButtonText;
123
- outline-offset: -1px;
124
- }
125
- }
@@ -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
  }
@@ -239,23 +239,6 @@ html.has-modal-open.had-scrollbar {
239
239
  }
240
240
  }
241
241
 
242
- @media (forced-colors: active) {
243
- dialog.modal {
244
- border-color: ButtonText;
245
- background: Canvas;
246
- color: CanvasText;
247
- box-shadow: none;
248
- }
249
-
250
- dialog.modal::backdrop {
251
- background: Canvas;
252
- }
253
-
254
- .dialog-icon {
255
- border: var(--border-width) solid CanvasText;
256
- }
257
- }
258
-
259
242
  /* Close controls. A close button is an action attached to a surface, so it
260
243
  never participates in the content flow. The modal and the drawer share the
261
244
  same simple placement — absolute at the panel's top inline-end, inside the
@@ -315,3 +298,8 @@ html.has-modal-open.had-scrollbar {
315
298
  outline: 2px solid transparent;
316
299
  box-shadow: var(--focus-ring-shadow);
317
300
  }
301
+ @media print {
302
+ dialog.modal[open] {
303
+ display: none !important;
304
+ }
305
+ }
@@ -67,6 +67,7 @@
67
67
  .nav-link:where([aria-current]:not([aria-current="false"])) {
68
68
  background: var(--surface-subtle);
69
69
  color: var(--text);
70
+ font-weight: var(--font-weight-bold);
70
71
  }
71
72
 
72
73
  .nav-link:hover {
@@ -74,7 +75,9 @@
74
75
  color: var(--text);
75
76
  }
76
77
 
77
- /* Nav list — a vertical navigation list. Used by .drawer for stacked links. */
78
+ /* Nav list — a vertical navigation list. Used by .drawer for stacked links.
79
+ Links fill each row so the whole line is an interactive target; the
80
+ horizontal .navbar-nav links keep their intrinsic size. */
78
81
  .nav-list {
79
82
  display: grid;
80
83
  --gap: var(--space-20);
@@ -88,18 +91,6 @@
88
91
  margin: 0;
89
92
  }
90
93
 
91
- @media (forced-colors: active) {
92
- .nav-link:where([aria-current]:not([aria-current="false"])) {
93
- outline: var(--border-width) solid Highlight;
94
- background: Highlight;
95
- color: HighlightText;
96
- forced-color-adjust: none;
97
- }
98
-
99
- .nav-link:hover:not(:where([aria-current]:not([aria-current="false"]))) {
100
- outline: var(--border-width) solid ButtonText;
101
- outline-offset: 2px;
102
- background: Canvas;
103
- color: CanvasText;
104
- }
94
+ .nav-list .nav-link {
95
+ inline-size: 100%;
105
96
  }
@@ -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
 
@@ -0,0 +1,101 @@
1
+ /*
2
+ * Rating — integer star rating drawn on native radio inputs.
3
+ *
4
+ * The checked radio remains the functional baseline. :has() progressively
5
+ * fills preceding stars without reversing DOM or keyboard order. Forced
6
+ * colors restores the platform radio presentation.
7
+ *
8
+ * Public hooks:
9
+ * --rating-size
10
+ * --rating-gap
11
+ * --rating-color
12
+ * --rating-empty
13
+ * --rating-star
14
+ */
15
+ :where(.rating) {
16
+ --intent: initial;
17
+ }
18
+
19
+ .rating {
20
+ --rating-size: 1.25rem;
21
+ --rating-gap: 0.1rem;
22
+ --rating-color: var(--intent, var(--warning));
23
+ --rating-empty: var(--border);
24
+ --rating-star: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='2 2 16 16'%3E%3Cpath d='M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 0 0 .95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 0 0-.364 1.118l1.07 3.292c.3.922-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 0 0-1.175 0l-2.8 2.034c-.783.57-1.838-.196-1.539-1.118l1.07-3.292a1 1 0 0 0-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 0 0 .951-.69l1.07-3.292Z'/%3E%3C/svg%3E");
25
+
26
+ display: inline-flex;
27
+ gap: 0;
28
+ color: var(--rating-color);
29
+ }
30
+
31
+ .rating > input[type="radio"] {
32
+ appearance: none;
33
+ flex: 0 0 auto;
34
+ inline-size: calc(var(--rating-size) + var(--rating-gap));
35
+ block-size: var(--rating-size);
36
+ margin: 0;
37
+ border: 0;
38
+ background: var(--rating-empty);
39
+ /* Contiguous radio boxes own hit-testing; the centered mask preserves the
40
+ star's proportions while leaving a purely visual gap between values. */
41
+ -webkit-mask: var(--rating-star) center / var(--rating-size) var(--rating-size) no-repeat;
42
+ mask: var(--rating-star) center / var(--rating-size) var(--rating-size) no-repeat;
43
+ cursor: pointer;
44
+ transition: background-color var(--duration);
45
+ }
46
+
47
+ .rating > input[type="radio"]:checked {
48
+ background: var(--rating-color);
49
+ }
50
+
51
+ .rating > input[type="radio"]:disabled {
52
+ cursor: not-allowed;
53
+ opacity: var(--disabled-opacity);
54
+ }
55
+
56
+ .rating > input[type="radio"]:focus-visible {
57
+ outline: var(--focus-outline);
58
+ outline-offset: var(--focus-outline-offset);
59
+ background: var(--rating-color);
60
+ }
61
+
62
+ /* Browsers without :has() still show the selected native radio as one filled
63
+ star. The enhancement only adds cumulative checked and hover previews. */
64
+ @supports selector(:has(*)) {
65
+ .rating > input[type="radio"]:has(~ input[type="radio"]:checked) {
66
+ background: var(--rating-color);
67
+ }
68
+
69
+ @media (hover: hover) {
70
+ .rating > input[type="radio"]:not(:disabled):hover,
71
+ .rating > input[type="radio"]:has(~ input[type="radio"]:not(:disabled):hover) {
72
+ background: var(--rating-color);
73
+ }
74
+
75
+ /* A hovered value previews exactly that value: later stars are emptied,
76
+ including a previously checked star beyond the pointer. */
77
+ .rating > input[type="radio"]:not(:disabled):hover ~ input[type="radio"] {
78
+ background: var(--rating-empty);
79
+ }
80
+ }
81
+ }
82
+
83
+ .rating.sm {
84
+ --rating-size: 1rem;
85
+ }
86
+
87
+ .rating.lg {
88
+ --rating-size: 1.75rem;
89
+ }
90
+
91
+ @media (forced-colors: active) {
92
+ .rating > input[type="radio"] {
93
+ appearance: auto;
94
+ inline-size: auto;
95
+ block-size: auto;
96
+ background: Canvas;
97
+ -webkit-mask: none;
98
+ mask: none;
99
+ opacity: 1;
100
+ }
101
+ }
@@ -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
+ }
@@ -0,0 +1,122 @@
1
+ /*
2
+ * Steps — semantic ordered-list progression. One intrinsic layout: items fill
3
+ * the row when they fit and hold a minimum width otherwise, so the sequence
4
+ * scrolls horizontally only when it genuinely cannot fit. No breakpoint decides
5
+ * this — the item minimum tracks the real content, not an arbitrary width.
6
+ *
7
+ * Markers come from the list counter; state stays in .complete and
8
+ * aria-current="step", so the component needs no runtime.
9
+ *
10
+ * Public hooks:
11
+ * --step-size
12
+ * --step-min
13
+ * --step-gap
14
+ * --step-line-size
15
+ * --step-marker-radius
16
+ * --step-complete-mark
17
+ * --step-connector
18
+ *
19
+ * Internal:
20
+ * --step-marker-bg
21
+ * --step-marker-border
22
+ * --step-marker-fg
23
+ * --step-line
24
+ */
25
+ .steps {
26
+ --step-size: 1.75rem;
27
+ --step-min: 7rem;
28
+ --step-gap: var(--space-20);
29
+ --step-line-size: var(--border-width);
30
+ --step-marker-bg: var(--surface);
31
+ --step-marker-border: var(--border);
32
+ --step-marker-fg: var(--text-muted);
33
+ --step-marker-radius: var(--radius-full);
34
+ --step-line: var(--border);
35
+
36
+ counter-reset: actual-step;
37
+ display: flex;
38
+ inline-size: 100%;
39
+ margin: 0;
40
+ padding: 0;
41
+ list-style: none;
42
+ /* Fill when it fits, scroll when it cannot: items never shrink below
43
+ --step-min, so labels keep a readable width and the sequence scrolls
44
+ horizontally instead of squishing. overscroll-behavior keeps the scroll
45
+ from chaining to the page. */
46
+ overflow-x: auto;
47
+ overscroll-behavior-inline: contain;
48
+ }
49
+
50
+ .steps > li {
51
+ position: relative;
52
+ display: grid;
53
+ flex: 1 0 var(--step-min);
54
+ grid-template-rows: var(--step-size) auto;
55
+ gap: var(--step-gap);
56
+ justify-items: center;
57
+ min-inline-size: max(var(--step-size), var(--step-min));
58
+ color: var(--text-muted);
59
+ text-align: center;
60
+ counter-increment: actual-step;
61
+ }
62
+
63
+ .steps > li::before {
64
+ content: counter(actual-step);
65
+ position: relative;
66
+ z-index: 1;
67
+ display: inline-grid;
68
+ place-items: center;
69
+ inline-size: var(--step-size);
70
+ block-size: var(--step-size);
71
+ border: var(--border-width) solid var(--step-marker-border);
72
+ border-radius: var(--step-marker-radius);
73
+ background: var(--step-marker-bg);
74
+ color: var(--step-marker-fg);
75
+ font-size: var(--font-size-sm);
76
+ font-weight: var(--font-weight-strong);
77
+ line-height: 1;
78
+ }
79
+
80
+ .steps > li::after {
81
+ content: "";
82
+ position: absolute;
83
+ inset-block-start: calc((var(--step-size) - var(--step-line-size)) / 2);
84
+ inset-inline-start: calc(50% + var(--step-size) / 2);
85
+ inline-size: calc(100% - var(--step-size));
86
+ block-size: var(--step-line-size);
87
+ /* A custom property resolves its var() before inheritance, so a default
88
+ --step-connector: var(--step-line) on .steps would freeze the parent line
89
+ color and a .complete child's connector would never follow its own line.
90
+ Falling back to the local --step-line here keeps each connector in sync;
91
+ an explicit --step-connector still wins when the author sets one. */
92
+ background: var(--step-connector, var(--step-line));
93
+ }
94
+
95
+ .steps > li:last-child::after {
96
+ content: none;
97
+ }
98
+
99
+ .steps > :is(.complete, [aria-current="step"]) {
100
+ --step-marker-bg: var(--state-selected);
101
+ --step-marker-border: var(--state-selected);
102
+ --step-marker-fg: var(--state-selected-fg);
103
+
104
+ color: var(--text);
105
+ }
106
+
107
+ /* Thicker border is the selected affordance in every mode — structural, and it
108
+ never competes with the outline channel owned by focus. */
109
+ .steps > :is(.complete, [aria-current="step"])::before {
110
+ border-width: calc(var(--border-width) * 2);
111
+ }
112
+
113
+ .steps > .complete {
114
+ --step-line: var(--state-selected);
115
+ }
116
+
117
+ /* A finished step may replace its number with a mark (e.g. a check glyph):
118
+ --step-complete-mark accepts any content value and falls back to the
119
+ counter, so existing numbered steppers are unchanged. */
120
+ .steps > .complete::before {
121
+ content: var(--step-complete-mark, counter(actual-step));
122
+ }
@@ -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
+ }
@@ -33,13 +33,14 @@
33
33
  }
34
34
 
35
35
  .tab {
36
+ /* Positioning context for the selected indicator pseudo. */
37
+ position: relative;
36
38
  display: inline-flex;
37
39
  align-items: center;
38
40
  gap: var(--tab-gap, 0.375em);
39
41
  min-block-size: var(--control-size);
40
42
  padding-inline: var(--space-30);
41
43
  border: 0;
42
- border-block-end: 2px solid transparent;
43
44
  border-radius: 0;
44
45
  background: transparent;
45
46
  color: var(--text-muted);
@@ -51,8 +52,6 @@
51
52
 
52
53
  .tabs[aria-orientation="vertical"] .tab {
53
54
  justify-content: flex-start;
54
- border-block-end: 0;
55
- border-inline-end: 2px solid transparent;
56
55
  padding-inline-end: var(--space-40);
57
56
  }
58
57
 
@@ -73,28 +72,27 @@
73
72
  }
74
73
 
75
74
  .tab[aria-selected="true"] {
76
- border-block-end-color: var(--intent, var(--state-selected));
77
75
  color: var(--intent, var(--state-selected));
76
+ /* Second, layout-free selected cue — the underline alone is color-only in
77
+ forced colors. */
78
+ font-weight: var(--font-weight-bold);
78
79
  }
79
80
 
80
- .tabs[aria-orientation="vertical"] .tab[aria-selected="true"] {
81
- border-block-end-color: transparent;
82
- border-inline-end-color: var(--intent, var(--state-selected));
81
+ /* The selected indicator lives on a positioned pseudo so that only the active
82
+ tab owns an underline: a transparent border on every tab would surface in
83
+ forced colors and erase the distinction. currentColor follows the palette the
84
+ UA forces, so no forced-colors block is needed. */
85
+ .tab[aria-selected="true"]::after {
86
+ content: "";
87
+ position: absolute;
88
+ inset-inline: 0;
89
+ inset-block-end: 0;
90
+ border-block-end: 2px solid currentColor;
83
91
  }
84
92
 
85
- @media (forced-colors: active) {
86
- .tab:hover:not(:where(:disabled, [aria-disabled="true"])) {
87
- text-decoration: underline;
88
- text-underline-offset: 0.18em;
89
- }
90
-
91
- .tab[aria-selected="true"] {
92
- border-block-end-color: Highlight;
93
- color: Highlight;
94
- }
95
-
96
- .tabs[aria-orientation="vertical"] .tab[aria-selected="true"] {
97
- border-block-end-color: transparent;
98
- border-inline-end-color: Highlight;
99
- }
93
+ .tabs[aria-orientation="vertical"] .tab[aria-selected="true"]::after {
94
+ inset-block: 0;
95
+ inset-inline: auto 0;
96
+ border-block-end: 0;
97
+ border-inline-end: 2px solid currentColor;
100
98
  }
@@ -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
+ }
@@ -24,6 +24,7 @@
24
24
  max-inline-size: min(24rem, calc(var(--viewport-inline) - 2rem));
25
25
  padding: 0.25em 0.6em;
26
26
  border-radius: var(--radius-sm);
27
+ border: var(--border-width) solid transparent;
27
28
  background: var(--tooltip-bg);
28
29
  color: var(--tooltip-fg);
29
30
  font-size: var(--font-size-xs);
@@ -102,13 +103,8 @@
102
103
  clip-path: polygon(0 50%, 100% 0, 100% 100%);
103
104
  }
104
105
 
105
- @media (forced-colors: active) {
106
+ @media print {
106
107
  .tooltip {
107
- --tooltip-bg: Canvas;
108
- --tooltip-fg: CanvasText;
109
- }
110
-
111
- .tooltip::before {
112
- display: none;
108
+ display: none !important;
113
109
  }
114
110
  }
@@ -15,12 +15,4 @@
15
15
  :focus:not(:focus-visible) {
16
16
  outline: none;
17
17
  }
18
-
19
- /* Pointer focus must still signal in high-contrast mode, where the shared
20
- box-shadow rings are disabled (--focus-ring-shadow: none). */
21
- @media (forced-colors: active) {
22
- :focus:not(:focus-visible) {
23
- outline: var(--focus-outline);
24
- }
25
- }
26
18
  }
@@ -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
+ }