@tempoxyz/ds 0.3.9 → 0.3.11

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 (128) hide show
  1. package/dist/docs/AGENTS.md +9 -0
  2. package/dist/docs/components/accordion.md +489 -0
  3. package/dist/docs/components/address-card.md +147 -0
  4. package/dist/docs/components/agent-logo.md +194 -0
  5. package/dist/docs/components/alert-dialog.md +631 -0
  6. package/dist/docs/components/alert.md +448 -0
  7. package/dist/docs/components/amount-input.md +305 -0
  8. package/dist/docs/components/amount.md +794 -0
  9. package/dist/docs/components/app-shell.md +559 -0
  10. package/dist/docs/components/asset-chip.md +294 -0
  11. package/dist/docs/components/autocomplete.md +619 -0
  12. package/dist/docs/components/avatar.md +321 -0
  13. package/dist/docs/components/badge.md +339 -0
  14. package/dist/docs/components/badged-logo.md +116 -0
  15. package/dist/docs/components/banner.md +379 -0
  16. package/dist/docs/components/bar-chart.md +208 -0
  17. package/dist/docs/components/breadcrumbs.md +380 -0
  18. package/dist/docs/components/button-group.md +286 -0
  19. package/dist/docs/components/button.md +650 -0
  20. package/dist/docs/components/calendar.md +375 -0
  21. package/dist/docs/components/callout.md +96 -0
  22. package/dist/docs/components/checkbox-group.md +521 -0
  23. package/dist/docs/components/checkbox.md +467 -0
  24. package/dist/docs/components/choice-group.md +516 -0
  25. package/dist/docs/components/choicebox.md +641 -0
  26. package/dist/docs/components/code.md +100 -0
  27. package/dist/docs/components/combobox.md +939 -0
  28. package/dist/docs/components/container.md +257 -0
  29. package/dist/docs/components/copy-button.md +642 -0
  30. package/dist/docs/components/date-picker.md +514 -0
  31. package/dist/docs/components/dialog.md +1332 -0
  32. package/dist/docs/components/drawer.md +966 -0
  33. package/dist/docs/components/empty.md +340 -0
  34. package/dist/docs/components/field.md +570 -0
  35. package/dist/docs/components/fieldset.md +398 -0
  36. package/dist/docs/components/frame.md +434 -0
  37. package/dist/docs/components/gauge.md +299 -0
  38. package/dist/docs/components/get-started-visual.md +117 -0
  39. package/dist/docs/components/grid.md +288 -0
  40. package/dist/docs/components/hero-visual.md +155 -0
  41. package/dist/docs/components/hex.md +487 -0
  42. package/dist/docs/components/icon-tile.md +292 -0
  43. package/dist/docs/components/identicon.md +371 -0
  44. package/dist/docs/components/inline.md +233 -0
  45. package/dist/docs/components/inset.md +225 -0
  46. package/dist/docs/components/kbd.md +423 -0
  47. package/dist/docs/components/list-group.md +981 -0
  48. package/dist/docs/components/loading-dots.md +237 -0
  49. package/dist/docs/components/menu.md +934 -0
  50. package/dist/docs/components/native-select.md +204 -0
  51. package/dist/docs/components/network-logo.md +248 -0
  52. package/dist/docs/components/number-field.md +543 -0
  53. package/dist/docs/components/otp-field.md +616 -0
  54. package/dist/docs/components/pagination.md +516 -0
  55. package/dist/docs/components/popover.md +544 -0
  56. package/dist/docs/components/progress.md +388 -0
  57. package/dist/docs/components/qr.md +314 -0
  58. package/dist/docs/components/radio-group.md +528 -0
  59. package/dist/docs/components/secret-value.md +406 -0
  60. package/dist/docs/components/select.md +751 -0
  61. package/dist/docs/components/separator.md +379 -0
  62. package/dist/docs/components/sidebar-menu.md +922 -0
  63. package/dist/docs/components/skeleton.md +517 -0
  64. package/dist/docs/components/slider.md +539 -0
  65. package/dist/docs/components/snippet.md +424 -0
  66. package/dist/docs/components/spinner.md +216 -0
  67. package/dist/docs/components/split-bar.md +139 -0
  68. package/dist/docs/components/split-button.md +464 -0
  69. package/dist/docs/components/stack.md +299 -0
  70. package/dist/docs/components/status-chip.md +226 -0
  71. package/dist/docs/components/status-indicator.md +119 -0
  72. package/dist/docs/components/status-row.md +174 -0
  73. package/dist/docs/components/stepper.md +451 -0
  74. package/dist/docs/components/success-hero.md +157 -0
  75. package/dist/docs/components/surface.md +639 -0
  76. package/dist/docs/components/switch.md +472 -0
  77. package/dist/docs/components/table.md +967 -0
  78. package/dist/docs/components/tabs.md +627 -0
  79. package/dist/docs/components/tempo-logo.md +185 -0
  80. package/dist/docs/components/text-input.md +624 -0
  81. package/dist/docs/components/textarea.md +352 -0
  82. package/dist/docs/components/toast.md +535 -0
  83. package/dist/docs/components/token-logo.md +371 -0
  84. package/dist/docs/components/token.md +247 -0
  85. package/dist/docs/components/tooltip.md +490 -0
  86. package/dist/docs/components/verification-code.md +116 -0
  87. package/dist/docs/components/visual.md +249 -0
  88. package/dist/docs/components.md +203 -0
  89. package/dist/docs/foundations/accessibility.md +377 -0
  90. package/dist/docs/foundations/agents.md +150 -0
  91. package/dist/docs/foundations/color.md +522 -0
  92. package/dist/docs/foundations/content/tone.md +194 -0
  93. package/dist/docs/foundations/content/voice.md +194 -0
  94. package/dist/docs/foundations/content/writing-style.md +406 -0
  95. package/dist/docs/foundations/iconography.md +282 -0
  96. package/dist/docs/foundations/intent.md +360 -0
  97. package/dist/docs/foundations/layout.md +508 -0
  98. package/dist/docs/foundations/linter.md +296 -0
  99. package/dist/docs/foundations/motion.md +151 -0
  100. package/dist/docs/foundations/radius-and-padding.md +86 -0
  101. package/dist/docs/foundations/typography.md +266 -0
  102. package/dist/docs/index.md +137 -0
  103. package/dist/docs/introduction/faq.md +132 -0
  104. package/dist/docs/introduction/getting-started.md +377 -0
  105. package/dist/docs/patterns/blockchain-values.md +709 -0
  106. package/dist/docs/patterns/dashboards.md +638 -0
  107. package/dist/docs/patterns/destructive-actions.md +626 -0
  108. package/dist/docs/patterns/empty-states.md +612 -0
  109. package/dist/docs/patterns/errors-and-validation.md +808 -0
  110. package/dist/docs/patterns/feedback-and-status.md +518 -0
  111. package/dist/docs/patterns/forms.md +679 -0
  112. package/dist/docs/patterns/loading.md +630 -0
  113. package/dist/docs/patterns/navigation.md +397 -0
  114. package/dist/docs/patterns/payments.md +765 -0
  115. package/dist/docs/patterns/responsive-layout.md +997 -0
  116. package/dist/docs/patterns/tables-and-lists.md +964 -0
  117. package/dist/docs/products/tempo-console.md +671 -0
  118. package/dist/docs/products/tempo-performance.md +437 -0
  119. package/dist/docs/products/tempo-wallet.md +438 -0
  120. package/dist/web/components/OtpField.css +1 -1
  121. package/dist/web/components/OtpField.d.ts +1 -1
  122. package/dist/web/components/OtpField.d.ts.map +1 -1
  123. package/dist/web/components/OtpField.js +3 -4
  124. package/dist/web/components/OtpField.js.map +1 -1
  125. package/dist/web/components/OtpField.js.zyzz.json +1 -1
  126. package/dist/web/index.js.zyzz.json +1 -1
  127. package/package.json +1 -1
  128. package/src/web/components/OtpField.tsx +2 -24
