actual-css 0.2.0 → 0.3.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 (214) hide show
  1. package/CHANGELOG.md +38 -1
  2. package/README.md +24 -9
  3. package/dist/actual-themes.min.css +1 -0
  4. package/dist/actual.css +1244 -915
  5. package/dist/actual.js +1 -3
  6. package/dist/actual.min.css +1 -1
  7. package/dist/optional.css +1048 -0
  8. package/dist/optional.min.css +1 -0
  9. package/docs/design-notes/browser-support.md +94 -14
  10. package/docs/design-notes/cascade-layer.md +1 -1
  11. package/docs/design-notes/enhancement-contract.md +11 -14
  12. package/docs/design-notes/forced-colors.md +105 -0
  13. package/docs/design-notes/naming.md +2 -1
  14. package/docs/design-notes/platform-alignment.md +492 -0
  15. package/docs/design-notes/references.md +8 -0
  16. package/docs/design-notes/widget-primitives.md +37 -1
  17. package/docs/navigation.json +135 -0
  18. package/docs/pages/components/accordion.md +80 -0
  19. package/docs/pages/components/alert.md +203 -0
  20. package/docs/pages/components/aura.md +35 -0
  21. package/docs/pages/components/avatar.md +95 -0
  22. package/docs/pages/components/badge.md +102 -0
  23. package/docs/pages/components/breadcrumb.md +19 -0
  24. package/docs/pages/components/button.md +205 -0
  25. package/docs/pages/components/card.md +183 -0
  26. package/docs/pages/components/chat.md +50 -0
  27. package/docs/pages/components/dialog.md +367 -0
  28. package/docs/pages/components/drawer.md +113 -0
  29. package/docs/pages/components/fab.md +118 -0
  30. package/docs/pages/components/key.md +33 -0
  31. package/docs/pages/components/meter.md +23 -0
  32. package/docs/pages/components/navbar.md +92 -0
  33. package/docs/pages/components/pagination.md +55 -0
  34. package/docs/pages/components/progress.md +29 -0
  35. package/docs/pages/components/skeleton.md +38 -0
  36. package/docs/pages/components/spinner.md +93 -0
  37. package/docs/pages/components/table.md +102 -0
  38. package/docs/pages/enhancements/flyout.md +391 -0
  39. package/docs/pages/enhancements/overview.md +36 -0
  40. package/docs/pages/enhancements/scrollspy.md +71 -0
  41. package/docs/pages/enhancements/status-bar.md +72 -0
  42. package/docs/pages/enhancements/tabs.md +80 -0
  43. package/docs/pages/enhancements/tooltip.md +130 -0
  44. package/docs/pages/examples/overview.md +41 -0
  45. package/docs/pages/forms/choice-cards.md +102 -0
  46. package/docs/pages/forms/controls.md +32 -0
  47. package/docs/pages/forms/detached-actions.md +59 -0
  48. package/docs/pages/forms/floating-field.md +88 -0
  49. package/docs/pages/forms/form-actions.md +56 -0
  50. package/docs/pages/forms/input-policy.md +91 -0
  51. package/docs/pages/forms/joined-controls.md +37 -0
  52. package/docs/pages/forms/labels.md +42 -0
  53. package/docs/pages/forms/otp.md +50 -0
  54. package/docs/pages/forms/overview.md +188 -0
  55. package/docs/pages/forms/password.md +40 -0
  56. package/docs/pages/forms/range.md +17 -0
  57. package/docs/pages/forms/states-and-sizes.md +321 -0
  58. package/docs/pages/forms/sticky-actions.md +187 -0
  59. package/docs/pages/forms/switch.md +31 -0
  60. package/docs/pages/forms/validation.md +241 -0
  61. package/docs/{mental-model.md → pages/foundations/philosophy.md} +33 -18
  62. package/docs/pages/foundations/theming.md +92 -0
  63. package/docs/pages/foundations/tokens.md +421 -0
  64. package/docs/{typography.md → pages/foundations/typography.md} +27 -9
  65. package/docs/pages/guides/bootstrap.md +438 -0
  66. package/docs/pages/guides/introduction.md +157 -0
  67. package/docs/pages/guides/optional-components.md +33 -0
  68. package/docs/pages/guides/progressive-enhancement.md +686 -0
  69. package/docs/pages/guides/tailwind.md +455 -0
  70. package/docs/pages/layout/app-shell.md +25 -0
  71. package/docs/pages/layout/center.md +29 -0
  72. package/docs/pages/layout/cluster.md +57 -0
  73. package/docs/pages/layout/frame.md +30 -0
  74. package/docs/pages/layout/grid.md +142 -0
  75. package/docs/pages/layout/list.md +41 -0
  76. package/docs/pages/layout/media.md +21 -0
  77. package/docs/pages/layout/overview.md +37 -0
  78. package/docs/pages/layout/scroll-snap.md +54 -0
  79. package/docs/pages/layout/sidebar-layout.md +27 -0
  80. package/docs/pages/layout/stack.md +18 -0
  81. package/docs/pages/layout/switcher.md +36 -0
  82. package/docs/pages/layout/topbar.md +23 -0
  83. package/docs/pages/patterns/actions.md +52 -0
  84. package/docs/pages/patterns/code-block.md +36 -0
  85. package/docs/pages/patterns/exclusive-accordion.md +16 -0
  86. package/docs/pages/patterns/hierarchical-nav.md +26 -0
  87. package/docs/pages/patterns/icon-input.md +25 -0
  88. package/docs/pages/patterns/intent-nesting.md +3 -0
  89. package/docs/pages/patterns/navigation-list.md +235 -0
  90. package/docs/pages/patterns/overview.md +58 -0
  91. package/docs/pages/patterns/structured-lists.md +47 -0
  92. package/docs/pages/utilities/accessibility.md +34 -0
  93. package/docs/pages/utilities/flex.md +105 -0
  94. package/docs/pages/utilities/links.md +18 -0
  95. package/docs/pages/utilities/list.md +31 -0
  96. package/docs/pages/utilities/overflow.md +43 -0
  97. package/docs/pages/utilities/overview.md +18 -0
  98. package/docs/pages/utilities/shape.md +25 -0
  99. package/docs/pages/utilities/sizing-wrapping.md +115 -0
  100. package/docs/pages/utilities/spacing.md +79 -0
  101. package/docs/pages/utilities/text.md +43 -0
  102. package/llms.txt +4 -4
  103. package/package.json +20 -12
  104. package/src/css/actual.css +12 -1
  105. package/src/css/base.css +3 -5
  106. package/src/css/components/accordion.css +18 -30
  107. package/src/css/components/actions.css +4 -1
  108. package/src/css/components/alert.css +126 -99
  109. package/src/css/components/avatar.css +16 -10
  110. package/src/css/components/badge.css +55 -35
  111. package/src/css/components/breadcrumb.css +7 -15
  112. package/src/css/components/busy.css +6 -0
  113. package/src/css/components/button.css +78 -32
  114. package/src/css/components/card.css +17 -37
  115. package/src/css/components/drawer.css +30 -13
  116. package/src/css/components/flyout.css +39 -109
  117. package/src/css/components/index.css +1 -0
  118. package/src/css/components/join.css +14 -2
  119. package/src/css/components/key.css +2 -0
  120. package/src/css/components/menu.css +125 -0
  121. package/src/css/components/meter.css +0 -23
  122. package/src/css/components/modal.css +162 -7
  123. package/src/css/components/navbar.css +17 -18
  124. package/src/css/components/overline.css +0 -7
  125. package/src/css/components/pagination.css +1 -1
  126. package/src/css/components/progress.css +8 -26
  127. package/src/css/components/skeleton.css +7 -0
  128. package/src/css/components/spinner.css +6 -7
  129. package/src/css/components/status-bar.css +15 -24
  130. package/src/css/components/surface.css +2 -8
  131. package/src/css/components/tab.css +19 -48
  132. package/src/css/components/table.css +7 -5
  133. package/src/css/components/tooltip.css +14 -7
  134. package/src/css/focus.css +14 -38
  135. package/src/css/forms/choice-card.css +13 -14
  136. package/src/css/forms/choice.css +21 -27
  137. package/src/css/forms/control.css +16 -17
  138. package/src/css/forms/custom-select.css +9 -11
  139. package/src/css/forms/form-actions.css +14 -3
  140. package/src/css/forms/form.css +7 -4
  141. package/src/css/forms/index.css +4 -4
  142. package/src/css/forms/input-icon.css +30 -12
  143. package/src/css/forms/native.css +50 -11
  144. package/src/css/forms/range.css +17 -9
  145. package/src/css/forms/select.css +8 -8
  146. package/src/css/forms/switch.css +14 -12
  147. package/src/css/forms/validation.css +32 -5
  148. package/src/css/grid.css +19 -18
  149. package/src/css/intents.css +2 -2
  150. package/src/css/layout.css +29 -1
  151. package/src/css/optional/aura.css +93 -0
  152. package/src/css/optional/chat.css +105 -0
  153. package/src/css/optional/fab.css +104 -0
  154. package/src/css/optional/floating-field.css +87 -0
  155. package/src/css/optional/index.css +5 -0
  156. package/src/css/optional/layout-extra.css +7 -4
  157. package/src/css/optional/otp.css +115 -0
  158. package/src/css/optional/scroll-snap.css +6 -0
  159. package/src/css/optional/scroller.css +13 -7
  160. package/src/css/optional/typography-fluid.css +5 -0
  161. package/src/css/optional/utilities-extra.css +17 -2
  162. package/src/css/prose.css +18 -8
  163. package/src/css/reset.css +0 -10
  164. package/src/css/theme.css +21 -5
  165. package/src/css/themes/cyberpunk.css +1 -1
  166. package/src/css/themes/dim.css +3 -3
  167. package/src/css/themes/edge.css +8 -10
  168. package/src/css/themes/index.css +3 -3
  169. package/src/css/themes/{dark.css → indigo.css} +5 -4
  170. package/src/css/themes/neon.css +5 -5
  171. package/src/css/themes/square.css +0 -3
  172. package/src/css/tokens.css +74 -20
  173. package/src/css/utilities.css +8 -10
  174. package/src/css/variants.css +37 -65
  175. package/src/js/command.js +1 -3
  176. package/src/js/context-menu.js +67 -35
  177. package/src/js/dialog.js +68 -79
  178. package/src/js/dismiss.js +15 -0
  179. package/src/js/enhance.js +54 -23
  180. package/src/js/enhancement-loader.js +301 -0
  181. package/src/js/events.js +3 -0
  182. package/src/js/filter.js +3 -3
  183. package/src/js/floating.js +101 -131
  184. package/src/js/flyout.js +104 -87
  185. package/src/js/focus-group.js +116 -0
  186. package/src/js/focus.js +6 -1
  187. package/src/js/index.js +4 -2
  188. package/src/js/input.js +1 -1
  189. package/src/js/keys.js +27 -0
  190. package/src/js/mask.js +7 -6
  191. package/src/js/menu.js +87 -34
  192. package/src/js/parse-config.js +47 -0
  193. package/src/js/password.js +9 -1
  194. package/src/js/scrollspy.js +12 -4
  195. package/src/js/selectors.js +0 -7
  196. package/src/js/status.js +3 -1
  197. package/src/js/surface.js +185 -136
  198. package/src/js/tab.js +36 -53
  199. package/src/js/tooltip.js +53 -31
  200. package/src/js/validation.js +34 -8
  201. package/dist/actual.js.map +0 -31
  202. package/docs/README.md +0 -28
  203. package/docs/components.md +0 -860
  204. package/docs/design-notes/previous-work.md +0 -5
  205. package/docs/forms.md +0 -1265
  206. package/docs/javascript.md +0 -496
  207. package/docs/layout.md +0 -480
  208. package/docs/patterns.md +0 -504
  209. package/docs/reserved.md +0 -7
  210. package/docs/tokens.md +0 -455
  211. package/docs/ui.md +0 -929
  212. package/docs/utilities.md +0 -384
  213. package/src/css/dialog-fallback.css +0 -56
  214. package/src/js/dialog-fallback.js +0 -163
