@tempoxyz/ds 0.3.6 → 0.3.7

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 (213) hide show
  1. package/dist/web/components/Accordion.js.zyzz.json +1 -1
  2. package/dist/web/components/AddressCard.css +1 -1
  3. package/dist/web/components/AddressCard.js.zyzz.json +1 -1
  4. package/dist/web/components/AgentLogo.css +1 -1
  5. package/dist/web/components/AgentLogo.js.zyzz.json +1 -1
  6. package/dist/web/components/Alert.js.zyzz.json +1 -1
  7. package/dist/web/components/AlertDialog.js.zyzz.json +1 -1
  8. package/dist/web/components/Amount.js.zyzz.json +1 -1
  9. package/dist/web/components/AmountInput.css +1 -1
  10. package/dist/web/components/AmountInput.js +1 -1
  11. package/dist/web/components/AmountInput.js.map +1 -1
  12. package/dist/web/components/AmountInput.js.zyzz.json +1 -1
  13. package/dist/web/components/AppShell.css +1 -1
  14. package/dist/web/components/AppShell.d.ts +9 -7
  15. package/dist/web/components/AppShell.d.ts.map +1 -1
  16. package/dist/web/components/AppShell.js +11 -9
  17. package/dist/web/components/AppShell.js.map +1 -1
  18. package/dist/web/components/AppShell.js.zyzz.json +1 -1
  19. package/dist/web/components/AssetChip.css +1 -1
  20. package/dist/web/components/AssetChip.js.zyzz.json +1 -1
  21. package/dist/web/components/Autocomplete.js.zyzz.json +1 -1
  22. package/dist/web/components/Avatar.js.zyzz.json +1 -1
  23. package/dist/web/components/Badge.css +1 -1
  24. package/dist/web/components/Badge.d.ts +1 -1
  25. package/dist/web/components/Badge.js.map +1 -1
  26. package/dist/web/components/Badge.js.zyzz.json +1 -1
  27. package/dist/web/components/BadgedLogo.css +1 -0
  28. package/dist/web/components/BadgedLogo.d.ts +44 -0
  29. package/dist/web/components/BadgedLogo.d.ts.map +1 -0
  30. package/dist/web/components/BadgedLogo.js +59 -0
  31. package/dist/web/components/BadgedLogo.js.map +1 -0
  32. package/dist/web/components/BadgedLogo.js.zyzz.json +1 -0
  33. package/dist/web/components/Banner.js.zyzz.json +1 -1
  34. package/dist/web/components/BarChart.css +1 -1
  35. package/dist/web/components/BarChart.d.ts +1 -1
  36. package/dist/web/components/BarChart.js.map +1 -1
  37. package/dist/web/components/BarChart.js.zyzz.json +1 -1
  38. package/dist/web/components/Breadcrumbs.js.zyzz.json +1 -1
  39. package/dist/web/components/Button.css +1 -1
  40. package/dist/web/components/Button.d.ts +40 -31
  41. package/dist/web/components/Button.d.ts.map +1 -1
  42. package/dist/web/components/Button.js +25 -8
  43. package/dist/web/components/Button.js.map +1 -1
  44. package/dist/web/components/Button.js.zyzz.json +1 -1
  45. package/dist/web/components/ButtonGroup.js.zyzz.json +1 -1
  46. package/dist/web/components/Calendar.css +1 -1
  47. package/dist/web/components/Calendar.js.zyzz.json +1 -1
  48. package/dist/web/components/Callout.js.zyzz.json +1 -1
  49. package/dist/web/components/Checkbox.css +1 -1
  50. package/dist/web/components/Checkbox.js.zyzz.json +1 -1
  51. package/dist/web/components/CheckboxGroup.js.zyzz.json +1 -1
  52. package/dist/web/components/ChoiceGroup.css +1 -1
  53. package/dist/web/components/ChoiceGroup.js.zyzz.json +1 -1
  54. package/dist/web/components/Choicebox.css +1 -1
  55. package/dist/web/components/Choicebox.js.zyzz.json +1 -1
  56. package/dist/web/components/Code.js.zyzz.json +1 -1
  57. package/dist/web/components/Combobox.css +1 -1
  58. package/dist/web/components/Combobox.js.zyzz.json +1 -1
  59. package/dist/web/components/Container.js.zyzz.json +1 -1
  60. package/dist/web/components/CopyButton.js.zyzz.json +1 -1
  61. package/dist/web/components/DatePicker.css +1 -1
  62. package/dist/web/components/DatePicker.js.zyzz.json +1 -1
  63. package/dist/web/components/Dialog.css +1 -1
  64. package/dist/web/components/Dialog.d.ts +14 -0
  65. package/dist/web/components/Dialog.d.ts.map +1 -1
  66. package/dist/web/components/Dialog.js +7 -5
  67. package/dist/web/components/Dialog.js.map +1 -1
  68. package/dist/web/components/Dialog.js.zyzz.json +1 -1
  69. package/dist/web/components/Drawer.css +1 -1
  70. package/dist/web/components/Drawer.js.zyzz.json +1 -1
  71. package/dist/web/components/Empty.js.zyzz.json +1 -1
  72. package/dist/web/components/Field.js.zyzz.json +1 -1
  73. package/dist/web/components/Fieldset.js.zyzz.json +1 -1
  74. package/dist/web/components/Frame.js.zyzz.json +1 -1
  75. package/dist/web/components/Gauge.js.zyzz.json +1 -1
  76. package/dist/web/components/GetStartedVisual.js.zyzz.json +1 -1
  77. package/dist/web/components/Grid.js.zyzz.json +1 -1
  78. package/dist/web/components/HeroVisual.js.zyzz.json +1 -1
  79. package/dist/web/components/Hex.js.zyzz.json +1 -1
  80. package/dist/web/components/IconTile.js.zyzz.json +1 -1
  81. package/dist/web/components/Identicon.js.zyzz.json +1 -1
  82. package/dist/web/components/Inline.js.zyzz.json +1 -1
  83. package/dist/web/components/Inset.js.zyzz.json +1 -1
  84. package/dist/web/components/Kbd.js.zyzz.json +1 -1
  85. package/dist/web/components/ListGroup.css +1 -1
  86. package/dist/web/components/ListGroup.d.ts +46 -4
  87. package/dist/web/components/ListGroup.d.ts.map +1 -1
  88. package/dist/web/components/ListGroup.js +11 -9
  89. package/dist/web/components/ListGroup.js.map +1 -1
  90. package/dist/web/components/ListGroup.js.zyzz.json +1 -1
  91. package/dist/web/components/LoadingDots.js.zyzz.json +1 -1
  92. package/dist/web/components/Menu.css +1 -1
  93. package/dist/web/components/Menu.js.zyzz.json +1 -1
  94. package/dist/web/components/NativeSelect.css +1 -1
  95. package/dist/web/components/NativeSelect.js.zyzz.json +1 -1
  96. package/dist/web/components/NetworkLogo.js.zyzz.json +1 -1
  97. package/dist/web/components/NumberField.js.zyzz.json +1 -1
  98. package/dist/web/components/OtpField.css +1 -1
  99. package/dist/web/components/OtpField.js.zyzz.json +1 -1
  100. package/dist/web/components/Pagination.css +1 -1
  101. package/dist/web/components/Pagination.js.zyzz.json +1 -1
  102. package/dist/web/components/Popover.css +1 -1
  103. package/dist/web/components/Popover.js.zyzz.json +1 -1
  104. package/dist/web/components/Progress.js.zyzz.json +1 -1
  105. package/dist/web/components/Qr.js.zyzz.json +1 -1
  106. package/dist/web/components/RadioGroup.css +1 -1
  107. package/dist/web/components/RadioGroup.js.zyzz.json +1 -1
  108. package/dist/web/components/SecretValue.js.zyzz.json +1 -1
  109. package/dist/web/components/Select.css +1 -1
  110. package/dist/web/components/Select.d.ts +14 -7
  111. package/dist/web/components/Select.d.ts.map +1 -1
  112. package/dist/web/components/Select.js +8 -6
  113. package/dist/web/components/Select.js.map +1 -1
  114. package/dist/web/components/Select.js.zyzz.json +1 -1
  115. package/dist/web/components/Separator.js.zyzz.json +1 -1
  116. package/dist/web/components/SidebarMenu.css +1 -1
  117. package/dist/web/components/SidebarMenu.d.ts +99 -9
  118. package/dist/web/components/SidebarMenu.d.ts.map +1 -1
  119. package/dist/web/components/SidebarMenu.js +56 -3
  120. package/dist/web/components/SidebarMenu.js.map +1 -1
  121. package/dist/web/components/SidebarMenu.js.zyzz.json +1 -1
  122. package/dist/web/components/Skeleton.js.zyzz.json +1 -1
  123. package/dist/web/components/Slider.js.zyzz.json +1 -1
  124. package/dist/web/components/Snippet.css +1 -1
  125. package/dist/web/components/Snippet.js.zyzz.json +1 -1
  126. package/dist/web/components/Spinner.css +1 -1
  127. package/dist/web/components/Spinner.d.ts +40 -12
  128. package/dist/web/components/Spinner.d.ts.map +1 -1
  129. package/dist/web/components/Spinner.js +8 -7
  130. package/dist/web/components/Spinner.js.map +1 -1
  131. package/dist/web/components/Spinner.js.zyzz.json +1 -1
  132. package/dist/web/components/SplitBar.js.zyzz.json +1 -1
  133. package/dist/web/components/SplitButton.js.zyzz.json +1 -1
  134. package/dist/web/components/Stack.js.zyzz.json +1 -1
  135. package/dist/web/components/StatusChip.js.zyzz.json +1 -1
  136. package/dist/web/components/StatusIndicator.js.zyzz.json +1 -1
  137. package/dist/web/components/StatusRow.js.zyzz.json +1 -1
  138. package/dist/web/components/Stepper.js.zyzz.json +1 -1
  139. package/dist/web/components/SuccessHero.css +1 -1
  140. package/dist/web/components/SuccessHero.d.ts.map +1 -1
  141. package/dist/web/components/SuccessHero.js +0 -2
  142. package/dist/web/components/SuccessHero.js.map +1 -1
  143. package/dist/web/components/SuccessHero.js.zyzz.json +1 -1
  144. package/dist/web/components/Surface.css +1 -1
  145. package/dist/web/components/Surface.d.ts +1 -1
  146. package/dist/web/components/Surface.js.map +1 -1
  147. package/dist/web/components/Surface.js.zyzz.json +1 -1
  148. package/dist/web/components/Switch.css +1 -1
  149. package/dist/web/components/Switch.d.ts +1 -1
  150. package/dist/web/components/Switch.js.map +1 -1
  151. package/dist/web/components/Switch.js.zyzz.json +1 -1
  152. package/dist/web/components/Table.css +1 -1
  153. package/dist/web/components/Table.js.zyzz.json +1 -1
  154. package/dist/web/components/Tabs.css +1 -1
  155. package/dist/web/components/Tabs.js.zyzz.json +1 -1
  156. package/dist/web/components/TempoLogo.js.zyzz.json +1 -1
  157. package/dist/web/components/TextInput.js.zyzz.json +1 -1
  158. package/dist/web/components/Textarea.css +1 -1
  159. package/dist/web/components/Textarea.js.zyzz.json +1 -1
  160. package/dist/web/components/Toast.css +1 -1
  161. package/dist/web/components/Toast.d.ts.map +1 -1
  162. package/dist/web/components/Toast.js +3 -3
  163. package/dist/web/components/Toast.js.map +1 -1
  164. package/dist/web/components/Toast.js.zyzz.json +1 -1
  165. package/dist/web/components/Token.css +1 -1
  166. package/dist/web/components/Token.d.ts.map +1 -1
  167. package/dist/web/components/Token.js +1 -1
  168. package/dist/web/components/Token.js.map +1 -1
  169. package/dist/web/components/Token.js.zyzz.json +1 -1
  170. package/dist/web/components/TokenLogo.js.zyzz.json +1 -1
  171. package/dist/web/components/Tooltip.css +1 -1
  172. package/dist/web/components/Tooltip.js.zyzz.json +1 -1
  173. package/dist/web/components/VerificationCode.js.zyzz.json +1 -1
  174. package/dist/web/components/Visual.css +1 -1
  175. package/dist/web/components/Visual.js.zyzz.json +1 -1
  176. package/dist/web/components/internal/Field.css +1 -1
  177. package/dist/web/components/internal/Field.js.zyzz.json +1 -1
  178. package/dist/web/components/internal/InputGroup.css +1 -1
  179. package/dist/web/components/internal/InputGroup.js.zyzz.json +1 -1
  180. package/dist/web/components/internal/Listbox.css +1 -1
  181. package/dist/web/components/internal/Listbox.js.zyzz.json +1 -1
  182. package/dist/web/components/internal/Streaks.js.zyzz.json +1 -1
  183. package/dist/web/index.d.ts +1 -0
  184. package/dist/web/index.d.ts.map +1 -1
  185. package/dist/web/index.js +1 -0
  186. package/dist/web/index.js.map +1 -1
  187. package/dist/web/index.js.zyzz.json +1 -1
  188. package/dist/web/vars.d.ts +34 -4
  189. package/dist/web/vars.d.ts.map +1 -1
  190. package/dist/web/vars.js.map +1 -1
  191. package/dist/web/vars.js.zyzz.json +1 -1
  192. package/dist/web/zyzz.config.d.ts +170 -20
  193. package/dist/web/zyzz.config.d.ts.map +1 -1
  194. package/dist/web/zyzz.config.js.zyzz.json +1 -1
  195. package/package.json +1 -1
  196. package/src/web/components/AmountInput.tsx +6 -1
  197. package/src/web/components/AppShell.tsx +19 -10
  198. package/src/web/components/Badge.tsx +1 -1
  199. package/src/web/components/BadgedLogo.tsx +125 -0
  200. package/src/web/components/BarChart.tsx +1 -1
  201. package/src/web/components/Button.tsx +66 -36
  202. package/src/web/components/Dialog.tsx +11 -0
  203. package/src/web/components/ListGroup.tsx +53 -7
  204. package/src/web/components/Select.tsx +15 -8
  205. package/src/web/components/SidebarMenu.tsx +162 -9
  206. package/src/web/components/Spinner.tsx +34 -11
  207. package/src/web/components/SuccessHero.tsx +2 -4
  208. package/src/web/components/Surface.tsx +1 -1
  209. package/src/web/components/Switch.tsx +1 -1
  210. package/src/web/components/Toast.tsx +14 -1
  211. package/src/web/components/Token.tsx +2 -4
  212. package/src/web/index.ts +1 -0
  213. package/src/web/vars.ts +25 -5
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tempoxyz/ds",
3
3
  "type": "module",
