@flamingo-stack/openframe-frontend-core 0.0.781 → 0.0.782

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 (215) hide show
  1. package/dist/case-study-card-WYZPBDDM.cjs +23 -0
  2. package/dist/{chunk-D46WFWWI.cjs → chunk-2EXD2RSC.cjs} +41 -39
  3. package/dist/{chunk-RJX33Y4Q.cjs → chunk-3INHO6GP.cjs} +22 -22
  4. package/dist/{chunk-JHDPEYZH.cjs → chunk-4DKPNN2A.cjs} +17 -17
  5. package/dist/{chunk-L4XOCV7S.cjs → chunk-4DQ4CNAG.cjs} +10 -10
  6. package/dist/{chunk-HTSRCFXP.cjs → chunk-4MQASMIU.cjs} +7 -7
  7. package/dist/{chunk-3VINTJPE.cjs → chunk-5WMRVMXH.cjs} +5 -5
  8. package/dist/{chunk-OHXB6HUX.cjs → chunk-5XAWVXNI.cjs} +320 -320
  9. package/dist/{chunk-VDTLVFCV.cjs → chunk-7DKDVJMR.cjs} +13 -13
  10. package/dist/{chunk-FWJ5YECN.cjs → chunk-7OJMOG34.cjs} +546 -544
  11. package/dist/{chunk-63OCO2G5.cjs → chunk-A3UZFFR3.cjs} +4 -4
  12. package/dist/{chunk-ODZ5XGWA.cjs → chunk-A4YUTS6Q.cjs} +2076 -2052
  13. package/dist/{chunk-T7SDYBST.cjs → chunk-BE4OUWTA.cjs} +54 -54
  14. package/dist/{chunk-YKJ4JN6T.cjs → chunk-C42BXIY7.cjs} +42 -42
  15. package/dist/{chunk-HPGCUYAZ.cjs → chunk-C5UUWXHE.cjs} +110 -110
  16. package/dist/{chunk-ZV6RQNXE.cjs → chunk-CBKXU77H.cjs} +46 -46
  17. package/dist/{chunk-IYU2ZH2J.cjs → chunk-CS5SZJFV.cjs} +2 -2
  18. package/dist/{chunk-Q7HXTF2C.cjs → chunk-DYF6XB27.cjs} +4 -4
  19. package/dist/{chunk-AKCBLZAO.cjs → chunk-E2ZZOYSM.cjs} +74 -62
  20. package/dist/{chunk-57NE7SHG.cjs → chunk-EFCWHAYC.cjs} +8 -8
  21. package/dist/{chunk-KWV23THO.cjs → chunk-EHQ2UGT4.cjs} +14 -14
  22. package/dist/{chunk-DJTPDK3W.cjs → chunk-F2FFJWOQ.cjs} +4 -4
  23. package/dist/{chunk-CDBFQVPA.cjs → chunk-FLS6DZEN.cjs} +46 -43
  24. package/dist/{chunk-VZKAAXU3.cjs → chunk-FS7CBRF4.cjs} +23 -23
  25. package/dist/{chunk-B6EQVH7D.cjs → chunk-GFRKNUJZ.cjs} +51 -51
  26. package/dist/{chunk-PID7LUDP.cjs → chunk-I7LRZR4Q.cjs} +31 -31
  27. package/dist/{chunk-A7FSNBNV.cjs → chunk-ISB7LW3D.cjs} +173 -172
  28. package/dist/{chunk-LDGR5VCK.cjs → chunk-IZKEWQJN.cjs} +4 -4
  29. package/dist/{chunk-X6TXMWBV.cjs → chunk-JGKPEGZN.cjs} +61 -61
  30. package/dist/{chunk-EYNQE2XN.cjs → chunk-LTF6UDGO.cjs} +6 -6
  31. package/dist/{chunk-2OYKOHFK.cjs → chunk-LUWYWYNC.cjs} +2 -2
  32. package/dist/{chunk-5PBWAF2D.cjs → chunk-MDGDL3C7.cjs} +44 -44
  33. package/dist/{chunk-N6YCOBLK.cjs → chunk-MGEUZRNQ.cjs} +101 -9
  34. package/dist/{chunk-YEJWNHD3.cjs → chunk-MKF6XX5A.cjs} +63 -63
  35. package/dist/{chunk-3IHGS44W.cjs → chunk-N2SVJJLU.cjs} +5 -5
  36. package/dist/{chunk-TZ6MTHUC.cjs → chunk-N5TPRCSS.cjs} +60 -60
  37. package/dist/{chunk-HLZUBJPL.cjs → chunk-N7QNWHJA.cjs} +197 -197
  38. package/dist/{chunk-YNDH72VU.cjs → chunk-NNNRN4OF.cjs} +14 -14
  39. package/dist/{chunk-JQWYXC2E.cjs → chunk-ODDUGLYF.cjs} +110 -110
  40. package/dist/{chunk-T4ZVKEDP.cjs → chunk-OMTH355I.cjs} +3 -3
  41. package/dist/{chunk-TH5X2BXK.cjs → chunk-QV4GHAWN.cjs} +115 -115
  42. package/dist/{chunk-DIY4SJZI.cjs → chunk-RT6QATX4.cjs} +19 -19
  43. package/dist/{chunk-UONQCE5N.cjs → chunk-TS6TISIF.cjs} +209 -208
  44. package/dist/{chunk-SMEX3BWO.cjs → chunk-UPQMFK5B.cjs} +7 -7
  45. package/dist/{chunk-XYKVOBIP.cjs → chunk-VH3KQSI2.cjs} +3 -3
  46. package/dist/{chunk-OFX6XEVN.cjs → chunk-VZUV2FCP.cjs} +13 -13
  47. package/dist/{chunk-QD7SKYCQ.cjs → chunk-WSBWP7IW.cjs} +3 -3
  48. package/dist/{chunk-RYIWADIB.cjs → chunk-XAA5LUHC.cjs} +12 -12
  49. package/dist/{chunk-XHJI7R4J.cjs → chunk-YX7X5QQN.cjs} +46 -46
  50. package/dist/{chunk-ZPYFPLPD.cjs → chunk-YXZOP726.cjs} +10 -10
  51. package/dist/{chunk-4XDYSGRL.cjs → chunk-ZFOMRALU.cjs} +17 -17
  52. package/dist/{chunk-AFJEUBDS.cjs → chunk-ZJS45HQZ.cjs} +29 -29
  53. package/dist/{cloud-tenants-UAFVHKX7.cjs → cloud-tenants-LXKEG5N4.cjs} +32 -32
  54. package/dist/components/calendar.cjs +10 -10
  55. package/dist/components/case-studies/index.cjs +52 -52
  56. package/dist/components/category-card.d.ts.map +1 -1
  57. package/dist/components/category-card.js +1 -0
  58. package/dist/components/chat/ask-display.js +2 -2
  59. package/dist/components/chat/chat-context-memory-bar.js +2 -2
  60. package/dist/components/chat/chat-context-picker.d.ts.map +1 -1
  61. package/dist/components/chat/chat-context-picker.js +4 -2
  62. package/dist/components/chat/chat-input.d.ts.map +1 -1
  63. package/dist/components/chat/chat-input.js +2 -1
  64. package/dist/components/chat/chat-nav-sidebar.d.ts.map +1 -1
  65. package/dist/components/chat/chat-nav-sidebar.js +12 -1
  66. package/dist/components/chat/guide-mode-banner.js +1 -1
  67. package/dist/components/chat/index.cjs +26 -26
  68. package/dist/components/chat/mingo-welcome.js +1 -1
  69. package/dist/components/contact/index.cjs +33 -33
  70. package/dist/components/docs/index.cjs +36 -36
  71. package/dist/components/embeds/index.cjs +33 -33
  72. package/dist/components/faq/index.cjs +34 -34
  73. package/dist/components/features/capability-explorer.d.ts.map +1 -1
  74. package/dist/components/features/capability-explorer.js +11 -1
  75. package/dist/components/features/command-box.js +1 -1
  76. package/dist/components/features/devices/devices-grid-filters.d.ts.map +1 -1
  77. package/dist/components/features/devices/devices-grid-filters.js +15 -4
  78. package/dist/components/features/filters-dropdown.d.ts.map +1 -1
  79. package/dist/components/features/filters-dropdown.js +6 -5
  80. package/dist/components/features/index.cjs +32 -32
  81. package/dist/components/features/model-token-rates.js +1 -1
  82. package/dist/components/features/notifications/notification-drawer.d.ts.map +1 -1
  83. package/dist/components/features/notifications/notification-drawer.js +1 -0
  84. package/dist/components/features/notifications/notification-tile.js +1 -1
  85. package/dist/components/features/paths-display.js +1 -1
  86. package/dist/components/features/remote-session/remote-session-events.d.ts.map +1 -1
  87. package/dist/components/features/remote-session/remote-session-events.js +1 -0
  88. package/dist/components/features/scripts/schedule-form/schedule-script-picker-card.js +1 -1
  89. package/dist/components/features/scripts/schedule-form/schedule-timing-fields.js +1 -1
  90. package/dist/components/features/select-button.js +0 -1
  91. package/dist/components/features/tags-selector.js +1 -1
  92. package/dist/components/help-center-pages/index.cjs +163 -163
  93. package/dist/components/index.cjs +205 -204
  94. package/dist/components/loading/loading-region.d.ts.map +1 -1
  95. package/dist/components/loading/loading-region.js +2 -2
  96. package/dist/components/meeting-scheduler/index.cjs +187 -187
  97. package/dist/components/navigation/index.cjs +35 -35
  98. package/dist/components/navigation/mobile-burger-menu.d.ts.map +1 -1
  99. package/dist/components/navigation/mobile-burger-menu.js +2 -1
  100. package/dist/components/navigation/multi-level-navigation.d.ts.map +1 -1
  101. package/dist/components/navigation/multi-level-navigation.js +4 -2
  102. package/dist/components/navigation/navigation-sidebar-item.js +3 -3
  103. package/dist/components/onboarding-guides/index.cjs +35 -35
  104. package/dist/components/product-demo/index.cjs +45 -45
  105. package/dist/components/related-content/index.cjs +17 -17
  106. package/dist/components/tickets/index.cjs +39 -39
  107. package/dist/components/ui/allowed-domains-input.d.ts.map +1 -1
  108. package/dist/components/ui/allowed-domains-input.js +1 -0
  109. package/dist/components/ui/button/button-styles.d.ts +12 -4
  110. package/dist/components/ui/button/button-styles.d.ts.map +1 -1
  111. package/dist/components/ui/button/button-styles.js +99 -7
  112. package/dist/components/ui/button/button.d.ts +1 -1
  113. package/dist/components/ui/button/button.d.ts.map +1 -1
  114. package/dist/components/ui/button/button.js +3 -0
  115. package/dist/components/ui/file-manager/file-manager-breadcrumb.d.ts.map +1 -1
  116. package/dist/components/ui/file-manager/file-manager-breadcrumb.js +1 -0
  117. package/dist/components/ui/file-manager/index.cjs +56 -55
  118. package/dist/components/ui/file-upload.d.ts.map +1 -1
  119. package/dist/components/ui/file-upload.js +5 -3
  120. package/dist/components/ui/hidden-tags-popup.js +1 -1
  121. package/dist/components/ui/hover-dropdown.js +1 -1
  122. package/dist/components/ui/index.cjs +32 -32
  123. package/dist/components/ui/info-card.js +1 -1
  124. package/dist/components/ui/info-section.d.ts.map +1 -1
  125. package/dist/components/ui/info-section.js +1 -0
  126. package/dist/components/ui/loading-scope.d.ts.map +1 -1
  127. package/dist/components/ui/loading-scope.js +2 -2
  128. package/dist/components/ui/modal-v2.js +1 -1
  129. package/dist/components/ui/organization-card.d.ts.map +1 -1
  130. package/dist/components/ui/organization-card.js +2 -1
  131. package/dist/components/ui/page-actions.d.ts +2 -2
  132. package/dist/components/ui/page-actions.d.ts.map +1 -1
  133. package/dist/components/ui/password-input.js +1 -1
  134. package/dist/components/ui/service-card.js +3 -3
  135. package/dist/components/ui/tab-selector.d.ts.map +1 -1
  136. package/dist/components/ui/tab-selector.js +3 -3
  137. package/dist/components/ui/test-run.d.ts.map +1 -1
  138. package/dist/components/ui/test-run.js +2 -1
  139. package/dist/components/ui/textarea.d.ts.map +1 -1
  140. package/dist/components/ui/textarea.js +2 -1
  141. package/dist/components/ui/ticket-attachments-list.d.ts.map +1 -1
  142. package/dist/components/ui/ticket-attachments-list.js +3 -2
  143. package/dist/components/ui/ticket-info-section.d.ts.map +1 -1
  144. package/dist/components/ui/ticket-info-section.js +1 -0
  145. package/dist/components/ui/ticket-note-card.d.ts.map +1 -1
  146. package/dist/components/ui/ticket-note-card.js +6 -4
  147. package/dist/components/ui/ticket-status-config-row.d.ts.map +1 -1
  148. package/dist/components/ui/ticket-status-config-row.js +1 -10
  149. package/dist/components/ui/toaster.js +1 -1
  150. package/dist/components/vendor-display-button.js +1 -1
  151. package/dist/{devices-TDKCKWRP.cjs → devices-PYJRYIQN.cjs} +33 -33
  152. package/dist/embed-shims/index.cjs +4 -4
  153. package/dist/embed-shims/next-image.cjs +4 -4
  154. package/dist/{embeddable-chat-HEL4Z2FL.cjs → embeddable-chat-7MSM62UC.cjs} +114 -114
  155. package/dist/hooks/index.cjs +10 -10
  156. package/dist/index.cjs +48 -48
  157. package/dist/{knowledge-A5KJ4CAD.cjs → knowledge-R4H2ZDX2.cjs} +33 -33
  158. package/dist/{logs-6WRBPIMM.cjs → logs-YWL6NK24.cjs} +41 -41
  159. package/dist/{policies-ICRXHW3H.cjs → policies-3COFYSKB.cjs} +36 -36
  160. package/dist/{remote-session-MEV5JL2Z.cjs → remote-session-IEOWFBF7.cjs} +17 -17
  161. package/dist/{scripts-AZVCZE6C.cjs → scripts-72EJDICT.cjs} +39 -39
  162. package/dist/{software-update-4ZBCGJGA.cjs → software-update-PPUVCLPO.cjs} +36 -36
  163. package/dist/{tickets-board-BJPQD3HE.cjs → tickets-board-EYKLSB36.cjs} +17 -17
  164. package/dist/{video-file-player-DETXW4OJ.cjs → video-file-player-ZU666VUY.cjs} +14 -14
  165. package/package.json +1 -1
  166. package/src/components/category-card.tsx +2 -0
  167. package/src/components/chat/ask-display.tsx +2 -2
  168. package/src/components/chat/chat-context-memory-bar.tsx +2 -2
  169. package/src/components/chat/chat-context-picker.tsx +4 -2
  170. package/src/components/chat/chat-input.tsx +3 -1
  171. package/src/components/chat/chat-nav-sidebar.tsx +10 -1
  172. package/src/components/chat/guide-mode-banner.tsx +1 -1
  173. package/src/components/chat/mingo-welcome.tsx +1 -1
  174. package/src/components/features/capability-explorer.tsx +8 -1
  175. package/src/components/features/command-box.tsx +1 -1
  176. package/src/components/features/devices/devices-grid-filters.tsx +8 -1
  177. package/src/components/features/filters-dropdown.tsx +6 -1
  178. package/src/components/features/model-token-rates.tsx +1 -1
  179. package/src/components/features/notifications/notification-drawer.tsx +1 -0
  180. package/src/components/features/notifications/notification-tile.tsx +1 -1
  181. package/src/components/features/paths-display.tsx +1 -1
  182. package/src/components/features/remote-session/remote-session-events.tsx +2 -0
  183. package/src/components/features/scripts/schedule-form/schedule-script-picker-card.tsx +1 -1
  184. package/src/components/features/scripts/schedule-form/schedule-timing-fields.tsx +1 -1
  185. package/src/components/features/select-button.tsx +2 -2
  186. package/src/components/features/tags-selector.tsx +1 -1
  187. package/src/components/loading/loading-region.tsx +4 -1
  188. package/src/components/navigation/mobile-burger-menu.tsx +3 -1
  189. package/src/components/navigation/multi-level-navigation.tsx +6 -2
  190. package/src/components/navigation/navigation-sidebar-item.tsx +5 -5
  191. package/src/components/ui/allowed-domains-input.tsx +1 -0
  192. package/src/components/ui/button/button-styles.ts +106 -9
  193. package/src/components/ui/button/button.tsx +3 -0
  194. package/src/components/ui/file-manager/file-manager-breadcrumb.tsx +1 -0
  195. package/src/components/ui/file-upload.tsx +5 -3
  196. package/src/components/ui/hidden-tags-popup.tsx +1 -1
  197. package/src/components/ui/hover-dropdown.tsx +1 -1
  198. package/src/components/ui/info-card.tsx +1 -1
  199. package/src/components/ui/info-section.tsx +2 -0
  200. package/src/components/ui/loading-scope.tsx +2 -1
  201. package/src/components/ui/modal-v2.tsx +1 -1
  202. package/src/components/ui/organization-card.tsx +3 -1
  203. package/src/components/ui/page-actions.tsx +14 -2
  204. package/src/components/ui/password-input.tsx +1 -1
  205. package/src/components/ui/service-card.tsx +3 -3
  206. package/src/components/ui/tab-selector.tsx +5 -3
  207. package/src/components/ui/test-run.tsx +2 -1
  208. package/src/components/ui/textarea.tsx +3 -1
  209. package/src/components/ui/ticket-attachments-list.tsx +3 -2
  210. package/src/components/ui/ticket-info-section.tsx +1 -0
  211. package/src/components/ui/ticket-note-card.tsx +6 -4
  212. package/src/components/ui/ticket-status-config-row.tsx +1 -6
  213. package/src/components/ui/toaster.tsx +1 -1
  214. package/src/components/vendor-display-button.tsx +1 -1
  215. package/dist/case-study-card-CYJ3JXU6.cjs +0 -23