@@ -0,0 +1,522 @@
1
+ # Color
2
+
3
+ Color tokens for text, surfaces, borders, and status, with contrast rules.
4
+
5
+ ## Overview
6
+
7
+ Color in Tempo is mostly black and white. Text, fills, and edges are neutrals at different strengths, so a screen reads through contrast and hierarchy rather than hue. A white card on a light gray page, black text, and one black primary action carry most of the interface.
8
+
9
+ Hue is the exception. Green, orange, and red report status in alerts, banners, and field states, and never decorate. Blue and violet mark tips and announcements.
10
+
11
+ Every color is a token named for its role. Backgrounds and containers fill, content colors text and icons, and lines and borders draw edges. Each token holds a light and a dark value, so one token works in both schemes.
12
+
13
+ ## Scales
14
+
15
+ Scales hold the raw colors that tokens draw from. Use a scale step directly only for charts and categories.
16
+
17
+ ### Neutral
18
+
19
+ | TokenLogo | Value |
20
+ | --- | --- |
21
+ | `neutral.000` | `#fff` |
22
+ | `neutral.003` | `#f7f7f7` |
23
+ | `neutral.004` | `#f5f5f5` |
24
+ | `neutral.006` | `#f0f0f0` |
25
+ | `neutral.008` | `#ebebeb` |
26
+ | `neutral.012` | `#e0e0e0` |
27
+ | `neutral.016` | `#d6d6d6` |
28
+ | `neutral.020` | `#ccc` |
29
+ | `neutral.024` | `#c2c2c2` |
30
+ | `neutral.028` | `#b8b8b8` |
31
+ | `neutral.032` | `#adadad` |
32
+ | `neutral.036` | `#a3a3a3` |
33
+ | `neutral.040` | `#999` |
34
+ | `neutral.044` | `#8f8f8f` |
35
+ | `neutral.048` | `#858585` |
36
+ | `neutral.052` | `#7a7a7a` |
37
+ | `neutral.056` | `#707070` |
38
+ | `neutral.060` | `#666` |
39
+ | `neutral.064` | `#5c5c5c` |
40
+ | `neutral.068` | `#525252` |
41
+ | `neutral.072` | `#474747` |
42
+ | `neutral.076` | `#3d3d3d` |
43
+ | `neutral.080` | `#333` |
44
+ | `neutral.084` | `#292929` |
45
+ | `neutral.088` | `#1f1f1f` |
46
+ | `neutral.092` | `#141414` |
47
+ | `neutral.096` | `#0a0a0a` |
48
+ | `neutral.100` | `#000` |
49
+
50
+ Each step names a percentage of black, from white to black.
51
+
52
+ ### Shade and Tint
53
+
54
+ | TokenLogo | Value |
55
+ | --- | --- |
56
+ | `neutral.shade.000` | `#00000000` |
57
+ | `neutral.shade.003` | `#00000008` |
58
+ | `neutral.shade.004` | `#0000000a` |
59
+ | `neutral.shade.008` | `#00000014` |
60
+ | `neutral.shade.012` | `#0000001f` |
61
+ | `neutral.shade.016` | `#00000029` |
62
+ | `neutral.shade.020` | `#00000033` |
63
+ | `neutral.shade.032` | `#00000052` |
64
+ | `neutral.shade.040` | `#00000066` |
65
+ | `neutral.shade.056` | `#0000008f` |
66
+ | `neutral.shade.060` | `#00000099` |
67
+ | `neutral.shade.080` | `#000000cc` |
68
+
69
+ | TokenLogo | Value |
70
+ | --- | --- |
71
+ | `neutral.tint.000` | `#ffffff00` |
72
+ | `neutral.tint.008` | `#ffffff14` |
73
+ | `neutral.tint.012` | `#ffffff1f` |
74
+ | `neutral.tint.016` | `#ffffff29` |
75
+ | `neutral.tint.020` | `#ffffff33` |
76
+ | `neutral.tint.024` | `#ffffff3d` |
77
+ | `neutral.tint.040` | `#ffffff66` |
78
+ | `neutral.tint.048` | `#ffffff7a` |
79
+ | `neutral.tint.052` | `#ffffff85` |
80
+ | `neutral.tint.056` | `#ffffff8f` |
81
+ | `neutral.tint.064` | `#ffffffa3` |
82
+ | `neutral.tint.080` | `#ffffffcc` |
83
+
84
+ Shades are black and tints are white, at the opacity the step names, so a fill keeps the same relative step on any background.
85
+
86
+ ### Accent
87
+
88
+ | Hue | Background | Foreground |
89
+ | --- | --- | --- |
90
+ | blue | `accent.blue.background` (`#4471ed` light, `#7498fb` dark) | `accent.blue.foreground` (`#3059d3` light, `#7498fb` dark) |
91
+ | cyan | `accent.cyan.background` (`#00aacb` light, `#48cce6` dark) | `accent.cyan.foreground` (`#056e85` light, `#48cce6` dark) |
92
+ | green | `accent.green.background` (`#13a963` light, `#59e5a4` dark) | `accent.green.foreground` (`#007441` light, `#59e5a4` dark) |
93
+ | magenta | `accent.magenta.background` (`#cc055b` light, `#e54088` dark) | `accent.magenta.foreground` (`#ba0352` light, `#e54088` dark) |
94
+ | olive | `accent.olive.background` (`#95b226` light, `#c4dc65` dark) | `accent.olive.foreground` (`#596d01` light, `#c4dc65` dark) |
95
+ | orange | `accent.orange.background` (`#e06f12` light, `#fa8e36` dark) | `accent.orange.foreground` (`#a14d04` light, `#fa8e36` dark) |
96
+ | purple | `accent.purple.background` (`#ba59dd` light, `#cf79ef` dark) | `accent.purple.foreground` (`#9835ba` light, `#cf79ef` dark) |
97
+ | red | `accent.red.background` (`#eb3417` light, `#f55c45` dark) | `accent.red.foreground` (`#bf1d00` light, `#f55c45` dark) |
98
+ | violet | `accent.violet.background` (`#7e59e4` light, `#9877f1` dark) | `accent.violet.foreground` (`#6f47d1` light, `#9877f1` dark) |
99
+
100
+ Each hue has a background, which switches with the scheme, and a foreground for accent text that stays readable on every surface.
101
+
102
+ ## Backgrounds
103
+
104
+ Two backgrounds sit below every component, and a third fills small chips on a visual. Components on them take their fills, borders, and text from the tokens that follow.
105
+
106
+ * `background.primary` (`#f5f5f5` light, `#141414` dark): The page behind cards, tables, and the app shell.
107
+ * `background.elevated` (`#fff` light, `#1a1a1a` dark): Cards, dialogs, menus, and popovers above the page.
108
+ * `background.secondary` (`#fff` light, `#141414` dark): Badges and chips that sit on a visual or a tint.
109
+
110
+ ## Containers
111
+
112
+ Containers fill components such as menu items, badges, and selected rows. They are translucent, so a fill keeps the same relative step on the page and on an elevated surface.
113
+
114
+ * `container.subtle` (`#00000008` light, `#ffffff14` dark): The faintest fill, such as a disabled field.
115
+ * `container.regular` (`#0000000a` light, `#ffffff14` dark): Secondary buttons, filled fields, and tags.
116
+ * `container.strong` (`#00000014` light, `#ffffff29` dark): Hovered and pressed controls, and slider tracks.
117
+ * `container.positive` (`rgb(19 169 99 / 0.08)` light, `rgb(89 229 164 / 0.16)` dark): Positive alerts and tags, and copied values.
118
+ * `container.warning` (`rgb(224 111 18 / 0.08)` light, `rgb(250 142 54 / 0.16)` dark): Warning alerts, banners, and tags.
119
+ * `container.negative` (`rgb(235 52 23 / 0.08)` light, `rgb(245 92 69 / 0.16)` dark): Negative alerts, banners, and tags.
120
+
121
+ ## Borders
122
+
123
+ Lines outline edges and divide content, and borders mark focus and validation.
124
+
125
+ * `line.secondary` (`#00000014` light, `#ffffff14` dark): Dividers, table borders, and resting outlined fields.
126
+ * `line.primary` (`#00000029` light, `#ffffff3d` dark): Outlined fields on hover, and unchecked choices.
127
+ * `border.focus` (`#00000066` light, `#ffffff85` dark): The 2px outline that controls draw on keyboard focus.
128
+ * `border.positive` (`rgb(19 169 99 / 0.16)` light, `rgb(89 229 164 / 0.32)` dark): The ring around a field that passed validation.
129
+ * `border.warning` (`rgb(224 111 18 / 0.16)` light, `rgb(250 142 54 / 0.32)` dark): A ring that flags a field the reader should check.
130
+ * `border.negative` (`rgb(235 52 23 / 0.16)` light, `rgb(245 92 69 / 0.32)` dark): The ring around an invalid field, checkbox, or switch.
131
+
132
+ ## Content
133
+
134
+ Content tokens color text and icons. Pick one by how much the reader needs it.
135
+
136
+ * `content.primary` (`#000` light, `#fff` dark): Titles, values, body text, and primary fills.
137
+ * `content.secondary` (`#0000008f` light, `#ffffffa3` dark): Descriptions, captions, table cells, and placeholders.
138
+ * `content.tertiary` (`#00000066` light, `#ffffff85` dark): Icons, chevrons, and separators beside a label.
139
+ * `content.subtle` (`#00000033` light, `#ffffff33` dark): Tracks and marks that carry no text, such as a gauge.
140
+ * `content.positive` (`#13a963` light, `#59e5a4` dark): Success text and icons, such as a confirmed payment.
141
+ * `content.warning` (`#e06f12` light, `#fa8e36` dark): Caution text and icons, such as a fee warning.
142
+ * `content.negative` (`#eb3417` light, `#f55c45` dark): Error text, field errors, and negative button fills.
143
+
144
+ ## Materials and Scrims
145
+
146
+ Materials are translucent layers in five strengths, and scrims dim the page behind an overlay. No component uses them yet.
147
+
148
+ * `material.ultrathin` (`#00000014` light, `#ffffff29` dark): The lightest translucent layer.
149
+ * `material.thin` (`#00000033` light, `#ffffff3d` dark): A light translucent layer.
150
+ * `material.regular` (`#00000066` light, `#ffffff7a` dark): A translucent layer of medium strength.
151
+ * `material.thick` (`#00000099` light, `#ffffffa3` dark): A dense translucent layer.
152
+ * `material.ultrathick` (`#000000cc` light, `#ffffffcc` dark): The densest translucent layer.
153
+
154
+ * `scrim.regular` (`#00000029`): Dims the page behind an overlay, at 16% black.
155
+ * `scrim.strong` (`#00000052`): Dims the page further, at 32% black.
156
+
157
+ ## Intents
158
+
159
+ Components that report status take an intent, not a hue, so a status looks the same everywhere. Choose the intent by the message. An [Alert](/docs/components/alert) tints its container and colors its indicator by intent.
160
+
161
+ * **Neutral (content.subtle):** Default states and features, such as a payment that is still pending.
162
+ * **Positive (content.positive):** Success, completion, and incoming funds, such as a confirmed payment.
163
+ * **Warning (content.warning):** A risk that the reader must know about before they confirm an action.
164
+ * **Negative (content.negative):** Errors, failures, and destructive actions, such as a payment that failed.
165
+ * **Tip (accent.blue.background):** Optional context, such as the token that pays the network fee for a payment.
166
+ * **Special (accent.violet.background):** A new feature to announce, such as a vault that earns on idle USDC balances.
167
+
168
+ Cyan, magenta, olive, and purple have no intent, so keep them for charts and categories.
169
+
170
+ ## Guidelines
171
+
172
+ ### Choose Tokens by Role
173
+
174
+ Pick the token that names the role, not a scale step that looks right in one scheme. Never write a raw color into product styles, and propose a token in the Design System project when none fits.
175
+
176
+ **Do:** Use tokens, which switch value with the scheme.
177
+
178
+ ```tsx
179
+ /**
180
+ * Do example for "Choose Tokens by Job" on the Color page.
181
+ * Use tokens, which switch value with the scheme.
182
+ * @module
183
+ */
184
+ import { style, variants } from '@tempoxyz/ds'
185
+
186
+ /** Renders the static preview, which the guideline card shows inert. */
187
+ export function Demo() {
188
+ return (
189
+ <div {...styles.row()}>
190
+ <div {...styles.panel({ scheme: 'light' })}>
191
+ <span {...styles.label()}>Balance</span>
192
+ <span {...styles.value()}>1,250.00 USDC</span>
193
+ </div>
194
+ <div {...styles.panel({ scheme: 'dark' })}>
195
+ <span {...styles.label()}>Balance</span>
196
+ <span {...styles.value()}>1,250.00 USDC</span>
197
+ </div>
198
+ </div>
199
+ )
200
+ }
201
+
202
+ namespace styles {
203
+ export const label = style({ color: 'content.secondary', typography: 'body.b3' })
204
+
205
+ export const panel = variants({
206
+ base: {
207
+ backgroundColor: 'background.elevated',
208
+ borderColor: 'line.secondary',
209
+ borderRadius: 'xs',
210
+ borderStyle: 'solid',
211
+ borderWidth: 'regular',
212
+ display: 'grid',
213
+ gap: '4',
214
+ padding: '16',
215
+ },
216
+ variants: {
217
+ scheme: {
218
+ dark: { colorScheme: 'dark !custom' },
219
+ light: { colorScheme: 'light !custom' },
220
+ },
221
+ },
222
+ })
223
+
224
+ export const row = style({ display: 'flex', flexWrap: 'wrap', gap: '12' })
225
+
226
+ export const value = style({ color: 'content.primary', typography: 'body.b1Strong' })
227
+ }
228
+ ```
229
+
230
+ **Don't:** Fix a color that only passes in one scheme.
231
+
232
+ ```tsx
233
+ /**
234
+ * Don't example for "Choose Tokens by Job" on the Color page.
235
+ * Don't fix a color that only passes in one scheme.
236
+ * @module
237
+ */
238
+ import { style, variants } from '@tempoxyz/ds'
239
+
240
+ /** Renders the static preview, which the guideline card shows inert. */
241
+ export function Demo() {
242
+ return (
243
+ <div {...styles.row()}>
244
+ <div {...styles.panel({ scheme: 'light' })}>
245
+ <span {...styles.label()}>Balance</span>
246
+ <span {...styles.value()}>1,250.00 USDC</span>
247
+ </div>
248
+ <div {...styles.panel({ scheme: 'dark' })}>
249
+ <span {...styles.label()}>Balance</span>
250
+ <span {...styles.value()}>1,250.00 USDC</span>
251
+ </div>
252
+ </div>
253
+ )
254
+ }
255
+
256
+ namespace styles {
257
+ export const label = style({ color: '#666666 !custom', typography: 'body.b3' })
258
+
259
+ export const panel = variants({
260
+ base: {
261
+ backgroundColor: 'background.elevated',
262
+ borderColor: 'line.secondary',
263
+ borderRadius: 'xs',
264
+ borderStyle: 'solid',
265
+ borderWidth: 'regular',
266
+ display: 'grid',
267
+ gap: '4',
268
+ padding: '16',
269
+ },
270
+ variants: {
271
+ scheme: {
272
+ dark: { colorScheme: 'dark !custom' },
273
+ light: { colorScheme: 'light !custom' },
274
+ },
275
+ },
276
+ })
277
+
278
+ export const row = style({ display: 'flex', flexWrap: 'wrap', gap: '12' })
279
+
280
+ export const value = style({ color: '#171717 !custom', typography: 'body.b1Strong' })
281
+ }
282
+ ```
283
+
284
+ ### Keep Text at Readable Contrast
285
+
286
+ Set text in the primary or secondary content color, since WCAG counts it as text. Field placeholders are the exception: they take the tertiary color, as the design draws them, below 4.5:1. Otherwise keep the tertiary color for glyphs, such as chevrons and separators.
287
+
288
+ **Do:** Set descriptions in `content.secondary`, which stays above 4.5:1 on both backgrounds.
289
+
290
+ ```tsx
291
+ /**
292
+ * Do example for "Keep Text at Readable Contrast" on the Color page.
293
+ * Set descriptions in `content.secondary`, which stays above 4.5:1 on both backgrounds.
294
+ * @module
295
+ */
296
+ import { style } from '@tempoxyz/ds'
297
+
298
+ /** Renders the static preview, which the guideline card shows inert. */
299
+ export function Demo() {
300
+ return (
301
+ <div {...styles.stack()}>
302
+ <span {...styles.title()}>Network fee</span>
303
+ <span {...styles.description()}>Paid in PathUSD. Settles with the payment.</span>
304
+ </div>
305
+ )
306
+ }
307
+
308
+ namespace styles {
309
+ export const description = style({ color: 'content.secondary', typography: 'body.b2' })
310
+
311
+ export const stack = style({ display: 'grid', gap: '4' })
312
+
313
+ export const title = style({ color: 'content.primary', typography: 'body.b1Strong' })
314
+ }
315
+ ```
316
+
317
+ **Don't:** Set text people need to read in `content.tertiary`.
318
+
319
+ ```tsx
320
+ /**
321
+ * Don't example for "Keep Text at Readable Contrast" on the Color page.
322
+ * Don't set text people need to read in `content.tertiary`.
323
+ * @module
324
+ */
325
+ import { style } from '@tempoxyz/ds'
326
+
327
+ /** Renders the static preview, which the guideline card shows inert. */
328
+ export function Demo() {
329
+ return (
330
+ <div {...styles.stack()}>
331
+ <span {...styles.title()}>Network fee</span>
332
+ <span {...styles.description()}>Paid in PathUSD. Settles with the payment.</span>
333
+ </div>
334
+ )
335
+ }
336
+
337
+ namespace styles {
338
+ export const description = style({ color: 'content.tertiary', typography: 'body.b2' })
339
+
340
+ export const stack = style({ display: 'grid', gap: '4' })
341
+
342
+ export const title = style({ color: 'content.primary', typography: 'body.b1Strong' })
343
+ }
344
+ ```
345
+
346
+ ### Reserve Accents for Meaning
347
+
348
+ Blue marks information, green success, orange caution, and red errors and destructive actions. Focus and selection stay neutral.
349
+
350
+ **Do:** Use red for an error or a removal.
351
+
352
+ ```tsx
353
+ <Alert intent="negative">
354
+ <Alert.Icon />
355
+ <Alert.Content>This access key was revoked.</Alert.Content>
356
+ </Alert>
357
+ ```
358
+
359
+ **Don't:** Use red to draw attention to a safe event.
360
+
361
+ ```tsx
362
+ <Alert intent="negative">
363
+ <Alert.Icon />
364
+ <Alert.Content>Your funds arrived.</Alert.Content>
365
+ </Alert>
366
+ ```
367
+
368
+ ### Use One Intent per Message
369
+
370
+ **Do:** Use a single warning for a single risk.
371
+
372
+ ```tsx
373
+ <Alert intent="warning">
374
+ <Alert.Icon />
375
+ <Alert.Content>This address has never received a payment from you.</Alert.Content>
376
+ </Alert>
377
+ ```
378
+
379
+ **Don't:** Stack alerts of different intents for one risk.
380
+
381
+ ```tsx
382
+ <Alert>
383
+ <Alert.Icon />
384
+ <Alert.Content>New address.</Alert.Content>
385
+ </Alert>
386
+ <Alert intent="warning">
387
+ <Alert.Icon />
388
+ <Alert.Content>Check the address.</Alert.Content>
389
+ </Alert>
390
+ <Alert intent="negative">
391
+ <Alert.Icon />
392
+ <Alert.Content>Payments cannot be reversed.</Alert.Content>
393
+ </Alert>
394
+ ```
395
+
396
+ ### Never Use Color Alone
397
+
398
+ Pair every status color with text or an icon. In charts, name each series in a label or a legend.
399
+
400
+ **Do:** Name each status in text beside its color.
401
+
402
+ ```tsx
403
+ /** Do example for "Never Use Color Alone" on the Color page: name each status in text beside its color. @module */
404
+ import { Alert, style } from '@tempoxyz/ds'
405
+
406
+ /** Renders the static preview, which the guideline card shows inert. */
407
+ export function Demo() {
408
+ return (
409
+ <div {...styles.stack()}>
410
+ <Alert intent="positive">
411
+ <Alert.Icon />
412
+ <Alert.Content>Confirmed</Alert.Content>
413
+ </Alert>
414
+ <Alert intent="warning">
415
+ <Alert.Icon />
416
+ <Alert.Content>Pending</Alert.Content>
417
+ </Alert>
418
+ <Alert intent="negative">
419
+ <Alert.Icon />
420
+ <Alert.Content>Failed</Alert.Content>
421
+ </Alert>
422
+ </div>
423
+ )
424
+ }
425
+
426
+ namespace styles {
427
+ export const stack = style({
428
+ display: 'flex',
429
+ flexDirection: 'column',
430
+ gap: '8',
431
+ width: '100% !custom',
432
+ })
433
+ }
434
+ ```
435
+
436
+ **Don't:** Rely on colored dots to tell Confirmed from Failed.
437
+
438
+ ```tsx
439
+ /**
440
+ * Don't example for "Never Use Color Alone" on the Color page.
441
+ * Don't rely on colored dots to tell Confirmed from Failed.
442
+ * @module
443
+ */
444
+ import { style, variants } from '@tempoxyz/ds'
445
+
446
+ /** Renders the static preview, which the guideline card shows inert. */
447
+ export function Demo() {
448
+ return (
449
+ <div {...styles.row()}>
450
+ <span {...styles.dot({ intent: 'positive' })} />
451
+ <span {...styles.dot({ intent: 'warning' })} />
452
+ <span {...styles.dot({ intent: 'negative' })} />
453
+ </div>
454
+ )
455
+ }
456
+
457
+ namespace styles {
458
+ export const dot = variants({
459
+ base: { borderRadius: 'full', cornerShape: 'round', height: '12', width: '12' },
460
+ variants: {
461
+ intent: {
462
+ negative: { backgroundColor: 'content.negative' },
463
+ positive: { backgroundColor: 'content.positive' },
464
+ warning: { backgroundColor: 'content.warning' },
465
+ },
466
+ },
467
+ })
468
+
469
+ export const row = style({ alignItems: 'center', display: 'flex', gap: '16' })
470
+ }
471
+ ```
472
+
473
+ ## Contrast
474
+
475
+ Tempo targets WCAG 2.2 AA in both schemes.
476
+
477
+ * **Body Text (4.5:1):** Body text against its background.
478
+ * **Large Text (3:1):** Text from 24px, or bold text from 18.66px.
479
+ * **Non-Text (3:1):** Focus outlines, state borders, and icons.
480
+
481
+ Each ratio lists light mode first. Several pairs fall below these targets.
482
+
483
+ * **Primary on Elevated (21:1 and 18.4:1):** `content.primary` on `background.elevated`. Passes for any text, in light and dark.
484
+ * **Secondary on Elevated (4.9:1 and 6.4:1):** `content.secondary` on `background.elevated`. Passes for body text, such as card descriptions.
485
+ * **Secondary on Page (4.8:1 and 6.5:1):** `content.secondary` on `background.primary`. Passes for body text, such as page captions.
486
+ * **Tertiary on Elevated (2.9:1 and 3.8:1):** `content.tertiary` glyphs, such as breadcrumb separators. Too faint for text in either scheme.
487
+ * **Status Text on Status Fill (2.8:1+ and 4.7:1+):** `content.positive`, `warning`, and `negative` on their containers. Below 4.5:1 in light mode.
488
+ * **Focus Outline (2.8:1 and 5.7:1):** The `border.focus` outline against the page. Below the 3:1 minimum in light mode.
489
+ * **Invalid Border (1.3:1 and 1.6:1):** The `border.negative` ring against a surface. Below 3:1, so pair it with `Field.Error` text.
490
+
491
+ Lines measure below 3:1, so they divide content but never identify a control alone. Disabled controls are exempt, but must still look disabled.
492
+
493
+ ## Accessibility
494
+
495
+ * **Visible Focus (border.focus):** Components draw a 2px outline on keyboard focus. Don't replace it with a color change alone.
496
+ * **Forced Colors (forced-colors):** Fields and buttons swap their resting outline for a system color outline on keyboard focus.
497
+
498
+ ## Related
499
+
500
+ [Typography](/docs/foundations/typography)
501
+
502
+ Covers text sizes that change contrast thresholds.
503
+
504
+ [Accessibility](/docs/foundations/accessibility)
505
+
506
+ Covers the full WCAG 2.2 AA checklist.
507
+
508
+ [Feedback and Status](/docs/patterns/feedback-and-status)
509
+
510
+ Covers choosing an intent.
511
+
512
+ [Banner](/docs/components/banner)
513
+
514
+ Use for status text on a status fill.
515
+
516
+ [Alert](/docs/components/alert)
517
+
518
+ Use for intent colors in context.
519
+
520
+ [Banner](/docs/components/banner)
521
+
522
+ Use for intent colors in context.