@@ -9,19 +9,35 @@
9
9
  * Disabled state uses the native disabled attribute for buttons and inputs.
10
10
  * Anchors have no native disabled state, so a.btn uses aria-disabled="true"
11
11
  * for disabled styling and activation guards.
12
+ *
13
+ * Public hooks:
14
+ * --btn-font-size
15
+ * --btn-min-size
16
+ * --btn-pad-x
17
+ * --btn-radius
18
+ * --btn-focus-color
19
+ * --btn-gap
20
+ * --btn-hover-overlay
21
+ *
22
+ * Internal:
23
+ * --btn-bg
24
+ * --btn-fg
25
+ * --btn-border
26
+ * --btn-focus-ring-color
27
+ * --btn-border-block-end-color
28
+ * --btn-border-block-end-width
29
+ * --spinner-width
12
30
  */
13
31
 
14
32
  /* Intent boundary — inherited intents do not tint this component.
15
33
  Local intent classes still win because :where() has zero specificity. */
16
34
  :where(.btn) {
17
- /* @sync intent-boundary */
18
35
  --intent: initial;
19
36
  --intent-fg: initial;
20
37
  --ui-bg: initial;
21
38
  --ui-fg: initial;
22
39
  --ui-border: initial;
23
40
  --ui-hover-bg: initial;
24
- /* @sync intent-boundary */
25
41
  }