@@ -304,7 +304,7 @@ export function MingoWelcome({
304
304
  </div>
305
305
  {/* Plain cross — no button chrome, just a clickable icon. */}
306
306
  <Button
307
- variant="transparent"
307
+ variant="bare"
308
308
  size="icon-sm"
309
309
  type="button"
310
310
  onClick={dismissPromo}
@@ -325,7 +325,12 @@ function CapabilityExplorerSkeleton({
325
325
  <div className="flex min-h-0 flex-col gap-1 overflow-hidden">
326
326
  <LoadingItems count={count}>
327
327
  {i => (
328
- <div data-loading-item data-state={i === 0 ? 'active' : undefined} className={JOB_TAB_CLASS}>
328
+ <div
329
+ data-loading-item
330
+ data-state={i === 0 ? 'active' : undefined}
331
+ data-tone="card"
332
+ className={JOB_TAB_CLASS}
333
+ >
329
334
  <JobHeadSkeleton />
330
335
  </div>
331
336
  )}
@@ -514,6 +519,8 @@ export function CapabilityExplorer({
514
519
  aria-selected={selected}
515
520
  aria-controls={panelId}
516
521
  data-state={selected ? 'active' : undefined}
522
+ // The selected capability is a bordered card; the others keep a transparent border and a hover fill.
523
+ data-tone="card"
517
524
  tabIndex={selected ? 0 : -1}
518
525
  onClick={() => onActiveChange(item.id)}
519
526
  className={JOB_TAB_LAYOUT_CLASS}
@@ -185,7 +185,7 @@ export function CommandBox({
185
185
  <div className={COMMAND_BOX_COPY_ROW_CLASS}>
186
186
  {commandText}
187
187
  <Button
188
- variant="transparent"
188
+ variant="bare"
189
189
  size="icon-sm"
190
190
  type="button"
191
191
  onClick={onCopy}
@@ -54,7 +54,14 @@ export function DevicesGridFilters({
54
54
  <FiltersDropdown
55
55
  key={column.key}
56
56
  triggerElement={
57
- <Button variant="link" size="compact" noPaddingX type="button" aria-label={`Filter by ${column.label}`}>
57
+ <Button
58
+ variant="link"
59
+ size="compact"
60
+ noPaddingX
61
+ type="button"
62
+ data-tone="neutral"
63
+ aria-label={`Filter by ${column.label}`}
64
+ >
58
65
  <span className="select-none whitespace-nowrap uppercase text-h5">{column.label}</span>
59
66
  <Filter02Icon className={cn(active && 'text-ods-accent')} />
60
67
  </Button>
@@ -395,9 +395,11 @@ export const FiltersDropdown: FC<FiltersDropdownProps> = ({
395
395
  ref={triggerRef as RefObject<HTMLButtonElement>}
396
396
  onClick={() => setIsOpen(!isOpen)}
397
397
  aria-expanded={isOpen}
398
+ // Filters applied: the trigger holds the accent. Otherwise the secondary text, the primary on hover.
399
+ data-state={getActiveFiltersCount() > 0 ? 'active' : undefined}
400
+ data-tone="neutral"
398
401
  >
399
402
  <span className="text-h5">{triggerLabel}</span>
400
- {getActiveFiltersCount() > 0 && <span className="size-1.5 rounded-full bg-ods-accent" />}
401
403
  </Button>
402
404
  )}
403
405
 
@@ -435,6 +437,7 @@ export const FiltersDropdown: FC<FiltersDropdownProps> = ({
435
437
  variant="link"
436
438
  size="compact"
437
439
  noPaddingX
440
+ data-tone="neutral"
438
441
  onClick={() => handleSelectAll(section.id, section)}
439
442
  >
440
443
  {allSelected ? 'Deselect All' : 'Select All'}
@@ -461,6 +464,8 @@ export const FiltersDropdown: FC<FiltersDropdownProps> = ({
461
464
  key={`${section.id}-${option.id}-${index}`}
462
465
  onClick={() => handleToggleOption(section.id, option.id, section.type)}
463
466
  data-state={isSelected ? 'active' : undefined}
467
+ // A checked option sits on the raised surface; the hover still shows over it.
468
+ data-tone="surface"
464
469
  >
465
470
  <FilterCheckbox checked={isSelected} />
466
471
  <span
@@ -233,7 +233,7 @@ export function ModelTokenRatesPopover({
233
233
  <DropdownMenu>
234
234
  <DropdownMenuTrigger asChild>
235
235
  <Button
236
- variant="transparent"
236
+ variant="bare"
237
237
  size="icon-sm"
238
238
  type="button"
239
239
  aria-label={MODEL_TOKEN_RATES_COPY.trigger}
@@ -75,6 +75,7 @@ export function NotificationDrawer({
75
75
  size="compact"
76
76
  noPaddingX
77
77
  type="button"
78
+ data-tone="neutral"
78
79
  disabled={unreadCount === 0}
79
80
  onClick={markAllRead}
80
81
  className="self-center"
@@ -255,7 +255,7 @@ function LoadedNotificationTile({
255
255
  },
256
256
  ].map(({ active, label, icon: actionIcon, onClick }) => (
257
257
  <Button
258
- variant="transparent"
258
+ variant="bare"
259
259
  size="icon-inline"
260
260
  key={label}
261
261
  type="button"
@@ -131,7 +131,7 @@ export function PathsDisplay({
131
131
  <span className="min-w-0 flex-1 truncate text-ods-text-primary text-h4">{path}</span>
132
132
  {showCopyButtons && onCopyPath && (
133
133
  <Button
134
- variant="transparent"
134
+ variant="bare"
135
135
  size="icon-sm"
136
136
  type="button"
137
137
  onClick={() => onCopyPath(path)}
@@ -35,6 +35,8 @@ export function RemoteSessionEventList({ events, activeEventId, onSelect, classN
35
35
  type="button"
36
36
  aria-current={event.id === activeEventId || undefined}
37
37
  data-state={event.id === activeEventId ? 'active' : undefined}
38
+ // The current event holds the border-grey fill under the cursor.
39
+ data-tone="muted"
38
40
  onClick={() => onSelect?.(event)}
39
41
  className="items-start"
40
42
  >
@@ -132,7 +132,7 @@ export function ScheduleScriptPickerCard({
132
132
  >
133
133
  {dragAndDropEnabled && (
134
134
  <Button
135
- variant="transparent"
135
+ variant="bare"
136
136
  size="icon"
137
137
  type="button"
138
138
  {...dragHandleProps}
@@ -306,7 +306,7 @@ export function ScheduleTimingFields({
306
306
  />
307
307
  {field.value && !fieldsDisabled && (
308
308
  <Button
309
- variant="transparent"
309
+ variant="bare"
310
310
  size="icon-sm"
311
311
  type="button"
312
312
  onClick={() => field.onChange(null)}
@@ -41,13 +41,13 @@ export const SelectButton = React.forwardRef<HTMLButtonElement, SelectButtonProp
41
41
  ) => {
42
42
  return (
43
43
  <Button
44
- // A selectable option drawn as a field: its accent border marks the picked one.
44
+ // A selectable option drawn as a field: the picked one (`aria-selected`) takes the accent border on the
45
+ // accent's light fill.
45
46
  variant="field"
46
47
  ref={ref}
47
48
  type="button"
48
49
  role="option"
49
50
  aria-selected={selected}
50
- data-state={selected ? 'open' : undefined}
51
51
  disabled={disabled}
52
52
  onClick={onClick}
53
53
  className={cn('content-md:h-16', className)}
@@ -123,7 +123,7 @@ export function TagsSelector({
123
123
  {tag.name}
124
124
  {!disabled && (
125
125
  <Button
126
- variant="transparent"
126
+ variant="bare"
127
127
  size="icon-inline"
128
128
  type="button"
129
129
  onClick={e => {
@@ -49,10 +49,13 @@ export function LoadingRegion({
49
49
  const announce = announceGiven && !inScope;
50
50
  // One tree for both states: `children` keeps its slot whether or not the status is drawn, so an
51
51
  // `announce` that flips at runtime does not remount the content (and drop its state and focus).
52
+ // The status comes AFTER the content: it takes no space (visually hidden, absolutely positioned), but placed first
53
+ // in a `space-y-*` stack it made the content the second child, which takes the stack's top margin, so a region
54
+ // that announced stood a gap taller than the same region inside a loading scope (a jump at hydration).
52
55
  return (
53
56
  <Tag aria-busy={announce ? 'true' : undefined} className={className} {...props}>
54
- {announce && status && <LoadingStatus label={label} />}
55
57
  {children}
58
+ {announce && status && <LoadingStatus label={label} />}
56
59
  </Tag>
57
60
  );
58
61
  }
@@ -83,6 +83,8 @@ export const MobileBurgerMenu = React.memo(function MobileBurgerMenuImpl({
83
83
  onClick={() => handleItemClick(item)}
84
84
  disabled={disabled}
85
85
  data-state={isActive && !disabled ? 'active' : undefined}
86
+ // Every entry is a bordered card; the current page's takes the accent border.
87
+ data-tone="framed"
86
88
  className={isGridItem ? 'min-w-0' : undefined}
87
89
  aria-current={isActive ? 'page' : undefined}
88
90
  // The same name the sidebar row announces — "Scripts (Beta)" — rather than
@@ -239,7 +241,7 @@ export const MobileBurgerMenu = React.memo(function MobileBurgerMenuImpl({
239
241
 
240
242
  {/* Logout Button */}
241
243
  {onLogout && (
242
- <Button variant="row" onClick={onLogout} disabled={disabled}>
244
+ <Button variant="row" data-tone="framed" onClick={onLogout} disabled={disabled}>
243
245
  <Logout02Icon className="size-4 text-ods-error" />
244
246
  <span className="flex-1 text-left text-ods-text-primary text-h6">Log Out</span>
245
247
  </Button>
@@ -270,6 +270,8 @@ function NavigationItem({
270
270
  <Button
271
271
  variant="row"
272
272
  data-state={isSelected ? 'active' : undefined}
273
+ // The open page holds its line's border-grey fill: no hover over it.
274
+ data-tone="muted"
273
275
  className={cn(NAV_ROW_LAYOUT_CLASS, NAV_ROW_HEIGHT.desktop)}
274
276
  onClick={() => onNodeClick(node)}
275
277
  aria-label={`${isSelected ? 'Selected' : 'Select'} ${node.name}`}
@@ -297,7 +299,7 @@ function NavigationItem({
297
299
 
298
300
  {hasChildren && (
299
301
  <Button
300
- variant="transparent"
302
+ variant="bare"
301
303
  size="icon-sm"
302
304
  className="mr-[var(--spacing-system-xxs)] shrink-0"
303
305
  onClick={e => {
@@ -399,6 +401,8 @@ function MobileNavigationItem({
399
401
  <Button
400
402
  variant="row"
401
403
  data-state={isSelected ? 'active' : undefined}
404
+ // The open page holds its line's border-grey fill: no hover over it.
405
+ data-tone="muted"
402
406
  className={cn(NAV_ROW_LAYOUT_CLASS, NAV_ROW_HEIGHT.mobile)}
403
407
  onClick={() => onNodeClick(node)}
404
408
  >
@@ -425,7 +429,7 @@ function MobileNavigationItem({
425
429
 
426
430
  {hasChildren && (
427
431
  <Button
428
- variant="transparent"
432
+ variant="bare"
429
433
  size="icon-sm"
430
434
  className="mr-[var(--spacing-system-xxs)] shrink-0"
431
435
  onClick={e => {
@@ -10,7 +10,7 @@ import { useLoadingScope } from '../ui/loading-scope';
10
10
  import { NavigationItemBadge } from './navigation-item-badge';
11
11
  import { UnreadDot } from './unread-dot';
12
12
 
13
- /** A sidebar row's height: the loaded row (a `row` button or link) and its loading form are one box. */
13
+ /** A sidebar row's height: the loaded row (a `rail` button or link) and its loading form are one box. */
14
14
  const NAV_SIDEBAR_ROW_LAYOUT_CLASS = 'h-14';
15
15
 
16
16
  interface IconProps {
@@ -118,7 +118,7 @@ function LoadedNavigationSidebarItemButton({
118
118
  // A row whose route is still committing: announced, never drawn as the current page.
119
119
  'aria-busy': isPending || undefined,
120
120
  };
121
- // The current page's row: the row kind's selected surface.
121
+ // The current page's entry: the rail kind's accent fill, text and bar (`aria-busy` above holds its hover wash).
122
122
  const rowState = isActive && !disabled ? 'active' : undefined;
123
123
 
124
124
  // An entry with a custom onClick has no destination of its own, and a
@@ -126,7 +126,7 @@ function LoadedNavigationSidebarItemButton({
126
126
  if (disabled || !item.path || item.onClick) {
127
127
  return (
128
128
  <Button
129
- variant="row"
129
+ variant="rail"
130
130
  type="button"
131
131
  onClick={event => onClick(item, event)}
132
132
  disabled={disabled}
@@ -140,7 +140,7 @@ function LoadedNavigationSidebarItemButton({
140
140
  }
141
141
 
142
142
  return (
143
- <Button asChild variant="row" data-state={rowState} className={NAV_SIDEBAR_ROW_LAYOUT_CLASS}>
143
+ <Button asChild variant="rail" data-state={rowState} className={NAV_SIDEBAR_ROW_LAYOUT_CLASS}>
144
144
  <Link href={item.path} onClick={event => onClick(item, event)} {...shared}>
145
145
  {content}
146
146
  </Link>
@@ -160,7 +160,7 @@ function LoadedNavigationSidebarItemButton({
160
160
  */
161
161
  function NavigationSidebarItemSkeleton({ showLabel }: { showLabel: boolean }) {
162
162
  return (
163
- <div className={cn(BUTTON_KIND_CLASS.row, NAV_SIDEBAR_ROW_LAYOUT_CLASS)} aria-hidden="true">
163
+ <div className={cn(BUTTON_KIND_CLASS.rail, NAV_SIDEBAR_ROW_LAYOUT_CLASS)} aria-hidden="true">
164
164
  <div className="relative flex flex-shrink-0 items-center justify-center">
165
165
  <UnifiedSkeleton className="h-6 w-6 rounded" />
166
166
  </div>
@@ -120,6 +120,7 @@ const AllowedDomainsInput = forwardRef<HTMLDivElement, AllowedDomainsInputProps>
120
120
  size="compact"
121
121
  noPaddingX
122
122
  type="button"
123
+ data-tone="primary"
123
124
  onClick={addDomain}
124
125
  disabled={disabled}
125
126
  leftIcon={<PlusCircle />}
@@ -6,8 +6,9 @@
6
6
  export const buttonSurfaceClasses = {
7
7
  accent:
8
8
  'bg-ods-accent text-ods-text-on-accent hover:bg-ods-accent-hover active:bg-ods-accent-active disabled:bg-ods-disabled aria-disabled:bg-ods-disabled',
9
+ // `data-tone="accent"`: the accent fill on hover (a card's "view" action).
9
10
  outline:
10
- 'bg-ods-card text-ods-text-primary hover:bg-ods-bg-hover active:bg-ods-bg-active disabled:bg-ods-card disabled:text-ods-text-disabled aria-disabled:bg-ods-card aria-disabled:text-ods-text-disabled',
11
+ 'bg-ods-card text-ods-text-primary hover:bg-ods-bg-hover active:bg-ods-bg-active disabled:bg-ods-card disabled:text-ods-text-disabled aria-disabled:bg-ods-card aria-disabled:text-ods-text-disabled data-[tone=accent]:hover:bg-ods-accent',
11
12
  transparent:
12
13
  'bg-transparent text-ods-text-primary hover:bg-ods-bg-hover active:bg-ods-bg-active disabled:bg-transparent disabled:text-ods-text-disabled aria-disabled:bg-transparent aria-disabled:text-ods-text-disabled',
13
14
  destructive:
@@ -31,7 +32,39 @@ export const buttonSurfaceClasses = {
31
32
  // hover, and no surface in any state. Promoted to a variant because the
32
33
  // disclosure toggle was a primitive <button> restating these classes. Pair
33
34
  // with size="compact" and noPaddingX so the label aligns with the text above.
34
- link: 'bg-transparent text-ods-text-secondary hover:bg-transparent hover:text-ods-accent active:bg-transparent active:text-ods-accent-active disabled:bg-transparent disabled:text-ods-text-disabled aria-disabled:bg-transparent aria-disabled:text-ods-text-disabled',
35
+ //
36
+ // States, from the element's attributes: `data-state="active"` holds the accent (a filter trigger with filters
37
+ // applied). `data-tone="neutral"` takes the primary text on hover instead of the accent (a secondary text action:
38
+ // "Select all", "Mark all read"); `data-tone="primary"` sits in the primary text and takes the accent on hover (a
39
+ // value that opens something: an info row's link, a breadcrumb).
40
+ link: [
41
+ 'bg-transparent text-ods-text-secondary hover:bg-transparent hover:text-ods-accent active:bg-transparent active:text-ods-accent-active disabled:bg-transparent disabled:text-ods-text-disabled aria-disabled:bg-transparent aria-disabled:text-ods-text-disabled',
42
+ 'data-[tone=neutral]:hover:text-ods-text-primary',
43
+ 'data-[tone=primary]:text-ods-text-primary data-[tone=primary]:hover:text-ods-accent',
44
+ 'data-[state=active]:text-ods-accent data-[state=active]:hover:text-ods-accent data-[tone=neutral]:data-[state=active]:hover:text-ods-accent',
45
+ ].join(' '),
46
+ // A bare glyph control with no surface (a composer's send / add-context, a copy, a close, a field's clear): the
47
+ // secondary text, the primary on hover and keyboard focus, faded when disabled. Distinct from `transparent`, which
48
+ // is primary text on a hover fill. States, from the element's attributes:
49
+ // - `aria-expanded="true"`: its popup is open, the glyph stays lit (primary);
50
+ // - `data-state="active"`: the glyph is armed (the composer's send while the editor has focus), the accent;
51
+ // - `data-tone="accent"`: a trigger that opens and presses in the accent (the composer's add-context);
52
+ // - `data-tone="danger"`: a removal that takes the error colour on hover;
53
+ // - `data-tone="error"` / `data-tone="success"`: a glyph drawn in that colour (an error's copy, a done state),
54
+ // dimmed on hover;
55
+ // - `data-tone="field"`: a field's adornment, lit in the accent while the field (the enclosing `group`) has focus
56
+ // and in the error colour while it is invalid (`data-invalid` on the group).
57
+ bare: [
58
+ 'bg-transparent text-ods-text-secondary outline-none hover:bg-transparent hover:text-ods-text-primary focus-visible:text-ods-text-primary active:bg-transparent',
59
+ 'aria-expanded:text-ods-text-primary',
60
+ 'data-[state=active]:text-ods-accent',
61
+ 'data-[tone=accent]:aria-expanded:text-ods-accent data-[tone=accent]:active:text-ods-accent',
62
+ 'data-[tone=danger]:hover:text-ods-error',
63
+ 'data-[tone=error]:text-ods-error data-[tone=error]:hover:text-ods-error data-[tone=error]:hover:opacity-80',
64
+ 'data-[tone=success]:text-ods-success data-[tone=success]:hover:text-ods-success data-[tone=success]:hover:opacity-80',
65
+ 'data-[tone=field]:group-has-[textarea:focus]:text-ods-accent data-[tone=field]:focus-visible:text-ods-accent data-[tone=field]:group-data-[invalid]:text-ods-error',
66
+ 'disabled:cursor-not-allowed disabled:bg-transparent disabled:opacity-40 disabled:hover:text-ods-text-secondary aria-disabled:opacity-40',
67
+ ].join(' '),
35
68
  // Chrome that sits ON media (video cards, image lightboxes): a scrim disc so
36
69
  // the glyph stays legible over an arbitrary frame. Promoted to a variant
37
70
  // because the walkthrough card, its theater and the bite cards were each
@@ -138,9 +171,14 @@ const KIND_FOCUS_INSET =
138
171
  * - `data-state="active"` (Radix's convention): the selected row, tab or chip;
139
172
  * - `data-state="open"` / `aria-expanded="true"`: the field whose popup is open;
140
173
  * - `data-invalid`: the field that failed validation (the marker every field exposes);
141
- * - `aria-pressed="true"`: the pressed chip.
174
+ * - `aria-pressed="true"`: the pressed chip;
175
+ * - `aria-selected="true"`: the chosen field (a choice drawn as a field);
176
+ * - `aria-busy="true"`: the rail entry whose route is committing.
177
+ *
178
+ * Where a kind has a second look for the same state (a selected row marked with the accent rather than the hover
179
+ * surface, the segmented control's accent segment), the control names it with `data-tone`, declared on the kind below.
142
180
  *
143
- * A call site's `className` on a kind is layout only (width, flex, position, margin).
181
+ * A call site's `className` on a kind is layout only (width, flex, position, margin): never a colour.
144
182
  */
145
183
  export const BUTTON_KIND_CLASS = {
146
184
  // A control drawn as a form field (a date picker's, a searchable select's, a popup's trigger): Input's box, border
@@ -150,22 +188,62 @@ export const BUTTON_KIND_CLASS = {
150
188
  'h-11 rounded-[6px] md:h-12',
151
189
  'border-ods-border bg-ods-card text-ods-text-primary text-h4 transition-colors duration-200',
152
190
  'enabled:hover:border-ods-border-hover enabled:hover:bg-ods-bg-hover enabled:active:border-ods-border-active enabled:active:bg-ods-bg-active',
191
+ // Open: the accent border, held under the cursor (a hover must not read as closing it).
153
192
  'data-[state=open]:border-ods-accent aria-expanded:border-ods-accent',
154
- 'data-[invalid]:border-ods-error enabled:data-[invalid]:hover:border-ods-error',
193
+ 'enabled:data-[state=open]:hover:border-ods-accent enabled:aria-expanded:hover:border-ods-accent',
194
+ // Selected (a choice drawn as a field, `aria-selected="true"`): the accent border on the accent's light fill,
195
+ // whose hover and press are the fill's own steps.
196
+ 'aria-selected:border-ods-accent aria-selected:bg-ods-open-yellow-light',
197
+ 'enabled:aria-selected:hover:border-ods-accent enabled:aria-selected:hover:bg-ods-open-yellow-light-hover',
198
+ 'enabled:aria-selected:active:border-ods-accent enabled:aria-selected:active:bg-ods-open-yellow-light-action',
199
+ // Invalid wins over open.
200
+ 'data-[invalid]:border-ods-error enabled:data-[invalid]:hover:border-ods-error enabled:data-[invalid]:active:border-ods-error',
201
+ 'data-[invalid]:data-[state=open]:border-ods-error data-[invalid]:aria-expanded:border-ods-error',
202
+ 'enabled:data-[invalid]:data-[state=open]:hover:border-ods-error enabled:data-[invalid]:aria-expanded:hover:border-ods-error',
155
203
  'disabled:cursor-not-allowed disabled:bg-ods-bg disabled:text-ods-text-disabled disabled:[&_svg]:text-ods-text-disabled',
156
204
  'outline-none focus-visible:border-ods-accent',
157
205
  '[&_svg]:shrink-0',
158
206
  ].join(' '),
159
207
  // A clickable row of a list, a menu or a navigation: full width, its content on the left, the hover and the
160
- // selected surface of the list.
208
+ // selected surface of the list. The selected row (`data-state="active"`) takes the hover surface; a list that marks
209
+ // its selection differently names its look with `data-tone` (the attribute, on the row and on its skeleton):
210
+ // - `accent`: the accent's secondary fill, no hover surface (a chat list's open conversation);
211
+ // - `link`: the accent text, no surface at all (a chat rail's navigation entries);
212
+ // - `surface`: the raised surface, the hover still showing over it (a filter dropdown's checked option);
213
+ // - `muted`: the border-grey fill, held under the cursor (a timeline's current event, a docs tree's open page);
214
+ // - `card`: a card with its border, no hover over it; idle rows keep a transparent border (a capability list);
215
+ // - `framed`: every row a bordered card, the selected one with the accent border (the mobile menu's entries).
161
216
  row: [
162
217
  'flex w-full items-center gap-[var(--spacing-system-s)] rounded-md px-[var(--spacing-system-m)] py-[var(--spacing-system-s)] text-left',
163
218
  'bg-transparent text-ods-text-primary text-h4 transition-colors duration-150',
164
- 'hover:bg-ods-bg-hover active:bg-ods-bg-active data-[state=active]:bg-ods-bg-active',
219
+ 'hover:bg-ods-bg-hover active:bg-ods-bg-active data-[state=active]:bg-ods-bg-hover',
220
+ 'data-[tone=accent]:hover:bg-transparent data-[tone=accent]:active:bg-transparent',
221
+ 'data-[tone=accent]:data-[state=active]:bg-ods-accent-secondary data-[tone=accent]:data-[state=active]:hover:bg-ods-accent-secondary data-[tone=accent]:data-[state=active]:active:bg-ods-accent-secondary',
222
+ 'data-[tone=link]:hover:bg-transparent data-[tone=link]:active:bg-transparent data-[tone=link]:data-[state=active]:bg-transparent data-[tone=link]:data-[state=active]:text-ods-accent',
223
+ 'data-[tone=surface]:data-[state=active]:bg-ods-bg-surface data-[tone=surface]:data-[state=active]:hover:bg-ods-bg-hover',
224
+ 'data-[tone=muted]:data-[state=active]:bg-ods-border',
225
+ 'data-[tone=card]:border data-[tone=card]:border-transparent data-[tone=card]:data-[state=active]:border-ods-border data-[tone=card]:data-[state=active]:bg-ods-card',
226
+ 'data-[tone=framed]:border data-[tone=framed]:border-ods-border data-[tone=framed]:bg-ods-card data-[tone=framed]:data-[state=active]:border-ods-accent data-[tone=framed]:data-[state=active]:bg-ods-card data-[tone=framed]:data-[state=active]:hover:bg-ods-bg-hover',
165
227
  'disabled:cursor-not-allowed disabled:text-ods-text-disabled disabled:hover:bg-transparent',
166
228
  KIND_FOCUS_INSET,
167
229
  '[&_svg]:shrink-0',
168
230
  ].join(' '),
231
+ // A navigation rail's entry (the app sidebar): the row's box with the rail's two layers. The hover is a wash on its
232
+ // own layer (`::after`), so it can fade while the current page switches in a single frame: the current page's entry
233
+ // (`data-state="active"`) takes the accent's light fill, the accent text and glyphs and the accent bar on its leading
234
+ // edge (`::before`). An entry whose route is still committing (`aria-busy="true"`) holds the hover wash and nothing
235
+ // else.
236
+ rail: [
237
+ 'relative isolate flex w-full items-center justify-start gap-[var(--spacing-system-s)] px-[var(--spacing-system-m)] py-[var(--spacing-system-s)] text-left',
238
+ 'bg-transparent text-ods-text-primary text-h4 [&_svg]:fill-ods-text-secondary',
239
+ "after:pointer-events-none after:absolute after:inset-0 after:-z-10 after:bg-ods-bg-hover after:opacity-0 after:transition-opacity after:duration-150 after:content-['']",
240
+ "before:absolute before:bottom-0 before:left-0 before:top-0 before:w-1 before:content-['']",
241
+ 'hover:after:opacity-100 aria-busy:after:opacity-100',
242
+ 'data-[state=active]:bg-ods-open-yellow-light data-[state=active]:text-ods-accent data-[state=active]:[&_svg]:fill-ods-accent data-[state=active]:before:bg-ods-accent data-[state=active]:after:opacity-0',
243
+ 'disabled:cursor-not-allowed disabled:text-ods-text-secondary disabled:opacity-50 disabled:after:opacity-0',
244
+ KIND_FOCUS_INSET,
245
+ '[&_svg]:shrink-0',
246
+ ].join(' '),
169
247
  // A tab of a tab strip or a tab list: secondary until it is the open one. A strip that marks its open and hovered
170
248
  // tab with the accent fade (the admin strip) says so on each tab with `data-emphasis="fade"`. ODS colour vars hold
171
249
  // hex values, so a Tailwind alpha modifier would produce no CSS; color-mix() derives the 10% stop from the token.
@@ -179,12 +257,29 @@ export const BUTTON_KIND_CLASS = {
179
257
  KIND_FOCUS_INSET,
180
258
  '[&_svg]:shrink-0',
181
259
  ].join(' '),
260
+ // A segment of a segmented control (`TabSelector`): the label on the strip, a hover fill, and the selected segment
261
+ // (`data-state="active"`) filled with the raised surface; `data-tone="accent"` fills it with the accent instead. A
262
+ // disabled selected segment keeps the raised surface with secondary text.
263
+ segment: [
264
+ 'flex items-center justify-center gap-[var(--spacing-system-xs)] whitespace-nowrap rounded-xs p-[var(--spacing-system-xsf)]',
265
+ 'bg-transparent text-ods-text-primary text-h4 transition-colors duration-200',
266
+ 'hover:bg-ods-bg-hover',
267
+ 'data-[state=active]:cursor-default data-[state=active]:bg-ods-bg-surface data-[state=active]:text-ods-text-primary data-[state=active]:hover:bg-ods-bg-surface',
268
+ 'data-[tone=accent]:data-[state=active]:bg-ods-accent data-[tone=accent]:data-[state=active]:text-ods-text-on-accent data-[tone=accent]:data-[state=active]:hover:bg-ods-accent',
269
+ 'disabled:cursor-not-allowed disabled:bg-transparent disabled:text-ods-text-secondary disabled:hover:bg-transparent',
270
+ 'disabled:data-[state=active]:bg-ods-bg-surface disabled:data-[state=active]:text-ods-text-secondary disabled:data-[state=active]:hover:bg-ods-bg-surface',
271
+ 'disabled:data-[tone=accent]:data-[state=active]:bg-ods-bg-surface disabled:data-[tone=accent]:data-[state=active]:text-ods-text-secondary disabled:data-[tone=accent]:data-[state=active]:hover:bg-ods-bg-surface',
272
+ KIND_FOCUS_INSET,
273
+ '[&_svg]:shrink-0',
274
+ ].join(' '),
182
275
  // A choice drawn as a pill (a filter, a quick question, an answer option): bordered, the accent when picked.
183
276
  chip: [
184
277
  'inline-flex h-8 items-center gap-[var(--spacing-system-xxs)] rounded-full border px-[var(--spacing-system-sf)]',
185
278
  'border-ods-border bg-ods-card text-ods-text-primary text-h6 transition-colors duration-200',
186
279
  'hover:border-ods-border-hover hover:bg-ods-bg-hover',
187
280
  'aria-pressed:border-ods-accent data-[state=active]:border-ods-accent',
281
+ // `data-tone="accent"`: a pill that links somewhere takes the accent border on hover (a vendor pill).
282
+ 'data-[tone=accent]:hover:border-ods-accent',
188
283
  'disabled:cursor-not-allowed disabled:text-ods-text-disabled disabled:hover:bg-ods-card',
189
284
  KIND_FOCUS,
190
285
  '[&_svg]:h-4 [&_svg]:w-4 [&_svg]:shrink-0',
@@ -193,11 +288,13 @@ export const BUTTON_KIND_CLASS = {
193
288
  // content is the look; the button gives it the click, the keyboard and the focus ring.
194
289
  card: ['relative block w-full rounded-md text-left text-ods-text-primary', KIND_FOCUS].join(' '),
195
290
  // A cell of a bar (a header's or a chat's top bar): the bar's full height, an icon or a short label, secondary
196
- // until hovered or pressed.
291
+ // until hovered. The current cell (`data-state="active"`: the header's open section) takes the pressed surface and
292
+ // the primary text.
197
293
  cell: [
198
294
  'flex h-full shrink-0 items-center justify-center gap-[var(--spacing-system-xsf)] px-[var(--spacing-system-m)]',
199
295
  'bg-transparent text-ods-text-secondary text-h4 transition-colors duration-200',
200
- 'hover:bg-ods-bg-hover hover:text-ods-text-primary aria-pressed:text-ods-accent aria-expanded:text-ods-text-primary data-[state=active]:text-ods-accent',
296
+ 'hover:bg-ods-bg-hover hover:text-ods-text-primary aria-expanded:text-ods-text-primary',
297
+ 'data-[state=active]:bg-ods-bg-active data-[state=active]:text-ods-text-primary',
201
298
  KIND_FOCUS_INSET,
202
299
  '[&_svg]:h-6 [&_svg]:w-6 [&_svg]:shrink-0',
203
300
  ].join(' '),
@@ -41,11 +41,14 @@ const buttonVariants = cva(
41
41
  glyph: buttonSurfaceClasses.glyph,
42
42
  overlay: buttonSurfaceClasses.overlay,
43
43
  link: buttonSurfaceClasses.link,
44
+ bare: buttonSurfaceClasses.bare,
44
45
  // The kinds that are not a plain push button own their whole box (`BUTTON_KIND_CLASS`); resolved
45
46
  // before this map is read, so `size` never reaches them. The entries only type the prop.
46
47
  field: '',
47
48
  row: '',
49
+ rail: '',
48
50
  tab: '',
51
+ segment: '',
49
52
  chip: '',
50
53
  card: '',
51
54
  cell: '',
@@ -17,6 +17,7 @@ export function FileManagerBreadcrumb({ items, onItemClick, className }: FileMan
17
17
  size="compact"
18
18
  noPaddingX
19
19
  type="button"
20
+ data-tone="primary"
20
21
  aria-current={index === items.length - 1 ? 'page' : undefined}
21
22
  onClick={() => onItemClick?.(item.path)}
22
23
  className="shrink-0"
@@ -417,7 +417,7 @@ export function FileUpload<T extends FileUploadCandidate = File>({
417
417
  </div>
418
418
  {onDownloadManagedFile && entry.status === 'uploaded' && (
419
419
  <Button
420
- variant="transparent"
420
+ variant="bare"
421
421
  size="icon-sm"
422
422
  type="button"
423
423
  onClick={() => onDownloadManagedFile(entry.id)}
@@ -429,7 +429,8 @@ export function FileUpload<T extends FileUploadCandidate = File>({
429
429
  )}
430
430
  {!disabled && (
431
431
  <Button
432
- variant="transparent"
432
+ variant="bare"
433
+ data-tone="error"
433
434
  size="icon-sm"
434
435
  type="button"
435
436
  onClick={() => onRemoveManagedFile?.(entry.id)}
@@ -457,7 +458,8 @@ export function FileUpload<T extends FileUploadCandidate = File>({
457
458
  </div>
458
459
  {!disabled && (
459
460
  <Button
460
- variant="transparent"
461
+ variant="bare"
462
+ data-tone="error"
461
463
  size="icon-sm"
462
464
  type="button"
463
465
  onClick={() => handleRemoveFile(index)}
@@ -51,7 +51,7 @@ export const HiddenTagsPopup = forwardRef(function HiddenTagsPopupImpl(
51
51
  </span>
52
52
  {!disabled && onRemove && (
53
53
  <Button
54
- variant="transparent"
54
+ variant="bare"
55
55
  size="icon-sm"
56
56
  type="button"
57
57
  onClick={e => {
@@ -211,7 +211,7 @@ export function HoverDropdown({
211
211
  const secondaryAction = item.secondaryAction;
212
212
  const secondary = secondaryAction ? (
213
213
  <Button
214
- variant="transparent"
214
+ variant="bare"
215
215
  size="icon-sm"
216
216
  type="button"
217
217
  onClick={e => {
@@ -303,7 +303,7 @@ function InfoCardValueRow({
303
303
  {icon}
304
304
  {copyable && (
305
305
  <Button
306
- variant="transparent"
306
+ variant="bare"
307
307
  size="icon-inline"
308
308
  type="button"
309
309
  onClick={() => copy(value)}
@@ -87,6 +87,8 @@ function TextValue({ value }: { value: InfoSectionTextValue }) {
87
87
  size="compact"
88
88
  noPaddingX
89
89
  type="button"
90
+ // The value reads in the primary text and takes the accent on hover.
91
+ data-tone="primary"
90
92
  href={href}
91
93
  openInNewTab={openInNewTab}
92
94
  onClick={href ? undefined : onClick}
@@ -121,12 +121,13 @@ export function AnnouncedLoadingScope({
121
121
  children: ReactNode;
122
122
  }) {
123
123
  const outer = useLoadingScope();
124
+ // The status follows the content, so in a `space-y-*` stack it never pushes the content down (see LoadingRegion).
124
125
  return (
125
126
  <>
126
- {status && active && !outer && <LoadingStatus label={label} />}
127
127
  <LoadingScope active={active} as={as} className={className}>
128
128
  {children}
129
129
  </LoadingScope>
130
+ {status && active && !outer && <LoadingStatus label={label} />}
130
131
  </>
131
132
  );
132
133
  }
@@ -376,7 +376,7 @@ const ModalHeader = forwardRef<HTMLDivElement, ModalHeaderProps>(({ children, cl
376
376
  <div className="flex min-w-0 flex-1 flex-col">{children}</div>
377
377
  {onClose && (
378
378
  <Button
379
- variant="transparent"
379
+ variant="bare"
380
380
  size="icon-sm"
381
381
  type="button"
382
382
  onClick={onClose}