@tempoxyz/ds 0.3.10 → 0.3.13

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 (210) hide show
  1. package/README.md +7 -6
  2. package/dist/docs/AGENTS.md +12 -0
  3. package/dist/docs/index.md +145 -0
  4. package/dist/docs/marketing/introduction/getting-started.md +241 -0
  5. package/dist/docs/product/components/accordion.md +489 -0
  6. package/dist/docs/product/components/address-card.md +147 -0
  7. package/dist/docs/product/components/agent-logo.md +194 -0
  8. package/dist/docs/product/components/alert-dialog.md +631 -0
  9. package/dist/docs/product/components/alert.md +448 -0
  10. package/dist/docs/product/components/amount-input.md +305 -0
  11. package/dist/docs/product/components/amount.md +794 -0
  12. package/dist/docs/product/components/app-shell.md +559 -0
  13. package/dist/docs/product/components/asset-chip.md +294 -0
  14. package/dist/docs/product/components/autocomplete.md +620 -0
  15. package/dist/docs/product/components/avatar.md +321 -0
  16. package/dist/docs/product/components/badge.md +339 -0
  17. package/dist/docs/product/components/badged-logo.md +116 -0
  18. package/dist/docs/product/components/banner.md +379 -0
  19. package/dist/docs/product/components/bar-chart.md +208 -0
  20. package/dist/docs/product/components/breadcrumbs.md +380 -0
  21. package/dist/docs/product/components/button-group.md +286 -0
  22. package/dist/docs/product/components/button.md +650 -0
  23. package/dist/docs/product/components/calendar.md +375 -0
  24. package/dist/docs/product/components/callout.md +96 -0
  25. package/dist/docs/product/components/checkbox-group.md +521 -0
  26. package/dist/docs/product/components/checkbox.md +467 -0
  27. package/dist/docs/product/components/choice-group.md +516 -0
  28. package/dist/docs/product/components/choicebox.md +641 -0
  29. package/dist/docs/product/components/code.md +100 -0
  30. package/dist/docs/product/components/combobox.md +939 -0
  31. package/dist/docs/product/components/container.md +257 -0
  32. package/dist/docs/product/components/copy-button.md +642 -0
  33. package/dist/docs/product/components/date-picker.md +514 -0
  34. package/dist/docs/product/components/dialog.md +1332 -0
  35. package/dist/docs/product/components/drawer.md +966 -0
  36. package/dist/docs/product/components/empty.md +340 -0
  37. package/dist/docs/product/components/field.md +570 -0
  38. package/dist/docs/product/components/fieldset.md +398 -0
  39. package/dist/docs/product/components/frame.md +434 -0
  40. package/dist/docs/product/components/gauge.md +299 -0
  41. package/dist/docs/product/components/get-started-visual.md +117 -0
  42. package/dist/docs/product/components/grid.md +288 -0
  43. package/dist/docs/product/components/hero-visual.md +155 -0
  44. package/dist/docs/product/components/hex.md +487 -0
  45. package/dist/docs/product/components/icon-tile.md +292 -0
  46. package/dist/docs/product/components/identicon.md +371 -0
  47. package/dist/docs/product/components/inline.md +233 -0
  48. package/dist/docs/product/components/inset.md +225 -0
  49. package/dist/docs/product/components/kbd.md +423 -0
  50. package/dist/docs/product/components/list-group.md +981 -0
  51. package/dist/docs/product/components/loading-dots.md +237 -0
  52. package/dist/docs/product/components/menu.md +934 -0
  53. package/dist/docs/product/components/native-select.md +204 -0
  54. package/dist/docs/product/components/network-logo.md +248 -0
  55. package/dist/docs/product/components/number-field.md +543 -0
  56. package/dist/docs/product/components/otp-field.md +616 -0
  57. package/dist/docs/product/components/pagination.md +516 -0
  58. package/dist/docs/product/components/popover.md +544 -0
  59. package/dist/docs/product/components/progress.md +388 -0
  60. package/dist/docs/product/components/qr.md +314 -0
  61. package/dist/docs/product/components/radio-group.md +528 -0
  62. package/dist/docs/product/components/secret-value.md +406 -0
  63. package/dist/docs/product/components/select.md +751 -0
  64. package/dist/docs/product/components/separator.md +379 -0
  65. package/dist/docs/product/components/sidebar-menu.md +922 -0
  66. package/dist/docs/product/components/skeleton.md +517 -0
  67. package/dist/docs/product/components/slider.md +539 -0
  68. package/dist/docs/product/components/snippet.md +424 -0
  69. package/dist/docs/product/components/spinner.md +216 -0
  70. package/dist/docs/product/components/split-bar.md +139 -0
  71. package/dist/docs/product/components/split-button.md +464 -0
  72. package/dist/docs/product/components/stack.md +299 -0
  73. package/dist/docs/product/components/status-chip.md +226 -0
  74. package/dist/docs/product/components/status-indicator.md +119 -0
  75. package/dist/docs/product/components/status-row.md +174 -0
  76. package/dist/docs/product/components/stepper.md +451 -0
  77. package/dist/docs/product/components/success-hero.md +157 -0
  78. package/dist/docs/product/components/surface.md +639 -0
  79. package/dist/docs/product/components/switch.md +472 -0
  80. package/dist/docs/product/components/table.md +967 -0
  81. package/dist/docs/product/components/tabs.md +627 -0
  82. package/dist/docs/product/components/tempo-logo.md +185 -0
  83. package/dist/docs/product/components/text-input.md +624 -0
  84. package/dist/docs/product/components/textarea.md +352 -0
  85. package/dist/docs/product/components/toast.md +535 -0
  86. package/dist/docs/product/components/token-logo.md +371 -0
  87. package/dist/docs/product/components/token.md +247 -0
  88. package/dist/docs/product/components/tooltip.md +490 -0
  89. package/dist/docs/product/components/verification-code.md +116 -0
  90. package/dist/docs/product/components/visual.md +249 -0
  91. package/dist/docs/product/components.md +203 -0
  92. package/dist/docs/product/foundations/accessibility.md +377 -0
  93. package/dist/docs/product/foundations/agents.md +150 -0
  94. package/dist/docs/product/foundations/color.md +522 -0
  95. package/dist/docs/product/foundations/content/tone.md +194 -0
  96. package/dist/docs/product/foundations/content/voice.md +194 -0
  97. package/dist/docs/product/foundations/content/writing-style.md +406 -0
  98. package/dist/docs/product/foundations/iconography.md +282 -0
  99. package/dist/docs/product/foundations/intent.md +360 -0
  100. package/dist/docs/product/foundations/layout.md +508 -0
  101. package/dist/docs/product/foundations/linter.md +496 -0
  102. package/dist/docs/product/foundations/motion.md +151 -0
  103. package/dist/docs/product/foundations/radius-and-padding.md +86 -0
  104. package/dist/docs/product/foundations/typography.md +266 -0
  105. package/dist/docs/product/introduction/faq.md +133 -0
  106. package/dist/docs/product/introduction/getting-started.md +377 -0
  107. package/dist/docs/product/patterns/blockchain-values.md +709 -0
  108. package/dist/docs/product/patterns/dashboards.md +638 -0
  109. package/dist/docs/product/patterns/destructive-actions.md +626 -0
  110. package/dist/docs/product/patterns/empty-states.md +612 -0
  111. package/dist/docs/product/patterns/errors-and-validation.md +808 -0
  112. package/dist/docs/product/patterns/feedback-and-status.md +518 -0
  113. package/dist/docs/product/patterns/forms.md +679 -0
  114. package/dist/docs/product/patterns/loading.md +630 -0
  115. package/dist/docs/product/patterns/navigation.md +397 -0
  116. package/dist/docs/product/patterns/payments.md +765 -0
  117. package/dist/docs/product/patterns/responsive-layout.md +997 -0
  118. package/dist/docs/product/patterns/tables-and-lists.md +964 -0
  119. package/dist/docs/product/products/tempo-console.md +671 -0
  120. package/dist/docs/product/products/tempo-performance.md +437 -0
  121. package/dist/docs/product/products/tempo-wallet.md +438 -0
  122. package/dist/marketing/index.d.ts +4 -0
  123. package/dist/marketing/index.d.ts.map +1 -0
  124. package/dist/marketing/index.js +4 -0
  125. package/dist/marketing/index.js.map +1 -0
  126. package/dist/marketing/index.js.zyzz.json +1 -0
  127. package/dist/marketing/vars.d.ts +907 -0
  128. package/dist/marketing/vars.d.ts.map +1 -0
  129. package/dist/marketing/vars.js +15 -0
  130. package/dist/marketing/vars.js.map +1 -0
  131. package/dist/marketing/vars.js.zyzz.json +1 -0
  132. package/dist/marketing/zyzz.config.d.ts +4512 -0
  133. package/dist/marketing/zyzz.config.d.ts.map +1 -0
  134. package/dist/marketing/zyzz.config.js +63 -0
  135. package/dist/marketing/zyzz.config.js.map +1 -0
  136. package/dist/marketing/zyzz.config.js.zyzz.json +1 -0
  137. package/dist/oxlint/index.d.ts +12 -1
  138. package/dist/oxlint/index.d.ts.map +1 -1
  139. package/dist/oxlint/index.js +39 -1
  140. package/dist/oxlint/index.js.map +1 -1
  141. package/dist/oxlint/internal/Docs.d.ts +9 -0
  142. package/dist/oxlint/internal/Docs.d.ts.map +1 -0
  143. package/dist/oxlint/internal/Docs.js +15 -0
  144. package/dist/oxlint/internal/Docs.js.map +1 -0
  145. package/dist/oxlint/internal/Imports.d.ts +1 -1
  146. package/dist/oxlint/internal/Imports.d.ts.map +1 -1
  147. package/dist/oxlint/internal/Imports.js +2 -0
  148. package/dist/oxlint/internal/Imports.js.map +1 -1
  149. package/dist/oxlint/internal/Styles.d.ts +40 -0
  150. package/dist/oxlint/internal/Styles.d.ts.map +1 -0
  151. package/dist/oxlint/internal/Styles.js +194 -0
  152. package/dist/oxlint/internal/Styles.js.map +1 -0
  153. package/dist/oxlint/internal/Tokens.d.ts +16 -0
  154. package/dist/oxlint/internal/Tokens.d.ts.map +1 -0
  155. package/dist/oxlint/internal/Tokens.js +39 -0
  156. package/dist/oxlint/internal/Tokens.js.map +1 -0
  157. package/dist/oxlint/rules/NoCrossSurface.d.ts +2 -2
  158. package/dist/oxlint/rules/NoCrossSurface.js +3 -3
  159. package/dist/oxlint/rules/NoCrossSurface.js.map +1 -1
  160. package/dist/oxlint/rules/NoCustomValues.d.ts +11 -0
  161. package/dist/oxlint/rules/NoCustomValues.d.ts.map +1 -0
  162. package/dist/oxlint/rules/NoCustomValues.js +150 -0
  163. package/dist/oxlint/rules/NoCustomValues.js.map +1 -0
  164. package/dist/oxlint/rules/NoSingleKeyShortcuts.d.ts +11 -0
  165. package/dist/oxlint/rules/NoSingleKeyShortcuts.d.ts.map +1 -0
  166. package/dist/oxlint/rules/NoSingleKeyShortcuts.js +158 -0
  167. package/dist/oxlint/rules/NoSingleKeyShortcuts.js.map +1 -0
  168. package/dist/oxlint/rules/PreferComponent.d.ts +3 -2
  169. package/dist/oxlint/rules/PreferComponent.d.ts.map +1 -1
  170. package/dist/oxlint/rules/PreferComponent.js +11 -4
  171. package/dist/oxlint/rules/PreferComponent.js.map +1 -1
  172. package/dist/oxlint/rules/PreferLayout.d.ts +9 -0
  173. package/dist/oxlint/rules/PreferLayout.d.ts.map +1 -0
  174. package/dist/oxlint/rules/PreferLayout.js +90 -0
  175. package/dist/oxlint/rules/PreferLayout.js.map +1 -0
  176. package/dist/oxlint/rules/PreferProgress.d.ts +9 -0
  177. package/dist/oxlint/rules/PreferProgress.d.ts.map +1 -0
  178. package/dist/oxlint/rules/PreferProgress.js +43 -0
  179. package/dist/oxlint/rules/PreferProgress.js.map +1 -0
  180. package/dist/oxlint/rules/PreferSurface.d.ts +9 -0
  181. package/dist/oxlint/rules/PreferSurface.d.ts.map +1 -0
  182. package/dist/oxlint/rules/PreferSurface.js +55 -0
  183. package/dist/oxlint/rules/PreferSurface.js.map +1 -0
  184. package/dist/oxlint/rules/RequireAccessibleName.d.ts.map +1 -1
  185. package/dist/oxlint/rules/RequireAccessibleName.js +3 -1
  186. package/dist/oxlint/rules/RequireAccessibleName.js.map +1 -1
  187. package/dist/oxlint/rules/RequireScrollRegion.d.ts +11 -0
  188. package/dist/oxlint/rules/RequireScrollRegion.d.ts.map +1 -0
  189. package/dist/oxlint/rules/RequireScrollRegion.js +71 -0
  190. package/dist/oxlint/rules/RequireScrollRegion.js.map +1 -0
  191. package/dist/web/icons/index.js.zyzz.json +1 -0
  192. package/package.json +6 -1
  193. package/src/marketing/README.md +21 -0
  194. package/src/marketing/index.ts +3 -0
  195. package/src/marketing/vars.ts +751 -0
  196. package/src/marketing/zyzz.config.ts +376 -0
  197. package/src/oxlint/index.ts +40 -1
  198. package/src/oxlint/internal/Docs.ts +18 -0
  199. package/src/oxlint/internal/Imports.ts +2 -1
  200. package/src/oxlint/internal/Styles.ts +240 -0
  201. package/src/oxlint/internal/Tokens.ts +43 -0
  202. package/src/oxlint/rules/NoCrossSurface.ts +3 -3
  203. package/src/oxlint/rules/NoCustomValues.ts +170 -0
  204. package/src/oxlint/rules/NoSingleKeyShortcuts.ts +190 -0
  205. package/src/oxlint/rules/PreferComponent.ts +11 -4
  206. package/src/oxlint/rules/PreferLayout.ts +96 -0
  207. package/src/oxlint/rules/PreferProgress.ts +49 -0
  208. package/src/oxlint/rules/PreferSurface.ts +70 -0
  209. package/src/oxlint/rules/RequireAccessibleName.ts +3 -1
  210. package/src/oxlint/rules/RequireScrollRegion.ts +92 -0