26
42
 
27
43
  .btn {
@@ -33,17 +49,17 @@
33
49
  --btn-border: var(--ui-border, var(--btn-default-border, transparent));
34
50
  --btn-radius: var(--radius);
35
51
  --btn-focus-color: var(--intent, var(--focus));
36
- --btn-hover-bg: var(--ui-hover-bg, var(--btn-bg));
37
52
  --btn-hover-overlay: var(--hover-overlay, rgb(0 0 0 / 0.08));
38
53
 
54
+ /* Positioning context for indicators, overlays, and grouped states. */
39
55
  position: relative;
40
56
  display: inline-flex;
41
57
  align-items: center;
42
58
  justify-content: center;
43
- gap: 0.375em;
59
+ gap: var(--btn-gap, 0.375em);
44
60
  min-block-size: var(--btn-min-size);
45
- padding-block: var(--space-2);
46
- padding-block: max(var(--space-1), calc((var(--btn-min-size) - 1lh) / 2));
61
+ padding-block: var(--space-20);
62
+ padding-block: max(var(--space-10), calc((var(--btn-min-size) - 1lh) / 2));
47
63
  padding-inline: var(--btn-pad-x);
48
64
  border: var(--border-width) solid var(--btn-border-color, var(--btn-border));
49
65
  border-block-end-color: var(
@@ -68,6 +84,11 @@
68
84
  transform var(--duration);
69
85
  }
70
86
 
87
+ .btn.icon-only {
88
+ inline-size: var(--btn-min-size);
89
+ padding-inline: 0;
90
+ }
91
+
71
92
  @supports (color: color-mix(in oklch, red, white)) {
72
93
  .btn {
73
94
  --btn-focus-ring-color: color-mix(in oklch, var(--btn-focus-color) 28%, transparent);
@@ -92,13 +113,40 @@
92
113
  --btn-hover-overlay: transparent;
93
114
  }
94
115
 
95
- /* The .soft/.outline/.ghost hover recipe (--ui-hover-bg) is shared with
96
- .badge in variants.css; the overlay darkening for solid/soft is .btn-specific. */
97
- @media (hover: hover) {
98
- .btn:not(:where(:disabled, [aria-disabled="true"])):hover {
99
- background: var(--btn-hover-bg);
100
- box-shadow: inset 0 0 0 999px var(--btn-hover-overlay);
116
+ /* Shared hover recipe — sets --ui-hover-bg consumed by the hover rule below.
117
+ Declared outside :hover: the variables resolve only when the hover rule
118
+ reads them, so the values are inert until interaction. */
119
+ .btn:is(.outline, .ghost) {
120
+ --ui-hover-bg: var(--surface-subtle);
121
+ }
122
+
123
+ @supports (color: color-mix(in oklch, red, white)) {
124
+ .btn:not(:where(.soft, .outline, .ghost, .link)) {
125
+ --ui-hover-bg: color-mix(in oklch, var(--intent, var(--neutral)) 88%, black);
101
126
  }
127
+
128
+ .btn.soft {
129
+ --ui-hover-bg: color-mix(
130
+ in oklch,
131
+ var(--surface) calc(var(--soft-bg-mix) - var(--soft-hover-alpha)),
132
+ var(--intent, var(--neutral))
133
+ );
134
+ }
135
+
136
+ .btn.outline {
137
+ --ui-hover-bg: color-mix(in oklch, var(--surface-subtle) 92%, var(--intent, var(--neutral)));
138
+ }
139
+
140
+ .btn.ghost {
141
+ --ui-hover-bg: color-mix(in oklch, var(--intent, var(--text)) 8%, transparent);
142
+ }
143
+ }
144
+
145
+ /* The .soft/.outline/.ghost hover recipe (--ui-hover-bg) is set locally above;
146
+ the overlay darkening for solid/soft is .btn-specific. */
147
+ .btn:not(:where(:disabled, [aria-disabled="true"])):hover {
148
+ background: var(--ui-hover-bg, var(--btn-bg));
149
+ box-shadow: inset 0 0 0 999px var(--btn-hover-overlay);
102
150
  }
103
151
 
104
152
  .btn:not(:where(:disabled, [aria-disabled="true"])):active {
@@ -123,16 +171,14 @@
123
171
  /* .btn.circle needs no local rule: the .circle utility (utilities.css) loads
124
172
  after components and overrides border-radius at equal specificity. */
125
173
 
126
- @media (hover: hover) {
127
- .btn.link:not(:where(:disabled, [aria-disabled="true"])):hover {
128
- background: transparent;
129
- color: var(--intent, var(--text));
130
- text-decoration-thickness: 0.12em;
131
- }
174
+ .btn.link:not(:where(:disabled, [aria-disabled="true"])):hover {
175
+ background: transparent;
176
+ color: var(--intent, var(--text));
177
+ text-decoration-thickness: 0.12em;
132
178
  }
133
179
 
134
180
  .btn:focus-visible {
135
- outline: none;
181
+ outline: 2px solid transparent;
136
182
  box-shadow: 0 0 0 var(--focus-ring-width) var(--btn-focus-ring-color, var(--focus-ring));
137
183
  }
138
184
 
@@ -166,13 +212,15 @@ a.btn[aria-disabled="true"] {
166
212
  --spinner-width: 0.15em;
167
213
  }
168
214
 
169
- /* Toggle */
170
- /* State must outrank the shared .outline/.ghost hover recipes: a pressed
171
- button keeps its intent background and foreground while hovered. */
172
- .btn:is([aria-pressed="true"], [aria-current]:not([aria-current="false"])):not(.link) {
173
- --btn-bg: var(--intent, var(--primary));
174
- --btn-fg: var(--intent-fg, var(--primary-fg));
175
- --btn-border: var(--intent, var(--primary));
215
+ /* Toggle and current state.
216
+ State must outrank the shared .outline/.ghost hover recipes: a pressed or
217
+ current button keeps its intent background and foreground while hovered.
218
+ aria-current="page" is the pagination current-page marker; aria-pressed
219
+ covers toggle buttons. */
220
+ .btn:is([aria-pressed="true"], [aria-current="page"]):not(.link) {
221
+ --btn-bg: var(--intent, var(--state-selected));
222
+ --btn-fg: var(--intent-fg, var(--state-selected-fg));
223
+ --btn-border: var(--intent, var(--state-selected));
176
224
  --ui-hover-bg: var(--btn-bg);
177
225
  }
178
226
 
@@ -190,18 +238,16 @@ a.btn[aria-disabled="true"] {
190
238
  color: LinkText;
191
239
  }
192
240
 
193
- .btn:is([aria-pressed="true"], [aria-current]:not([aria-current="false"])):not(.link) {
241
+ .btn:is([aria-pressed="true"], [aria-current="page"]):not(.link) {
194
242
  border-color: Highlight;
195
243
  background: Highlight;
196
244
  color: HighlightText;
197
245
  forced-color-adjust: none;
198
246
  }
199
247
 
200
- @media (hover: hover) {
201
- .btn:hover:not(:where(:disabled, [aria-disabled="true"], .link)) {
202
- outline: var(--border-width) solid ButtonText;
203
- outline-offset: 2px;
204
- }
248
+ .btn:hover:not(:where(:disabled, [aria-disabled="true"], .link)) {
249
+ outline: var(--border-width) solid ButtonText;
250
+ outline-offset: 2px;
205
251
  }
206
252
 
207
253
  .btn:disabled,
@@ -4,28 +4,36 @@
4
4
  * Surface, border, radius, padding, density, and elevation stay local here.
5
5
  * Direct header/footer children are structural slots. Page layout remains the
6
6
  * job of markup and layout primitives outside the card.
7
+ *
8
+ * Public hooks:
9
+ * --card-pad
10
+ * --card-radius
11
+ * --card-max-inline-size
12
+ *
13
+ * Child contract:
14
+ * Direct .bleed children escape --card-pad (negative inset, wider media);
15
+ * edge children inherit the card corners. .bleed must be a direct child.
16
+ *
17
+ * Internal:
18
+ * --heading
19
+ * --busy-overlay-bg
7
20
  */
8
21
  /* Intent boundary — inherited intents do not tint this component.
9
22
  Local intent classes still win because :where() has zero specificity. */
10
23
  :where(.card) {
11
- /* @sync intent-boundary */
12
- --intent: initial;
13
- --intent-fg: initial;
14
24
  --ui-bg: initial;
15
25
  --ui-fg: initial;
16
26
  --ui-border: initial;
17
- --ui-hover-bg: initial;
18
- /* @sync intent-boundary */
19
27
  }
20
28
 
21
29
  .card {
22
- --card-pad: var(--space-4);
30
+ --card-pad: var(--space-40);
23
31
  --card-radius: var(--radius-lg);
24
32
  --card-max-inline-size: none;
25
33
 
26
34
  max-inline-size: var(--card-max-inline-size);
27
35
  padding: var(--card-pad);
28
- background: var(--ui-bg, var(--surface));
36
+ background: var(--ui-bg, var(--surface-raised));
29
37
  color: var(--ui-fg, var(--text));
30
38
  border: var(--border-width) solid var(--ui-border, var(--border));
31
39
  border-radius: var(--card-radius);
@@ -40,7 +48,7 @@
40
48
  flex-wrap: wrap;
41
49
  align-items: var(--items-align, center);
42
50
  justify-content: space-between;
43
- gap: var(--space-3);
51
+ gap: var(--space-30);
44
52
  min-inline-size: 0;
45
53
  }
46
54
 
@@ -67,16 +75,8 @@
67
75
  .card.subtle {
68
76
  background: var(--surface-subtle);
69
77
  }
70
- .card.inverted {
71
- background: var(--surface-solid);
72
- color: var(--surface);
73
- border-color: transparent;
74
- --heading: var(--surface);
75
- --busy-overlay-bg: var(--surface-solid);
76
- }
77
-
78
78
  .card.compact {
79
- --card-pad: var(--space-2);
79
+ --card-pad: var(--space-20);
80
80
  }
81
81
 
82
82
  /* Full-bleed children */
@@ -118,23 +118,3 @@
118
118
  .card > :where(header, footer, figure).bleed {
119
119
  padding: var(--card-pad);
120
120
  }
121
-
122
- @media (forced-colors: active) {
123
- .card {
124
- border-color: ButtonText;
125
- background: Canvas;
126
- color: CanvasText;
127
- }
128
-
129
- .card.raised {
130
- box-shadow: none;
131
- }
132
-
133
- .card.inverted {
134
- --heading: CanvasText;
135
-
136
- border-color: ButtonText;
137
- background: Canvas;
138
- color: CanvasText;
139
- }
140
- }
@@ -6,16 +6,30 @@
6
6
  * discrete display/overlay transitions. The drawer is a layout shell only —
7
7
  * navigation content inside uses .nav-link and .accordion, which carry their
8
8
  * own link/summary styling.
9
+ *
10
+ * Public hooks:
11
+ * --drawer-size
12
+ * --drawer-pad
13
+ *
14
+ * Internal:
15
+ * --drawer-closed-translate (side/RTL positioning state)
9
16
  */
10
17
  dialog.drawer {
11
18
  --drawer-size: 20rem;
12
- --drawer-pad: var(--space-4);
19
+ --drawer-pad: var(--space-40);
13
20
  --drawer-closed-translate: -100%;
14
21
 
22
+ /* Keep the surface anchored to the viewport in the top layer. This also
23
+ provides the containing block for the absolutely positioned close button.
24
+ A percentage height resolves against the fixed containing viewport, while
25
+ dynamic viewport units can be stale in embedded previews. */
26
+ position: fixed;
27
+ inset-block-start: 0;
28
+
15
29
  inline-size: min(calc(var(--viewport-inline) * 0.9), var(--drawer-size));
16
- block-size: var(--viewport-block);
30
+ block-size: 100%;
17
31
  max-inline-size: none;
18
- max-block-size: var(--viewport-block);
32
+ max-block-size: 100%;
19
33
  margin: 0;
20
34
  padding: var(--drawer-pad);
21
35
  border: 0;
@@ -34,7 +48,7 @@ dialog.drawer {
34
48
  dialog.drawer[open] {
35
49
  display: grid;
36
50
  grid-template-rows: auto minmax(0, 1fr) auto;
37
- gap: var(--space-4);
51
+ gap: var(--space-40);
38
52
  opacity: 1;
39
53
  transform: translateX(0);
40
54
  }
@@ -105,18 +119,21 @@ dialog.drawer nav {
105
119
  }
106
120
 
107
121
  @media (forced-colors: active) {
108
- dialog.drawer {
109
- border-inline-end-color: ButtonText;
122
+ dialog.drawer::backdrop {
110
123
  background: Canvas;
111
- color: CanvasText;
112
- box-shadow: none;
113
124
  }
125
+ }
114
126
 
115
- dialog.drawer[data-side="end"] {
116
- border-inline-start-color: ButtonText;
127
+ @media (prefers-reduced-motion: no-preference) {
128
+ dialog.drawer.is-static {
129
+ animation: actual-dialog-static var(--duration-slow) var(--ease);
117
130
  }
131
+ }
118
132
 
119
- dialog.drawer::backdrop {
120
- background: Canvas;
121
- }
133
+ /* The close button shares the modal's interior placement (modal.css): absolute
134
+ at the panel's top inline-end, out of the content flow. The header reserves
135
+ inline room so the title never runs under it. */
136
+ dialog.drawer > header,
137
+ dialog.drawer > form > header {
138
+ padding-inline-end: calc(var(--control-size) + var(--space-30));
122
139
  }
@@ -9,7 +9,16 @@
9
9
  * .is-sheet marks "auto" resolved to bottom-sheet presentation on coarse
10
10
  * pointers. Authors opt into mobile behavior with
11
11
  * [data-flyout-mobile="sheet|auto|none|anchored"].
12
- * --flyout-trigger-width is set by surface.js while an open flyout is positioned.
12
+ * --surface-anchor-width is set by surface.js while an open flyout is positioned.
13
+ *
14
+ * Public hooks:
15
+ * --flyout-inline-size
16
+ * --flyout-max-inline-size
17
+ *
18
+ * Internal:
19
+ * --menu-item-size (derived from --control-size for menu rows)
20
+ * --available-height (measured by floating.js)
21
+ * --surface-anchor-width (measured by surface.js)
13
22
  */
14
23
 
15
24
  .flyout-trigger {
@@ -32,28 +41,32 @@
32
41
  }
33
42
 
34
43
  .flyout {
44
+ --menu-item-size: var(--control-size);
35
45
  z-index: var(--z-menu);
36
46
  /* No-JS fallback: below the trigger inside the relative .flyout-trigger.
37
47
  surface.js switches an open flyout to position: fixed with measured
38
48
  coordinates; this only covers a flyout revealed without the runtime. */
39
49
  position: absolute;
40
- inset-block-start: calc(100% + var(--space-1));
50
+ inset-block-start: calc(100% + var(--space-10));
41
51
  inset-inline-start: 0;
42
52
  display: grid;
43
- gap: var(--space-1);
53
+ gap: var(--space-10);
44
54
  inline-size: var(--flyout-inline-size, max-content);
45
55
  min-inline-size: min(
46
- var(--flyout-trigger-width, 0px),
47
- min(var(--flyout-max-inline-size, 20rem), calc(var(--viewport-inline) - var(--space-6)))
56
+ var(--surface-anchor-width, 0px),
57
+ min(var(--flyout-max-inline-size, 20rem), calc(var(--viewport-inline) - var(--space-60)))
48
58
  );
49
59
  max-inline-size: min(
50
60
  var(--flyout-max-inline-size, 20rem),
51
- calc(var(--viewport-inline) - var(--space-6))
61
+ calc(var(--viewport-inline) - var(--space-60))
62
+ );
63
+ max-block-size: min(
64
+ 24rem,
65
+ var(--available-height, calc(var(--viewport-block) - var(--space-60)))
52
66
  );
53
- max-block-size: min(24rem, var(--available-height, calc(var(--viewport-block) - var(--space-6))));
54
67
  overflow: auto;
55
68
  margin: 0;
56
- padding: var(--space-1);
69
+ padding: var(--space-10);
57
70
  border: var(--border-width) solid var(--border);
58
71
  border-radius: var(--radius);
59
72
  background: var(--surface-raised);
@@ -62,9 +75,9 @@
62
75
  opacity: 1;
63
76
  scale: 1;
64
77
  transition:
65
- opacity var(--duration-fast, var(--duration)) var(--ease, ease),
66
- scale var(--duration-fast, var(--duration)) var(--ease, ease),
67
- translate var(--duration-fast, var(--duration)) var(--ease, ease);
78
+ opacity var(--duration-fast) var(--ease),
79
+ scale var(--duration-fast) var(--ease),
80
+ translate var(--duration-fast) var(--ease);
68
81
  }
69
82
 
70
83
  .flyout.is-open:not(.is-sheet) {
@@ -81,6 +94,7 @@
81
94
  /* Bottom-sheet presentation. surface.js toggles .is-sheet when the flyout is
82
95
  opened on coarse pointer / narrow viewports. */
83
96
  .flyout.is-sheet {
97
+ --menu-item-size: var(--control-size-lg);
84
98
  position: fixed;
85
99
  inset-inline: 0;
86
100
  inset-block-start: auto;
@@ -91,8 +105,8 @@
91
105
  max-block-size: min(calc(var(--viewport-block) * 0.75), 32rem);
92
106
  overflow: auto;
93
107
  grid-template-columns: 1fr;
94
- padding: max(var(--space-3), env(safe-area-inset-top)) var(--space-3)
95
- max(var(--space-3), env(safe-area-inset-bottom));
108
+ padding: max(var(--space-30), env(safe-area-inset-top)) var(--space-30)
109
+ max(var(--space-30), env(safe-area-inset-bottom));
96
110
  border-end-start-radius: 0;
97
111
  border-end-end-radius: 0;
98
112
  }
@@ -105,23 +119,23 @@
105
119
 
106
120
  .flyout.is-sheet[hidden] {
107
121
  scale: 1;
108
- translate: 0 var(--space-3);
122
+ translate: 0 var(--space-30);
109
123
  }
110
124
 
111
125
  @starting-style {
112
126
  .flyout.is-sheet.is-open {
113
127
  scale: 1;
114
- translate: 0 var(--space-3);
128
+ translate: 0 var(--space-30);
115
129
  }
116
130
  }
117
131
 
118
132
  @supports (transition-behavior: allow-discrete) {
119
133
  .flyout {
120
134
  transition:
121
- opacity var(--duration-fast, var(--duration)) var(--ease, ease),
122
- scale var(--duration-fast, var(--duration)) var(--ease, ease),
123
- translate var(--duration-fast, var(--duration)) var(--ease, ease),
124
- display var(--duration-fast, var(--duration)) allow-discrete;
135
+ opacity var(--duration-fast) var(--ease),
136
+ scale var(--duration-fast) var(--ease),
137
+ translate var(--duration-fast) var(--ease),
138
+ display var(--duration-fast) allow-discrete;
125
139
  }
126
140
  }
127
141
 
@@ -131,7 +145,7 @@
131
145
 
132
146
  .flyout > :where(section, footer) {
133
147
  min-inline-size: 0;
134
- padding: var(--space-2);
148
+ padding: var(--space-20);
135
149
  }
136
150
 
137
151
  .flyout > section > :where(h1, h2, h3, h4, h5, h6) {
@@ -143,8 +157,8 @@
143
157
 
144
158
  .flyout > section > ul {
145
159
  display: grid;
146
- gap: var(--space-1);
147
- margin: var(--space-1) 0 0;
160
+ gap: var(--space-10);
161
+ margin: var(--space-10) 0 0;
148
162
  padding: 0;
149
163
  list-style: none;
150
164
  }
@@ -153,90 +167,6 @@
153
167
  margin: 0;
154
168
  }
155
169
 
156
- /* Item contract: a button, link, or menuitem placed directly in the flyout
157
- or directly in any of its <li> (bare list, or grouped section > ul > li).
158
- menu.js mirrors this contract in MENU_ITEM_SELECTOR — keep them in sync.
159
- :where(li) keeps specificity at a single class (0,1,0) so consumer
160
- overrides behave the same as a plain .flyout > rule. */
161
- :is(.flyout, .flyout :where(li)) > :where(button, a, [role="menuitem"]) {
162
- display: flex;
163
- align-items: center;
164
- justify-content: flex-start;
165
- gap: var(--space-2);
166
- inline-size: 100%;
167
- min-block-size: var(--control-size);
168
- padding-inline: var(--space-2);
169
- border: 0;
170
- border-radius: var(--radius-sm);
171
- background: none;
172
- color: var(--text);
173
- font: inherit;
174
- font-weight: var(--font-weight);
175
- text-decoration: none;
176
- text-align: start;
177
- white-space: nowrap;
178
- cursor: pointer;
179
- }
180
-
181
- :is(.flyout.is-sheet, .flyout.is-sheet :where(li)) > :where(button, a, [role="menuitem"]) {
182
- min-block-size: var(--control-size-lg);
183
- }
184
-
185
- :is(.flyout, .flyout :where(li)) > :where(button, a, [role="menuitem"]):focus-visible {
186
- background: var(--surface-subtle);
187
- outline: var(--focus-outline);
188
- outline-offset: var(--focus-outline-offset);
189
- }
190
-
191
- :is(.flyout, .flyout :where(li))
192
- > :where(button, a, [role="menuitem"]):is(:disabled, [aria-disabled="true"]) {
193
- cursor: not-allowed;
194
- opacity: var(--disabled-opacity);
195
- }
196
-
197
- :is(.flyout, .flyout :where(li)) > :where(a, [role="menuitem"])[aria-disabled="true"] {
198
- pointer-events: none;
199
- }
200
-
201
- @media (hover: hover) {
202
- :is(.flyout, .flyout :where(li))
203
- > :where(button, a, [role="menuitem"]):hover:not(
204
- :where(:focus-visible, :disabled, [aria-disabled="true"])
205
- ) {
206
- background: var(--surface-subtle);
207
- outline: none;
208
- }
209
- }
210
-
211
- .flyout > hr,
212
- .flyout > li > hr {
213
- inline-size: 100%;
214
- margin: 0;
215
- margin-block: var(--space-1);
216
- border: 0;
217
- border-block-start: var(--border-width) solid var(--border);
218
- }
219
-
220
- @media (forced-colors: active) {
221
- .flyout {
222
- border-color: ButtonText;
223
- background: Canvas;
224
- color: CanvasText;
225
- box-shadow: none;
226
- }
227
-
228
- :is(.flyout, .flyout :where(li)) > :where(button, a, [role="menuitem"]):focus-visible {
229
- background: Canvas;
230
- color: CanvasText;
231
- }
232
-
233
- @media (hover: hover) {
234
- :is(.flyout, .flyout :where(li))
235
- > :where(button, a, [role="menuitem"]):hover:not(:where(:disabled, [aria-disabled="true"])) {
236
- outline: var(--border-width) solid ButtonText;
237
- outline-offset: -1px;
238
- background: Canvas;
239
- color: CanvasText;
240
- }
241
- }
242
- }
170
+ /* Vertical dividers — .menu-separator (menu.css) is the canonical way to
171
+ separate menu items. Flyout panels that are not menus use auto grid gap
172
+ declared on .flyout itself. */
@@ -12,6 +12,7 @@
12
12
 
13
13
  /* Overlays */
14
14
  @import "flyout.css";
15
+ @import "menu.css";
15
16
  @import "tooltip.css";
16
17
 
17
18
  /* Disclosure */
@@ -7,6 +7,18 @@
7
7
  * Cascade note: .join writes border-radius longhands on its direct children.
8
8
  * Keep this file loaded after control/component styles so those corners win at
9
9
  * equal specificity.
10
+ *
11
+ * Public hooks:
12
+ * --join-radius
13
+ *
14
+ * Internal:
15
+ * --join-ss
16
+ * --join-se
17
+ * --join-es
18
+ * --join-ee
19
+ * --control-border
20
+ * --control-bg
21
+ * --control-fg
10
22
  */
11
23
  .join {
12
24
  --join-radius: var(--radius);
@@ -34,7 +46,7 @@
34
46
  display: inline-flex;
35
47
  justify-content: center;
36
48
  min-block-size: var(--control-size);
37
- padding-inline: var(--space-3);
49
+ padding-inline: var(--space-30);
38
50
  border: var(--border-width) solid var(--control-border);
39
51
  border-radius: var(--radius);
40
52
  background: var(--control-bg);
@@ -95,7 +107,7 @@
95
107
  focus, and unsupported :has() browsers retain the control's normal ring. */
96
108
  .join:has(> :is(.input, .textarea, .select):focus-visible) {
97
109
  border-radius: var(--join-radius);
98
- outline: none;
110
+ outline: 2px solid transparent;
99
111
  box-shadow: var(--focus-ring-shadow);
100
112
  }
101
113
 
@@ -3,6 +3,8 @@
3
3
  *
4
4
  * Use .key outside prose. Plain kbd inside .prose gets the same visual
5
5
  * treatment from prose.css so authored content stays convenient.
6
+ *
7
+ * Visual twin of .prose kbd (prose.css) — keep the two recipes in sync.
6
8
  */
7
9
  .key {
8
10
  display: inline-flex;