4
- "version": "0.3.6",
4
+ "version": "0.3.7",
5
5
  "license": "MIT",
6
6
  "files": [
7
7
  "DESIGN.md",
@@ -204,8 +204,13 @@ namespace styles {
204
204
  typography: 'body.b3',
205
205
  },
206
206
  '& > :is(p, [data-field-error])': { flexBasis: '100% !custom' },
207
- // A `Field.Description`, such as the balance, takes the tertiary color.
207
+ // A `Field.Description`, such as the balance, takes the tertiary color, and the negative
208
+ // color while the amount is invalid.
208
209
  '& > p': { color: 'content.tertiary' },
210
+ '&[data-invalid] > p, [data-invalid] & > p': { color: 'content.negative' },
211
+ // Focus keeps the fill at rest beside the inset focus border of the field. `:where` keeps the
212
+ // invalid and disabled fills above it.
213
+ '&:where(:has(input:focus-visible))': { backgroundColor: 'container.regular' },
209
214
  },
210
215
  },
211
216
  { id: 'tds/web/amount-input' },
@@ -710,6 +710,8 @@ namespace styles {
710
710
  { id: 'tds/web/app-shell' },
711
711
  )
712
712
 
713
+ // The Platform Wallet Sidebar pads its content by 20px, and by 32px above the header, which
714
+ // sits 32px above the menu.
713
715
  export const sidebar = variants(
714
716
  {
715
717
  base: {
@@ -719,9 +721,9 @@ namespace styles {
719
721
  flexDirection: 'column',
720
722
  flexShrink: '0 !custom',
721
723
  minHeight: '0',
722
- paddingBottom: '16',
723
- paddingInline: '12',
724
- rowGap: '16',
724
+ paddingBottom: '20',
725
+ paddingInline: '20',
726
+ rowGap: '32',
725
727
  },
726
728
  },
727
729
  { id: 'tds/web/app-shell/sidebar' },
@@ -744,7 +746,7 @@ namespace styles {
744
746
  // server-rendered markup, which always emits the wide layout.
745
747
  export const inline = style(
746
748
  {
747
- borderInlineEndColor: 'line.secondary',
749
+ borderInlineEndColor: 'line.tertiary',
748
750
  borderInlineEndStyle: 'solid',
749
751
  borderInlineEndWidth: '1px !custom',
750
752
  height: '100dvh !custom',
@@ -753,10 +755,15 @@ namespace styles {
753
755
  position: 'sticky',
754
756
  width: 'var(--sidebar-width, 16rem) !custom',
755
757
  '@media (max-width: 1023px)': { display: 'none !custom' },
756
- // A collapsed sidebar stays inline as a column of icons, which `SidebarMenu` centers.
758
+ // A collapsed sidebar stays inline as a column of icons, which `SidebarMenu` centers in 12px
759
+ // of padding.
757
760
  '@media (min-width: 768px) and (max-width: 1023px)': {
758
761
  selectors: {
759
- '&[data-app-shell-rail]': { display: 'flex !custom', width: '4rem !custom' },
762
+ '&[data-app-shell-rail]': {
763
+ display: 'flex !custom',
764
+ paddingInline: '12',
765
+ width: '4rem !custom',
766
+ },
760
767
  },
761
768
  },
762
769
  },
@@ -770,11 +777,11 @@ namespace styles {
770
777
  '--tds-sidebar-scrollbar-thumb': `${vars.color.line.primary}`,
771
778
  flex: '1 !custom',
772
779
  // Bleeding into the sidebar's inline padding puts the scrollbar at the sidebar edge.
773
- marginInline: '-12',
780
+ marginInline: '-20',
774
781
  minHeight: '0',
775
782
  overflowY: 'auto',
776
783
  overscrollBehavior: 'contain',
777
- paddingInline: '12',
784
+ paddingInline: '20',
778
785
  // A transparent track leaves only the thin thumb over the sidebar fill.
779
786
  scrollbarColor: 'var(--tds-sidebar-scrollbar-thumb) transparent !custom',
780
787
  // Classic scrollbars keep their own lane instead of covering content at the edge.
@@ -784,6 +791,8 @@ namespace styles {
784
791
  '@media (min-width: 768px) and (max-width: 1023px)': {
785
792
  selectors: {
786
793
  '[data-app-shell-rail] &': {
794
+ marginInline: '-12',
795
+ paddingInline: '12',
787
796
  scrollbarGutter: 'auto !custom',
788
797
  scrollbarWidth: 'none !custom',
789
798
  },
@@ -838,14 +847,14 @@ namespace styles {
838
847
  flexDirection: 'column',
839
848
  flexShrink: '0 !custom',
840
849
  marginTop: 'auto !custom',
841
- rowGap: '8',
850
+ rowGap: '12',
842
851
  },
843
852
  },
844
853
  { id: 'tds/web/app-shell/sidebar-footer' },
845
854
  )
846
855
 
847
856
  export const sidebarHeader = variants(
848
- { base: { flexShrink: '0 !custom', paddingTop: '16' } },
857
+ { base: { flexShrink: '0 !custom', paddingTop: '32' } },
849
858
  { id: 'tds/web/app-shell/sidebar-header' },
850
859
  )
851
860
 
@@ -139,7 +139,7 @@ namespace styles {
139
139
  variant: {
140
140
  // The accent text follows the inverse content color, which turns black in dark mode.
141
141
  accent: { backgroundColor: 'accent.blue.background', color: 'inverse.content.primary' },
142
- elevated: { backgroundColor: 'background.elevated', color: 'content.primary' },
142
+ elevated: { backgroundColor: 'background.secondary', color: 'content.primary' },
143
143
  inverse: {
144
144
  backgroundColor: 'inverse.background.secondary',
145
145
  color: 'inverse.content.primary',
@@ -0,0 +1,125 @@
1
+ /** The Badged Logo: a token or app logo with a small badge at its lower trailing corner. @module */
2
+ import type { ComponentPropsWithRef } from 'react'
3
+ import { style, vars } from '../zyzz.config.js'
4
+
5
+ /**
6
+ * A logo with a small round badge on its lower trailing corner, such as the arrow that marks a
7
+ * token or an app as the destination of a transfer. The root insets the logo 6px, so the badge and
8
+ * its ring stay inside its 56px box.
9
+ *
10
+ * - Put one logo first: a `TokenLogo`, or a `BadgedLogo.Tile` for an app without artwork. The root
11
+ * sizes it to 44px.
12
+ * - Put one `BadgedLogo.Badge` with one 12px icon last.
13
+ * - Name what the logo and the badge mean in text beside it. The parts add no semantics.
14
+ *
15
+ * @example
16
+ * <BadgedLogo.Root>
17
+ * <TokenLogo symbol="USDC" />
18
+ * <BadgedLogo.Badge>
19
+ * <ArrowRight />
20
+ * </BadgedLogo.Badge>
21
+ * </BadgedLogo.Root>
22
+ *
23
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/span | MDN span element}
24
+ * @see {@link https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html | WCAG 2.2 Non-text Content}
25
+ */
26
+ export namespace BadgedLogo {
27
+ /** Renders the 20px circle of the badge, which centers one 12px icon in the primary color. */
28
+ export function Badge(props: Badge.Props) {
29
+ const { className, ...attributes } = props
30
+
31
+ return (
32
+ <span
33
+ data-badged-logo-badge=""
34
+ {...attributes}
35
+ {...styles.badge({ className, style: props.style })}
36
+ />
37
+ )
38
+ }
39
+
40
+ export namespace Badge {
41
+ export type Props = ComponentPropsWithRef<'span'>
42
+ }
43
+
44
+ /** Renders the 56px `<span>` that holds the logo and its `BadgedLogo.Badge`. */
45
+ export function Root(props: Root.Props) {
46
+ const { className, ...attributes } = props
47
+
48
+ return <span {...attributes} {...styles.root({ className, style: props.style })} />
49
+ }
50
+
51
+ export namespace Root {
52
+ export type Props = ComponentPropsWithRef<'span'>
53
+ }
54
+
55
+ /**
56
+ * Renders a 44px rounded square that stands in for the logo of an app without artwork, such as
57
+ * the first letter of its name.
58
+ */
59
+ export function Tile(props: Tile.Props) {
60
+ const { className, ...attributes } = props
61
+
62
+ return <span {...attributes} {...styles.tile({ className, style: props.style })} />
63
+ }
64
+
65
+ export namespace Tile {
66
+ export type Props = ComponentPropsWithRef<'span'>
67
+ }
68
+ }
69
+
70
+ namespace styles {
71
+ // A 2px ring in the elevated background separates the badge from the logo below it.
72
+ export const badge = style(
73
+ {
74
+ alignItems: 'center',
75
+ backgroundColor: 'background.secondary',
76
+ borderRadius: 'full',
77
+ boxShadow: `0 0 0 2px ${vars.color.background.elevated} !custom`,
78
+ boxSizing: 'border-box',
79
+ color: 'content.primary',
80
+ cornerShape: 'round',
81
+ display: 'inline-flex',
82
+ height: '20',
83
+ insetBlockEnd: '4',
84
+ insetInlineEnd: '4',
85
+ justifyContent: 'center',
86
+ position: 'absolute',
87
+ width: '20',
88
+ selectors: { '& > svg': { flexShrink: '0 !custom', height: '12', width: '12' } },
89
+ },
90
+ { id: 'tds/web/badged-logo/badge' },
91
+ )
92
+
93
+ // The logo takes 44px whatever size it sets itself, such as the 32px of a large `TokenLogo`.
94
+ export const root = style(
95
+ {
96
+ alignItems: 'center',
97
+ display: 'inline-flex',
98
+ flexShrink: '0 !custom',
99
+ justifyContent: 'center',
100
+ padding: '6',
101
+ position: 'relative',
102
+ verticalAlign: 'middle',
103
+ selectors: {
104
+ '& > :not([data-badged-logo-badge])': { height: '44', width: '44' },
105
+ },
106
+ },
107
+ { id: 'tds/web/badged-logo' },
108
+ )
109
+
110
+ export const tile = style(
111
+ {
112
+ alignItems: 'center',
113
+ backgroundColor: 'container.regular',
114
+ borderRadius: 'xs',
115
+ boxSizing: 'border-box',
116
+ color: 'content.primary',
117
+ display: 'inline-flex',
118
+ height: '44',
119
+ justifyContent: 'center',
120
+ typography: 'heading.h3',
121
+ width: '44',
122
+ },
123
+ { id: 'tds/web/badged-logo/tile' },
124
+ )
125
+ }
@@ -406,7 +406,7 @@ namespace styles {
406
406
  export const tooltip = variants(
407
407
  {
408
408
  base: {
409
- backgroundColor: 'background.elevated',
409
+ backgroundColor: 'background.menu',
410
410
  borderRadius: 'sm',
411
411
  boxShadow: 'regular',
412
412
  boxSizing: 'border-box',
@@ -42,6 +42,7 @@ export function Button(props: Button.Props) {
42
42
  loading = false,
43
43
  shape,
44
44
  size,
45
+ underline,
45
46
  variant,
46
47
  vars,
47
48
  ...attributes
@@ -50,13 +51,22 @@ export function Button(props: Button.Props) {
50
51
  return (
51
52
  <BaseUi.Button
52
53
  {...attributes}
53
- {...styles.button({ appearance, className, shape, size, style: props.style, variant, vars })}
54
+ {...styles.button({
55
+ appearance,
56
+ className,
57
+ shape,
58
+ size,
59
+ style: props.style,
60
+ underline,
61
+ variant,
62
+ vars,
63
+ })}
54
64
  aria-busy={loading || undefined}
55
65
  data-loading={loading || undefined}
56
66
  disabled={disabled || loading}
57
67
  >
58
- <span {...styles.content({ loading })}>{children}</span>
59
- {loading && <Spinner aria-label="Loading" {...styles.spinner()} />}
68
+ {loading && <Spinner aria-label="Loading" />}
69
+ <span {...styles.content()}>{children}</span>
60
70
  </BaseUi.Button>
61
71
  )
62
72
  }
@@ -66,8 +76,8 @@ export namespace Button {
66
76
  zyzz.Props.Variants<typeof styles.button> & {
67
77
  disabled?: boolean | undefined
68
78
  /**
69
- * Disables the button, sets `aria-busy`, and swaps the label for a `Spinner` labelled
70
- * "Loading" while keeping the button's width.
79
+ * Disables the button, sets `aria-busy`, and renders a `Spinner` labelled "Loading" before
80
+ * the label, which stays visible.
71
81
  *
72
82
  * @default false
73
83
  */
@@ -87,6 +97,7 @@ export namespace Button {
87
97
  render,
88
98
  shape,
89
99
  size,
100
+ underline,
90
101
  variant,
91
102
  vars,
92
103
  ...attributes
@@ -96,8 +107,17 @@ export namespace Button {
96
107
  defaultTagName: 'a',
97
108
  props: {
98
109
  ...attributes,
99
- ...styles.button({ appearance, className, shape, size, style: props.style, variant, vars }),
100
- children: <span {...styles.content({ loading: false })}>{children}</span>,
110
+ ...styles.button({
111
+ appearance,
112
+ className,
113
+ shape,
114
+ size,
115
+ style: props.style,
116
+ underline,
117
+ variant,
118
+ vars,
119
+ }),
120
+ children: <span {...styles.content()}>{children}</span>,
101
121
  },
102
122
  ref,
103
123
  render,
@@ -163,9 +183,18 @@ namespace styles {
163
183
  flexShrink: '0 !custom',
164
184
  minWidth: '0',
165
185
  paddingInline: '0',
166
- selectors: { '& > span:first-child': { minWidth: '0 !custom' } },
186
+ // A loading icon button shows the spinner alone in the center of the circle.
187
+ selectors: {
188
+ '& > span:last-child': { minWidth: '0 !custom' },
189
+ '&[data-loading] > span:last-child': { display: 'none' },
190
+ },
167
191
  },
168
192
  },
193
+ // The large Platform Icon Button draws a 20px icon.
194
+ {
195
+ when: { shape: 'icon', size: 'large' },
196
+ style: { selectors: { '& > span > svg': { fontSize: '20' } } },
197
+ },
169
198
  // The Platform Icon Button fills its primary circle a step lighter than the primary
170
199
  // Button in light mode.
171
200
  {
@@ -261,7 +290,13 @@ namespace styles {
261
290
  },
262
291
  },
263
292
  ],
264
- defaultVariants: { appearance: 'base', shape: 'default', size: 'medium', variant: 'primary' },
293
+ defaultVariants: {
294
+ appearance: 'base',
295
+ shape: 'default',
296
+ size: 'medium',
297
+ underline: false,
298
+ variant: 'primary',
299
+ },
265
300
  variants: {
266
301
  /**
267
302
  * The surface under the button: the page for `base`, an inverse surface, such as
@@ -284,18 +319,18 @@ namespace styles {
284
319
  borderRadius: 'sm',
285
320
  height: '48',
286
321
  paddingInline: '32',
287
- selectors: { '& > span:first-child': { minWidth: '1rem !custom' } },
322
+ selectors: { '& > span:last-child': { minWidth: '1rem !custom' } },
288
323
  },
289
324
  medium: {
290
325
  borderRadius: 'sm',
291
326
  height: '40',
292
327
  paddingInline: '24',
293
- selectors: { '& > span:first-child': { minWidth: '2rem !custom' } },
328
+ selectors: { '& > span:last-child': { minWidth: '2rem !custom' } },
294
329
  },
295
330
  small: {
296
331
  height: '32',
297
332
  paddingInline: '16',
298
- selectors: { '& > span:first-child': { minWidth: '3rem !custom' } },
333
+ selectors: { '& > span:last-child': { minWidth: '3rem !custom' } },
299
334
  },
300
335
  xsmall: {
301
336
  borderRadius: 'full',
@@ -303,9 +338,21 @@ namespace styles {
303
338
  height: '24',
304
339
  paddingInline: '12',
305
340
  typography: 'body.b3Strong',
306
- selectors: { '& > span > svg': { fontSize: '12' } },
341
+ selectors: {
342
+ '& > [role="progressbar"]': { height: '12', width: '12' },
343
+ '& > span > svg': { fontSize: '12' },
344
+ },
307
345
  },
308
346
  },
347
+ /**
348
+ * Underlines the label, such as a `text` button inside running content.
349
+ *
350
+ * @default false
351
+ */
352
+ underline: {
353
+ false: {},
354
+ true: { textDecoration: 'underline !custom', textUnderlineOffset: '0.2em !custom' },
355
+ },
309
356
  // Each pressed fill follows its hover fill, so it wins while the pointer stays over the
310
357
  // button.
311
358
  variant: {
@@ -354,7 +401,7 @@ namespace styles {
354
401
  padding: '0',
355
402
  typography: 'body.b2Strong',
356
403
  selectors: {
357
- '& > span:first-child': { minWidth: '0 !custom' },
404
+ '& > span:last-child': { minWidth: '0 !custom' },
358
405
  '& > span > svg': { fontSize: '12' },
359
406
  '&:focus-visible': { outlineOffset: '2' },
360
407
  '&:hover:not(:disabled)': { color: 'content.primary' },
@@ -366,30 +413,13 @@ namespace styles {
366
413
  { id: 'tds/web/button' },
367
414
  )
368
415
 
369
- export const content = variants(
416
+ export const content = style(
370
417
  {
371
- base: {
372
- alignItems: 'inherit !custom',
373
- columnGap: 'inherit !custom',
374
- display: 'inline-flex',
375
- justifyContent: 'center',
376
- transitionDuration: '160',
377
- transitionProperty: 'opacity, visibility !custom',
378
- transitionTimingFunction: 'snappy',
379
- },
380
- defaultVariants: { loading: false },
381
- variants: { loading: { false: {}, true: { opacity: '0 !custom', visibility: 'hidden' } } },
418
+ alignItems: 'inherit !custom',
419
+ columnGap: 'inherit !custom',
420
+ display: 'inline-flex',
421
+ justifyContent: 'center',
382
422
  },
383
423
  { id: 'tds/web/button/content' },
384
424
  )
385
-
386
- export const spinner = style(
387
- {
388
- insetBlockStart: '50% !custom',
389
- insetInlineStart: '50% !custom',
390
- position: 'absolute',
391
- translate: '-50% -50% !custom',
392
- },
393
- { id: 'tds/web/button/spinner' },
394
- )
395
425
  }
@@ -114,6 +114,8 @@ export namespace Dialog {
114
114
  * - `stacked`: puts a `Dialog.Nav` above left-aligned text, for step 2 onward of a flow.
115
115
  * - `centered`: centers the hero and the text below a `Dialog.Nav`, for acknowledgments,
116
116
  * onboarding, progress and success.
117
+ * - `hero`: centers the parts as `centered` does, without the 16px below the `Dialog.Nav`, for
118
+ * a hero that brings its own space above it, such as a `Visual`.
117
119
  */
118
120
  export function Header(props: Header.Props) {
119
121
  const { className, layout, vars, ...attributes } = props
@@ -328,6 +330,15 @@ export namespace styles {
328
330
  textAlign: 'center',
329
331
  selectors: { '& > :is(div, h2, p)': { alignSelf: 'stretch' } },
330
332
  },
333
+ // A hero header centers its parts as a centered one does, directly below the nav row.
334
+ hero: {
335
+ alignItems: 'center',
336
+ textAlign: 'center',
337
+ selectors: {
338
+ '& > :is(div, h2, p)': { alignSelf: 'stretch' },
339
+ '& > [data-dialog-nav]': { marginBottom: '0' },
340
+ },
341
+ },
331
342
  // The text keeps 16px clear of the 40px close button, and starts 8px lower to meet it.
332
343
  inline: {
333
344
  selectors: { '[data-close-button] &': { paddingInlineEnd: '56', paddingTop: '8' } },
@@ -211,8 +211,10 @@ export namespace ListGroup {
211
211
 
212
212
  /**
213
213
  * Holds the selected `value` of the rows inside it, each a radio with its own `value`, in a
214
- * `radiogroup` element. The rows stack 12px apart, each in its own bordered block. The group is
215
- * one Tab stop, and arrow keys move focus and selection together. Pass the choices as a type
214
+ * `radiogroup` element. The rows stack 12px apart. The group is one Tab stop, and arrow keys move
215
+ * focus and selection together. `variant` sets how a row shows its selection: `radio` rows sit in
216
+ * bordered blocks with a ring at the end, and `option` rows, such as the accounts of a picker, pad
217
+ * 12px without a border or ring and fill when selected. Defaults to `variant="radio"`. Pass the choices as a type
216
218
  * argument, such as `ListGroup.RadioGroup<'maya' | 'acme'>`, to narrow `value`, `defaultValue`
217
219
  * and `onValueChange`.
218
220
  *
@@ -230,12 +232,13 @@ export namespace ListGroup {
230
232
  * </ListGroup.RadioGroup>
231
233
  */
232
234
  export function RadioGroup<const value extends string = string>(props: RadioGroup.Props<value>) {
233
- const { className, vars, ...attributes } = props
235
+ const { className, variant, vars, ...attributes } = props
234
236
 
235
237
  return (
236
238
  <BaseUiRadioGroup.RadioGroup
237
239
  {...attributes}
238
- {...styles.radioGroup({ className, style: props.style, vars })}
240
+ data-variant={variant ?? 'radio'}
241
+ {...styles.radioGroup({ className, style: props.style, variant, vars })}
239
242
  />
240
243
  )
241
244
  }
@@ -466,7 +469,7 @@ namespace styles {
466
469
  paddingInline: '8',
467
470
  width: 'auto !custom',
468
471
  },
469
- '& [data-select-trigger]:not(:disabled, [data-disabled], [data-invalid], [aria-invalid="true"])':
472
+ '& [data-select-trigger][role="combobox"]:is(*, :hover, [data-popup-open]):not(:disabled, [data-disabled], [data-invalid], [aria-invalid="true"])':
470
473
  {
471
474
  backgroundColor: 'transparent !custom',
472
475
  },
@@ -612,6 +615,12 @@ namespace styles {
612
615
  boxShadow: 'inset 0 0 0 5px currentColor !custom',
613
616
  color: 'content.primary',
614
617
  },
618
+ // An option row shows its selection with its fill, so its radio stays focusable but unseen.
619
+ '[data-variant="option"] > [data-radio] > &': {
620
+ opacity: '0 !custom',
621
+ pointerEvents: 'none',
622
+ position: 'absolute',
623
+ },
615
624
  },
616
625
  '@media (prefers-reduced-motion: reduce)': { transitionProperty: 'color' },
617
626
  },
@@ -627,6 +636,16 @@ namespace styles {
627
636
  rowGap: '12',
628
637
  typography: 'body.b2',
629
638
  },
639
+ defaultVariants: { variant: 'radio' },
640
+ variants: {
641
+ /**
642
+ * How a row shows its selection: `radio` with a bordered block and a ring, or `option` with
643
+ * a fill and no border.
644
+ *
645
+ * @default 'radio'
646
+ */
647
+ variant: { option: {}, radio: {} },
648
+ },
630
649
  },
631
650
  { id: 'tds/web/list-group/radio-group' },
632
651
  )
@@ -672,7 +691,7 @@ namespace styles {
672
691
  // The block keeps a 16px radius with 16px sides, below the usual one and a half times.
673
692
  variant: {
674
693
  boxed: {
675
- backgroundColor: 'background.secondary',
694
+ backgroundColor: 'background.elevated',
676
695
  borderColor: 'line.secondary',
677
696
  borderRadius: 'sm',
678
697
  borderStyle: 'solid',
@@ -682,7 +701,7 @@ namespace styles {
682
701
  },
683
702
  plain: {},
684
703
  spaced: {
685
- backgroundColor: 'background.secondary',
704
+ backgroundColor: 'background.elevated',
686
705
  borderColor: 'line.secondary',
687
706
  borderRadius: 'sm',
688
707
  borderStyle: 'solid',
@@ -804,10 +823,37 @@ namespace styles {
804
823
  cursor: 'default',
805
824
  opacity: '0.4 !custom',
806
825
  },
826
+ // An option row keeps a transparent 1px border inside its 12px padding. It fills on hover
827
+ // and when selected, and draws the focus ring as an outline.
828
+ '[data-variant="option"] > &[data-radio]': {
829
+ backgroundColor: 'transparent !custom',
830
+ borderColor: 'transparent !custom',
831
+ paddingBlock: 'calc(0.75rem - 1px) !custom',
832
+ paddingInline: 'calc(0.75rem - 1px) !custom',
833
+ transitionProperty: 'background-color, opacity, outline-color, scale !custom',
834
+ },
835
+ '[data-variant="option"] > &[data-radio]:hover:not(:has(> [data-disabled]))': {
836
+ backgroundColor: 'action.tertiary.fill.hover',
837
+ borderColor: 'transparent !custom',
838
+ },
839
+ '[data-variant="option"] > &[data-radio]:has(> [data-checked]):is(*, :hover)': {
840
+ backgroundColor: 'container.subtle',
841
+ borderColor: 'transparent !custom',
842
+ borderWidth: 'regular',
843
+ },
844
+ '[data-variant="option"] > &[data-radio]:has(> [role="radio"]:focus-visible)': {
845
+ borderColor: 'transparent !custom',
846
+ borderWidth: 'regular',
847
+ outlineColor: 'border.focus',
848
+ outlineOffset: '-2',
849
+ },
807
850
  },
808
851
  '@media (prefers-reduced-motion: reduce)': {
809
852
  selectors: {
810
853
  '&[data-radio]': { transitionProperty: 'background-color, border-color, opacity' },
854
+ '[data-variant="option"] > &[data-radio]': {
855
+ transitionProperty: 'background-color, opacity, outline-color',
856
+ },
811
857
  '&[data-radio]:active': { scale: 'none !custom' },
812
858
  },
813
859
  },
@@ -255,9 +255,10 @@ export namespace Select {
255
255
  }
256
256
 
257
257
  /**
258
- * Matches `TextInput.Group` in height, variant, fill, radius and invalid treatment, and spaces
259
- * its parts 6px apart. Focus keeps the fill and draws a 2px ring, which adds 2px to the padding
260
- * on each side. A disabled trigger dims to 40% opacity, and an open one takes the hover fill.
258
+ * Matches `TextInput.Group` in height, variant, radius and invalid treatment, and spaces its parts
259
+ * 6px apart. The `default` trigger takes the quiet control fill, which darkens on hover. Focus
260
+ * keeps the fill and draws a 2px ring, which adds 2px to the padding on each side. A disabled
261
+ * trigger dims to 40% opacity, and an open one takes the hover fill.
261
262
  */
262
263
  export function Trigger(props: Trigger.Props) {
263
264
  const { className, invalid, variant, vars, ...attributes } = props
@@ -338,13 +339,14 @@ namespace styles {
338
339
 
339
340
  // The trigger also takes the shared field surface, whose stylesheet can land anywhere in the
340
341
  // cascade, so each rule here outranks it with an attribute that Base UI sets. Unlike that
341
- // surface, focus keeps the fill and a disabled trigger keeps its colors at 40% opacity.
342
+ // surface, the trigger fills with the quiet control fill, focus keeps the fill, and a disabled
343
+ // trigger keeps its colors at 40% opacity.
342
344
  export const trigger = variants(
343
345
  {
344
346
  base: {
345
347
  selectors: {
346
- '&[data-popup-open]:not([data-invalid], :disabled, [data-disabled])': {
347
- backgroundColor: 'container.strong',
348
+ '&[role="combobox"][data-popup-open]:not([data-invalid], :disabled, [data-disabled])': {
349
+ backgroundColor: 'action.secondary.fill.hover',
348
350
  },
349
351
  '&[role="combobox"]': { columnGap: '6' },
350
352
  // The 2px focus ring adds its width to the padding, so the content moves 2px inward and
@@ -363,11 +365,16 @@ namespace styles {
363
365
  variant: {
364
366
  default: {
365
367
  selectors: {
368
+ '&[role="combobox"]:not([data-invalid], [data-invalid] *)': {
369
+ backgroundColor: 'container.control',
370
+ },
371
+ '&[role="combobox"]:hover:not([data-invalid], [data-invalid] *, :disabled, [data-disabled])':
372
+ { backgroundColor: 'action.secondary.fill.hover' },
366
373
  '&[role="combobox"]:focus-visible:not([data-invalid], [data-invalid] *)': {
367
- backgroundColor: 'container.regular',
374
+ backgroundColor: 'container.control',
368
375
  },
369
376
  '&[role="combobox"]:is(:disabled, [data-disabled])': {
370
- backgroundColor: 'container.regular',
377
+ backgroundColor: 'container.control',
371
378
  },
372
379
  },
373
380
  },