@@ -0,0 +1,624 @@
1
+ # TextInput
2
+
3
+ A text field with optional prefix, suffix, and clear button.
4
+
5
+ [View source and API](https://github.com/tempoxyz/ds/blob/main/src/web/components/TextInput.tsx)
6
+
7
+ Figma: [Inputs/Input](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-3846) (`TextInput.Group`), [Inputs/Search Input](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-2265) (`TextInput.Group`), [Inputs/Text Input](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-1285) (`TextInput.Group`), [Search](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=1186-3365) (`TextInput.Group`), [Text Input](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=1186-3316) (`TextInput.Group`)
8
+
9
+ ## Overview
10
+
11
+ An input is a filled text field composed from a group, an optional prefix and suffix, a control, and a clear button.
12
+
13
+ ## Examples
14
+
15
+ ### Basic
16
+
17
+ ```tsx
18
+ import { Field, style, TextInput } from '@tempoxyz/ds'
19
+
20
+ export const fill = true
21
+ export function Demo() {
22
+ return (
23
+ <Field.Root {...styles.field()}>
24
+ <Field.Label>To</Field.Label>
25
+ <TextInput.Group>
26
+ <TextInput.Control autoComplete="email" placeholder="Address, email or contact" />
27
+ </TextInput.Group>
28
+ <Field.Description>Paste an address or pick a contact</Field.Description>
29
+ </Field.Root>
30
+ )
31
+ }
32
+
33
+ namespace styles {
34
+ export const field = style({ maxWidth: '24rem !custom' })
35
+ }
36
+ ```
37
+
38
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/TextInput/Basic.tsx)
39
+
40
+ ### Icons
41
+
42
+ Put an icon that gives the field its meaning, such as `Search` or `Mail`, in `TextInput.Prefix`. Put an icon for an action on the field, such as paste, in `TextInput.Suffix`.
43
+
44
+ ```tsx
45
+ import { Field, style, TextInput } from '@tempoxyz/ds'
46
+ import { Clipboard, Search } from '@tempoxyz/ds/icons'
47
+
48
+ export const fill = true
49
+ export function Demo() {
50
+ return (
51
+ <Field.Root {...styles.field()}>
52
+ <Field.Label>Recipient</Field.Label>
53
+ <TextInput.Group>
54
+ <TextInput.Prefix>
55
+ <Search />
56
+ </TextInput.Prefix>
57
+ <TextInput.Control defaultValue="maya@studio.co" />
58
+ <TextInput.Suffix>
59
+ <Clipboard />
60
+ </TextInput.Suffix>
61
+ </TextInput.Group>
62
+ <Field.Description>Paste an address or pick a contact</Field.Description>
63
+ </Field.Root>
64
+ )
65
+ }
66
+
67
+ namespace styles {
68
+ export const field = style({ maxWidth: '24rem !custom' })
69
+ }
70
+ ```
71
+
72
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/TextInput/Icons.tsx)
73
+
74
+ ### Search
75
+
76
+ Add a `Search` icon in `TextInput.Prefix` and a `TextInput.Clear`. The clear button shows once the field has a value, and Escape empties the field before it closes an enclosing [Dialog](dialog.md).
77
+
78
+ ```tsx
79
+ import { Field, style, TextInput } from '@tempoxyz/ds'
80
+ import { Search } from '@tempoxyz/ds/icons'
81
+
82
+ export const fill = true
83
+ export function Demo() {
84
+ return (
85
+ <Field.Root {...styles.field()}>
86
+ <TextInput.Group>
87
+ <TextInput.Prefix>
88
+ <Search />
89
+ </TextInput.Prefix>
90
+ <TextInput.Control
91
+ aria-label="Search contacts"
92
+ defaultValue="Maya"
93
+ placeholder="Name, email or address"
94
+ type="search"
95
+ />
96
+ <TextInput.Clear />
97
+ </TextInput.Group>
98
+ </Field.Root>
99
+ )
100
+ }
101
+
102
+ namespace styles {
103
+ export const field = style({ maxWidth: '24rem !custom' })
104
+ }
105
+ ```
106
+
107
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/TextInput/Search.tsx)
108
+
109
+ ### Variant
110
+
111
+ Set `variant` on `TextInput.Group` to `default` (default) or `outlined`, which draws a 1px line instead of the fill.
112
+
113
+ ```tsx
114
+ import { Field, style, TextInput } from '@tempoxyz/ds'
115
+
116
+ export function Demo() {
117
+ return (
118
+ <div {...styles.grid()}>
119
+ {(['default', 'outlined'] as const).map((variant) => (
120
+ <Field.Root key={variant}>
121
+ <Field.Label>To</Field.Label>
122
+ <TextInput.Group variant={variant}>
123
+ <TextInput.Control placeholder="Address, email or contact" />
124
+ </TextInput.Group>
125
+ <Field.Description>Paste an address or pick a contact</Field.Description>
126
+ </Field.Root>
127
+ ))}
128
+ </div>
129
+ )
130
+ }
131
+
132
+ namespace styles {
133
+ export const grid = style({
134
+ alignItems: 'start',
135
+ display: 'grid',
136
+ gap: '24',
137
+ gridTemplateColumns: 'repeat(auto-fill, minmax(min(280px, 100%), 1fr)) !custom',
138
+ })
139
+ }
140
+ ```
141
+
142
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/TextInput/Variant.tsx)
143
+
144
+ ### Size
145
+
146
+ Set `size` on `TextInput.Group` to `medium` (default) or `large`, a 52px search bar with 20px icons and larger corners.
147
+
148
+ ```tsx
149
+ import { Field, style, TextInput } from '@tempoxyz/ds'
150
+ import { Search } from '@tempoxyz/ds/icons'
151
+
152
+ export function Demo() {
153
+ return (
154
+ <div {...styles.grid()}>
155
+ {(['medium', 'large'] as const).map((size) => (
156
+ <Field.Root key={size}>
157
+ <TextInput.Group size={size}>
158
+ <TextInput.Prefix>
159
+ <Search />
160
+ </TextInput.Prefix>
161
+ <TextInput.Control
162
+ aria-label="Search tokens"
163
+ placeholder="Search tokens"
164
+ type="search"
165
+ />
166
+ <TextInput.Clear />
167
+ </TextInput.Group>
168
+ </Field.Root>
169
+ ))}
170
+ </div>
171
+ )
172
+ }
173
+
174
+ namespace styles {
175
+ export const grid = style({
176
+ alignItems: 'start',
177
+ display: 'grid',
178
+ gap: '24',
179
+ gridTemplateColumns: 'repeat(auto-fill, minmax(min(280px, 100%), 1fr)) !custom',
180
+ })
181
+ }
182
+ ```
183
+
184
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/TextInput/Size.tsx)
185
+
186
+ ### Appearance
187
+
188
+ Set `appearance` on `TextInput.Group` to `base` (default) or `overlay`, which draws a translucent fill and white text over an image or video.
189
+
190
+ ```tsx
191
+ import { Field, style, TextInput } from '@tempoxyz/ds'
192
+ import { Search } from '@tempoxyz/ds/icons'
193
+
194
+ export const fill = true
195
+ export function Demo() {
196
+ return (
197
+ <div {...styles.backdrop()}>
198
+ <Field.Root {...styles.field()}>
199
+ <TextInput.Group appearance="overlay" size="large">
200
+ <TextInput.Prefix>
201
+ <Search />
202
+ </TextInput.Prefix>
203
+ <TextInput.Control aria-label="Search places" placeholder="Search places" type="search" />
204
+ <TextInput.Clear />
205
+ </TextInput.Group>
206
+ </Field.Root>
207
+ </div>
208
+ )
209
+ }
210
+
211
+ namespace styles {
212
+ // A dark gradient stands in for the image or video under an overlay field.
213
+ export const backdrop = style({
214
+ backgroundImage: 'linear-gradient(135deg, #3d3d3d, #141414) !custom',
215
+ borderRadius: 'sm',
216
+ padding: '32',
217
+ })
218
+
219
+ export const field = style({ maxWidth: '24rem !custom' })
220
+ }
221
+ ```
222
+
223
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/TextInput/Appearance.tsx)
224
+
225
+ ### Invalid
226
+
227
+ Set `invalid` or `success` on `TextInput.Group`, or inherit either from `Field.Root`. A state set after mount plays a one-shot nudge or pop.
228
+
229
+ ```tsx
230
+ import { Field, style, TextInput } from '@tempoxyz/ds'
231
+ import { useState } from 'react'
232
+
233
+ export function Demo() {
234
+ return (
235
+ <div {...styles.grid()}>
236
+ <Send />
237
+ <Field.Root
238
+ validate={(value) => (String(value).includes('@') ? null : 'Enter a valid email.')}
239
+ validationMode="onChange"
240
+ >
241
+ <Field.Label>Email</Field.Label>
242
+ <TextInput.Group>
243
+ <TextInput.Control defaultValue="alice@example.com" type="email" />
244
+ </TextInput.Group>
245
+ <Field.Description>Remove the @ to swap in the error.</Field.Description>
246
+ <Field.Error />
247
+ </Field.Root>
248
+ <Field.Root invalid>
249
+ <Field.Label>Email</Field.Label>
250
+ <TextInput.Group>
251
+ <TextInput.Control defaultValue="alice" type="email" />
252
+ </TextInput.Group>
253
+ <Field.Description>Used for receipts.</Field.Description>
254
+ <Field.Error match>Enter a valid email.</Field.Error>
255
+ </Field.Root>
256
+ </div>
257
+ )
258
+ }
259
+
260
+ function Send() {
261
+ const [amount, setAmount] = useState('')
262
+ const invalid = Number(amount) > 1240.5
263
+
264
+ return (
265
+ <Field.Root invalid={invalid} success={amount !== '' && !invalid}>
266
+ <Field.Label>You send</Field.Label>
267
+ <TextInput.Group>
268
+ <TextInput.Prefix>$</TextInput.Prefix>
269
+ <TextInput.Control inputMode="decimal" onValueChange={setAmount} placeholder="0.00" />
270
+ <TextInput.Suffix>USDC</TextInput.Suffix>
271
+ </TextInput.Group>
272
+ <Field.Description>Balance 1,240.50 USDC</Field.Description>
273
+ <Field.Error match={invalid}>Exceeds your balance.</Field.Error>
274
+ </Field.Root>
275
+ )
276
+ }
277
+
278
+ namespace styles {
279
+ export const grid = style({
280
+ alignItems: 'start',
281
+ display: 'grid',
282
+ gap: '24',
283
+ gridTemplateColumns: 'repeat(auto-fill, minmax(min(280px, 100%), 1fr)) !custom',
284
+ })
285
+ }
286
+ ```
287
+
288
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/TextInput/Invalid.tsx)
289
+
290
+ ### Disabled
291
+
292
+ Disable a field only when the app sets the value, and say why in the `Field.Description`.
293
+
294
+ ```tsx
295
+ import { Field, style, TextInput } from '@tempoxyz/ds'
296
+
297
+ export const fill = true
298
+ export function Demo() {
299
+ return (
300
+ <Field.Root disabled {...styles.field()}>
301
+ <Field.Label>Network</Field.Label>
302
+ <TextInput.Group>
303
+ <TextInput.Control defaultValue="Tempo" />
304
+ </TextInput.Group>
305
+ <Field.Description>Set by the app you are paying</Field.Description>
306
+ </Field.Root>
307
+ )
308
+ }
309
+
310
+ namespace styles {
311
+ export const field = style({ maxWidth: '24rem !custom' })
312
+ }
313
+ ```
314
+
315
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/TextInput/Disabled.tsx)
316
+
317
+ ## Anatomy
318
+
319
+ Import the component and assemble its parts.
320
+
321
+ ```tsx
322
+ import { TextInput } from '@tempoxyz/ds'
323
+
324
+ <TextInput.Group>
325
+ <TextInput.Prefix />
326
+ <TextInput.Control />
327
+ <TextInput.Suffix />
328
+ <TextInput.Clear />
329
+ </TextInput.Group>
330
+ ```
331
+
332
+ ## Guidelines
333
+
334
+ ### When to Use
335
+
336
+ * Collect one line of text, such as an email or a 0x address.
337
+ * Search a list or table, with a `Search` icon in `TextInput.Prefix` and a `TextInput.Clear`.
338
+
339
+ ### When Not to Use
340
+
341
+ * Use [AmountInput](amount-input.md) for token and fiat amounts. It rejects non-decimals and adds a Max action.
342
+ * Use [NumberField](number-field.md) for quantities that step up and down.
343
+ * Use [Textarea](textarea.md) for text over one line.
344
+ * Use [OtpField](otp-field.md) for one-time passcodes.
345
+ * Use [Select](select.md) or [Combobox](combobox.md) if the value must come from a list, and [Autocomplete](autocomplete.md) if a list only suggests values.
346
+
347
+ ### Always Show a Label
348
+
349
+ A placeholder disappears on the first keystroke. Use it only for a format example, such as `0x…`. Leave out the visible label only when the dialog title already names the field, such as a search field, and set `aria-label`.
350
+
351
+ **Do:** Name the field in a `Field.Label` and keep the placeholder as a format hint.
352
+
353
+ ```tsx
354
+ /**
355
+ * Do example for "Always Show a Label" on the TextInput page.
356
+ * Name the field in a `Field.Label` and keep the placeholder as a format hint.
357
+ * @module
358
+ */
359
+ import { Field, style, TextInput } from '@tempoxyz/ds'
360
+
361
+ /** Renders the static preview, which the guideline card shows inert. */
362
+ export function Demo() {
363
+ return (
364
+ <Field.Root {...styles.field()}>
365
+ <Field.Label>Recipient address</Field.Label>
366
+ <TextInput.Group>
367
+ <TextInput.Control placeholder="0x…" />
368
+ </TextInput.Group>
369
+ </Field.Root>
370
+ )
371
+ }
372
+
373
+ namespace styles {
374
+ export const field = style({ maxWidth: '18rem !custom', width: '100% !custom' })
375
+ }
376
+ ```
377
+
378
+ **Don't:** Use the placeholder as the only label.
379
+
380
+ ```tsx
381
+ /**
382
+ * Don't example for "Always Show a Label" on the TextInput page.
383
+ * Don't use the placeholder as the only label.
384
+ * @module
385
+ */
386
+ import { style, TextInput } from '@tempoxyz/ds'
387
+
388
+ /** Renders the static preview, which the guideline card shows inert. */
389
+ export function Demo() {
390
+ return (
391
+ <TextInput.Group {...styles.field()}>
392
+ <TextInput.Control aria-label="Recipient address" placeholder="Recipient address" />
393
+ </TextInput.Group>
394
+ )
395
+ }
396
+
397
+ namespace styles {
398
+ export const field = style({ maxWidth: '18rem !custom', width: '100% !custom' })
399
+ }
400
+ ```
401
+
402
+ ### Size the Field to Its Value
403
+
404
+ `TextInput.Group` fills its container. If the value is short, narrow it with `style` or `className`.
405
+
406
+ **Do:** Give a short value a short field, and a long value the full column.
407
+
408
+ ```tsx
409
+ /**
410
+ * Do example for "Size the Field to Its Value" on the TextInput page.
411
+ * Give a short value a short field, and a long value the full column.
412
+ * @module
413
+ */
414
+ import { Field, style, TextInput } from '@tempoxyz/ds'
415
+
416
+ /** Renders the static preview, which the guideline card shows inert. */
417
+ export function Demo() {
418
+ return (
419
+ <div {...styles.form()}>
420
+ <Field.Root>
421
+ <Field.Label>TokenLogo symbol</Field.Label>
422
+ <TextInput.Group {...styles.short()}>
423
+ <TextInput.Control defaultValue="USDC" />
424
+ </TextInput.Group>
425
+ </Field.Root>
426
+ <Field.Root>
427
+ <Field.Label>Webhook URL</Field.Label>
428
+ <TextInput.Group>
429
+ <TextInput.Control defaultValue="https://api.example.com/hooks" type="url" />
430
+ </TextInput.Group>
431
+ </Field.Root>
432
+ </div>
433
+ )
434
+ }
435
+
436
+ namespace styles {
437
+ export const form = style({
438
+ display: 'grid',
439
+ gap: '16',
440
+ maxWidth: '18rem !custom',
441
+ width: '100% !custom',
442
+ })
443
+
444
+ export const short = style({ width: '7rem !custom' })
445
+ }
446
+ ```
447
+
448
+ **Don't:** Stretch a four-character symbol across the column or squeeze a URL into a small box.
449
+
450
+ ```tsx
451
+ /**
452
+ * Don't example for "Size the Field to Its Value" on the TextInput page.
453
+ * Don't stretch a four-character symbol across the column or squeeze a URL into a small box.
454
+ * @module
455
+ */
456
+ import { Field, style, TextInput } from '@tempoxyz/ds'
457
+
458
+ /** Renders the static preview, which the guideline card shows inert. */
459
+ export function Demo() {
460
+ return (
461
+ <div {...styles.form()}>
462
+ <Field.Root>
463
+ <Field.Label>TokenLogo symbol</Field.Label>
464
+ <TextInput.Group>
465
+ <TextInput.Control defaultValue="USDC" />
466
+ </TextInput.Group>
467
+ </Field.Root>
468
+ <Field.Root>
469
+ <Field.Label>Webhook URL</Field.Label>
470
+ <TextInput.Group {...styles.short()}>
471
+ <TextInput.Control defaultValue="https://api.example.com/hooks" type="url" />
472
+ </TextInput.Group>
473
+ </Field.Root>
474
+ </div>
475
+ )
476
+ }
477
+
478
+ namespace styles {
479
+ export const form = style({
480
+ display: 'grid',
481
+ gap: '16',
482
+ maxWidth: '18rem !custom',
483
+ width: '100% !custom',
484
+ })
485
+
486
+ export const short = style({ width: '7rem !custom' })
487
+ }
488
+ ```
489
+
490
+ ### Use Prefixes and Suffixes for Fixed Context
491
+
492
+ Put a currency symbol, protocol, or search icon in `TextInput.Prefix`, and a unit such as `USDC` in `TextInput.Suffix`.
493
+
494
+ **Do:** Show the unit in `TextInput.Suffix` so people type only the number.
495
+
496
+ ```tsx
497
+ /**
498
+ * Do example for "Use Prefixes and Suffixes for Fixed Context" on the TextInput page.
499
+ * Show the unit in `TextInput.Suffix` so people type only the number.
500
+ * @module
501
+ */
502
+ import { Field, style, TextInput } from '@tempoxyz/ds'
503
+
504
+ /** Renders the static preview, which the guideline card shows inert. */
505
+ export function Demo() {
506
+ return (
507
+ <Field.Root {...styles.field()}>
508
+ <Field.Label>Slippage tolerance</Field.Label>
509
+ <TextInput.Group>
510
+ <TextInput.Control defaultValue="0.5" inputMode="decimal" />
511
+ <TextInput.Suffix>%</TextInput.Suffix>
512
+ </TextInput.Group>
513
+ </Field.Root>
514
+ )
515
+ }
516
+
517
+ namespace styles {
518
+ export const field = style({ width: '9rem !custom' })
519
+ }
520
+ ```
521
+
522
+ **Don't:** Make people type the unit into the value.
523
+
524
+ ```tsx
525
+ /**
526
+ * Don't example for "Use Prefixes and Suffixes for Fixed Context" on the TextInput page.
527
+ * Don't make people type the unit into the value.
528
+ * @module
529
+ */
530
+ import { Field, style, TextInput } from '@tempoxyz/ds'
531
+
532
+ /** Renders the static preview, which the guideline card shows inert. */
533
+ export function Demo() {
534
+ return (
535
+ <Field.Root {...styles.field()}>
536
+ <Field.Label>Slippage tolerance</Field.Label>
537
+ <TextInput.Group>
538
+ <TextInput.Control defaultValue="0.5%" />
539
+ </TextInput.Group>
540
+ </Field.Root>
541
+ )
542
+ }
543
+
544
+ namespace styles {
545
+ export const field = style({ width: '9rem !custom' })
546
+ }
547
+ ```
548
+
549
+ * **Content:** Keep prefixes and suffixes to a symbol, icon, or one short word. Put other buttons beside the group, not in a suffix.
550
+ * **Icons:** Use a leading icon for meaning, such as `Search`, and a trailing icon for an action on the field, such as paste or scan.
551
+ * **Clear:** `TextInput.Clear` appears only inside a `Field.Root` once the control has a value. Add it to search fields and filters, not to one-time forms.
552
+
553
+ ### Help Browsers and Keyboards Fill the Field
554
+
555
+ Set `type`, `inputMode`, and `autoComplete` on `TextInput.Control` to select the on-screen keyboard and enable saved values.
556
+
557
+ * **Email (email):** Set `type="email"` and `autoComplete="email"`.
558
+ * **Name (name):** Set `autoComplete="name"`.
559
+ * **URL or Webhook (url):** Set `type="url"` and `autoComplete="url"`.
560
+ * **Search (search):** Set `type="search"`.
561
+ * **0x Address (off):** Turn off autofill, capitalizing, and spellcheck.
562
+ * **Numeric Code (numeric):** Set `inputMode="numeric"` and turn off autofill.
563
+
564
+ Avoid `type="number"` for amounts and identifiers. It drops leading zeros, accepts `e`, and some browsers change the value on scroll.
565
+
566
+ For labels, descriptions, errors, and validation timing, see [Field](field.md).
567
+
568
+ ## Accessibility
569
+
570
+ An input renders a native `<input>` inside a styled group. Inside a `Field.Root`, it links to the label, description, and error. See [Accessibility](../foundations/accessibility.md) for the baseline.
571
+
572
+ ### Semantics
573
+
574
+ * **TextInput.Control (textbox):** A native input whose role follows its type.
575
+ * **TextInput.Group (div):** Has no role of its own.
576
+ * **Invalid and Success (aria-invalid):** `invalid` sets `aria-invalid`. `success` sets no ARIA state.
577
+ * **TextInput.Prefix and TextInput.Suffix:** Plain text, such as "$" or "USDC". Not part of the input's name.
578
+ * **TextInput.Clear (button):** Labelled "Clear". Its hit area extends past its circle.
579
+
580
+ ### Keyboard
581
+
582
+ * **Move to the TextInput (Tab):** Moves focus to the input, then to the clear button when shown.
583
+ * **Clear the TextInput (Enter or Space):** On the clear button, empties the input and returns focus to it.
584
+ * **Clear Before Closing (Escape):** With a clear button, Escape empties a filled input first.
585
+
586
+ All other keys follow the native input.
587
+
588
+ ### Size and Motion
589
+
590
+ * **Target Size (48px):** Every field is 48px tall, above the 24px WCAG 2.2 minimum.
591
+ * **Reduced Motion (reduce):** Under reduced motion, the nudge and pop don't play.
592
+
593
+ ### Your Responsibilities
594
+
595
+ * **Label Every TextInput (Field.Label):** Use a visible label, or an `aria-label` when none fits. A prefix icon doesn't name it.
596
+ * **Name the Unit:** If the unit matters, put it in the label or description, such as "Amount in USDC".
597
+ * **Declare the Purpose (autoComplete):** Set it with `type` and `inputMode` to declare the field's purpose. WCAG requires it for personal data.
598
+ * **Explain Invalid Values (Field.Error):** Don't rely on the red fill alone. Explain an invalid value in text, such as "Enter an amount above 0".
599
+
600
+ ## Related
601
+
602
+ [Field](field.md)
603
+
604
+ Use for labels, descriptions, and errors.
605
+
606
+ [AmountInput](amount-input.md)
607
+
608
+ Use for token and fiat amounts.
609
+
610
+ [Textarea](textarea.md)
611
+
612
+ Use for multi-line text.
613
+
614
+ [Combobox](combobox.md)
615
+
616
+ Use for picking from a long list by typing.
617
+
618
+ [Forms](../patterns/forms.md)
619
+
620
+ Covers laying out fields and actions.
621
+
622
+ [Errors and Validation](../patterns/errors-and-validation.md)
623
+
624
+ Covers when and where to show errors.