@sableui/react 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +95 -0
  3. package/CONTRIBUTING.md +2 -2
  4. package/DESIGN.md +55 -16
  5. package/README.md +12 -0
  6. package/dist/{_contract-C2M1SjBf.d.ts → _contract-CkqXDVww.d.ts} +25 -2
  7. package/dist/blocks/danger-zone-block.js +3 -3
  8. package/dist/blocks/danger-zone-block.meta.js +1 -1
  9. package/dist/blocks/faq-block.js +3 -3
  10. package/dist/blocks/goal-progress-block.js +1 -1
  11. package/dist/blocks/notification-list-block.js +1 -1
  12. package/dist/blocks/pricing-table-block.js +12 -12
  13. package/dist/blocks/product-card-block.js +3 -3
  14. package/dist/blocks/product-card-block.meta.js +3 -3
  15. package/dist/blocks/profile-card-block.js +1 -1
  16. package/dist/blocks/profile-form-block.js +3 -3
  17. package/dist/blocks/revenue-breakdown-block.js +5 -5
  18. package/dist/blocks/stat-row-block.js +3 -3
  19. package/dist/blocks/task-list-block.js +2 -2
  20. package/dist/blocks/team-members-block.js +1 -1
  21. package/dist/blocks/wallet-block.js +4 -4
  22. package/dist/blocks/wallet-block.meta.js +3 -3
  23. package/dist/components/accordion/accordion.meta.js +2 -2
  24. package/dist/components/app-header/app-header.meta.js +1 -1
  25. package/dist/components/app-shell/app-shell.meta.js +2 -2
  26. package/dist/components/area-chart/area-chart.meta.js +3 -3
  27. package/dist/components/badge/badge.js +13 -4
  28. package/dist/components/badge/badge.meta.js +24 -13
  29. package/dist/components/badge/badge.variants.js +37 -23
  30. package/dist/components/banner/banner.meta.js +8 -8
  31. package/dist/components/banner/banner.variants.js +3 -3
  32. package/dist/components/bar-chart/bar-chart.meta.js +4 -4
  33. package/dist/components/breadcrumbs/breadcrumbs.meta.js +1 -1
  34. package/dist/components/button/button.meta.js +3 -3
  35. package/dist/components/card/card.js +16 -7
  36. package/dist/components/card/card.meta.js +4 -4
  37. package/dist/components/checkbox-card/checkbox-card.meta.js +10 -10
  38. package/dist/components/checkbox-card/checkbox-card.variants.js +15 -15
  39. package/dist/components/code-block/code-block.meta.js +1 -1
  40. package/dist/components/command-menu/command-menu.meta.js +1 -1
  41. package/dist/components/context-card/context-card.meta.js +2 -2
  42. package/dist/components/destructive-action-modal/destructive-action-modal.meta.js +1 -1
  43. package/dist/components/dialog/dialog.meta.js +2 -2
  44. package/dist/components/disclosure-picker/disclosure-picker.js +48 -2
  45. package/dist/components/disclosure-picker/disclosure-picker.meta.js +3 -2
  46. package/dist/components/empty-state/empty-state.meta.js +4 -4
  47. package/dist/components/gradient-art/gradient-art.meta.js +3 -3
  48. package/dist/components/label/label.meta.js +1 -1
  49. package/dist/components/line-chart/line-chart.meta.js +3 -3
  50. package/dist/components/meter/meter.js +1 -1
  51. package/dist/components/meter/meter.meta.js +2 -2
  52. package/dist/components/meter/meter.variants.js +1 -1
  53. package/dist/components/middle-truncate/middle-truncate.meta.js +4 -4
  54. package/dist/components/note/note.meta.js +3 -3
  55. package/dist/components/page-header/page-header.js +1 -1
  56. package/dist/components/pagination/pagination.meta.js +2 -2
  57. package/dist/components/phone/phone.meta.js +1 -1
  58. package/dist/components/pie-chart/pie-chart.meta.js +3 -3
  59. package/dist/components/pill/pill.js +4 -2
  60. package/dist/components/pill/pill.meta.js +19 -2
  61. package/dist/components/pill/pill.variants.js +23 -2
  62. package/dist/components/popover/popover.meta.js +2 -2
  63. package/dist/components/radar-chart/radar-chart.meta.js +3 -3
  64. package/dist/components/radial-chart/radial-chart.meta.js +3 -3
  65. package/dist/components/radio/radio.meta.js +2 -2
  66. package/dist/components/radio-card/radio-card.meta.js +6 -6
  67. package/dist/components/radio-card/radio-card.variants.js +15 -15
  68. package/dist/components/scroller/scroller.meta.js +1 -1
  69. package/dist/components/search-input/search-input.meta.js +1 -1
  70. package/dist/components/section-header/section-header.js +1 -1
  71. package/dist/components/segmented-bar/segmented-bar.js +1 -1
  72. package/dist/components/segmented-bar/segmented-bar.meta.js +6 -6
  73. package/dist/components/segmented-bar/segmented-bar.variants.js +4 -4
  74. package/dist/components/select/select.meta.js +2 -2
  75. package/dist/components/sheet/sheet.meta.js +1 -1
  76. package/dist/components/side-nav/side-nav.meta.js +1 -1
  77. package/dist/components/sidebar/sidebar.meta.js +1 -1
  78. package/dist/components/sparkline/sparkline.meta.js +4 -4
  79. package/dist/components/spinner/spinner.meta.js +1 -1
  80. package/dist/components/status-dot/status-dot.js +4 -1
  81. package/dist/components/status-dot/status-dot.meta.js +8 -16
  82. package/dist/components/status-dot/status-dot.variants.js +13 -4
  83. package/dist/components/table/table.meta.js +4 -4
  84. package/dist/components/tabs/tabs.meta.js +1 -1
  85. package/dist/components/textarea/textarea.meta.js +2 -2
  86. package/dist/components/toast/toast.meta.js +1 -1
  87. package/dist/components/tooltip/tooltip.meta.js +2 -2
  88. package/dist/file-hashes.json +120 -118
  89. package/dist/foundations/spacing.js +8 -8
  90. package/dist/index.d.ts +137 -72
  91. package/dist/index.js +12 -1
  92. package/dist/lib/chart/types.js +3 -3
  93. package/dist/lib/palette.js +103 -0
  94. package/dist/lib/selection-card.js +4 -4
  95. package/dist/palette.css +669 -0
  96. package/dist/patterns/auth-page.js +6 -6
  97. package/dist/patterns/auth-page.meta.js +3 -3
  98. package/dist/patterns/brand-landing-page.js +2 -2
  99. package/dist/patterns/chat-panel.js +2 -2
  100. package/dist/patterns/dashboard-page.js +71 -71
  101. package/dist/patterns/dashboard-page.meta.js +5 -5
  102. package/dist/patterns/feed-page.js +25 -25
  103. package/dist/patterns/feed-page.meta.js +3 -3
  104. package/dist/patterns/landing-page.js +1 -1
  105. package/dist/patterns/list-detail-page.js +17 -17
  106. package/dist/patterns/settings-page.js +17 -17
  107. package/dist/presets/index.d.ts +1 -1
  108. package/dist/presets/index.js +17 -2
  109. package/dist/styles.css +115 -50
  110. package/dist/{theme-provider-S0NVJs2Y.d.ts → theme-provider-CdSicAad.d.ts} +1 -1
  111. package/dist/themes/_contract.js +22 -8
  112. package/dist/themes/atelier/atelier.css +13 -13
  113. package/dist/themes/atelier/index.d.ts +1 -1
  114. package/dist/themes/ember/ember.css +13 -13
  115. package/dist/themes/ember/index.d.ts +1 -1
  116. package/dist/themes/index.d.ts +2 -2
  117. package/dist/themes/meridian/index.d.ts +1 -1
  118. package/dist/themes/meridian/meridian.css +13 -13
  119. package/dist/themes/poppy/index.d.ts +1 -1
  120. package/dist/themes/poppy/poppy.css +19 -16
  121. package/dist/themes/prism/index.d.ts +1 -1
  122. package/dist/themes/prism/prism.css +18 -15
  123. package/dist/themes/sable/index.d.ts +1 -1
  124. package/dist/themes/sable/sable.css +18 -22
  125. package/dist/themes/verdant/index.d.ts +1 -1
  126. package/dist/themes/verdant/verdant.css +15 -15
  127. package/llms/blocks/danger-zone-block.md +4 -4
  128. package/llms/blocks/faq-block.md +3 -3
  129. package/llms/blocks/goal-progress-block.md +1 -1
  130. package/llms/blocks/notification-list-block.md +1 -1
  131. package/llms/blocks/pricing-table-block.md +12 -12
  132. package/llms/blocks/product-card-block.md +6 -6
  133. package/llms/blocks/profile-card-block.md +1 -1
  134. package/llms/blocks/profile-form-block.md +3 -3
  135. package/llms/blocks/revenue-breakdown-block.md +5 -5
  136. package/llms/blocks/stat-row-block.md +3 -3
  137. package/llms/blocks/task-list-block.md +2 -2
  138. package/llms/blocks/team-members-block.md +1 -1
  139. package/llms/blocks/wallet-block.md +7 -7
  140. package/llms/components/accordion.md +2 -2
  141. package/llms/components/app-header.md +1 -1
  142. package/llms/components/app-shell.md +2 -2
  143. package/llms/components/area-chart.md +1 -1
  144. package/llms/components/badge.md +15 -7
  145. package/llms/components/banner.md +6 -6
  146. package/llms/components/bar-chart.md +2 -2
  147. package/llms/components/breadcrumbs.md +1 -1
  148. package/llms/components/button.md +3 -3
  149. package/llms/components/card.md +4 -4
  150. package/llms/components/checkbox-card.md +5 -5
  151. package/llms/components/code-block.md +1 -1
  152. package/llms/components/command-menu.md +1 -1
  153. package/llms/components/context-card.md +2 -2
  154. package/llms/components/destructive-action-modal.md +1 -1
  155. package/llms/components/dialog.md +2 -2
  156. package/llms/components/disclosure-picker.md +2 -1
  157. package/llms/components/empty-state.md +4 -4
  158. package/llms/components/gradient-art.md +3 -3
  159. package/llms/components/label.md +1 -1
  160. package/llms/components/line-chart.md +1 -1
  161. package/llms/components/meter.md +2 -2
  162. package/llms/components/middle-truncate.md +5 -5
  163. package/llms/components/note.md +3 -3
  164. package/llms/components/pagination.md +2 -2
  165. package/llms/components/phone.md +1 -1
  166. package/llms/components/pie-chart.md +1 -1
  167. package/llms/components/pill.md +12 -2
  168. package/llms/components/popover.md +2 -2
  169. package/llms/components/radar-chart.md +1 -1
  170. package/llms/components/radial-chart.md +1 -1
  171. package/llms/components/radio-card.md +4 -4
  172. package/llms/components/radio.md +2 -2
  173. package/llms/components/scroller.md +1 -1
  174. package/llms/components/search-input.md +1 -1
  175. package/llms/components/segmented-bar.md +7 -7
  176. package/llms/components/select.md +2 -2
  177. package/llms/components/sheet.md +1 -1
  178. package/llms/components/side-nav.md +1 -1
  179. package/llms/components/sidebar.md +1 -1
  180. package/llms/components/sparkline.md +2 -2
  181. package/llms/components/spinner.md +1 -1
  182. package/llms/components/status-dot.md +4 -4
  183. package/llms/components/table.md +8 -8
  184. package/llms/components/tabs.md +1 -1
  185. package/llms/components/textarea.md +2 -2
  186. package/llms/components/toast.md +1 -1
  187. package/llms/components/tooltip.md +2 -2
  188. package/llms/foundations/spacing.md +7 -7
  189. package/llms/layout/grid.md +1 -1
  190. package/llms/layout/page.md +1 -1
  191. package/llms/layout/region.md +1 -1
  192. package/llms/layout/split-pane.md +1 -1
  193. package/llms/patterns/auth-page.md +11 -11
  194. package/llms/patterns/brand-landing-page.md +2 -2
  195. package/llms/patterns/chat-panel.md +2 -2
  196. package/llms/patterns/dashboard-page.md +78 -78
  197. package/llms/patterns/feed-page.md +31 -31
  198. package/llms/patterns/landing-page.md +1 -1
  199. package/llms/patterns/list-detail-page.md +18 -18
  200. package/llms/patterns/settings-page.md +18 -18
  201. package/llms/reference/token-intent.md +25 -11
  202. package/llms/reference/utilities.md +1 -1
  203. package/llms/tasks/adding-a-component.md +4 -1
  204. package/llms/tasks/building-a-page.md +40 -0
  205. package/llms/tasks/converting-an-app.md +3 -1
  206. package/llms/tasks/using-presets.md +4 -0
  207. package/llms-full.txt +429 -347
  208. package/llms.txt +7 -7
  209. package/package.json +4 -4
  210. package/registry.json +369 -233
  211. package/scripts/lint-rules.mjs +26 -5
  212. package/skills/sable/SKILL.md +14 -3
package/llms-full.txt CHANGED
@@ -205,7 +205,7 @@ composes with `render`).
205
205
 
206
206
  ## How it works
207
207
 
208
- - Components reference **semantic tokens** (`bg-card`, `text-muted-foreground`, `bg-tone-edition`), never raw palette steps or inline colors. Text/icons on a tone fill use the theme-adaptive `text-tone-*-foreground` ink; scrims and media surfaces use `bg-overlay`/`text-overlay-foreground`. Positive/success states use the flat `--success` / `--success-foreground` token (mirrors `--destructive`; there is **no** `tone-success`). A theme = a CSS variable set; re-skinning never touches component code. App-level theme switching is first-class via `<SableThemeProvider>` and `useSableTheme()`.
208
+ - Components reference **semantic tokens** (`bg-card`, `text-muted-foreground`, `bg-tone-1`), never raw palette steps or inline colors. Text/icons on a tone fill use the theme-adaptive `text-tone-*-foreground` ink; scrims and media surfaces use `bg-overlay`/`text-overlay-foreground`. Positive/success states use the flat `--success` / `--success-foreground` token (mirrors `--destructive`; there is **no** `tone-success`). A theme = a CSS variable set; re-skinning never touches component code. App-level theme switching is first-class via `<SableThemeProvider>` and `useSableTheme()`.
209
209
  - Typography is **paired utilities** (`text-heading-1`, `text-body-md`) bundling size+weight+line-height+tracking.
210
210
  - Layout is **fluid-first / no global max-width**: an edge-to-edge page with a fluid `--page-inset`, an 8px rhythm unit, a 12-col grid that folds, and opt-in per-region max-widths. Primitives: `Region`, `Page` (route shell = Region + Stack), `Bleed` (content-area-aware full-bleed, via container units), `SplitPane`, `Stack`/`Row`/`Center`, `Grid`/`Columns`/`Col`, `SafeArea`. `Columns count` / `Col span`+`start` / `Grid cols` accept **responsive breakpoint maps** (`{ base, md, lg }`).
211
211
  - `<Icon name="…">` resolves a semantic name through an injected `<IconProvider>`; with no provider it renders Reicon. Replace the set with IconProvider or extend it with composeIconSets(appExtras, reiconIconSet). Concrete imports stay in the shared adapter; never copy SVG paths into an app.
@@ -337,7 +337,10 @@ because the meta wins *whole* and silently discards the other.
337
337
 
338
338
  ## The hard rules
339
339
 
340
- 1. **Semantic tokens only** — never hex/oklch, never a raw palette step.
340
+ 1. **Semantic tokens only** — never hex/oklch, never a raw palette step, not
341
+ even the generated palette's (`bg-blue-500`; that is for apps). A component
342
+ that takes a palette family as `tone` sets `data-palette` to it and paints
343
+ from the slots that fills (`bg-palette-wash text-palette-text`), as Badge does.
341
344
  2. **Paired typography utilities** — never `text-{size}` + `font-{weight}`.
342
345
  3. **`primitives/` is behaviour, `components/` is the public API.** Base UI is
343
346
  wrapped and re-exported; consumers import only from the root barrel.
@@ -668,6 +671,25 @@ Pick each route's `max` by what the person mainly does on it:
668
671
  (a copy in `light` or `dark` wins over it); keep the role names and change
669
672
  the values.
670
673
 
674
+ ## Columns and rhythm
675
+
676
+ - **Flex or grid.** One direction is `Stack` (column) or `Row` (row). Content
677
+ that has to line up across rows *and* columns is `Grid`/`Columns`. A card's
678
+ single column is a `Stack`, never a one-track grid.
679
+ - **Columns are a choice per route.** Record it in the brief next to the width:
680
+ which content spans which of the 12 columns, and where it folds
681
+ (`<Col span={{ base: 12, lg: 8 }}>`). The grid does not fold on its own. A
682
+ route that is one reading measure has no columns, and the brief says so.
683
+ - **Vertical space is measured in lines.** The baseline is one line of body
684
+ text split into steps (8px at the default type). Gap numbers on `Stack`/
685
+ `Row`/`Grid` are steps; in Tailwind, even steps (`gap-4`, `py-6`, `mt-12`)
686
+ are whole steps and odd ones (`py-3`, `gap-5`) are half a step. Title to
687
+ description is one step, between groups one to two lines, between sections
688
+ three or more.
689
+ - **It is a rhythm, not a lock.** A card with a button or a code block is as
690
+ tall as its content. Keep the spacing between blocks in steps and let
691
+ components be; never pad or nudge them to pin text to lines.
692
+
671
693
  ## Typography: pick the tier by ROLE, not by size
672
694
 
673
695
  | tier | for | not |
@@ -697,6 +719,27 @@ intent, and what it gets confused with, is in
697
719
  Ink is never chosen independently of its surface: `bg-card text-card-foreground`,
698
720
  as a pair.
699
721
 
722
+ ### More colour: the generated palette
723
+
724
+ When a page needs colour that is not a role (tags, avatars, project labels, a
725
+ chart legend, illustration), reach for the generated palette, not hex and not a
726
+ theme's own ramp. Every theme has `neutral`, `red`, `orange`, `yellow`,
727
+ `green`, `teal`, `blue`, `purple` and `pink` (50–950), plus a scale under
728
+ every tone (`tone-1-100`, `success-700`). Each family leans toward the brand, so
729
+ the set belongs to the theme, and neutral is the theme's own page-to-ink ramp.
730
+
731
+ - On a component, the family is the tone: `<Badge tone="teal">Research</Badge>`,
732
+ `<Pill tone="blue">`, `<StatusDot tone="pink" />`. A family is a category with
733
+ no meaning attached; a status is still `success`, `warning` or `destructive`.
734
+ - On your own element, use the pairs: `bg-blue-100 text-blue-800` (a tag),
735
+ `text-blue-700` (text on the page), `bg-blue-700 text-blue-50` (a solid
736
+ fill), `border-blue-300` (a hairline). Steps follow the mode (a low step is
737
+ always near the page), so these need no `dark:`.
738
+ - Surfaces, body text and actions still take roles. The palette never replaces
739
+ `bg-card` or `bg-primary`.
740
+
741
+ The full reference is `llms/reference/token-intent.md` → Generated palette.
742
+
700
743
  ## The traps
701
744
 
702
745
  - **A `Card` is a container, not a heading or a landmark.** If its title is the
@@ -1224,7 +1267,9 @@ loudest and least clearly ("invalid hook call").
1224
1267
 
1225
1268
  The most reliable signal that a conversion is real: the app's own CSS variable
1226
1269
  block shrinks to almost nothing. Anything that remains should be *domain* colour,
1227
- not UI colour.
1270
+ not UI colour. Most domain colour has a home too: a tag or category colour maps
1271
+ to a palette family (`<Badge tone="blue">`, `bg-blue-100 text-blue-800`), which
1272
+ follows the theme where the app's own hex never did.
1228
1273
 
1229
1274
  Grep for orphaned references afterwards. A CSS variable that no longer exists
1230
1275
  resolves to nothing and the declaration is dropped — **silently**. A checkerboard
@@ -2510,6 +2555,10 @@ prints a complete starting record; the public `PRESET_TOKEN_NAMES` is authoritat
2510
2555
  Font stacks are requirements, not downloaded fonts. Declare matching stacks in
2511
2556
  the font metadata and tokens. Resolve internal palette variables before saving;
2512
2557
  portable presets may reference public Sable tokens, not private palette names.
2558
+ A preset does not carry the generated palette (`bg-blue-500` and the rest): it is
2559
+ computed from the preset's own primary, page and ink. `theme-palette-harmony`
2560
+ (0 keeps the families' own hues, 1 is the default lean toward the brand) is the
2561
+ one palette token a preset may set.
2513
2562
  The validator rejects unknown tokens and CSS that can escape its declaration.
2514
2563
  `sable preset --check` also holds labels on brand and status fills to 4.5:1 and APCA Lc 60
2515
2564
  and suggests a pair that reads; a preset may keep a pair deliberately through
@@ -2825,9 +2874,9 @@ muted-foreground is contrast-tested on every surface; a 60% alpha is not.
2825
2874
 
2826
2875
  # Spacing & sizing
2827
2876
 
2828
- One 4px scale governs every gap, inset and size, and space is assigned by role. Controls read a height ladder that a density scope can retune all at once.
2877
+ One scale, derived from the type: the baseline is one line of body text, split into steps every gap, inset and line height lands on. Space is assigned by role. Controls read a height ladder that a density scope can retune all at once.
2829
2878
 
2830
- **4px base · 8px rhythm · 3 roles · 1 control ladder**
2879
+ **Baseline from the type · 8px steps · 3 roles · 1 control ladder**
2831
2880
 
2832
2881
  ## Spacing by role
2833
2882
 
@@ -2836,8 +2885,8 @@ Steps below 2 (8px) are optical correction only.
2836
2885
  | Name | Value | Use |
2837
2886
  |---|---|---|
2838
2887
  | `Inset p-4` | 16px | Padding inside a surface. p-3 for a dense card, px-3 py-2 for a row. |
2839
- | `Stack, within a group gap-1.5 – gap-2` | 6–8px | Label to field, title to description. |
2840
- | `Stack, between groups gap-4 – gap-6` | 16–24px | Field to field, card to card. |
2888
+ | `Stack, within a group gap-2` | 1 step · 8px | Label to field, title to description. |
2889
+ | `Stack, between groups gap-4 – gap-6` | 2–3 steps · 16–24px | Field to field, card to card. |
2841
2890
  | `Stack, between sections gap-12+` | 48px+ | Major regions of a page. |
2842
2891
  | `Inline gap-2 – gap-3` | 8–12px | Icon to label, control to control. |
2843
2892
 
@@ -2863,8 +2912,8 @@ Comfortable values; data-density="compact" (or <Density>) redefines every step f
2863
2912
 
2864
2913
  ## Rules
2865
2914
 
2866
- 1. **Every margin, padding and gap is a step on the 4px scale.** Off-scale values break the rhythm that lets adjacent components line up without coordination.
2867
- 2. **Padding, gaps and insets in a component sit on the spacing scale: a step (p-4, gap-2) or --spacing(n) inside an arbitrary value (p-[var(--card-p,--spacing(6))]).** The density (Character) control works by retuning --spacing, so anything on the scale moves with it. A literal p-[1.5rem] or padding: "12px" stays put while everything around it tightens. Keep fixed rem/px for what must not scale: a 1px hairline, a ring width. *Enforced by design-lint: fixed-spacing (library source).*
2915
+ 1. **Every margin, padding and gap is a step on the scale, and vertical space between blocks is whole baseline steps: an even Tailwind step (2, 4, 6, 8, 12) or a Stack/Row/Grid gap number.** The baseline is one line of body text split into steps; line heights round to it. An odd step (3, 5) is half a baseline step. It is a rhythm to keep, not a grid to force: components stay as tall as their content.
2916
+ 2. **Padding, gaps and insets in a component sit on the spacing scale: a step (p-4, gap-2) or --spacing(n) inside an arbitrary value (p-[var(--card-p,--spacing(6))]).** --spacing derives from the baseline step and the density (Character) control moves the leading it comes from, so anything on the scale moves with it. A literal p-[1.5rem] or padding: "12px" stays put while everything around it tightens. Keep fixed rem/px for what must not scale: a 1px hairline, a ring width. *Enforced by design-lint: fixed-spacing (library source).*
2868
2917
  3. **Related things sit closer than unrelated things.** Proximity groups content without a divider line. Use space first; reach for a border only when space is not enough.
2869
2918
  4. **Space lives on the container (gap), not on children (margins).** gap survives reordering, conditional children and RTL; per-child margins do not.
2870
2919
  5. **Control heights read the --control-* ladder.** A density scope retunes every control at once. A hardcoded height has to be found and overridden by hand. *Enforced by sable-lint: raw-control-height.*
@@ -2902,7 +2951,7 @@ Every control, menu and popup inside retunes, including portaled overlays.
2902
2951
  <Stack gap={3}> between groups (24px), <Stack gap={1}> inside a group (8px)
2903
2952
  ```
2904
2953
 
2905
- Two spacing steps do the work of every divider, with less ink. Stack's numeric gap counts --rhythm-unit — two spacing steps — so it follows the density control like every other spacing value.
2954
+ Spacing steps do the work of every divider, with less ink. Stack's numeric gap counts --rhythm-unit — one baseline step — so it follows the type and the density control like every other spacing value.
2906
2955
 
2907
2956
  **Related:** `sable docs shape` · `sable docs typography` · `sable docs building-a-tool` · `sable token layout`
2908
2957
 
@@ -3265,7 +3314,7 @@ The region is in the accessibility tree before its text changes, so the change i
3265
3314
 
3266
3315
  # AuthPage
3267
3316
 
3268
- Split sign-in page: a form column (brand mark, heading, email + password, primary sign-in, passkey and wallet alternatives, sign-up switch, terms) beside a brand panel painted from the theme's tones with a featured release card. Below lg the panel drops away and the form is the whole screen.
3317
+ Split sign-in page: a form column (brand mark, heading, email + password, primary sign-in, passkey and email-link alternatives, sign-up switch, terms) beside a brand panel painted from the theme's tones with a customer story card. Below lg the panel drops away and the form is the whole screen.
3269
3318
 
3270
3319
  **Intent:** Reach for this for sign-in, sign-up, forgot-password, and any single-purpose one-column form route (invite accept, unsubscribe). Not for multi-section forms (use SettingsPage).
3271
3320
 
@@ -3276,14 +3325,14 @@ Split sign-in page: a form column (brand mark, heading, email + password, primar
3276
3325
  - The heading is `text-heading-1` in an h1 — it is the page outline, so it takes a heading tier, not a card title.
3277
3326
  - Brand mark, form and terms are a three-part column: mark pinned to the top, form vertically centred in the remaining space, terms pinned to the bottom.
3278
3327
  - Forgot password sits under the password field, right-aligned, before the submit button — so it comes right after the field in tab order. Field has no label-row action slot yet.
3279
- - One primary action. Passkey and wallet are outline buttons under an `or` Separator; switching to sign-up is a quiet `link-underline` link.
3328
+ - One primary action. Passkey and email link are outline buttons under an `or` Separator; switching to sign-up is a quiet `link-underline` link.
3280
3329
  - Validation uses Field `validate` for product copy instead of the browser's generic required message. A rejected sign-in comes back through Form `errors` on the password field; the submit Button shows `loading` while the request is in flight.
3281
3330
  - The brand panel artwork is a mesh of the theme's decorative tones over `--primary`, so it re-paints for every theme and mode. The relative-colour `oklch(from var(--tone-*) …)` lift needs an `inline-color` disable with a reason.
3282
3331
  - The panel is `hidden lg:flex`: on a phone the form is the whole screen, with no decorative scroll above it.
3283
3332
 
3284
3333
  ## Accessibility
3285
3334
 
3286
- - The route renders inside <main> with one h1; the brand panel is an <aside> labelled "Featured release".
3335
+ - The route renders inside <main> with one h1; the brand panel is an <aside> labelled "Customer story".
3287
3336
  - Field auto-wires labels, describedby, and invalid state onto the inputs; on submit, the first invalid field is focused.
3288
3337
  - Autocomplete attributes (`email webauthn`, `current-password`) keep password managers and passkey autofill working.
3289
3338
  - The Forgot password and Create one links use `hit-target` to meet the 44px touch floor without changing layout.
@@ -3314,7 +3363,7 @@ export function AuthPage() {
3314
3363
  <Stack gap={1.5}>
3315
3364
  <h1 className="text-heading-1">Welcome back</h1>
3316
3365
  <p className="text-body-lg text-muted-foreground">
3317
- Sign in to collect new releases and manage your editions.
3366
+ Sign in to pick up where you and your team left off.
3318
3367
  </p>
3319
3368
  </Stack>
3320
3369
 
@@ -3352,8 +3401,8 @@ export function AuthPage() {
3352
3401
  <Button variant="outline" size="cta" className="w-full" iconStart={<Icon name="key" />}>
3353
3402
  Sign in with a passkey
3354
3403
  </Button>
3355
- <Button variant="outline" size="cta" className="w-full" iconStart={<Icon name="wallet" />}>
3356
- Connect a wallet
3404
+ <Button variant="outline" size="cta" className="w-full" iconStart={<Icon name="mail" />}>
3405
+ Email me a sign-in link
3357
3406
  </Button>
3358
3407
  </Stack>
3359
3408
 
@@ -3380,23 +3429,23 @@ export function AuthPage() {
3380
3429
  </Region>
3381
3430
 
3382
3431
  {/* The brand panel is decoration: on a phone the form is the whole screen. */}
3383
- <aside aria-label="Featured release" className="hidden p-3 lg:flex">
3432
+ <aside aria-label="Customer story" className="hidden p-3 lg:flex">
3384
3433
  {/* Art painted from the theme's tones over its brand, so it follows every look. */}
3385
3434
  <GradientArt seed="Untitled (gold rush)" className="flex flex-1 flex-col justify-end rounded-xl p-8">
3386
3435
  <Card className="max-w-md">
3387
3436
  <CardContent>
3388
3437
  <Stack gap={3}>
3389
- <Badge tone="edition" className="self-start">
3390
- Edition of 50
3438
+ <Badge tone="1" className="self-start">
3439
+ Customer story
3391
3440
  </Badge>
3392
3441
  <p className="text-title-lg">
3393
- “Every edition is a conversation with whoever holds it next.”
3442
+ “We moved three teams over in a week, and nobody asked for the old tool back.”
3394
3443
  </p>
3395
3444
  <Entity
3396
3445
  className="p-0"
3397
3446
  leading={<Avatar variant="art" fallback="MO" />}
3398
3447
  title="Mara Okafor"
3399
- subtitle="Untitled (gold rush), 2026"
3448
+ subtitle="Head of Design, Northwind"
3400
3449
  />
3401
3450
  </Stack>
3402
3451
  </CardContent>
@@ -3457,7 +3506,7 @@ const spaces = [
3457
3506
 
3458
3507
  const routes = [
3459
3508
  { label: "Intake", state: "Open", tone: "info" as const },
3460
- { label: "Review", state: "Live", tone: "edition" as const },
3509
+ { label: "Review", state: "Live", tone: "1" as const },
3461
3510
  { label: "Archive", state: "Safe", tone: "success" as const },
3462
3511
  ]
3463
3512
 
@@ -3635,7 +3684,7 @@ export function BrandLandingPage() {
3635
3684
  <div className="flex items-center justify-between border-b border-border px-4 py-3">
3636
3685
  <Row gap={1}>
3637
3686
  <StatusDot tone="warning" />
3638
- <StatusDot tone="edition" />
3687
+ <StatusDot tone="1" />
3639
3688
  <StatusDot tone="success" />
3640
3689
  </Row>
3641
3690
  <span className="text-label-sm text-muted-foreground">Live workspace</span>
@@ -4029,7 +4078,7 @@ export function ChatPanel() {
4029
4078
  render={<section aria-labelledby="chat-title" />}
4030
4079
  className="flex h-[calc(100dvh_-_--spacing(12))] flex-col overflow-hidden"
4031
4080
  >
4032
- <CardHeader className="border-b border-border pb-(--card-p)">
4081
+ <CardHeader className="pb-(--card-p) shadow-[inset_0_-1px_0_var(--border)]">
4033
4082
  {/* CardTitle carries the type; the h1 inside it is the page heading. */}
4034
4083
  <CardTitle>
4035
4084
  <h1 id="chat-title">Scroll behaviour</h1>
@@ -4168,7 +4217,7 @@ export function ChatPanel() {
4168
4217
  </Message>
4169
4218
  </MessageScroller>
4170
4219
 
4171
- <CardFooter className="border-t border-border pt-(--card-p)">
4220
+ <CardFooter className="pt-(--card-p) shadow-[inset_0_1px_0_var(--border)]">
4172
4221
  <ChatComposer
4173
4222
  className="flex-1"
4174
4223
  placeholder="Ask a follow-up…"
@@ -4228,18 +4277,18 @@ export function ChatPanel() {
4228
4277
 
4229
4278
  # DashboardPage
4230
4279
 
4231
- App-shell creator dashboard: a branded Sidebar with an account footer (AppHeader + BottomNav below lg), a KPI row, an edge-to-edge editions table with sell-through Meters, and supporting payout, collector, release and activity cards — the default shape for an operator or analytics home route.
4280
+ App-shell admin dashboard: a branded Sidebar with an account footer (AppHeader + BottomNav below lg), a KPI row, an edge-to-edge accounts table with seat-usage Meters, and supporting payout, top-customer, release and activity cards — the default shape for an operator or analytics home route.
4232
4281
 
4233
- **Intent:** Reach for this when a route's job is monitoring or managing many records (sales, orders, editions, posts) with a few headline numbers. Not for reading-focused routes (use FeedPage) or single-record routes (use ListDetailPage's detail side).
4282
+ **Intent:** Reach for this when a route's job is monitoring or managing many records (sales, orders, accounts, posts) with a few headline numbers. Not for reading-focused routes (use FeedPage) or single-record routes (use ListDetailPage's detail side).
4234
4283
 
4235
4284
  ## Notes
4236
4285
 
4237
4286
  - Region cap is `wide` (1280px): dashboards hold tables and multi-column grids, never a reading column.
4238
- - Hierarchy runs headline → records → context: KPI row, then the primary table beside its two closest companions (payout, top collectors), then the lower-priority lists (upcoming, activity). Every row of the grid is filled so the route ends on content, not empty canvas.
4287
+ - Hierarchy runs headline → records → context: KPI row, then the primary table beside its two closest companions (payout, top customers), then the lower-priority lists (upcoming, activity). Every row of the grid is filled so the route ends on content, not empty canvas.
4239
4288
  - Stat cards: CardHeader carries the label (CardDescription) and the value; KPI values are `text-mono-xl` — the metric mono tier, tabular figures — never a heading tier. The trend Badge sits with its comparison note in CardContent, so the value keeps the full card width when the row folds to 2×2 on a phone.
4240
4289
  - Stat cards use an explicit cols map (2 → 4) — a 4-stat row must fold to 2×2, never strand an orphan the way auto-fit can.
4241
4290
  - The table runs flush to the card edges with `<CardContent flush>`, which hands the card's padding to the table (`--table-edge-px`), so the first and last columns line up with the CardHeader at any card density — never hand-set pl-6/pr-6. Numeric columns align end in mono; Table scrolls horizontally inside the card on narrow screens.
4242
- - Sold / supply is a `Meter` (how full a known range is), not `Progress` (task progress). Status is `Badge appearance="plain" dot`, with the app's own states mapped to tones once at the top of the file.
4291
+ - Seats used / seats is a `Meter` (how full a known range is), not `Progress` (task progress). Status is `Badge appearance="plain" dot`, with the app's own states mapped to tones once at the top of the file.
4243
4292
  - Lists are Entity rows in CardList, inset to the card padding and divided by hairlines — never CardContent with hand-tuned px. A card that shares a grid row with a taller one becomes `flex flex-col` and pins its CardFooter with `mt-auto`.
4244
4293
  - Brand and account live in the Sidebar: SidebarHeader holds the mark + wordmark, SidebarFooter holds the signed-in Entity with an OverflowMenu. Sidebar rebinds the page ink tokens to its own, so the Entity and OverflowMenu read correctly even when a theme paints the rail dark on a light page.
4245
4294
  - The page has exactly one heading-1 (PageHeader); card titles are Title tier, not headings.
@@ -4251,7 +4300,7 @@ App-shell creator dashboard: a branded Sidebar with an account footer (AppHeader
4251
4300
  - AppShell provides the skip link and <main id="main-content"> landmark.
4252
4301
  - The active SidebarItem and BottomNavItem carry aria-current via `active`.
4253
4302
  - DataTable headers are real <th>; sortable columns are announced.
4254
- - Each sell-through Meter has an aria-label naming the edition and aria-valuetext reading "184 of 250 sold".
4303
+ - Each seat-usage Meter has an aria-label naming the account and aria-valuetext reading "184 of 250 seats used".
4255
4304
  - Avatars beside a visible name use an empty alt so the name is not read twice.
4256
4305
 
4257
4306
  ## Source
@@ -4259,11 +4308,11 @@ App-shell creator dashboard: a branded Sidebar with an account footer (AppHeader
4259
4308
  ```tsx
4260
4309
  import { AppHeader, AppShell, Avatar, Badge, BottomNav, BottomNavItem, Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardList, CardTitle, DataTable, Entity, Grid, Icon, MenuItem, MenuSeparator, Meter, NotificationBadge, OverflowMenu, Page, PageHeader, RelativeTime, Row, Sidebar, SidebarBrand, SidebarFooter, SidebarHeader, SidebarItem, SidebarNav, Stack } from "@sableui/react"
4261
4310
 
4262
- // Edition status → Badge tone. Sable has no status vocabulary of its own;
4311
+ // Account status → Badge tone. Sable has no status vocabulary of its own;
4263
4312
  // an app maps ITS states onto tones once, here, and every badge follows.
4264
- const STATUS_TONE = { live: "success", scheduled: "info", "sold-out": "edition", closed: "neutral" } as const
4265
- const STATUS_LABEL = { live: "Live", scheduled: "Scheduled", "sold-out": "Sold out", closed: "Closed" } as const
4266
- type EditionStatus = keyof typeof STATUS_TONE
4313
+ const STATUS_TONE = { active: "success", onboarding: "info", full: "1", cancelled: "neutral" } as const
4314
+ const STATUS_LABEL = { active: "Active", onboarding: "Onboarding", full: "Seats full", cancelled: "Cancelled" } as const
4315
+ type AccountStatus = keyof typeof STATUS_TONE
4267
4316
 
4268
4317
  const MIN = 60 * 1000
4269
4318
  const HOUR = 60 * MIN
@@ -4276,54 +4325,54 @@ const shortDate = (offset: number) =>
4276
4325
 
4277
4326
  const stats = [
4278
4327
  { label: "Revenue", value: "$18,240", delta: "+8.2%", up: true, note: "vs. $16,857" },
4279
- { label: "Collects", value: "1,284", delta: "+3.1%", up: true, note: "182 first-time" },
4280
- { label: "Collectors", value: "312", delta: "+12.4%", up: true, note: "Best month yet" },
4281
- { label: "Sell-through", value: "64%", delta: "−1.8%", up: false, note: "6 open editions" },
4328
+ { label: "Active seats", value: "1,284", delta: "+3.1%", up: true, note: "182 added" },
4329
+ { label: "Accounts", value: "312", delta: "+12.4%", up: true, note: "Best month yet" },
4330
+ { label: "Seat usage", value: "64%", delta: "−1.8%", up: false, note: "6 near their limit" },
4282
4331
  ]
4283
4332
 
4284
- const editions = [
4285
- { id: "ed_01", title: "Gold Rush", status: "live" as const, sold: 184, supply: 250, price: 48, released: -6 * DAY },
4286
- { id: "ed_02", title: "Quiet Harbor", status: "sold-out" as const, sold: 100, supply: 100, price: 120, released: -19 * DAY },
4287
- { id: "ed_03", title: "Dusk, no. 4", status: "live" as const, sold: 61, supply: 150, price: 64, released: -2 * DAY },
4288
- { id: "ed_04", title: "Tidewater", status: "live" as const, sold: 212, supply: 300, price: 36, released: -27 * DAY },
4289
- { id: "ed_05", title: "Salt Study", status: "closed" as const, sold: 38, supply: 75, price: 90, released: -44 * DAY },
4290
- { id: "ed_06", title: "Paper Moon", status: "sold-out" as const, sold: 50, supply: 50, price: 220, released: -58 * DAY },
4291
- { id: "ed_07", title: "Night Signal", status: "scheduled" as const, sold: 0, supply: 200, price: 56, released: 3 * DAY },
4292
- { id: "ed_08", title: "Low Tide, Blue", status: "closed" as const, sold: 72, supply: 120, price: 42, released: -71 * DAY },
4293
- { id: "ed_09", title: "Ember Field", status: "sold-out" as const, sold: 80, supply: 80, price: 140, released: -86 * DAY },
4333
+ const accounts = [
4334
+ { id: "ac_01", title: "Northwind", status: "active" as const, used: 184, seats: 250, price: 16, started: -6 * DAY },
4335
+ { id: "ac_02", title: "Harbor Labs", status: "full" as const, used: 100, seats: 100, price: 20, started: -19 * DAY },
4336
+ { id: "ac_03", title: "Brightline", status: "active" as const, used: 61, seats: 150, price: 16, started: -2 * DAY },
4337
+ { id: "ac_04", title: "Tidewater Co.", status: "active" as const, used: 212, seats: 300, price: 12, started: -27 * DAY },
4338
+ { id: "ac_05", title: "Saltworks", status: "cancelled" as const, used: 38, seats: 75, price: 20, started: -44 * DAY },
4339
+ { id: "ac_06", title: "Paper Moon", status: "full" as const, used: 50, seats: 50, price: 24, started: -58 * DAY },
4340
+ { id: "ac_07", title: "Signal Studio", status: "onboarding" as const, used: 0, seats: 200, price: 16, started: 3 * DAY },
4341
+ { id: "ac_08", title: "Bluewater", status: "cancelled" as const, used: 72, seats: 120, price: 12, started: -71 * DAY },
4342
+ { id: "ac_09", title: "Ember & Field", status: "full" as const, used: 80, seats: 80, price: 20, started: -86 * DAY },
4294
4343
  ]
4295
- const editionCount = 24
4344
+ const accountCount = 312
4296
4345
 
4297
4346
  const payout = {
4298
- amount: 8214.6,
4347
+ amount: 8862.64,
4299
4348
  date: 5 * DAY,
4300
4349
  lines: [
4301
- { label: "Sales", value: 9127.33 },
4302
- { label: "Platform fee (10%)", value: -912.73 },
4350
+ { label: "Subscriptions", value: 9127.33 },
4351
+ { label: "Processing (2.9%)", value: -264.69 },
4303
4352
  { label: "Deposits to", value: "Checking ••4821" },
4304
4353
  ],
4305
4354
  }
4306
4355
 
4307
- const collectors = [
4308
- { id: "co_01", name: "Nina Simone", handle: "@nina", initials: "NS", pieces: 14, spent: 1460 },
4309
- { id: "co_02", name: "Grace Hopper", handle: "@grace", initials: "GH", pieces: 11, spent: 1128 },
4310
- { id: "co_03", name: "Frida Kahlo", handle: "@frida", initials: "FK", pieces: 9, spent: 942 },
4311
- { id: "co_04", name: "Mary Shelley", handle: "@mary", initials: "MS", pieces: 6, spent: 588 },
4356
+ const customers = [
4357
+ { id: "cu_01", name: "Northwind", handle: "northwind.io", initials: "NW", seats: 184, spent: 2944 },
4358
+ { id: "cu_02", name: "Tidewater Co.", handle: "tidewater.co", initials: "TC", seats: 212, spent: 2544 },
4359
+ { id: "cu_03", name: "Harbor Labs", handle: "harborlabs.com", initials: "HL", seats: 100, spent: 2000 },
4360
+ { id: "cu_04", name: "Ember & Field", handle: "emberfield.com", initials: "EF", seats: 80, spent: 1600 },
4312
4361
  ]
4313
4362
 
4314
4363
  const upcoming = [
4315
- { id: "up_01", title: "Night Signal", detail: "Edition of 200 · $56", at: 3 * DAY },
4316
- { id: "up_02", title: "Tidewater, second printing", detail: "Edition of 100 · $40", at: 12 * DAY },
4317
- { id: "up_03", title: "Dusk, no. 5", detail: "Edition of 150 · $64", at: 26 * DAY },
4318
- { id: "up_04", title: "Quiet Harbor, artist proofs", detail: "Edition of 10 · $480", at: 34 * DAY },
4364
+ { id: "up_01", title: "Version 2.4", detail: "Shared reports · All plans", at: 3 * DAY },
4365
+ { id: "up_02", title: "Audit log export", detail: "Business plan · Beta", at: 12 * DAY },
4366
+ { id: "up_03", title: "Version 2.5", detail: "Offline mode · All plans", at: 26 * DAY },
4367
+ { id: "up_04", title: "Usage-based billing", detail: "Studio and Business · Beta", at: 34 * DAY },
4319
4368
  ]
4320
4369
 
4321
4370
  const activity = [
4322
- { id: "ac_01", who: "Nina Simone", initials: "NS", what: "collected Gold Rush · 185 of 250", when: 2 * MIN },
4323
- { id: "ac_02", who: "Grace Hopper", initials: "GH", what: "collected Dusk, no. 4 · 61 of 150", when: 26 * MIN },
4324
- { id: "ac_03", who: "Frida Kahlo", initials: "FK", what: "tipped $20 on Tidewater", when: 1 * HOUR },
4325
- { id: "ac_04", who: "Mary Shelley", initials: "MS", what: "started following you", when: 3 * HOUR },
4326
- { id: "ac_05", who: "Nina Simone", initials: "NS", what: "set a reminder for Night Signal", when: 6 * HOUR },
4371
+ { id: "ev_01", who: "Nina Simone", initials: "NS", what: "added 12 seats to Northwind · 184 of 250", when: 2 * MIN },
4372
+ { id: "ev_02", who: "Grace Hopper", initials: "GH", what: "upgraded Brightline to Studio", when: 26 * MIN },
4373
+ { id: "ev_03", who: "Frida Kahlo", initials: "FK", what: "paid invoice INV-0042 for Tidewater Co.", when: 1 * HOUR },
4374
+ { id: "ev_04", who: "Mary Shelley", initials: "MS", what: "joined the Harbor Labs workspace", when: 3 * HOUR },
4375
+ { id: "ev_05", who: "Nina Simone", initials: "NS", what: "booked onboarding for Signal Studio", when: 6 * HOUR },
4327
4376
  ]
4328
4377
 
4329
4378
  export function DashboardPage() {
@@ -4345,8 +4394,8 @@ export function DashboardPage() {
4345
4394
  bottomNav={
4346
4395
  <BottomNav aria-label="Primary">
4347
4396
  <BottomNavItem icon={<Icon name="grid" />} label="Overview" showLabel active render={<a href="/" />} />
4348
- <BottomNavItem icon={<Icon name="image-stack" />} label="Editions" showLabel render={<a href="/editions" />} />
4349
- <BottomNavItem icon={<Icon name="user" />} label="Collectors" showLabel render={<a href="/collectors" />} />
4397
+ <BottomNavItem icon={<Icon name="folder" />} label="Accounts" showLabel render={<a href="/accounts" />} />
4398
+ <BottomNavItem icon={<Icon name="user" />} label="Customers" showLabel render={<a href="/customers" />} />
4350
4399
  <BottomNavItem icon={<Icon name="bell" />} label="Activity" showLabel render={<a href="/activity" />} />
4351
4400
  <BottomNavItem icon={<Icon name="gear" />} label="Settings" showLabel render={<a href="/settings" />} />
4352
4401
  </BottomNav>
@@ -4358,8 +4407,8 @@ export function DashboardPage() {
4358
4407
  </SidebarHeader>
4359
4408
  <SidebarNav>
4360
4409
  <SidebarItem icon={<Icon name="grid" />} label="Overview" active render={<a href="/" />} />
4361
- <SidebarItem icon={<Icon name="image-stack" />} label="Editions" render={<a href="/editions" />} />
4362
- <SidebarItem icon={<Icon name="user" />} label="Collectors" render={<a href="/collectors" />} />
4410
+ <SidebarItem icon={<Icon name="folder" />} label="Accounts" render={<a href="/accounts" />} />
4411
+ <SidebarItem icon={<Icon name="user" />} label="Customers" render={<a href="/customers" />} />
4363
4412
  <SidebarItem
4364
4413
  icon={<Icon name="bell" />}
4365
4414
  label="Activity"
@@ -4390,7 +4439,7 @@ export function DashboardPage() {
4390
4439
  <Page max="wide" gap={4} className="py-6">
4391
4440
  <PageHeader
4392
4441
  title="Overview"
4393
- description="Sales and collector activity for the last 30 days."
4442
+ description="Revenue and account activity for the last 30 days."
4394
4443
  actions={
4395
4444
  <Row gap={2}>
4396
4445
  <Button variant="outline">
@@ -4399,7 +4448,7 @@ export function DashboardPage() {
4399
4448
  </Button>
4400
4449
  <Button>
4401
4450
  <Icon name="plus" />
4402
- New edition
4451
+ New account
4403
4452
  </Button>
4404
4453
  </Row>
4405
4454
  }
@@ -4429,43 +4478,43 @@ export function DashboardPage() {
4429
4478
  <Grid cols={{ base: 1, xl: 3 }} gap={3}>
4430
4479
  <Card className="flex flex-col xl:col-span-2">
4431
4480
  <CardHeader>
4432
- <CardTitle>Editions</CardTitle>
4433
- <CardDescription>Sales across your releases, newest first.</CardDescription>
4481
+ <CardTitle>Accounts</CardTitle>
4482
+ <CardDescription>Seats across your customers, newest first.</CardDescription>
4434
4483
  </CardHeader>
4435
4484
  <CardContent flush className="mt-4 flex-1">
4436
4485
  <DataTable
4437
- data={editions}
4486
+ data={accounts}
4438
4487
  rowKey={(row) => row.id}
4439
- defaultSort={{ key: "released", direction: "desc" }}
4488
+ defaultSort={{ key: "started", direction: "desc" }}
4440
4489
  columns={[
4441
- { key: "title", header: "Edition", sortable: true, className: "text-label-md whitespace-nowrap" },
4490
+ { key: "title", header: "Account", sortable: true, className: "text-label-md whitespace-nowrap" },
4442
4491
  {
4443
4492
  key: "status",
4444
4493
  header: "Status",
4445
4494
  cell: (row) => (
4446
- <Badge appearance="plain" tone={STATUS_TONE[row.status as EditionStatus]} dot>
4447
- {STATUS_LABEL[row.status as EditionStatus]}
4495
+ <Badge appearance="plain" tone={STATUS_TONE[row.status as AccountStatus]} dot>
4496
+ {STATUS_LABEL[row.status as AccountStatus]}
4448
4497
  </Badge>
4449
4498
  ),
4450
4499
  },
4451
4500
  {
4452
- key: "sold",
4453
- header: "Sold",
4501
+ key: "used",
4502
+ header: "Seats",
4454
4503
  sortable: true,
4455
- sortValue: (row) => row.sold / row.supply,
4504
+ sortValue: (row) => row.used / row.seats,
4456
4505
  cell: (row) => (
4457
4506
  <Row gap={3} align="center" className="min-w-40">
4458
4507
  <Meter
4459
- value={row.sold}
4460
- max={row.supply}
4508
+ value={row.used}
4509
+ max={row.seats}
4461
4510
  size="sm"
4462
- tone={row.sold === row.supply ? "success" : "primary"}
4463
- aria-label={`${row.title} sold`}
4464
- getAriaValueText={(_, v) => `${v} of ${row.supply} sold`}
4511
+ tone={row.used === row.seats ? "success" : "primary"}
4512
+ aria-label={`${row.title} seats used`}
4513
+ getAriaValueText={(_, v) => `${v} of ${row.seats} seats used`}
4465
4514
  className="flex-1"
4466
4515
  />
4467
4516
  <span className="text-mono-caption whitespace-nowrap text-muted-foreground">
4468
- {row.sold}/{row.supply}
4517
+ {row.used}/{row.seats}
4469
4518
  </span>
4470
4519
  </Row>
4471
4520
  ),
@@ -4475,21 +4524,21 @@ export function DashboardPage() {
4475
4524
  header: "Revenue",
4476
4525
  align: "end",
4477
4526
  sortable: true,
4478
- sortValue: (row) => row.sold * row.price,
4527
+ sortValue: (row) => row.used * row.price,
4479
4528
  cell: (row) =>
4480
- row.sold > 0 ? (
4481
- <span className="text-mono-sm">{usd.format(row.sold * row.price)}</span>
4529
+ row.used > 0 ? (
4530
+ <span className="text-mono-sm">{usd.format(row.used * row.price)}</span>
4482
4531
  ) : (
4483
4532
  <span className="text-mono-sm text-muted-foreground">—</span>
4484
4533
  ),
4485
4534
  },
4486
4535
  {
4487
- key: "released",
4488
- header: "Release",
4536
+ key: "started",
4537
+ header: "Started",
4489
4538
  align: "end",
4490
4539
  sortable: true,
4491
4540
  cell: (row) => (
4492
- <span className="text-body-sm whitespace-nowrap text-muted-foreground">{shortDate(row.released)}</span>
4541
+ <span className="text-body-sm whitespace-nowrap text-muted-foreground">{shortDate(row.started)}</span>
4493
4542
  ),
4494
4543
  },
4495
4544
  ]}
@@ -4497,10 +4546,10 @@ export function DashboardPage() {
4497
4546
  </CardContent>
4498
4547
  <CardFooter className="justify-between">
4499
4548
  <span className="text-caption text-muted-foreground">
4500
- Showing {editions.length} of {editionCount} editions
4549
+ Showing {accounts.length} of {accountCount} accounts
4501
4550
  </span>
4502
- <Button variant="ghost" size="sm" bleed="end" render={<a href="/editions" />}>
4503
- View all editions
4551
+ <Button variant="ghost" size="sm" bleed="end" render={<a href="/accounts" />}>
4552
+ View all accounts
4504
4553
  <Icon name="arrow-right" />
4505
4554
  </Button>
4506
4555
  </CardFooter>
@@ -4531,21 +4580,21 @@ export function DashboardPage() {
4531
4580
  </Card>
4532
4581
  <Card>
4533
4582
  <CardHeader>
4534
- <CardTitle>Top collectors</CardTitle>
4583
+ <CardTitle>Top customers</CardTitle>
4535
4584
  <CardDescription>By spend this month.</CardDescription>
4536
4585
  <CardAction>
4537
- <Button variant="ghost" size="sm" render={<a href="/collectors" />}>
4586
+ <Button variant="ghost" size="sm" render={<a href="/customers" />}>
4538
4587
  View all
4539
4588
  </Button>
4540
4589
  </CardAction>
4541
4590
  </CardHeader>
4542
4591
  <CardList>
4543
- {collectors.map((person) => (
4592
+ {customers.map((person) => (
4544
4593
  <Entity
4545
4594
  key={person.id}
4546
4595
  leading={<Avatar variant="art" size="sm" fallback={person.initials} alt="" />}
4547
4596
  title={person.name}
4548
- subtitle={`${person.handle} · ${person.pieces} pieces`}
4597
+ subtitle={`${person.handle} · ${person.seats} seats`}
4549
4598
  action={<span className="text-mono-sm">{usd.format(person.spent)}</span>}
4550
4599
  />
4551
4600
  ))}
@@ -4557,7 +4606,7 @@ export function DashboardPage() {
4557
4606
  <Card className="flex flex-col">
4558
4607
  <CardHeader>
4559
4608
  <CardTitle>Upcoming releases</CardTitle>
4560
- <CardDescription>Scheduled to open to collectors.</CardDescription>
4609
+ <CardDescription>Scheduled to ship to customers.</CardDescription>
4561
4610
  </CardHeader>
4562
4611
  <CardList>
4563
4612
  {upcoming.map((release) => (
@@ -4570,7 +4619,7 @@ export function DashboardPage() {
4570
4619
  ))}
4571
4620
  </CardList>
4572
4621
  <CardFooter className="mt-auto">
4573
- <Button variant="outline" size="sm" render={<a href="/editions/new" />}>
4622
+ <Button variant="outline" size="sm" render={<a href="/releases/new" />}>
4574
4623
  <Icon name="calendar" />
4575
4624
  Schedule a release
4576
4625
  </Button>
@@ -4579,7 +4628,7 @@ export function DashboardPage() {
4579
4628
  <Card className="xl:col-span-2">
4580
4629
  <CardHeader>
4581
4630
  <CardTitle>Activity</CardTitle>
4582
- <CardDescription>Collects, tips, and follows.</CardDescription>
4631
+ <CardDescription>Seats, upgrades, and invoices.</CardDescription>
4583
4632
  <CardAction>
4584
4633
  <Button variant="ghost" size="sm" render={<a href="/activity" />}>
4585
4634
  View all
@@ -4609,7 +4658,7 @@ export function DashboardPage() {
4609
4658
 
4610
4659
  # FeedPage
4611
4660
 
4612
- Social reading feed with a side rail: a feed-width main column (composer, For you / Following Tabs, one Card per post, an edition drop with price, supply Meter and Collect, a Skeleton for the next page) beside a sticky rail of who-to-follow and upcoming drops that drops away below lg.
4661
+ Social reading feed with a side rail: a feed-width main column (composer, For you / Following Tabs, one Card per post, an event post with price, capacity Meter and Register, a Skeleton for the next page) beside a sticky rail of who-to-follow and upcoming events that drops away below lg.
4613
4662
 
4614
4663
  **Intent:** Reach for this for timelines, activity streams, and any reverse-chronological reading surface where people also act on what they read. Not for record management (DashboardPage) or side-by-side browsing (ListDetailPage).
4615
4664
 
@@ -4619,7 +4668,7 @@ Social reading feed with a side rail: a feed-width main column (composer, For yo
4619
4668
  - Feed scope is `Tabs` (underline, stretched to the column with `flex`): peer views of the same feed. The composer sits above them because posting applies to both.
4620
4669
  - The composer is a Card holding `Textarea autoResize` plus ghost icon Buttons and a secondary `Post` — not ChatComposer, which submits on Enter and is for conversations.
4621
4670
  - Each post is a Card: an Entity byline in a CardList (with an OverflowMenu for post actions), body copy, then media in an AspectBox at the asset's own ratio, inset with `rounded-concentric-lg/6` so its corners follow the card's in every theme.
4622
- - An edition drop adds a `Badge appearance="overlay"` on the artwork, title and price, the one primary Button on the page (`Collect`), and a `Meter` for supply — collected against a known maximum is a level, not task progress.
4671
+ - An event post adds a `Badge appearance="overlay"` on the image, title and price, the one primary Button on the page (`Register`), and a `Meter` for capacity — seats taken against a known maximum is a level, not task progress.
4623
4672
  - Actions are ghost sm Buttons (icon + count) in a Row pulled out by the button's own padding (`-ms-3`) so the first icon sits on the text edge.
4624
4673
  - Loading the next page shows a Skeleton post in the shape the post will land in, above the explicit load-more Button in its `loading` state — predictable for keyboard and AT users, unlike infinite scroll.
4625
4674
 
@@ -4628,7 +4677,7 @@ Social reading feed with a side rail: a feed-width main column (composer, For yo
4628
4677
  - Each post is an <article>, so AT users can jump between posts; the rail is a labelled <aside> landmark.
4629
4678
  - Rail card titles render as h2 (CardTitle `render={<h2 />}`) under the page's one h1.
4630
4679
  - Icon-only buttons and every Follow / Remind button carry aria-labels naming their target; counts stay in the accessible name.
4631
- - The supply Meter is role=meter with a getAriaValueText reading ("62 of 100 collected").
4680
+ - The capacity Meter is role=meter with a getAriaValueText reading ("28 of 40 seats taken").
4632
4681
  - The Skeleton card is aria-busy; the loading Button keeps its label ("Loading posts").
4633
4682
 
4634
4683
  ## Source
@@ -4645,7 +4694,7 @@ type Post = {
4645
4694
  following: boolean
4646
4695
  body: string
4647
4696
  media?: { src: string; alt: string }
4648
- drop?: { title: string; price: string; collected: number; supply: number }
4697
+ event?: { title: string; price: string; registered: number; capacity: number }
4649
4698
  likes: number
4650
4699
  comments: number
4651
4700
  }
@@ -4658,9 +4707,9 @@ const posts: Post[] = [
4658
4707
  initials: "AL",
4659
4708
  time: "2h",
4660
4709
  following: true,
4661
- body: "Gold Rush is live. 100 signed prints, each paired with the on-chain original. Thank you for waiting.",
4662
- media: { src: "/media/edition-cover.svg", alt: "Gold Rush, a gold-leaf study in overlapping arcs" },
4663
- drop: { title: "Gold Rush", price: "$120", collected: 62, supply: 100 },
4710
+ body: "Registration for the spring workshop is open. Two days of plotter drawing in a small group. See you there.",
4711
+ media: { src: "/media/edition-cover.svg", alt: "Workshop poster, a gold-leaf study in overlapping arcs" },
4712
+ event: { title: "Spring plotter workshop", price: "$120", registered: 28, capacity: 40 },
4664
4713
  likes: 128,
4665
4714
  comments: 24,
4666
4715
  },
@@ -4683,7 +4732,7 @@ const posts: Post[] = [
4683
4732
  initials: "NS",
4684
4733
  time: "8h",
4685
4734
  following: false,
4686
- body: "Collectors of Dusk, no. 3 — your unlockable score is now in your library. Headphones on for this one.",
4735
+ body: "Thanks to everyone at the Dusk listening session — the full recording is now in your library. Headphones on.",
4687
4736
  likes: 214,
4688
4737
  comments: 31,
4689
4738
  },
@@ -4695,8 +4744,8 @@ const suggestions = [
4695
4744
  ]
4696
4745
 
4697
4746
  const upcoming = [
4698
- { id: "d_01", title: "Quiet Harbor", artist: "Mary Shelley", initials: "MS", date: "Sat, Oct 12" },
4699
- { id: "d_02", title: "Dusk, no. 4", artist: "Nina Simone", initials: "NS", date: "Tue, Oct 15" },
4747
+ { id: "e_01", title: "Quiet Harbor talk", host: "Mary Shelley", initials: "MS", date: "Sat, Oct 12" },
4748
+ { id: "e_02", title: "Dusk, live", host: "Nina Simone", initials: "NS", date: "Tue, Oct 15" },
4700
4749
  ]
4701
4750
 
4702
4751
  const renderPost = (post: Post) => (
@@ -4722,35 +4771,35 @@ const renderPost = (post: Post) => (
4722
4771
  {post.media ? (
4723
4772
  <AspectBox ratio={16 / 9} className="overflow-hidden rounded-concentric-lg/6 bg-muted">
4724
4773
  <img src={post.media.src} alt={post.media.alt} className="size-full object-cover" />
4725
- {post.drop ? (
4774
+ {post.event ? (
4726
4775
  <div className="absolute start-3 top-3">
4727
4776
  <Badge appearance="overlay" size="sm" dot>
4728
- Live drop
4777
+ Open now
4729
4778
  </Badge>
4730
4779
  </div>
4731
4780
  ) : null}
4732
4781
  </AspectBox>
4733
4782
  ) : null}
4734
- {post.drop ? (
4783
+ {post.event ? (
4735
4784
  <Stack gap={1.5}>
4736
4785
  <Row gap={2} align="center" justify="between">
4737
4786
  <div className="min-w-0">
4738
- <p className="text-title-md text-foreground">{post.drop.title}</p>
4787
+ <p className="text-title-md text-foreground">{post.event.title}</p>
4739
4788
  <p className="text-body-sm text-muted-foreground">
4740
- {`${post.drop.price} · Edition of ${post.drop.supply}`}
4789
+ {`${post.event.price} · ${post.event.capacity} seats`}
4741
4790
  </p>
4742
4791
  </div>
4743
- <Button>{`Collect for ${post.drop.price}`}</Button>
4792
+ <Button>{`Register for ${post.event.price}`}</Button>
4744
4793
  </Row>
4745
4794
  <Meter
4746
- value={post.drop.collected}
4747
- max={post.drop.supply}
4795
+ value={post.event.registered}
4796
+ max={post.event.capacity}
4748
4797
  size="sm"
4749
- getAriaValueText={(_, value) => `${value} of ${post.drop?.supply} collected`}
4798
+ getAriaValueText={(_, value) => `${value} of ${post.event?.capacity} seats taken`}
4750
4799
  >
4751
4800
  <Row gap={2} align="center" justify="between" className="mb-2">
4752
- <MeterLabel>{`${post.drop.collected} collected`}</MeterLabel>
4753
- <MeterValue>{(_, value) => `${(post.drop?.supply ?? 0) - value} left`}</MeterValue>
4801
+ <MeterLabel>{`${post.event.registered} registered`}</MeterLabel>
4802
+ <MeterValue>{(_, value) => `${(post.event?.capacity ?? 0) - value} left`}</MeterValue>
4754
4803
  </Row>
4755
4804
  <MeterTrack />
4756
4805
  </Meter>
@@ -4791,15 +4840,15 @@ export function FeedPage() {
4791
4840
  autoResize
4792
4841
  rows={2}
4793
4842
  aria-label="New post"
4794
- placeholder="Share a process shot or an upcoming drop"
4843
+ placeholder="Share a process shot or an upcoming event"
4795
4844
  />
4796
4845
  <Row gap={1} align="center" justify="between">
4797
4846
  <Row gap={0.5}>
4798
4847
  <Button variant="ghost" size="icon-sm" aria-label="Add images">
4799
4848
  <Icon name="image-stack" />
4800
4849
  </Button>
4801
- <Button variant="ghost" size="icon-sm" aria-label="Attach an edition">
4802
- <Icon name="gem" />
4850
+ <Button variant="ghost" size="icon-sm" aria-label="Add an event">
4851
+ <Icon name="calendar" />
4803
4852
  </Button>
4804
4853
  </Row>
4805
4854
  <Button variant="secondary" size="sm">
@@ -4866,17 +4915,17 @@ export function FeedPage() {
4866
4915
  </Card>
4867
4916
  <Card>
4868
4917
  <CardHeader>
4869
- <CardTitle render={<h2 />}>Upcoming drops</CardTitle>
4918
+ <CardTitle render={<h2 />}>Upcoming events</CardTitle>
4870
4919
  </CardHeader>
4871
4920
  <CardList>
4872
- {upcoming.map((drop) => (
4921
+ {upcoming.map((event) => (
4873
4922
  <Entity
4874
- key={drop.id}
4875
- leading={<Avatar variant="art" fallback={drop.initials} alt={drop.artist} />}
4876
- title={drop.title}
4877
- subtitle={`${drop.artist} · ${drop.date}`}
4923
+ key={event.id}
4924
+ leading={<Avatar variant="art" fallback={event.initials} alt={event.host} />}
4925
+ title={event.title}
4926
+ subtitle={`${event.host} · ${event.date}`}
4878
4927
  action={
4879
- <Button variant="ghost" size="icon-sm" aria-label={`Remind me about ${drop.title}`}>
4928
+ <Button variant="ghost" size="icon-sm" aria-label={`Remind me about ${event.title}`}>
4880
4929
  <Icon name="bell" />
4881
4930
  </Button>
4882
4931
  }
@@ -5181,7 +5230,7 @@ export function LandingPage() {
5181
5230
  <CardDescription>Team plan</CardDescription>
5182
5231
  </CardHeader>
5183
5232
  <CardContent>
5184
- <Meter value={64} tone="standard">
5233
+ <Meter value={64} tone="3">
5185
5234
  <Row justify="between" wrap className="mb-2">
5186
5235
  <MeterLabel>Used</MeterLabel>
5187
5236
  <MeterValue>{(_, value) => `${(value * 30).toLocaleString("en-US")} of 3,000`}</MeterValue>
@@ -5464,9 +5513,9 @@ const roleLabel: Record<Role, string> = {
5464
5513
 
5465
5514
  const roleSummary: Record<Role, string> = {
5466
5515
  owner: "Full access, including payouts and billing. Transfer ownership in studio settings.",
5467
- admin: "Manages members, editions and payouts.",
5468
- editor: "Drafts, prices and releases editions.",
5469
- viewer: "Sees editions and sales, but can't change them.",
5516
+ admin: "Manages members, projects and billing.",
5517
+ editor: "Drafts, edits and publishes projects.",
5518
+ viewer: "Sees projects and reports, but can't change them.",
5470
5519
  }
5471
5520
 
5472
5521
  type Activity = { id: string; icon: string; text: string; when: string }
@@ -5481,7 +5530,7 @@ type Member = {
5481
5530
  handle?: string
5482
5531
  joined?: string
5483
5532
  lastActive?: string
5484
- editions?: number
5533
+ projects?: number
5485
5534
  invitedBy?: string
5486
5535
  sent?: string
5487
5536
  expires?: string
@@ -5494,40 +5543,40 @@ const seats = 10
5494
5543
  const initialMembers: Member[] = [
5495
5544
  {
5496
5545
  id: "u_01", name: "Ada Lovelace", handle: "@ada", email: "ada@atelier.art", initials: "AL", role: "owner", pending: false,
5497
- joined: "Mar 4, 2024", lastActive: "Online now", editions: 12,
5546
+ joined: "Mar 4, 2024", lastActive: "Online now", projects: 12,
5498
5547
  activity: [
5499
- { id: "a1", icon: "gem", text: "Released Analytical Engine No. 3 — edition of 50", when: "2 hours ago" },
5548
+ { id: "a1", icon: "file", text: "Published the Analytical Engine No. 3 roadmap", when: "2 hours ago" },
5500
5549
  { id: "a2", icon: "mail", text: "Invited louise@bourgeois.studio as Editor", when: "2 days ago" },
5501
5550
  ],
5502
5551
  },
5503
5552
  {
5504
5553
  id: "u_02", name: "Grace Hopper", handle: "@grace", email: "grace@atelier.art", initials: "GH", role: "admin", pending: false,
5505
- joined: "Mar 9, 2024", lastActive: "20 minutes ago", editions: 4,
5554
+ joined: "Mar 9, 2024", lastActive: "20 minutes ago", projects: 4,
5506
5555
  activity: [
5507
5556
  { id: "a1", icon: "user", text: "Changed Frida Kahlo's role to Viewer", when: "Yesterday" },
5508
- { id: "a2", icon: "edit", text: "Updated pricing on First Compiler — edition of 10", when: "3 days ago" },
5509
- { id: "a3", icon: "gem", text: "Released First Compiler — edition of 10", when: "Sep 28" },
5557
+ { id: "a2", icon: "edit", text: "Updated the budget on First Compiler", when: "3 days ago" },
5558
+ { id: "a3", icon: "file", text: "Created the First Compiler project", when: "Sep 28" },
5510
5559
  ],
5511
5560
  },
5512
5561
  {
5513
5562
  id: "u_03", name: "Mary Shelley", handle: "@mary", email: "mary@atelier.art", initials: "MS", role: "editor", pending: false,
5514
- joined: "Jun 18, 2024", lastActive: "3 hours ago", editions: 7,
5563
+ joined: "Jun 18, 2024", lastActive: "3 hours ago", projects: 7,
5515
5564
  activity: [
5516
5565
  { id: "a1", icon: "calendar", text: "Scheduled Frankenstein Studies for Oct 14", when: "3 hours ago" },
5517
- { id: "a2", icon: "gem", text: "Released The Last Man — edition of 25", when: "Sep 21" },
5566
+ { id: "a2", icon: "file", text: "Published The Last Man report", when: "Sep 21" },
5518
5567
  ],
5519
5568
  },
5520
5569
  {
5521
5570
  id: "u_04", name: "Nina Simone", handle: "@nina", email: "nina@atelier.art", initials: "NS", role: "editor", pending: false,
5522
- joined: "Jan 12, 2025", lastActive: "Yesterday", editions: 3,
5571
+ joined: "Jan 12, 2025", lastActive: "Yesterday", projects: 3,
5523
5572
  activity: [
5524
- { id: "a1", icon: "upload", text: "Uploaded artwork for Little Girl Blue", when: "Yesterday" },
5525
- { id: "a2", icon: "gem", text: "Released Feeling Good — edition of 100", when: "Aug 30" },
5573
+ { id: "a1", icon: "upload", text: "Uploaded files for Little Girl Blue", when: "Yesterday" },
5574
+ { id: "a2", icon: "file", text: "Published the Feeling Good brief", when: "Aug 30" },
5526
5575
  ],
5527
5576
  },
5528
5577
  {
5529
5578
  id: "u_05", name: "Frida Kahlo", handle: "@frida", email: "frida@atelier.art", initials: "FK", role: "viewer", pending: false,
5530
- joined: "Jul 2, 2025", lastActive: "4 days ago", editions: 0,
5579
+ joined: "Jul 2, 2025", lastActive: "4 days ago", projects: 0,
5531
5580
  activity: [{ id: "a1", icon: "comment", text: "Commented on the Las Dos Fridas draft", when: "4 days ago" }],
5532
5581
  },
5533
5582
  {
@@ -5760,8 +5809,8 @@ export function ListDetailPage() {
5760
5809
  <dd className={selected.lastActive === "Online now" ? "text-success" : undefined}>
5761
5810
  {selected.lastActive}
5762
5811
  </dd>
5763
- <dt>Editions released</dt>
5764
- <dd>{selected.editions}</dd>
5812
+ <dt>Projects owned</dt>
5813
+ <dd>{selected.projects}</dd>
5765
5814
  </dl>
5766
5815
  )}
5767
5816
  </div>
@@ -5814,7 +5863,7 @@ export function ListDetailPage() {
5814
5863
  description={
5815
5864
  selected.pending
5816
5865
  ? `${selected.email} won't be able to join with the link they were sent.`
5817
- : "They lose access to the studio right away. Editions they released stay on sale."
5866
+ : "They lose access to the workspace right away. Projects they own stay with the team."
5818
5867
  }
5819
5868
  confirmLabel={selected.pending ? "Revoke invite" : "Remove member"}
5820
5869
  onConfirm={() => remove(selected.id)}
@@ -5879,17 +5928,17 @@ const timezones = [
5879
5928
  ]
5880
5929
 
5881
5930
  const notifications = [
5882
- { name: "notifyCollects", title: "New collects", description: "When someone collects your work", on: true },
5883
- { name: "notifySoldOut", title: "Sold out", description: "When an edition sells its last copy", on: true },
5884
- { name: "notifyFollowers", title: "New followers", description: "When a collector follows you", on: false },
5885
- { name: "notifyDigest", title: "Weekly digest", description: "Sales and collectors, every Monday", on: true },
5931
+ { name: "notifyComments", title: "New comments", description: "When someone comments on your work", on: true },
5932
+ { name: "notifyInvoicePaid", title: "Invoice paid", description: "When a customer pays an invoice", on: true },
5933
+ { name: "notifyMembers", title: "New members", description: "When someone joins your workspace", on: false },
5934
+ { name: "notifyDigest", title: "Weekly digest", description: "Activity and usage, every Monday", on: true },
5886
5935
  { name: "notifyProduct", title: "Product updates", description: "New Atelier features", on: false },
5887
5936
  ]
5888
5937
 
5889
5938
  const payouts = [
5890
5939
  { label: "Next payout", value: "$1,284.60", note: "Friday, Oct 16" },
5891
5940
  { label: "Pending", value: "$412.00", note: "Clears in 7 days" },
5892
- { label: "Paid this year", value: "$18,920.00", note: "Across 214 collects" },
5941
+ { label: "Paid this year", value: "$18,920.00", note: "Across 214 invoices" },
5893
5942
  ]
5894
5943
 
5895
5944
  export function SettingsPage() {
@@ -5914,8 +5963,8 @@ export function SettingsPage() {
5914
5963
  bottomNav={
5915
5964
  <BottomNav aria-label="Primary">
5916
5965
  <BottomNavItem icon={<Icon name="grid" />} label="Overview" showLabel render={<a href="/" />} />
5917
- <BottomNavItem icon={<Icon name="image-stack" />} label="Editions" showLabel render={<a href="/editions" />} />
5918
- <BottomNavItem icon={<Icon name="user" />} label="Collectors" showLabel render={<a href="/collectors" />} />
5966
+ <BottomNavItem icon={<Icon name="folder" />} label="Accounts" showLabel render={<a href="/accounts" />} />
5967
+ <BottomNavItem icon={<Icon name="user" />} label="Customers" showLabel render={<a href="/customers" />} />
5919
5968
  <BottomNavItem icon={<Icon name="bell" />} label="Activity" showLabel render={<a href="/activity" />} />
5920
5969
  <BottomNavItem icon={<Icon name="gear" />} label="Settings" showLabel active render={<a href="/settings" />} />
5921
5970
  </BottomNav>
@@ -5927,8 +5976,8 @@ export function SettingsPage() {
5927
5976
  </SidebarHeader>
5928
5977
  <SidebarNav>
5929
5978
  <SidebarItem icon={<Icon name="grid" />} label="Overview" render={<a href="/" />} />
5930
- <SidebarItem icon={<Icon name="image-stack" />} label="Editions" render={<a href="/editions" />} />
5931
- <SidebarItem icon={<Icon name="user" />} label="Collectors" render={<a href="/collectors" />} />
5979
+ <SidebarItem icon={<Icon name="folder" />} label="Accounts" render={<a href="/accounts" />} />
5980
+ <SidebarItem icon={<Icon name="user" />} label="Customers" render={<a href="/customers" />} />
5932
5981
  <SidebarItem
5933
5982
  icon={<Icon name="bell" />}
5934
5983
  label="Activity"
@@ -5982,7 +6031,7 @@ export function SettingsPage() {
5982
6031
  <Card id="profile" className="scroll-mt-6">
5983
6032
  <CardHeader>
5984
6033
  <CardTitle render={<h2 />}>Profile</CardTitle>
5985
- <CardDescription>How collectors see you on Atelier.</CardDescription>
6034
+ <CardDescription>How your team sees you on Atelier.</CardDescription>
5986
6035
  </CardHeader>
5987
6036
  {/* The one explicit save on the page: profile text is drafted, then
5988
6037
  committed. Toggles below apply the moment they change. */}
@@ -6013,14 +6062,14 @@ export function SettingsPage() {
6013
6062
  <Input defaultValue="ada" autoComplete="username" spellCheck={false} />
6014
6063
  </Field>
6015
6064
  </Grid>
6016
- <Field label="Bio" name="bio" description="Shown on your profile and edition pages.">
6065
+ <Field label="Bio" name="bio" description="Shown on your profile and in comments.">
6017
6066
  <Textarea
6018
6067
  rows={3}
6019
- defaultValue="Generative artist working in plotted ink and code. New editions of 25 to 100 every month."
6068
+ defaultValue="Product designer working on tools for small teams. Writing about process every month."
6020
6069
  />
6021
6070
  </Field>
6022
6071
  <Grid cols={{ base: 1, sm: 2 }} gap={3}>
6023
- <Field label="Timezone" name="timezone" description="Release times use this zone.">
6072
+ <Field label="Timezone" name="timezone" description="Due dates use this zone.">
6024
6073
  <Select defaultValue="europe-london" items={timezones}>
6025
6074
  <SelectTrigger />
6026
6075
  <SelectContent>
@@ -6045,7 +6094,7 @@ export function SettingsPage() {
6045
6094
  <CardHeader>
6046
6095
  <CardTitle render={<h2 />}>Notifications</CardTitle>
6047
6096
  <CardDescription>
6048
- Email updates about your editions. Changes save automatically.
6097
+ Email updates about your projects. Changes save automatically.
6049
6098
  </CardDescription>
6050
6099
  </CardHeader>
6051
6100
  <CardList>
@@ -6108,7 +6157,7 @@ export function SettingsPage() {
6108
6157
  <Entity
6109
6158
  leading={<Icon name="download" className="text-lg text-muted-foreground" />}
6110
6159
  title="Export data"
6111
- subtitle="Editions, sales, and collectors"
6160
+ subtitle="Projects, invoices, and members"
6112
6161
  action={
6113
6162
  <Button variant="outline" size="sm">
6114
6163
  Export
@@ -6122,8 +6171,8 @@ export function SettingsPage() {
6122
6171
  <CardHeader>
6123
6172
  <CardTitle render={<h2 />}>Delete account</CardTitle>
6124
6173
  <CardDescription>
6125
- Permanently remove your profile, editions, and sales history. Collectors keep
6126
- the editions they own.
6174
+ Permanently remove your profile, projects, and billing history. Files you shared
6175
+ stay with your team.
6127
6176
  </CardDescription>
6128
6177
  </CardHeader>
6129
6178
  <CardFooter className="justify-end">
@@ -6140,7 +6189,7 @@ export function SettingsPage() {
6140
6189
  open={deleteOpen}
6141
6190
  onOpenChange={setDeleteOpen}
6142
6191
  title="Delete your account?"
6143
- description="This permanently removes your profile, 12 editions, and sales history. Pending payouts are still sent. This can't be undone."
6192
+ description="This permanently removes your profile, 12 projects, and billing history. Your final invoice is still sent. This can't be undone."
6144
6193
  confirmLabel="Delete account"
6145
6194
  confirmingLabel="Deleting…"
6146
6195
  confirmText="ada"
@@ -6417,7 +6466,7 @@ A delete-account card: what deleting removes and what survives, one destructive
6417
6466
 
6418
6467
  - Copied from the SettingsPage page shell's Delete account section.
6419
6468
  - The button never acts directly: it opens the modal, whose confirm stays disabled until the handle is typed.
6420
- - The description says what is lost AND what is kept ("Collectors keep the editions they own"), which is what people want to know before they decide.
6469
+ - The description says what is lost AND what is kept ("Files you shared stay with your team"), which is what people want to know before they decide.
6421
6470
 
6422
6471
  ## Accessibility
6423
6472
 
@@ -6430,7 +6479,7 @@ A delete-account card: what deleting removes and what survives, one destructive
6430
6479
  import { useState } from "react"
6431
6480
  import { Button, Card, CardDescription, CardFooter, CardHeader, CardTitle, cn, DestructiveActionModal } from "@sableui/react"
6432
6481
 
6433
- const account = { handle: "ada", editions: 12 }
6482
+ const account = { handle: "ada", projects: 12 }
6434
6483
 
6435
6484
  export interface DangerZoneBlockProps {
6436
6485
  className?: string
@@ -6448,7 +6497,7 @@ export function DangerZoneBlock({ className, framed = true, onDelete }: DangerZo
6448
6497
  <CardHeader>
6449
6498
  <CardTitle render={<h2 />}>Delete account</CardTitle>
6450
6499
  <CardDescription>
6451
- Permanently remove your profile, editions, and sales history. Collectors keep the editions they own.
6500
+ Permanently remove your profile, projects, and billing history. Files you shared stay with your team.
6452
6501
  </CardDescription>
6453
6502
  </CardHeader>
6454
6503
  <CardFooter actions className="justify-end">
@@ -6461,7 +6510,7 @@ export function DangerZoneBlock({ className, framed = true, onDelete }: DangerZo
6461
6510
  open={open}
6462
6511
  onOpenChange={setOpen}
6463
6512
  title="Delete your account?"
6464
- description={`This permanently removes your profile, ${account.editions} editions, and sales history. Pending payouts are still sent. This can't be undone.`}
6513
+ description={`This permanently removes your profile, ${account.projects} projects, and billing history. Your final invoice is still sent. This can't be undone.`}
6465
6514
  confirmLabel="Delete account"
6466
6515
  confirmingLabel="Deleting…"
6467
6516
  confirmText={account.handle}
@@ -6690,10 +6739,10 @@ const intro = {
6690
6739
  }
6691
6740
 
6692
6741
  const questions = [
6693
- { q: "Is there a free plan?", a: "Yes. Starter is free forever for three live editions, with no fees on your first $1,000 in sales." },
6742
+ { q: "Is there a free plan?", a: "Yes. Starter is free forever for three active projects, with no card needed to sign up." },
6694
6743
  { q: "Can I change plans later?", a: "Any time. Upgrades apply immediately and are prorated; downgrades take effect at the end of the billing period." },
6695
- { q: "Who owns the work I sell?", a: "You do. Collectors own the editions they buy, and you keep the rights to the work itself." },
6696
- { q: "How do payouts work?", a: "Sales clear after seven days and are paid out every Friday to the account you connect in settings." },
6744
+ { q: "Who owns the data I add?", a: "You do. You can export everything in your workspace at any time, and we never sell it." },
6745
+ { q: "How does billing work?", a: "Plans are billed per seat at the start of each period, to the card or account you add in settings." },
6697
6746
  ]
6698
6747
 
6699
6748
  export interface FaqBlockProps {
@@ -6906,7 +6955,7 @@ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,
6906
6955
 
6907
6956
  const goals = [
6908
6957
  { goal: "Releases", progress: 82 },
6909
- { goal: "Collectors", progress: 64 },
6958
+ { goal: "Customers", progress: 64 },
6910
6959
  { goal: "Revenue", progress: 91 },
6911
6960
  ]
6912
6961
 
@@ -7470,7 +7519,7 @@ An activity list: Entity rows with the actor's Avatar, who did what, and a compa
7470
7519
  import { Avatar, Card, CardList, cn, Entity, StatusDot } from "@sableui/react"
7471
7520
 
7472
7521
  const notifications = [
7473
- { initials: "MO", name: "Mara Okafor", what: "collected your edition", when: "2m", unread: true },
7522
+ { initials: "MO", name: "Mara Okafor", what: "invited you to Q3 planning", when: "2m", unread: true },
7474
7523
  { initials: "SR", name: "Sam Rivera", what: "left a comment on Dusk", when: "1h", unread: false },
7475
7524
  ]
7476
7525
 
@@ -7747,32 +7796,32 @@ const tiers = [
7747
7796
  {
7748
7797
  id: "starter",
7749
7798
  name: "Starter",
7750
- blurb: "For one person shipping their first editions.",
7799
+ blurb: "For one person getting a first project going.",
7751
7800
  monthly: 0,
7752
7801
  yearly: 0,
7753
7802
  cta: "Start for free",
7754
7803
  featured: false,
7755
- features: ["3 live editions", "Collector profiles", "Community support"],
7804
+ features: ["3 active projects", "Shared docs", "Community support"],
7756
7805
  },
7757
7806
  {
7758
7807
  id: "studio",
7759
7808
  name: "Studio",
7760
- blurb: "For working artists with a steady release schedule.",
7809
+ blurb: "For small teams shipping on a steady schedule.",
7761
7810
  monthly: 20,
7762
7811
  yearly: 16,
7763
7812
  cta: "Start free trial",
7764
7813
  featured: true,
7765
- features: ["Unlimited editions", "Scheduled drops", "Sales analytics", "Priority support"],
7814
+ features: ["Unlimited projects", "Scheduled reports", "Usage analytics", "Priority support"],
7766
7815
  },
7767
7816
  {
7768
- id: "collective",
7769
- name: "Collective",
7770
- blurb: "For galleries and teams releasing together.",
7817
+ id: "business",
7818
+ name: "Business",
7819
+ blurb: "For companies running several teams at once.",
7771
7820
  monthly: 48,
7772
7821
  yearly: 40,
7773
7822
  cta: "Talk to sales",
7774
7823
  featured: false,
7775
- features: ["Everything in Studio", "Up to 10 seats", "Shared payouts", "Roles and approvals"],
7824
+ features: ["Everything in Studio", "Up to 10 seats", "SSO and audit log", "Roles and approvals"],
7776
7825
  },
7777
7826
  ]
7778
7827
 
@@ -7792,10 +7841,10 @@ export function PricingTableBlock({ className, onChoose }: PricingTableBlockProp
7792
7841
  <Stack gap={10}>
7793
7842
  <Stack gap={4} align="center" className="text-center">
7794
7843
  <h2 id={titleId} className="text-balance text-heading-1">
7795
- Pricing that grows with your practice
7844
+ Pricing that grows with your team
7796
7845
  </h2>
7797
7846
  <p className="max-w-(--measure-prose) text-body-lg text-muted-foreground">
7798
- Every plan includes unlimited collectors and no fees on your first $1,000 in sales.
7847
+ Every plan includes unlimited guests, and you can switch plans at any time.
7799
7848
  </p>
7800
7849
  <ToggleGroup value={[period]} onValueChange={(v: unknown[]) => v[0] && setPeriod(v[0] as Period)} aria-label="Billing period">
7801
7850
  <Toggle value="monthly">Monthly</Toggle>
@@ -7807,14 +7856,14 @@ export function PricingTableBlock({ className, onChoose }: PricingTableBlockProp
7807
7856
  <Card
7808
7857
  key={tier.id}
7809
7858
  render={<article />}
7810
- className={cn("flex flex-col", tier.featured && "border-primary ring-1 ring-primary")}
7859
+ className={cn("flex flex-col", tier.featured && "outline-primary ring-1 ring-primary")}
7811
7860
  >
7812
7861
  <CardHeader>
7813
7862
  <CardTitle render={<h3 />}>{tier.name}</CardTitle>
7814
7863
  <CardDescription>{tier.blurb}</CardDescription>
7815
7864
  {tier.featured ? (
7816
7865
  <CardAction>
7817
- <Badge tone="edition" size="sm">
7866
+ <Badge tone="1" size="sm">
7818
7867
  Most popular
7819
7868
  </Badge>
7820
7869
  </CardAction>
@@ -7852,15 +7901,15 @@ export function PricingTableBlock({ className, onChoose }: PricingTableBlockProp
7852
7901
 
7853
7902
  # ProductCardBlock
7854
7903
 
7855
- A product card: edge-to-edge artwork, title and maker, an edition Badge, and the price beside a single buy action.
7904
+ A product card: edge-to-edge product image, title and maker, a status Badge, and the price beside a single buy action.
7856
7905
 
7857
- **Intent:** Reach for this for a product, edition or listing in a grid or a featured slot. For a row in a dense list, use an Entity with the image as `leading` instead.
7906
+ **Intent:** Reach for this for a product or listing in a grid or a featured slot. For a row in a dense list, use an Entity with the image as `leading` instead.
7858
7907
 
7859
7908
  **Block:** Commerce · card · designed for 16rem–20rem wide (fills its container up to the max)
7860
7909
 
7861
7910
  ## Notes
7862
7911
 
7863
- - The artwork runs to the card's edges; the card clips it to its own corners (overflow-hidden).
7912
+ - The image runs to the card's edges; the card clips it to its own corners (overflow-hidden).
7864
7913
  - Prices are `text-mono-lg` — the metric mono tier with tabular figures — never a heading tier.
7865
7914
  - The placeholder art is painted from the palette's decorative tones so it follows the theme; swap it for the product image with real alt text.
7866
7915
 
@@ -7875,7 +7924,7 @@ A product card: edge-to-edge artwork, title and maker, an edition Badge, and the
7875
7924
  import { AspectBox, Badge, Button, Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle, cn, MediaImage } from "@sableui/react"
7876
7925
 
7877
7926
  // `image` is the product shot URL; left empty, it paints art seeded by the title.
7878
- const product = { title: "Untitled (gold rush)", maker: "Mara Okafor", image: "", edition: "12 of 50", price: "0.42 SOL" }
7927
+ const product = { title: "Linen throw blanket", maker: "Northfield Home", image: "", badge: "New", price: "$64.00" }
7879
7928
 
7880
7929
 
7881
7930
  export interface ProductCardBlockProps {
@@ -7896,12 +7945,12 @@ export function ProductCardBlock({ className, framed = true, onCollect }: Produc
7896
7945
  <CardTitle>{product.title}</CardTitle>
7897
7946
  <CardDescription>{product.maker}</CardDescription>
7898
7947
  <CardAction>
7899
- <Badge tone="edition">{product.edition}</Badge>
7948
+ <Badge tone="1">{product.badge}</Badge>
7900
7949
  </CardAction>
7901
7950
  </CardHeader>
7902
7951
  <CardFooter className="justify-between pt-4">
7903
7952
  <span className="text-mono-lg">{product.price}</span>
7904
- <Button onClick={onCollect}>Collect</Button>
7953
+ <Button onClick={onCollect}>Add to cart</Button>
7905
7954
  </CardFooter>
7906
7955
  </Card>
7907
7956
  )
@@ -7939,7 +7988,7 @@ const profile = {
7939
7988
  name: "Juno Avery",
7940
7989
  initials: "JA",
7941
7990
  handle: "@juno · Sound and light",
7942
- bio: "Field recordings, slowed down. New edition every full moon.",
7991
+ bio: "Field recordings, slowed down. New track every full moon.",
7943
7992
  stats: [
7944
7993
  { value: "128", label: "posts" },
7945
7994
  { value: "4.2k", label: "followers" },
@@ -8031,7 +8080,7 @@ const profile = {
8031
8080
  name: "Ada Lovelace",
8032
8081
  initials: "AL",
8033
8082
  handle: "ada",
8034
- bio: "Generative artist working in plotted ink and code. New editions of 25 to 100 every month.",
8083
+ bio: "Product designer working on tools for small teams. Writing about process every month.",
8035
8084
  timezone: "europe-london",
8036
8085
  }
8037
8086
 
@@ -8056,7 +8105,7 @@ export function ProfileFormBlock({ className, framed = true, onSave }: ProfileFo
8056
8105
  <Card variant={framed ? "default" : "flat"} className={cn("w-full max-w-2xl", className)}>
8057
8106
  <CardHeader>
8058
8107
  <CardTitle render={<h2 />}>Profile</CardTitle>
8059
- <CardDescription>How collectors see you.</CardDescription>
8108
+ <CardDescription>How your team sees you.</CardDescription>
8060
8109
  </CardHeader>
8061
8110
  {/* The one explicit save: profile text is drafted, then committed. */}
8062
8111
  <Form
@@ -8090,7 +8139,7 @@ export function ProfileFormBlock({ className, framed = true, onSave }: ProfileFo
8090
8139
  <Input defaultValue={profile.handle} autoComplete="username" spellCheck={false} />
8091
8140
  </Field>
8092
8141
  </Grid>
8093
- <Field label="Bio" name="bio" description="Shown on your profile and edition pages.">
8142
+ <Field label="Bio" name="bio" description="Shown on your profile and in comments.">
8094
8143
  <Textarea rows={3} defaultValue={profile.bio} />
8095
8144
  </Field>
8096
8145
  <Grid cols={{ base: 1, sm: 2 }} gap={3}>
@@ -8147,10 +8196,10 @@ import { Badge, Card, CardAction, CardContent, CardDescription, CardHeader, cn,
8147
8196
  const revenue = { label: "Revenue this month", total: "$12,480", change: "+8.2%" }
8148
8197
 
8149
8198
  const segments = [
8150
- { label: "Editions", value: 42, tone: "edition" },
8151
- { label: "Collectibles", value: 27, tone: "collectible" },
8152
- { label: "Standard", value: 19, tone: "standard" },
8153
- { label: "Commissions", value: 12, tone: "info" },
8199
+ { label: "Direct", value: 42, tone: "1" },
8200
+ { label: "Partners", value: 27, tone: "2" },
8201
+ { label: "Referrals", value: 19, tone: "3" },
8202
+ { label: "Resellers", value: 12, tone: "info" },
8154
8203
  ] as const
8155
8204
 
8156
8205
  export interface RevenueBreakdownBlockProps {
@@ -8172,7 +8221,7 @@ export function RevenueBreakdownBlock({ className, framed = true }: RevenueBreak
8172
8221
  </CardHeader>
8173
8222
  <CardContent className="flex flex-col gap-4 pt-2">
8174
8223
  <span className="text-mono-xl">{revenue.total}</span>
8175
- <SegmentedBar label="Revenue by type" segments={[...segments]} size="lg" />
8224
+ <SegmentedBar label="Revenue by channel" segments={[...segments]} size="lg" />
8176
8225
  {/* Two columns while each entry fits whole, one when they would not:
8177
8226
  sized in em, so a larger type or a narrower card drops to one.
8178
8227
  aria-hidden: the bar already reads every share aloud. */}
@@ -8467,9 +8516,9 @@ import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle, cn, G
8467
8516
 
8468
8517
  const stats = [
8469
8518
  { label: "Revenue", value: "$18,240", delta: "+8.2%", up: true, note: "vs. $16,857" },
8470
- { label: "Collects", value: "1,284", delta: "+3.1%", up: true, note: "182 first-time" },
8471
- { label: "Collectors", value: "312", delta: "+12.4%", up: true, note: "Best month yet" },
8472
- { label: "Sell-through", value: "64%", delta: "−1.8%", up: false, note: "6 open editions" },
8519
+ { label: "Orders", value: "1,284", delta: "+3.1%", up: true, note: "182 first-time" },
8520
+ { label: "New customers", value: "312", delta: "+12.4%", up: true, note: "Best month yet" },
8521
+ { label: "Conversion", value: "3.4%", delta: "−0.2%", up: false, note: "6 open trials" },
8473
8522
  ]
8474
8523
 
8475
8524
  export interface StatRowBlockProps {
@@ -8604,9 +8653,9 @@ import { useState, useId } from "react"
8604
8653
  import { Badge, Button, Card, CardAction, CardContent, CardFooter, CardHeader, CardTitle, Checkbox, cn, Icon, Label } from "@sableui/react"
8605
8654
 
8606
8655
  const tasks = [
8607
- { title: "Pick the cover type", tag: "Design", tone: "collectible", done: true },
8656
+ { title: "Pick the cover type", tag: "Design", tone: "2", done: true },
8608
8657
  { title: "Wire up checkout", tag: "Engineering", tone: "info", done: false },
8609
- { title: "Draft launch post", tag: "Launch", tone: "edition", done: false },
8658
+ { title: "Draft launch post", tag: "Launch", tone: "1", done: false },
8610
8659
  ] as const
8611
8660
 
8612
8661
  export interface TaskListBlockProps {
@@ -8723,7 +8772,7 @@ export function TeamMembersBlock({ className, framed = true, onInvite, onMemberA
8723
8772
  subtitle={m.email}
8724
8773
  action={
8725
8774
  <div className="flex items-center gap-2">
8726
- <Badge appearance={m.role === "Pending" ? "outline" : "plain"} tone={m.role === "Owner" ? "edition" : "neutral"} size="sm">
8775
+ <Badge appearance={m.role === "Pending" ? "outline" : "plain"} tone={m.role === "Owner" ? "1" : "neutral"} size="sm">
8727
8776
  {m.role}
8728
8777
  </Badge>
8729
8778
  {/* The owner is not managed from here; a placeholder keeps the column aligned. */}
@@ -9138,7 +9187,7 @@ export function VerifyCodeBlock({ className, framed = true, onVerify, onResend }
9138
9187
 
9139
9188
  A balance card: the balance in the metric mono tier, Send and Receive as an equal pair, and recent activity as Entity rows with signed amounts.
9140
9189
 
9141
- **Intent:** Reach for this for a wallet, account balance or credits summary with its latest movements. For a full transaction history with filters, use a DataTable.
9190
+ **Intent:** Reach for this for an account balance, store credit or credits summary with its latest movements. For a full transaction history with filters, use a DataTable.
9142
9191
 
9143
9192
  **Block:** Commerce · card · designed for 18rem–20rem wide (fills its container up to the max)
9144
9193
 
@@ -9146,24 +9195,24 @@ A balance card: the balance in the metric mono tier, Send and Receive as an equa
9146
9195
 
9147
9196
  - The balance is CardTitle at `text-mono-xl` — tabular figures, never a heading tier — under its label as CardDescription.
9148
9197
  - Send is primary and Receive secondary, side by side at equal width: two directions of one action, with sending the riskier and more frequent.
9149
- - Incoming amounts are `text-success`; outgoing stay muted rather than red, because a withdrawal is not an error.
9198
+ - Incoming amounts are `text-success`; outgoing stay muted rather than red, because a payout or refund is not an error.
9150
9199
 
9151
9200
  ## Accessibility
9152
9201
 
9153
9202
  - Each activity row's leading swatch is aria-hidden; the title and detail carry the meaning.
9154
- - Amounts keep their sign in text (+0.80 / -1.50), so the direction does not depend on colour.
9203
+ - Amounts keep their sign in text (+49.00 / -850.00), so the direction does not depend on colour.
9155
9204
 
9156
9205
  ## Source
9157
9206
 
9158
9207
  ```tsx
9159
9208
  import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle, cn, Entity, Icon } from "@sableui/react"
9160
9209
 
9161
- const balance = "14.62 SOL"
9210
+ const balance = "$2,418.50"
9162
9211
 
9163
9212
  const activity = [
9164
- { title: "Sold “Dusk, no. 4”", detail: "Edition · 2h ago", amount: "+0.80", tone: "edition" },
9165
- { title: "Royalty", detail: "Collectible · Yesterday", amount: "+0.12", tone: "collectible" },
9166
- { title: "Withdrawal", detail: "To Phantom · Mon", amount: "-1.50", tone: "standard" },
9213
+ { title: "Subscription", detail: "Team plan · 2h ago", amount: "+49.00", tone: "1" },
9214
+ { title: "Refund", detail: "Order 1042 · Yesterday", amount: "-24.00", tone: "2" },
9215
+ { title: "Payout", detail: "To checking · Mon", amount: "-850.00", tone: "3" },
9167
9216
  ]
9168
9217
 
9169
9218
  export interface WalletBlockProps {
@@ -9273,7 +9322,7 @@ import { PageHeader, Region } from "@sableui/react"
9273
9322
  ```tsx
9274
9323
  import { Card, CardHeader, CardTitle, Grid, Region } from "@sableui/react"
9275
9324
 
9276
- <Region max="wide"><Grid minItemWidth="16rem"><Card><CardHeader><CardTitle>Revenue</CardTitle></CardHeader></Card><Card><CardHeader><CardTitle>Collects</CardTitle></CardHeader></Card></Grid></Region>
9325
+ <Region max="wide"><Grid minItemWidth="16rem"><Card><CardHeader><CardTitle>Revenue</CardTitle></CardHeader></Card><Card><CardHeader><CardTitle>Orders</CardTitle></CardHeader></Card></Grid></Region>
9277
9326
  ```
9278
9327
 
9279
9328
  ### Fluid workspace: the queue's table takes the extra width
@@ -9323,7 +9372,7 @@ Use Page as a route content shell that combines Region sizing with vertical rhyt
9323
9372
  ```tsx
9324
9373
  import { Card, CardContent, Page, PageHeader } from "@sableui/react"
9325
9374
 
9326
- <Page as="main" max="feed"><PageHeader title="Home" description="Latest posts from artists you follow." /><Card><CardContent><p className="text-body-md">First post.</p></CardContent></Card></Page>
9375
+ <Page as="main" max="feed"><PageHeader title="Home" description="Latest posts from people you follow." /><Card><CardContent><p className="text-body-md">First post.</p></CardContent></Card></Page>
9327
9376
  ```
9328
9377
 
9329
9378
  ---
@@ -9394,7 +9443,7 @@ import { Card, CardDescription, CardHeader, Grid } from "@sableui/react"
9394
9443
  ```tsx
9395
9444
  import { Badge, Grid } from "@sableui/react"
9396
9445
 
9397
- <Grid cols={{ base: 1, md: 2, lg: 4 }}><Badge appearance="plain" tone="standard" dot /><Badge appearance="plain" tone="warning" dot /><Badge appearance="plain" tone="neutral" dot /><Badge appearance="plain" tone="destructive" dot /></Grid>
9446
+ <Grid cols={{ base: 1, md: 2, lg: 4 }}><Badge appearance="plain" tone="3" dot /><Badge appearance="plain" tone="warning" dot /><Badge appearance="plain" tone="neutral" dot /><Badge appearance="plain" tone="destructive" dot /></Grid>
9398
9447
  ```
9399
9448
 
9400
9449
  ---
@@ -9733,7 +9782,7 @@ Use SplitPane for responsive two-pane layouts where a primary pane and supportin
9733
9782
  ```tsx
9734
9783
  import { AspectBox, Button, SplitPane, Stack } from "@sableui/react"
9735
9784
 
9736
- <SplitPane ratio="2/1" primary={<AspectBox ratio={1}><img src={post.imageUrl} alt={post.title} className="size-full object-cover" /></AspectBox>} secondary={<Stack gap={2}><h1 className="text-heading-1">{post.title}</h1><p className="text-body-md text-muted-foreground">{post.description}</p><Button>Collect</Button></Stack>} />
9785
+ <SplitPane ratio="2/1" primary={<AspectBox ratio={1}><img src={post.imageUrl} alt={post.title} className="size-full object-cover" /></AspectBox>} secondary={<Stack gap={2}><h1 className="text-heading-1">{post.title}</h1><p className="text-body-md text-muted-foreground">{post.description}</p><Button>Add to cart</Button></Stack>} />
9737
9786
  ```
9738
9787
 
9739
9788
  ---
@@ -9896,7 +9945,7 @@ Use Accordion to reveal a vertical set of related sections while keeping the sur
9896
9945
  ```tsx
9897
9946
  import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger } from "@sableui/react"
9898
9947
 
9899
- <Accordion defaultValue={["a"]}><AccordionItem value="a"><AccordionTrigger>Is it permanent?</AccordionTrigger><AccordionPanel>Yes — stored on Arweave.</AccordionPanel></AccordionItem><AccordionItem value="b"><AccordionTrigger>How much?</AccordionTrigger><AccordionPanel>A one-time mint fee.</AccordionPanel></AccordionItem></Accordion>
9948
+ <Accordion defaultValue={["a"]}><AccordionItem value="a"><AccordionTrigger>Can I cancel anytime?</AccordionTrigger><AccordionPanel>Yes — from billing settings.</AccordionPanel></AccordionItem><AccordionItem value="b"><AccordionTrigger>How much?</AccordionTrigger><AccordionPanel>$16 per seat each month.</AccordionPanel></AccordionItem></Accordion>
9900
9949
  ```
9901
9950
 
9902
9951
  ### Multiple open
@@ -9912,7 +9961,7 @@ import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger } from "@sab
9912
9961
  ```tsx
9913
9962
  import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger, Icon } from "@sableui/react"
9914
9963
 
9915
- <Accordion defaultValue={["a"]}><AccordionItem value="a"><AccordionTrigger indicator={<Icon name="plus" className="text-xs text-muted-foreground transition-transform [[data-panel-open]_&]:rotate-45" />}>Is it permanent?</AccordionTrigger><AccordionPanel>Yes — stored on Arweave.</AccordionPanel></AccordionItem></Accordion>
9964
+ <Accordion defaultValue={["a"]}><AccordionItem value="a"><AccordionTrigger indicator={<Icon name="plus" className="text-xs text-muted-foreground transition-transform [[data-panel-open]_&]:rotate-45" />}>Can I cancel anytime?</AccordionTrigger><AccordionPanel>Yes — from billing settings.</AccordionPanel></AccordionItem></Accordion>
9916
9965
  ```
9917
9966
 
9918
9967
  ---
@@ -9942,7 +9991,7 @@ Use AppHeader for compact application chrome at the top of a screen, usually wit
9942
9991
  | --- | --- | --- | --- |
9943
9992
  | leading | ReactNode | — | Leading slot — back button, create action, or menu trigger. |
9944
9993
  | title | ReactNode | — | Center slot — brand wordmark or contextual page title. |
9945
- | actions | ReactNode | — | Trailing slot — wallet trigger, action buttons. |
9994
+ | actions | ReactNode | — | Trailing slot — account menu, action buttons. |
9946
9995
 
9947
9996
  ## Tokens used
9948
9997
 
@@ -10064,7 +10113,7 @@ import { AppHeader, AppShell, Banner, BottomNav, BottomNavItem, Icon, PageHeader
10064
10113
  // <Link> = your router's link (react-router, next/link, @tanstack/router, …)
10065
10114
 
10066
10115
  <AppShell
10067
- banners={<Banner tone="warning">Network congested — mints may be slow.</Banner>}
10116
+ banners={<Banner tone="warning">Sync is delayed — changes may be slow to appear.</Banner>}
10068
10117
  header={<AppHeader title="Studio" />}
10069
10118
  sidebar={
10070
10119
  <Sidebar>
@@ -10083,7 +10132,7 @@ import { AppHeader, AppShell, Banner, BottomNav, BottomNavItem, Icon, PageHeader
10083
10132
  }
10084
10133
  >
10085
10134
  <Region max="feed">
10086
- <PageHeader title="Home" description="Latest posts from artists you follow." />
10135
+ <PageHeader title="Home" description="Latest posts from people you follow." />
10087
10136
  </Region>
10088
10137
  </AppShell>
10089
10138
  ```
@@ -10185,7 +10234,7 @@ Use AreaChart for volume over time — visitors, revenue, usage — where the fi
10185
10234
 
10186
10235
  ## Tokens used
10187
10236
 
10188
- `tone-edition`, `tone-collectible`, `tone-standard`, `tone-info`, `tone-warning`, `primary`, `muted-foreground`, `border`, `popover`, `popover-foreground`, `foreground`, `background`
10237
+ `tone-1`, `tone-2`, `tone-3`, `tone-info`, `tone-warning`, `primary`, `muted-foreground`, `border`, `popover`, `popover-foreground`, `foreground`, `background`
10189
10238
 
10190
10239
  ## Motion
10191
10240
 
@@ -10544,7 +10593,7 @@ import { Avatar, Entity, StatusDot } from "@sableui/react"
10544
10593
 
10545
10594
  **Status:** stable
10546
10595
 
10547
- Compact static label, set on two axes: appearance (soft, outline, solid, overlay on media, plain text) and tone (neutral or a semantic tone). An optional leading icon, or a status dot (with pulse) for state.
10596
+ Compact static label, set on two axes: appearance (soft, outline, solid, overlay on media, plain text) and tone (neutral, a semantic tone, or a palette family like blue for a category with no meaning attached). An optional leading icon, or a status dot (with pulse) for state.
10548
10597
 
10549
10598
  **Keywords:** badge, tag, label, status, metadata, chip, pill, tone, category
10550
10599
 
@@ -10555,7 +10604,7 @@ Use Badge for any short static label: metadata, categories, tone-coded labels, a
10555
10604
  ### Best practices
10556
10605
 
10557
10606
  - Do: Keep badge text short: one to three words or a compact count.
10558
- - Do: Use tone variants when the badge carries meaning, such as success, warning, destructive, edition, or collectible.
10607
+ - Do: Use tone variants when the badge carries meaning, such as success, warning or destructive, or a decorative tone for a category or plan.
10559
10608
  - Do: Pair icons with text; the icon reinforces meaning but should not be the only cue.
10560
10609
  - Do: Choose appearance and tone separately: appearance is how loud it is (soft by default, solid for emphasis, overlay on images, plain for a status line); tone is what it means.
10561
10610
  - Don't: Use a Badge as a button. If it triggers an action, use Button, Toggle, or a menu item.
@@ -10574,7 +10623,7 @@ Use Badge for any short static label: metadata, categories, tone-coded labels, a
10574
10623
  | Name | Type | Default | Description |
10575
10624
  | --- | --- | --- | --- |
10576
10625
  | appearance | "soft" \| "outline" \| "solid" \| "overlay" \| "plain" | "soft" | How it is drawn. soft: a tinted fill (neutral is the secondary surface). outline: a hairline. solid: the tone at full strength (neutral is the inverted chip). overlay: legible on images — a dark scrim, or the solid tone. plain: no fill or padding, a status line. |
10577
- | tone | "neutral" \| "standard" \| "collectible" \| "edition" \| "info" \| "success" \| "warning" \| "destructive" | "neutral" | What it means. Every appearance takes every tone. |
10626
+ | tone | "neutral" \| "1" \| "2" \| "3" \| "info" \| "success" \| "warning" \| "destructive" \| "red" \| "orange" \| "yellow" \| "green" \| "teal" \| "blue" \| "purple" \| "pink" | "neutral" | What it means. Every appearance takes every tone. The palette families (red … pink) are categories with no meaning attached — tags, labels, project colours — painted from the theme's generated palette, so they lean toward the brand and read in both modes. |
10578
10627
  | size | "sm" \| "md" | "md" | "sm" uses tighter padding + text-label-sm; "md" is the default look. |
10579
10628
  | icon | ReactNode | — | Optional leading glyph (e.g. <Icon name="lock" />), rendered before the children with the base gap. Ignored when dot is set. |
10580
10629
  | dot | boolean | false | A leading status dot in the badge's tone. With appearance="plain" it is a status line (● Ready). |
@@ -10583,12 +10632,12 @@ Use Badge for any short static label: metadata, categories, tone-coded labels, a
10583
10632
  ## Variants
10584
10633
 
10585
10634
  - **appearance**: soft, outline, solid, overlay, plain
10586
- - **tone**: neutral, standard, collectible, edition, info, success, warning, destructive
10635
+ - **tone**: neutral, 1, 2, 3, info, success, warning, destructive, red, orange, yellow, green, teal, blue, purple, pink
10587
10636
  - **size**: sm, md
10588
10637
 
10589
10638
  ## Tokens used
10590
10639
 
10591
- `secondary`, `secondary-foreground`, `border`, `foreground`, `background`, `muted`, `muted-foreground`, `tone-standard`, `tone-collectible`, `tone-edition`, `tone-info`, `success`, `tone-warning`, `destructive`, `overlay`, `overlay-foreground`, `tone-standard-foreground`, `tone-collectible-foreground`, `tone-edition-foreground`, `tone-info-foreground`, `success-foreground`, `tone-warning-foreground`, `destructive-foreground`
10640
+ `palette-wash`, `palette-wash-strong`, `palette-edge`, `palette-dot`, `palette-solid`, `palette-text`, `palette-solid-ink`, `secondary`, `secondary-foreground`, `border`, `foreground`, `background`, `muted`, `muted-foreground`, `tone-1`, `tone-2`, `tone-3`, `tone-info`, `success`, `tone-warning`, `destructive`, `overlay`, `overlay-foreground`, `tone-1-foreground`, `tone-2-foreground`, `tone-3-foreground`, `tone-info-foreground`, `success-foreground`, `tone-warning-foreground`, `destructive-foreground`
10592
10641
 
10593
10642
  ## Accessibility
10594
10643
 
@@ -10607,7 +10656,15 @@ Use Badge for any short static label: metadata, categories, tone-coded labels, a
10607
10656
  ```tsx
10608
10657
  import { Badge, Row } from "@sableui/react"
10609
10658
 
10610
- <Row gap={1}><Badge tone="edition">Edition</Badge><Badge tone="collectible">Collectible</Badge><Badge tone="standard">Standard</Badge></Row>
10659
+ <Row gap={1}><Badge tone="1">Business</Badge><Badge tone="2">Studio</Badge><Badge tone="3">Starter</Badge></Row>
10660
+ ```
10661
+
10662
+ ### Palette colours (categories)
10663
+
10664
+ ```tsx
10665
+ import { Badge, Row } from "@sableui/react"
10666
+
10667
+ <Row gap={1}><Badge tone="blue">Design</Badge><Badge tone="teal">Research</Badge><Badge tone="orange">Marketing</Badge><Badge tone="pink" appearance="outline">Ops</Badge><Badge tone="purple" appearance="solid">Launch</Badge></Row>
10611
10668
  ```
10612
10669
 
10613
10670
  ### Solid
@@ -10647,7 +10704,7 @@ import { Badge } from "@sableui/react"
10647
10704
  ```tsx
10648
10705
  import { Badge, Icon, Row } from "@sableui/react"
10649
10706
 
10650
- <Row gap={1}><Badge>12 editions</Badge><Badge appearance="outline">Open edition</Badge><Badge tone="info" icon={<Icon name="info" />}>Curated</Badge></Row>
10707
+ <Row gap={1}><Badge>12 projects</Badge><Badge appearance="outline">Draft</Badge><Badge tone="info" icon={<Icon name="info" />}>Curated</Badge></Row>
10651
10708
  ```
10652
10709
 
10653
10710
  ### Semantic state
@@ -10664,7 +10721,7 @@ import { Badge, Row } from "@sableui/react"
10664
10721
 
10665
10722
  **Status:** stable
10666
10723
 
10667
- Full-width, page-level announcement bar. A tone (neutral, info, success, warning, edition, destructive) × an appearance (soft tint or loud solid fill), and an optional leading scope label. Centers its message, or — with a trailing action — switches to a leading-text + trailing-button layout. Optionally sticky and dismissible.
10724
+ Full-width, page-level announcement bar. A tone (neutral, info, success, warning, the decorative 1, destructive) × an appearance (soft tint or loud solid fill), and an optional leading scope label. Centers its message, or — with a trailing action — switches to a leading-text + trailing-button layout. Optionally sticky and dismissible.
10668
10725
 
10669
10726
  **Keywords:** banner, announcement, alert, status, message, warning, success, offline, sticky
10670
10727
 
@@ -10693,7 +10750,7 @@ Use Banner for page-level or app-level messages that affect the current experien
10693
10750
 
10694
10751
  | Name | Type | Default | Description |
10695
10752
  | --- | --- | --- | --- |
10696
- | tone | "neutral" \| "info" \| "success" \| "warning" \| "edition" \| "destructive" | "neutral" | Neutral surface or a tone that encodes the announcement's intent (info / success / warning / edition / destructive). |
10753
+ | tone | "neutral" \| "info" \| "success" \| "warning" \| "1" \| "destructive" | "neutral" | Neutral surface or a tone that encodes the announcement's intent (info / success / warning / the decorative 1 / destructive). |
10697
10754
  | appearance | "soft" \| "solid" | "soft" | "soft" is a quiet tinted fill; "solid" is the full tone with its paired ink — for loud, can't-miss states (offline, chain-down, RPC errors). |
10698
10755
  | label | ReactNode | — | A short scope label ("Preview", "Staging", a project name) in a leading chip — a Badge that reads on soft and solid fills. |
10699
10756
  | action | ReactNode | — | A trailing action (e.g. a Button). Switches the bar to leading text + trailing action instead of centered text. |
@@ -10704,12 +10761,12 @@ Use Banner for page-level or app-level messages that affect the current experien
10704
10761
 
10705
10762
  ## Variants
10706
10763
 
10707
- - **tone**: neutral, info, success, warning, edition, destructive
10764
+ - **tone**: neutral, info, success, warning, 1, destructive
10708
10765
  - **appearance**: soft, solid
10709
10766
 
10710
10767
  ## Tokens used
10711
10768
 
10712
- `secondary`, `secondary-foreground`, `foreground`, `background`, `tone-info`, `tone-info-foreground`, `success`, `success-foreground`, `tone-warning`, `tone-warning-foreground`, `tone-edition`, `tone-edition-foreground`, `destructive`, `destructive-foreground`
10769
+ `secondary`, `secondary-foreground`, `foreground`, `background`, `tone-info`, `tone-info-foreground`, `success`, `success-foreground`, `tone-warning`, `tone-warning-foreground`, `tone-1`, `tone-1-foreground`, `destructive`, `destructive-foreground`
10713
10770
 
10714
10771
  ## Accessibility
10715
10772
 
@@ -10737,7 +10794,7 @@ import { Banner } from "@sableui/react"
10737
10794
  ```tsx
10738
10795
  import { Banner } from "@sableui/react"
10739
10796
 
10740
- <Banner tone="destructive" appearance="solid" live="assertive">Blockchain unavailable. Collect and buy are temporarily disabled.</Banner>
10797
+ <Banner tone="destructive" appearance="solid" live="assertive">Payments are down. Checkout and upgrades are temporarily disabled.</Banner>
10741
10798
  ```
10742
10799
 
10743
10800
  ### Success
@@ -10769,7 +10826,7 @@ import { Banner } from "@sableui/react"
10769
10826
  ```tsx
10770
10827
  import { Banner, Button } from "@sableui/react"
10771
10828
 
10772
- <Banner tone="warning" action={<Button variant="outline" size="sm">Reconnect</Button>}>Wallet connection paused.</Banner>
10829
+ <Banner tone="warning" action={<Button variant="outline" size="sm">Reconnect</Button>}>Calendar sync paused.</Banner>
10773
10830
  ```
10774
10831
 
10775
10832
  ### App-shell outage
@@ -10826,13 +10883,13 @@ Use BarChart to compare amounts across categories — the default chart for "how
10826
10883
  | stacked | boolean \| "expand" | false | Stack the series in one bar, or `"expand"` to show each category as 100%. Unstacked series sit side by side. |
10827
10884
  | labels | boolean \| "inside" | false | Print each bar's value at its end, or (`"inside"`, horizontal) the category name inside the bar as well. |
10828
10885
  | colorBy | "series" \| "index" | "series" | Colour by series, or give each category its own tone (one series). |
10829
- | negativeTone | "edition" \| "collectible" \| "standard" \| "info" \| "warning" \| "primary" \| "neutral" \| "success" \| "destructive" | — | Colour negative values with this tone. |
10886
+ | negativeTone | "1" \| "2" \| "3" \| "info" \| "warning" \| "primary" \| "neutral" \| "success" \| "destructive" | — | Colour negative values with this tone. |
10830
10887
  | radius | number | 4 | Corner radius of each bar's far end, in px. Stacks round only the outer segment. |
10831
10888
  | barWidth | number | 24 | Maximum thickness of a bar, in px: the band's leftover is air, not a fatter bar. Raise it for a bolder presentation look. |
10832
10889
 
10833
10890
  ## Tokens used
10834
10891
 
10835
- `tone-edition`, `tone-collectible`, `tone-standard`, `tone-info`, `tone-warning`, `primary`, `muted-foreground`, `border`, `popover`, `popover-foreground`, `foreground`, `background`
10892
+ `tone-1`, `tone-2`, `tone-3`, `tone-info`, `tone-warning`, `primary`, `muted-foreground`, `border`, `popover`, `popover-foreground`, `foreground`, `background`
10836
10893
 
10837
10894
  ## Motion
10838
10895
 
@@ -11064,7 +11121,7 @@ Use Breadcrumbs to show the user's location inside a hierarchy and provide light
11064
11121
  ```tsx
11065
11122
  import { BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, Breadcrumbs, BreadcrumbSeparator } from "@sableui/react"
11066
11123
 
11067
- <Breadcrumbs><BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem><BreadcrumbSeparator /><BreadcrumbItem><BreadcrumbLink href="/library">Library</BreadcrumbLink></BreadcrumbItem><BreadcrumbSeparator /><BreadcrumbItem><BreadcrumbPage>Editions</BreadcrumbPage></BreadcrumbItem></Breadcrumbs>
11124
+ <Breadcrumbs><BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem><BreadcrumbSeparator /><BreadcrumbItem><BreadcrumbLink href="/library">Library</BreadcrumbLink></BreadcrumbItem><BreadcrumbSeparator /><BreadcrumbItem><BreadcrumbPage>Reports</BreadcrumbPage></BreadcrumbItem></Breadcrumbs>
11068
11125
  ```
11069
11126
 
11070
11127
  ### Custom separator
@@ -11359,7 +11416,7 @@ Use Button when the user commits an action, submits a form, opens UI, or confirm
11359
11416
  ```tsx
11360
11417
  import { Button } from "@sableui/react"
11361
11418
 
11362
- <Button>Collect</Button>
11419
+ <Button>Save changes</Button>
11363
11420
  ```
11364
11421
 
11365
11422
  ### Secondary + ghost
@@ -11416,7 +11473,7 @@ import { Button, Row } from "@sableui/react"
11416
11473
  ```tsx
11417
11474
  import { Button, Row } from "@sableui/react"
11418
11475
 
11419
- <Row gap={1}><Button variant="secondary">Cancel</Button><Button variant="destructive">Delete edition</Button></Row>
11476
+ <Row gap={1}><Button variant="secondary">Cancel</Button><Button variant="destructive">Delete project</Button></Row>
11420
11477
  ```
11421
11478
 
11422
11479
  ### Leading icon
@@ -11499,7 +11556,7 @@ import { Button } from "@sableui/react"
11499
11556
  ```tsx
11500
11557
  import { Banner, Button } from "@sableui/react"
11501
11558
 
11502
- <Banner tone="warning" appearance="solid" action={<Button variant="on-color" size="sm">Reconnect</Button>}>Wallet connection paused.</Banner>
11559
+ <Banner tone="warning" appearance="solid" action={<Button variant="on-color" size="sm">Reconnect</Button>}>Calendar sync paused.</Banner>
11503
11560
  ```
11504
11561
 
11505
11562
  ---
@@ -11667,7 +11724,7 @@ Use Card for a discrete thing with its own boundary, such as one product, one no
11667
11724
  ```tsx
11668
11725
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@sableui/react"
11669
11726
 
11670
- <Card><CardHeader><CardTitle>Edition</CardTitle><CardDescription>Limited run of 100.</CardDescription></CardHeader><CardContent><p className="text-body-md">Each collector receives a signed print and the on-chain edition.</p></CardContent></Card>
11727
+ <Card><CardHeader><CardTitle>Studio plan</CardTitle><CardDescription>Up to 10 seats.</CardDescription></CardHeader><CardContent><p className="text-body-md">Every seat gets unlimited projects and shared reports.</p></CardContent></Card>
11671
11728
  ```
11672
11729
 
11673
11730
  ### With a header action
@@ -11675,7 +11732,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@sabl
11675
11732
  ```tsx
11676
11733
  import { Button, Card, CardAction, CardContent, CardHeader, CardTitle, Icon } from "@sableui/react"
11677
11734
 
11678
- <Card><CardHeader><CardTitle>Edition</CardTitle><CardAction><Button variant="ghost" size="icon-sm" aria-label="More"><Icon name="ellipsis" /></Button></CardAction></CardHeader><CardContent><p className="text-body-md text-muted-foreground">24 of 100 collected.</p></CardContent></Card>
11735
+ <Card><CardHeader><CardTitle>Seats</CardTitle><CardAction><Button variant="ghost" size="icon-sm" aria-label="More"><Icon name="ellipsis" /></Button></CardAction></CardHeader><CardContent><p className="text-body-md text-muted-foreground">24 of 100 seats in use.</p></CardContent></Card>
11679
11736
  ```
11680
11737
 
11681
11738
  ### Compact section panel
@@ -11691,7 +11748,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@sableui/react"
11691
11748
  ```tsx
11692
11749
  import { Badge, Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@sableui/react"
11693
11750
 
11694
- <Card><CardHeader><CardTitle>Midnight Archive</CardTitle><CardDescription>Edition #24 of 100</CardDescription><CardAction><Badge tone="edition">Edition</Badge></CardAction></CardHeader><CardContent><p className="text-body-sm text-muted-foreground">A signed print and token-gated download for collectors.</p></CardContent><CardFooter><Button size="sm">Collect</Button><Button size="sm" variant="ghost">Preview</Button></CardFooter></Card>
11751
+ <Card><CardHeader><CardTitle>Midnight Archive</CardTitle><CardDescription>Template · 24 pages</CardDescription><CardAction><Badge tone="1">New</Badge></CardAction></CardHeader><CardContent><p className="text-body-sm text-muted-foreground">A project template with a brief, a timeline and a review board.</p></CardContent><CardFooter><Button size="sm">Use template</Button><Button size="sm" variant="ghost">Preview</Button></CardFooter></Card>
11695
11752
  ```
11696
11753
 
11697
11754
  ### Whole card as a link
@@ -11707,7 +11764,7 @@ import { Card, CardContent, CardDescription, CardTitle, Icon } from "@sableui/re
11707
11764
  ```tsx
11708
11765
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@sableui/react"
11709
11766
 
11710
- <Card density="compact"><CardHeader><CardTitle>Revenue</CardTitle><CardDescription>Last 30 days</CardDescription></CardHeader><CardContent><p className="text-heading-3">128.4 SOL</p><p className="text-body-sm text-success">+12.8% from previous period</p></CardContent></Card>
11767
+ <Card density="compact"><CardHeader><CardTitle>Revenue</CardTitle><CardDescription>Last 30 days</CardDescription></CardHeader><CardContent><p className="text-heading-3">$12,840</p><p className="text-body-sm text-success">+12.8% from previous period</p></CardContent></Card>
11711
11768
  ```
11712
11769
 
11713
11770
  ### List card
@@ -12075,17 +12132,17 @@ Use CheckboxCard when an option needs more visual weight, supporting text, or ic
12075
12132
  | title | ReactNode | — | The card's primary label (text-title-sm). |
12076
12133
  | description | ReactNode | — | Optional supporting copy beneath the title (text-body-sm, muted). |
12077
12134
  | icon | ReactNode | — | Optional glyph (a solid <Icon>) in the tone colour. Its presence switches to the two-dimensional card RadioCard uses — icon top-left, checkbox top-right, copy below — so the two cards match in one form. |
12078
- | tone | "neutral" \| "standard" \| "collectible" \| "edition" \| "info" \| "warning" | "neutral" | Semantic colour the checked state carries across the border, checkbox box, and icon. |
12135
+ | tone | "neutral" \| "3" \| "2" \| "1" \| "info" \| "warning" | "neutral" | Semantic colour the checked state carries across the border, checkbox box, and icon. |
12079
12136
  | value | string | — | Names the card inside a group-owned CheckboxGroup: the card is checked when the group's value array includes it. Also the value submitted with a form. Inside a CheckboxGroup, `name` stands in for `value` when `value` is omitted. |
12080
12137
  | checked / onCheckedChange / defaultChecked / disabled | Base UI Checkbox.Root props | — | Standard checkbox state. Without a group-owned value, each card is independent — drive checked/onCheckedChange per item for multi-select. |
12081
12138
 
12082
12139
  ## Variants
12083
12140
 
12084
- - **tone**: neutral, standard, collectible, edition, info, warning
12141
+ - **tone**: neutral, 3, 2, 1, info, warning
12085
12142
 
12086
12143
  ## Tokens used
12087
12144
 
12088
- `input`, `card`, `ring`, `primary`, `primary-foreground`, `accent`, `foreground`, `muted-foreground`, `tone-standard`, `tone-standard-foreground`, `tone-collectible`, `tone-collectible-foreground`, `tone-edition`, `tone-edition-foreground`, `tone-info`, `tone-info-foreground`, `tone-warning`, `tone-warning-foreground`
12145
+ `input`, `card`, `ring`, `primary`, `primary-foreground`, `accent`, `foreground`, `muted-foreground`, `tone-3`, `tone-3-foreground`, `tone-2`, `tone-2-foreground`, `tone-1`, `tone-1-foreground`, `tone-info`, `tone-info-foreground`, `tone-warning`, `tone-warning-foreground`
12089
12146
 
12090
12147
  ## Accessibility
12091
12148
 
@@ -12121,7 +12178,7 @@ import { Checkbox, CheckboxCard, CheckboxGroup, Field } from "@sableui/react"
12121
12178
  ```tsx
12122
12179
  import { CheckboxCard } from "@sableui/react"
12123
12180
 
12124
- <CheckboxCard title="Email me a receipt" description="Sent when the mint settles" checked={receipt} onCheckedChange={setReceipt} />
12181
+ <CheckboxCard title="Email me a receipt" description="Sent when the payment settles" checked={receipt} onCheckedChange={setReceipt} />
12125
12182
  ```
12126
12183
 
12127
12184
  ### Toned with description
@@ -12129,7 +12186,7 @@ import { CheckboxCard } from "@sableui/react"
12129
12186
  ```tsx
12130
12187
  import { CheckboxCard } from "@sableui/react"
12131
12188
 
12132
- <CheckboxCard tone="edition" title="Editions" description="Sell as numbered NFT editions" defaultChecked />
12189
+ <CheckboxCard tone="1" title="Reports" description="Weekly usage reports by email" defaultChecked />
12133
12190
  ```
12134
12191
 
12135
12192
  ---
@@ -12215,7 +12272,7 @@ Use CodeBlock for multiline code samples that users may read, inspect, or copy.
12215
12272
 
12216
12273
  - Do: Give the snippet a filename, or a sentence of surrounding copy, when it helps readers and agents place it.
12217
12274
  - Do: Keep examples complete enough to teach the intended pattern.
12218
- - Do: Wire highlighting once with CodeHighlightProvider and map token colors to semantic tone tokens (keywords → tone-edition, strings → success, comments → muted-foreground), so code follows the theme instead of a fixed palette.
12275
+ - Do: Wire highlighting once with CodeHighlightProvider and map token colors to semantic tone tokens (keywords → tone-1, strings → success, comments → muted-foreground), so code follows the theme instead of a fixed palette.
12219
12276
  - Don't: Do not put short inline identifiers in a CodeBlock; use Code for those.
12220
12277
  - Don't: Do not paste code with the indentation the surrounding file had. A block indented to sit inside your JSX shows the reader leading whitespace that is not part of the example.
12221
12278
 
@@ -12708,7 +12765,7 @@ import { CommandMenu, CommandMenuGroup, CommandMenuItem } from "@sableui/react"
12708
12765
  ```tsx
12709
12766
  import { CommandMenu, CommandMenuGroup, CommandMenuItem } from "@sableui/react"
12710
12767
 
12711
- <CommandMenu open={open} onOpenChange={setOpen}><CommandMenuGroup label="Create"><CommandMenuItem icon="plus" onSelect={() => createPost()}>New post</CommandMenuItem><CommandMenuItem icon="sparkles" onSelect={() => createEdition()}>New edition</CommandMenuItem></CommandMenuGroup><CommandMenuGroup label="Navigation"><CommandMenuItem icon="home" onSelect={() => navigate("/")}>Home</CommandMenuItem><CommandMenuItem icon="gear" onSelect={() => navigate("/settings")}>Settings</CommandMenuItem></CommandMenuGroup></CommandMenu>
12768
+ <CommandMenu open={open} onOpenChange={setOpen}><CommandMenuGroup label="Create"><CommandMenuItem icon="plus" onSelect={() => createPost()}>New post</CommandMenuItem><CommandMenuItem icon="sparkles" onSelect={() => createProject()}>New project</CommandMenuItem></CommandMenuGroup><CommandMenuGroup label="Navigation"><CommandMenuItem icon="home" onSelect={() => navigate("/")}>Home</CommandMenuItem><CommandMenuItem icon="gear" onSelect={() => navigate("/settings")}>Settings</CommandMenuItem></CommandMenuGroup></CommandMenu>
12712
12769
  ```
12713
12770
 
12714
12771
  ### Consumer-owned ranking
@@ -12759,7 +12816,7 @@ useCommandMenuShortcut(() => setOpen((value) => !value))
12759
12816
 
12760
12817
  **Status:** stable
12761
12818
 
12762
- Hover/focus-triggered rich preview card over Base UI PreviewCard. Reveals contextual content (a profile, an edition summary) from an inline anchor; 20rem-wide surface with delayed open and collision-aware positioning.
12819
+ Hover/focus-triggered rich preview card over Base UI PreviewCard. Reveals contextual content (a profile, a project summary) from an inline anchor; 20rem-wide surface with delayed open and collision-aware positioning.
12763
12820
 
12764
12821
  **Keywords:** context card, rich tooltip, hover card, preview
12765
12822
 
@@ -12818,7 +12875,7 @@ Use ContextCard to show richer contextual content near a trigger without taking
12818
12875
  ```tsx
12819
12876
  import { Avatar, ContextCard, ContextCardContent, ContextCardTrigger, Entity } from "@sableui/react"
12820
12877
 
12821
- <ContextCard><ContextCardTrigger render={<a href='/u/ada' className='text-title-sm underline-offset-2 hover:underline'>@ada</a>} /><ContextCardContent><Entity className='[--entity-px:0] [--entity-py:0]' leading={<Avatar fallback='AL' size='lg' />} title='Ada Lovelace' subtitle='@ada' /><p className='mt-3 text-body-sm text-muted-foreground'>Minting generative editions on Solana. Permanently on Arweave.</p></ContextCardContent></ContextCard>
12878
+ <ContextCard><ContextCardTrigger render={<a href='/u/ada' className='text-title-sm underline-offset-2 hover:underline'>@ada</a>} /><ContextCardContent><Entity className='[--entity-px:0] [--entity-py:0]' leading={<Avatar fallback='AL' size='lg' />} title='Ada Lovelace' subtitle='@ada' /><p className='mt-3 text-body-sm text-muted-foreground'>Product designer. Building tools for small teams.</p></ContextCardContent></ContextCard>
12822
12879
  ```
12823
12880
 
12824
12881
  ### With stat row
@@ -13304,7 +13361,7 @@ import { DestructiveActionModal } from "@sableui/react"
13304
13361
  ```tsx
13305
13362
  import { DestructiveActionModal, Note } from "@sableui/react"
13306
13363
 
13307
- <DestructiveActionModal open={open} onOpenChange={setOpen} title='Delete this post?' confirmText='gold rush' confirmLabel='Delete forever' onConfirm={handleDelete}><Note tone='warning'>Collectors who minted this edition keep their copies.</Note></DestructiveActionModal>
13364
+ <DestructiveActionModal open={open} onOpenChange={setOpen} title='Delete this post?' confirmText='gold rush' confirmLabel='Delete forever' onConfirm={handleDelete}><Note tone='warning'>People you shared this post with lose access to it.</Note></DestructiveActionModal>
13308
13365
  ```
13309
13366
 
13310
13367
  ---
@@ -13418,7 +13475,7 @@ import { Button, Carousel, Dialog, DialogContent, DialogTrigger } from "@sableui
13418
13475
  ```tsx
13419
13476
  import { Button, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@sableui/react"
13420
13477
 
13421
- <Dialog><DialogTrigger render={<Button>Open</Button>} /><DialogContent><DialogHeader><DialogTitle>Edition details</DialogTitle><DialogDescription>A limited run of 100, stored permanently on Arweave.</DialogDescription></DialogHeader><DialogFooter><DialogClose render={<Button variant="secondary">Close</Button>} /><DialogClose render={<Button>Collect</Button>} /></DialogFooter></DialogContent></Dialog>
13478
+ <Dialog><DialogTrigger render={<Button>Open</Button>} /><DialogContent><DialogHeader><DialogTitle>Plan details</DialogTitle><DialogDescription>Up to 10 seats, billed monthly per seat.</DialogDescription></DialogHeader><DialogFooter><DialogClose render={<Button variant="secondary">Close</Button>} /><DialogClose render={<Button>Upgrade</Button>} /></DialogFooter></DialogContent></Dialog>
13422
13479
  ```
13423
13480
 
13424
13481
  ### Settings form
@@ -13434,7 +13491,7 @@ import { Button, Dialog, DialogClose, DialogContent, DialogDescription, DialogFo
13434
13491
  ```tsx
13435
13492
  import { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, Button } from "@sableui/react"
13436
13493
 
13437
- <AlertDialog><AlertDialogTrigger render={<Button variant="destructive">Delete edition</Button>} /><AlertDialogContent><AlertDialogHeader><AlertDialogTitle>Delete edition?</AlertDialogTitle><AlertDialogDescription>This removes the edition and cannot be undone.</AlertDialogDescription></AlertDialogHeader><AlertDialogFooter><AlertDialogClose render={<Button variant="secondary">Cancel</Button>} /><Button variant="destructive" onClick={deleteEdition}>Delete edition</Button></AlertDialogFooter></AlertDialogContent></AlertDialog>
13494
+ <AlertDialog><AlertDialogTrigger render={<Button variant="destructive">Delete project</Button>} /><AlertDialogContent><AlertDialogHeader><AlertDialogTitle>Delete project?</AlertDialogTitle><AlertDialogDescription>This removes the project and cannot be undone.</AlertDialogDescription></AlertDialogHeader><AlertDialogFooter><AlertDialogClose render={<Button variant="secondary">Cancel</Button>} /><Button variant="destructive" onClick={deleteProject}>Delete project</Button></AlertDialogFooter></AlertDialogContent></AlertDialog>
13438
13495
  ```
13439
13496
 
13440
13497
  ---
@@ -13482,7 +13539,7 @@ Use DisclosurePicker for one-of-many choices in a docked panel or inspector wher
13482
13539
  | DisclosurePickerTrigger | { label?: ReactNode; media?: ReactNode; children?: ReactNode } + Base UI Collapsible.Trigger props | The row. `label` is a muted leading caption on a fixed column so stacked rows align; `media` a leading visual (a ThemePreview tile); children the value, truncated on one line. ArrowDown opens it, and steps into the list once open. |
13483
13540
  | DisclosurePickerPanel | { search?: { value: string; onValueChange(value: string): void; placeholder?: string; label?: string } } + Collapsible.Panel props | Opens beneath the row. `search` opts in to a SearchInput above the list (off by default); it is controlled because the consumer filters, and it is cleared when the row closes. Escape clears a query first, then closes; ArrowDown in the field steps into the list. |
13484
13541
  | DisclosurePickerList | { value: string; onValueChange(value: string): void; aria-label?: string } + div props | A listbox. ArrowUp/ArrowDown, Home/End and type-ahead move focus without applying anything (ArrowUp from the first option returns to the search field); Enter, Space or a click picks the focused option — onValueChange fires once, only when it differs from `value` — and closes the row. Escape closes without a pick. |
13485
- | DisclosurePickerOption | { value: string; children?: ReactNode; media?: ReactNode; note?: ReactNode; disabled?: boolean; textValue?: string } | One option row. `media` is a leading visual (a ThemePreview tile); `note` a muted trailing annotation — options without one show a check when chosen. `disabled` skips it for the keyboard and refuses the pick; `textValue` is what type-ahead matches when the name is not plain text. |
13542
+ | DisclosurePickerOption | { value: string; children?: ReactNode; media?: ReactNode; note?: ReactNode; disabled?: boolean; textValue?: string; onRemove?: () => void; removeLabel?: string } | One option row. `media` is a leading visual (a ThemePreview tile); `note` a muted trailing annotation — options without one show a check when chosen. `disabled` skips it for the keyboard and refuses the pick; `textValue` is what type-ahead matches when the name is not plain text. `onRemove` makes the option removable — for entries the person added (an imported preset, a saved font): a trash glyph at the row's end on hover or focus (always on touch), and Delete or Backspace on the focused row; removing never picks, and focus moves to a neighbour first. `removeLabel` is the glyph's tooltip. |
13486
13543
  | DisclosurePickerEmpty | { children?: ReactNode } | The no-match line, rendered in place of the list. |
13487
13544
  | useDisclosurePicker | () => { open: boolean; close(): void; size: "md" \| "xs" } | For a list the picker does not own (a Tree) that should close the row when something is picked. |
13488
13545
 
@@ -13514,6 +13571,7 @@ Use DisclosurePicker for one-of-many choices in a docked panel or inspector wher
13514
13571
  - Why a listbox and not a radio group: a radio group's checked state follows focus, so every arrow press would be a pick — wrong for a choice that opens a confirm, loads a font or resets settings. Base UI ships no standalone listbox (Select is popup-only, Combobox's inline list is focus-in-the-input, Composite is internal), so the keyboard model lives here.
13515
13572
  - Opening from the keyboard (Enter, Space, ArrowDown) moves focus into the panel: the search field when there is one, otherwise the chosen option (or the first). A pointer open focuses the search field on a fine pointer only, so touch does not raise the on-screen keyboard.
13516
13573
  - Escape closes the row without changing the value (clearing a query first) and returns focus to the trigger; a pick does the same. Focus is not pulled back when it has already moved on, e.g. to a confirm dialog the pick opened.
13574
+ - A removable option (onRemove) announces aria-keyshortcuts="Delete"; Delete or Backspace removes it and focus moves to the next option (or the previous). The trash glyph is a pointer target only (aria-hidden, not focusable): an option's children are presentational, so a nested button would be unreachable by assistive tech anyway.
13517
13575
  - The panel animates with motion-disclosure, which is static under prefers-reduced-motion or MotionProvider off; the scroll that keeps the row in view is instant, never smooth.
13518
13576
 
13519
13577
  ## Related
@@ -13624,7 +13682,7 @@ Use EmptyState when a content area has no items, no search results, or no first-
13624
13682
 
13625
13683
  ### Best practices
13626
13684
 
13627
- - Do: Explain what is empty in plain language, such as "No posts yet" or "No matching collectors".
13685
+ - Do: Explain what is empty in plain language, such as "No posts yet" or "No matching customers".
13628
13686
  - Do: Offer one next step when the user can recover, create, clear filters, or start setup.
13629
13687
  - Do: Keep the icon decorative and make the title carry the meaning.
13630
13688
  - Do: For a search or filter result, name the query back to the user and offer the way out, such as "No results for 'quarterly'" with a Clear filters action. An empty result that does not say what was searched leaves the user unsure whether the query or the data was wrong.
@@ -13697,7 +13755,7 @@ import { Button, EmptyState, Icon } from "@sableui/react"
13697
13755
  ```tsx
13698
13756
  import { Button, EmptyState, Icon, Row } from "@sableui/react"
13699
13757
 
13700
- <EmptyState surface="card" icon={<Icon name="sparkles" />} title="Start your collection" description="Create a first edition, import an existing drop, or invite collaborators." action={<Row gap={1}><Button>Create edition</Button><Button variant="ghost">Import</Button></Row>} />
13758
+ <EmptyState surface="card" icon={<Icon name="sparkles" />} title="Start your first project" description="Create a project, import one from a file, or invite collaborators." action={<Row gap={1}><Button>Create project</Button><Button variant="ghost">Import</Button></Row>} />
13701
13759
  ```
13702
13760
 
13703
13761
  ### Page-level error
@@ -13713,7 +13771,7 @@ import { Button, EmptyState } from "@sableui/react"
13713
13771
  ```tsx
13714
13772
  import { Button, EmptyState } from "@sableui/react"
13715
13773
 
13716
- <EmptyState tone="destructive" surface="card" title="Could not load this edition" description="Check your connection and try again." action={<Button variant="secondary">Retry</Button>} />
13774
+ <EmptyState tone="destructive" surface="card" title="Could not load this project" description="Check your connection and try again." action={<Button variant="secondary">Retry</Button>} />
13717
13775
  ```
13718
13776
 
13719
13777
  ### Empty table body
@@ -13721,7 +13779,7 @@ import { Button, EmptyState } from "@sableui/react"
13721
13779
  ```tsx
13722
13780
  import { DataTable, EmptyState } from "@sableui/react"
13723
13781
 
13724
- <DataTable data={[]} columns={[{ key: "name", header: "Collector" }, { key: "status", header: "Status" }]} empty={<EmptyState title="No collectors yet" description="Collectors appear here after the first mint." />} />
13782
+ <DataTable data={[]} columns={[{ key: "name", header: "Customer" }, { key: "status", header: "Status" }]} empty={<EmptyState title="No customers yet" description="Customers appear here after the first order." />} />
13725
13783
  ```
13726
13784
 
13727
13785
  ---
@@ -14459,7 +14517,7 @@ Use GradientArt where a real image belongs but none exists yet, or where the ima
14459
14517
  | Name | Type | Default | Description |
14460
14518
  | --- | --- | --- | --- |
14461
14519
  | variant | "mesh" \| "grain" | "mesh" | mesh: soft spots of colour pulled together by gentle waves. grain: glowing light over deep corners with a printed texture. |
14462
- | tones | ("info" \| "standard" \| "collectible" \| "edition" \| "warning")[] | — | Paint these `--tone-*` colours instead of the preset's art palette. Leave unset to follow the preset's art direction. |
14520
+ | tones | ("info" \| "3" \| "2" \| "1" \| "warning")[] | — | Paint these `--tone-*` colours instead of the preset's art palette. Leave unset to follow the preset's art direction. |
14463
14521
  | seed | string \| number | — | Fixes the composition: the same seed paints the same art in every theme. Pass a stable id. |
14464
14522
  | children | ReactNode | — | Rendered above the art — initials, a glyph, a short label. |
14465
14523
  | className | string | — | Size and shape: aspect, height, rounding. |
@@ -14470,7 +14528,7 @@ Use GradientArt where a real image belongs but none exists yet, or where the ima
14470
14528
 
14471
14529
  ## Tokens used
14472
14530
 
14473
- `art-1`, `art-2`, `art-3`, `art-4`, `art-5`, `art-6`, `art-ground`, `art-warp`, `art-softness`, `art-grain`, `primary`, `tone-info`, `tone-standard`, `tone-collectible`, `tone-edition`, `tone-warning`
14531
+ `art-1`, `art-2`, `art-3`, `art-4`, `art-5`, `art-6`, `art-ground`, `art-warp`, `art-softness`, `art-grain`, `primary`, `tone-info`, `tone-3`, `tone-2`, `tone-1`, `tone-warning`
14474
14532
 
14475
14533
  ## Motion
14476
14534
 
@@ -14503,7 +14561,7 @@ import { GradientArt } from "@sableui/react"
14503
14561
  ```tsx
14504
14562
  import { GradientArt } from "@sableui/react"
14505
14563
 
14506
- <GradientArt variant="grain" seed="juno" tones={["info", "edition"]} className="h-20 rounded-md" />
14564
+ <GradientArt variant="grain" seed="juno" tones={["info", "1"]} className="h-20 rounded-md" />
14507
14565
  ```
14508
14566
 
14509
14567
  ### Avatar with initials
@@ -14854,7 +14912,7 @@ import { Field, Input } from "@sableui/react"
14854
14912
  ```tsx
14855
14913
  import { Label } from "@sableui/react"
14856
14914
 
14857
- <Label htmlFor="royalty" hint="A percentage of every resale, paid to you.">Royalty</Label>
14915
+ <Label htmlFor="discount" hint="Taken off every order this code is used on.">Discount</Label>
14858
14916
  ```
14859
14917
 
14860
14918
  ---
@@ -14906,7 +14964,7 @@ Use LineChart to compare how two or more measures change over the same sequence,
14906
14964
 
14907
14965
  ## Tokens used
14908
14966
 
14909
- `tone-edition`, `tone-collectible`, `tone-standard`, `tone-info`, `tone-warning`, `primary`, `muted-foreground`, `border`, `popover`, `popover-foreground`, `foreground`, `background`
14967
+ `tone-1`, `tone-2`, `tone-3`, `tone-info`, `tone-warning`, `primary`, `muted-foreground`, `border`, `popover`, `popover-foreground`, `foreground`, `background`
14910
14968
 
14911
14969
  ## Motion
14912
14970
 
@@ -15813,7 +15871,7 @@ Use Meter to show a static measurement against a known maximum — storage used,
15813
15871
  | value | number | — | The current reading, between min and max. Required. |
15814
15872
  | min | number | 0 | The bottom of the range. |
15815
15873
  | max | number | 100 | The top of the range. |
15816
- | tone | "primary" \| "standard" \| "success" \| "warning" \| "destructive" | "primary" | Fill colour, mapped to a semantic token. Choose it from your own thresholds. |
15874
+ | tone | "primary" \| "3" \| "success" \| "warning" \| "destructive" | "primary" | Fill colour, mapped to a semantic token. Choose it from your own thresholds. |
15817
15875
  | shape | "bar" \| "ring" | "bar" | bar is a horizontal track; ring is a circular gauge for a small, glanceable reading — the same value, min, max and tones. |
15818
15876
  | size | "sm" \| "md" \| "lg" | "md" | Ring diameter (40 / 64 / 96px at the default spacing; the centre value steps with it), or bar thickness on the same sm / md / lg rails as Progress. |
15819
15877
  | showValue | boolean | — | Print the value in the ring's centre (formatted like MeterValue). |
@@ -15824,7 +15882,7 @@ Use Meter to show a static measurement against a known maximum — storage used,
15824
15882
 
15825
15883
  ## Variants
15826
15884
 
15827
- - **tone**: primary, standard, success, warning, destructive
15885
+ - **tone**: primary, 3, success, warning, destructive
15828
15886
  - **shape**: bar, ring
15829
15887
  - **size**: sm, md, lg
15830
15888
 
@@ -15933,12 +15991,12 @@ Use MiddleTruncate when both the beginning and end of a long identifier must rem
15933
15991
 
15934
15992
  ## Examples
15935
15993
 
15936
- ### Solana address
15994
+ ### API key
15937
15995
 
15938
15996
  ```tsx
15939
15997
  import { MiddleTruncate } from "@sableui/react"
15940
15998
 
15941
- <MiddleTruncate text="5J7sX2QpHt9vK4mNbR8wYcZ3dF6gH1jL0pA8xQpVnTu" />
15999
+ <MiddleTruncate text="demo_key_7f3a9c2e5b8d1f4a6c0e9b3d7a2f5c8e1b4" />
15942
16000
  ```
15943
16001
 
15944
16002
  ### Custom start/end counts
@@ -15946,7 +16004,7 @@ import { MiddleTruncate } from "@sableui/react"
15946
16004
  ```tsx
15947
16005
  import { MiddleTruncate } from "@sableui/react"
15948
16006
 
15949
- <MiddleTruncate text="0xAbC1234567890dEf1234567890aBcDeF12345678" startChars={4} endChars={4} />
16007
+ <MiddleTruncate text="9f2c4e7a1b3d5f60c8e2a4b6d8f0e1c3a5b7d9f2" startChars={4} endChars={4} />
15950
16008
  ```
15951
16009
 
15952
16010
  ### Copyable value
@@ -15954,8 +16012,8 @@ import { MiddleTruncate } from "@sableui/react"
15954
16012
  ```tsx
15955
16013
  import { Button, Icon, MiddleTruncate, Row, useCopy } from "@sableui/react"
15956
16014
 
15957
- const { copy, copied } = useCopy(address)
15958
- <Row gap={1}><MiddleTruncate text={address} /><Button variant="ghost" size="icon-xs" aria-label="Copy address" onClick={copy}><Icon name={copied ? "check" : "copy"} /></Button></Row>
16015
+ const { copy, copied } = useCopy(commitHash)
16016
+ <Row gap={1}><MiddleTruncate text={commitHash} /><Button variant="ghost" size="icon-xs" aria-label="Copy commit hash" onClick={copy}><Icon name={copied ? "check" : "copy"} /></Button></Row>
15959
16017
  ```
15960
16018
 
15961
16019
  ---
@@ -16015,7 +16073,7 @@ Use Note for helpful supporting information that should sit near the relevant co
16015
16073
  ```tsx
16016
16074
  import { Note } from "@sableui/react"
16017
16075
 
16018
- <Note tone="info">Editions mint on Solana mainnet.</Note>
16076
+ <Note tone="info">Reports refresh every hour.</Note>
16019
16077
  ```
16020
16078
 
16021
16079
  ### With title
@@ -16023,7 +16081,7 @@ import { Note } from "@sableui/react"
16023
16081
  ```tsx
16024
16082
  import { Note } from "@sableui/react"
16025
16083
 
16026
- <Note tone="warning" title="Low balance">Top up SOL before minting.</Note>
16084
+ <Note tone="warning" title="Low balance">Add credits before your next export.</Note>
16027
16085
  ```
16028
16086
 
16029
16087
  ### Warning with a remedy, inside a dialog
@@ -16039,7 +16097,7 @@ import { Note } from "@sableui/react"
16039
16097
  ```tsx
16040
16098
  import { Note } from "@sableui/react"
16041
16099
 
16042
- <Note tone="neutral" icon="lock">Pricing and supply cannot be changed after an edition is purchased.</Note>
16100
+ <Note tone="neutral" icon="lock">Pricing cannot be changed after an order is placed.</Note>
16043
16101
  ```
16044
16102
 
16045
16103
  ---
@@ -16523,7 +16581,7 @@ Use Pagination when a result set is split into discrete pages and the user needs
16523
16581
  ### Best practices
16524
16582
 
16525
16583
  - Do: Keep page state controlled by the parent route or data layer so URL, filters, and result fetches stay in sync.
16526
- - Do: Use a specific label when multiple pagers appear on a page, such as "Collector pages".
16584
+ - Do: Use a specific label when multiple pagers appear on a page, such as "Customer pages".
16527
16585
  - Do: Reset to page 1 when filters or search terms materially change the result set.
16528
16586
  - Don't: Use Pagination for tiny lists where all items fit comfortably on one page.
16529
16587
  - Don't: Hide disabled previous/next buttons at the bounds; disabled controls make the current position clearer.
@@ -16589,7 +16647,7 @@ const [page, setPage] = useState(8)
16589
16647
  ```tsx
16590
16648
  import { DataTable, Pagination, Stack } from "@sableui/react"
16591
16649
 
16592
- <Stack gap={2}><DataTable data={rows} columns={columns} rowKey={(row) => row.id} /><Pagination label="Collector pages" page={page} total={12} onPageChange={setPage} /></Stack>
16650
+ <Stack gap={2}><DataTable data={rows} columns={columns} rowKey={(row) => row.id} /><Pagination label="Customer pages" page={page} total={12} onPageChange={setPage} /></Stack>
16593
16651
  ```
16594
16652
 
16595
16653
  ### Server query state
@@ -16899,7 +16957,7 @@ Use Phone to frame a mobile product preview when the device context is useful.
16899
16957
  ```tsx
16900
16958
  import { Phone } from "@sableui/react"
16901
16959
 
16902
- <Phone><div className="p-4"><h2 className="text-title-lg">Activity</h2><p className="text-body-sm text-muted-foreground">3 new collects today.</p></div></Phone>
16960
+ <Phone><div className="p-4"><h2 className="text-title-lg">Activity</h2><p className="text-body-sm text-muted-foreground">3 new orders today.</p></div></Phone>
16903
16961
  ```
16904
16962
 
16905
16963
  ### Mockup, not a responsive preview
@@ -16969,7 +17027,7 @@ Use PieChart for a share of one total across two to six categories — traffic s
16969
17027
 
16970
17028
  ## Tokens used
16971
17029
 
16972
- `tone-edition`, `tone-collectible`, `tone-standard`, `tone-info`, `tone-warning`, `primary`, `muted-foreground`, `border`, `popover`, `popover-foreground`, `foreground`, `background`
17030
+ `tone-1`, `tone-2`, `tone-3`, `tone-info`, `tone-warning`, `primary`, `muted-foreground`, `border`, `popover`, `popover-foreground`, `foreground`, `background`
16973
17031
 
16974
17032
  ## Motion
16975
17033
 
@@ -17057,6 +17115,7 @@ Use Pill for a row of filters or choices a person clicks to narrow or toggle wha
17057
17115
  | selected | boolean | false | Marks the pill as chosen. Solid lifts onto the primary surface; outline takes a primary tint and soft primary edge. Sets aria-pressed. |
17058
17116
  | appearance | "solid" \| "outline" | "solid" | solid for a filter row; outline for quiet chips that annotate other content (a reaction under a message, a tag on a card), where a filled chip would compete with what it annotates. |
17059
17117
  | size | "md" \| "sm" | "md" | md sits on the density ladder's xs control height (28px); sm is 24px, for chips under or inside other content. |
17118
+ | tone | "neutral" \| "red" \| "orange" \| "yellow" \| "green" \| "teal" \| "blue" \| "purple" \| "pink" | "neutral" | neutral is the chip as described. A palette family colours it as a category, like the choices in a tag picker: a wash at rest, the solid family colour when selected. Painted from the theme's generated palette. |
17060
17119
  | icon | ReactNode | — | Optional leading glyph (e.g. <Icon name="lock" />), rendered before the children with the base gap. |
17061
17120
 
17062
17121
  ## Variants
@@ -17064,10 +17123,11 @@ Use Pill for a row of filters or choices a person clicks to narrow or toggle wha
17064
17123
  - **appearance**: solid, outline
17065
17124
  - **size**: md, sm
17066
17125
  - **selected**: true, false
17126
+ - **tone**: neutral, red, orange, yellow, green, teal, blue, purple, pink
17067
17127
 
17068
17128
  ## Tokens used
17069
17129
 
17070
- `secondary`, `secondary-foreground`, `accent`, `accent-foreground`, `primary`, `primary-foreground`, `card`, `border`, `muted-foreground`, `foreground`
17130
+ `palette-wash`, `palette-wash-strong`, `palette-edge`, `palette-solid`, `palette-text`, `palette-solid-ink`, `secondary`, `secondary-foreground`, `accent`, `accent-foreground`, `primary`, `primary-foreground`, `card`, `border`, `muted-foreground`, `foreground`
17071
17131
 
17072
17132
  ## Accessibility
17073
17133
 
@@ -17080,12 +17140,20 @@ Use Pill for a row of filters or choices a person clicks to narrow or toggle wha
17080
17140
 
17081
17141
  ## Examples
17082
17142
 
17143
+ ### Tag picker
17144
+
17145
+ ```tsx
17146
+ import { Pill, Row } from "@sableui/react"
17147
+
17148
+ <Row gap={1}><Pill tone="blue" selected onClick={() => {}}>Design</Pill><Pill tone="teal" onClick={() => {}}>Research</Pill><Pill tone="orange" onClick={() => {}}>Marketing</Pill></Row>
17149
+ ```
17150
+
17083
17151
  ### Filter row
17084
17152
 
17085
17153
  ```tsx
17086
17154
  import { Pill, Row } from "@sableui/react"
17087
17155
 
17088
- <Row gap={1}><Pill selected onClick={() => {}}>All</Pill><Pill onClick={() => {}}>Editions</Pill><Pill onClick={() => {}}>Collectibles</Pill></Row>
17156
+ <Row gap={1}><Pill selected onClick={() => {}}>All</Pill><Pill onClick={() => {}}>Projects</Pill><Pill onClick={() => {}}>Reports</Pill></Row>
17089
17157
  ```
17090
17158
 
17091
17159
  ### Outline, small (reactions)
@@ -17175,7 +17243,7 @@ Use Popover for anchored secondary content that needs more room than a tooltip b
17175
17243
  ```tsx
17176
17244
  import { Button, Popover, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger } from "@sableui/react"
17177
17245
 
17178
- <Popover><PopoverTrigger render={<Button variant="secondary">Details</Button>} /><PopoverContent><PopoverTitle>Edition details</PopoverTitle><PopoverDescription>A limited run of 100, stored permanently on Arweave.</PopoverDescription></PopoverContent></Popover>
17246
+ <Popover><PopoverTrigger render={<Button variant="secondary">Details</Button>} /><PopoverContent><PopoverTitle>Plan details</PopoverTitle><PopoverDescription>Up to 10 seats, billed monthly per seat.</PopoverDescription></PopoverContent></Popover>
17179
17247
  ```
17180
17248
 
17181
17249
  ### With close
@@ -17183,7 +17251,7 @@ import { Button, Popover, PopoverContent, PopoverDescription, PopoverTitle, Popo
17183
17251
  ```tsx
17184
17252
  import { Button, Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger } from "@sableui/react"
17185
17253
 
17186
- <Popover><PopoverTrigger render={<Button variant="secondary">Open</Button>} /><PopoverContent><PopoverTitle>Heads up</PopoverTitle><PopoverDescription>Collecting mints a compressed NFT to your wallet.</PopoverDescription><PopoverClose render={<Button size="sm">Got it</Button>} /></PopoverContent></Popover>
17254
+ <Popover><PopoverTrigger render={<Button variant="secondary">Open</Button>} /><PopoverContent><PopoverTitle>Heads up</PopoverTitle><PopoverDescription>Upgrading starts a new billing period today.</PopoverDescription><PopoverClose render={<Button size="sm">Got it</Button>} /></PopoverContent></Popover>
17187
17255
  ```
17188
17256
 
17189
17257
  ---
@@ -17333,7 +17401,7 @@ Use RadarChart to compare the shape of a profile across five to eight dimensions
17333
17401
 
17334
17402
  ## Tokens used
17335
17403
 
17336
- `tone-edition`, `tone-collectible`, `tone-standard`, `tone-info`, `tone-warning`, `primary`, `muted-foreground`, `border`, `popover`, `popover-foreground`, `foreground`, `background`
17404
+ `tone-1`, `tone-2`, `tone-3`, `tone-info`, `tone-warning`, `primary`, `muted-foreground`, `border`, `popover`, `popover-foreground`, `foreground`, `background`
17337
17405
 
17338
17406
  ## Motion
17339
17407
 
@@ -17444,7 +17512,7 @@ Use RadialChart for progress toward goals (several rings), a single score or usa
17444
17512
 
17445
17513
  ## Tokens used
17446
17514
 
17447
- `tone-edition`, `tone-collectible`, `tone-standard`, `tone-info`, `tone-warning`, `primary`, `muted-foreground`, `border`, `popover`, `popover-foreground`, `foreground`, `background`
17515
+ `tone-1`, `tone-2`, `tone-3`, `tone-info`, `tone-warning`, `primary`, `muted-foreground`, `border`, `popover`, `popover-foreground`, `foreground`, `background`
17448
17516
 
17449
17517
  ## Motion
17450
17518
 
@@ -17568,7 +17636,7 @@ import { Field, Fieldset, FieldsetContent, FieldsetLegend, Radio, RadioGroup } f
17568
17636
  <FieldsetContent>
17569
17637
  <RadioGroup defaultValue="standard">
17570
17638
  <Field label="Standard" orientation="horizontal"><Radio value="standard" /></Field>
17571
- <Field label="Edition" orientation="horizontal"><Radio value="edition" /></Field>
17639
+ <Field label="Poll" orientation="horizontal"><Radio value="poll" /></Field>
17572
17640
  </RadioGroup>
17573
17641
  </FieldsetContent>
17574
17642
  </Fieldset>
@@ -17584,7 +17652,7 @@ import { Field, Fieldset, FieldsetContent, FieldsetLegend, Radio, RadioGroup } f
17584
17652
  <FieldsetContent>
17585
17653
  <RadioGroup defaultValue="standard">
17586
17654
  <Field label="Standard" orientation="horizontal"><Radio value="standard" /></Field>
17587
- <Field label="Edition" orientation="horizontal" disabled><Radio value="edition" /></Field>
17655
+ <Field label="Poll" orientation="horizontal" disabled><Radio value="poll" /></Field>
17588
17656
  </RadioGroup>
17589
17657
  </FieldsetContent>
17590
17658
  </Fieldset>
@@ -17633,18 +17701,18 @@ Use RadioCard for visual choice cards when users compare a small set of named op
17633
17701
  | description | ReactNode | — | Optional supporting copy beneath the title (text-body-sm, muted). |
17634
17702
  | trailing | ReactNode | — | End-aligned on the title's line: a price, a delivery date, a count. The title takes the rest of the row. |
17635
17703
  | icon | ReactNode | — | Leading icon (e.g. a solid <Icon>). Its presence switches to the two-dimensional card — icon top-left, indicator top-right, copy below — and the glyph is rendered as a solid-fill icon in the tone colour. |
17636
- | tone | "neutral" \| "standard" \| "collectible" \| "edition" \| "info" \| "warning" | "neutral" | Semantic colour the selected state carries across the border, radio, and icon glyph. Neutral uses the primary ring with a muted wash. |
17704
+ | tone | "neutral" \| "3" \| "2" \| "1" \| "info" \| "warning" | "neutral" | Semantic colour the selected state carries across the border, radio, and icon glyph. Neutral uses the primary ring with a muted wash. |
17637
17705
  | disabled | boolean | false | Dims the card and blocks selection. The group skips it during keyboard navigation. |
17638
17706
  | titleClassName | string | — | Merged onto the title span (default text-title-sm) — e.g. to tighten copy in a two-column layout. |
17639
17707
  | descriptionClassName | string | — | Merged onto the description span (default text-body-sm text-muted-foreground). |
17640
17708
 
17641
17709
  ## Variants
17642
17710
 
17643
- - **tone**: neutral, standard, collectible, edition, info, warning
17711
+ - **tone**: neutral, 3, 2, 1, info, warning
17644
17712
 
17645
17713
  ## Tokens used
17646
17714
 
17647
- `input`, `card`, `foreground`, `primary`, `muted`, `ring`, `muted-foreground`, `tone-standard`, `tone-collectible`, `tone-edition`, `tone-info`, `tone-warning`
17715
+ `input`, `card`, `foreground`, `primary`, `muted`, `ring`, `muted-foreground`, `tone-3`, `tone-2`, `tone-1`, `tone-info`, `tone-warning`
17648
17716
 
17649
17717
  ## Accessibility
17650
17718
 
@@ -17665,7 +17733,7 @@ Use RadioCard for visual choice cards when users compare a small set of named op
17665
17733
  ```tsx
17666
17734
  import { Icon, RadioCard, RadioGroup } from "@sableui/react"
17667
17735
 
17668
- <RadioGroup defaultValue="post" className="grid sm:grid-cols-3"><RadioCard value="post" tone="standard" icon={<Icon name="plus" />} title="Standard" description="Share without minting" /><RadioCard value="collectible" tone="collectible" icon={<Icon name="gem" />} title="Collectible" description="Free to collect as NFT" /><RadioCard value="edition" tone="edition" icon={<Icon name="image-stack" />} title="Edition" description="Sell as NFT editions" /></RadioGroup>
17736
+ <RadioGroup defaultValue="starter" className="grid sm:grid-cols-3"><RadioCard value="starter" tone="3" icon={<Icon name="plus" />} title="Starter" description="Free for one person" /><RadioCard value="studio" tone="2" icon={<Icon name="gem" />} title="Studio" description="For small teams" /><RadioCard value="business" tone="1" icon={<Icon name="image-stack" />} title="Business" description="SSO and audit log" /></RadioGroup>
17669
17737
  ```
17670
17738
 
17671
17739
  ### Plan picker (plain)
@@ -17811,7 +17879,7 @@ Use Scroller to create a contained overflow region for content that naturally ex
17811
17879
  ```tsx
17812
17880
  import { Scroller } from "@sableui/react"
17813
17881
 
17814
- <Scroller label="Editions" className="h-64"><ul className="space-y-2 p-4">{items.map((item) => (<li key={item.id} className="text-body-sm">{item.title}</li>))}</ul></Scroller>
17882
+ <Scroller label="Projects" className="h-64"><ul className="space-y-2 p-4">{items.map((item) => (<li key={item.id} className="text-body-sm">{item.title}</li>))}</ul></Scroller>
17815
17883
  ```
17816
17884
 
17817
17885
  ### Capped, scrolls past the cap
@@ -18014,7 +18082,7 @@ import { SearchInput } from "@sableui/react"
18014
18082
  ```tsx
18015
18083
  import { Field, SearchInput } from "@sableui/react"
18016
18084
 
18017
- <Field label="Search posts"><SearchInput defaultValue="editions" className="max-w-80" /></Field>
18085
+ <Field label="Search posts"><SearchInput defaultValue="roadmap" className="max-w-80" /></Field>
18018
18086
  ```
18019
18087
 
18020
18088
  ### Disabled
@@ -18212,11 +18280,11 @@ Use SegmentedBar when a stat card or row needs to show how a total divides into
18212
18280
  ## Variants
18213
18281
 
18214
18282
  - **size**: sm, md, lg
18215
- - **tone**: edition, collectible, standard, info, primary, neutral, success, warning, destructive
18283
+ - **tone**: 1, 2, 3, info, primary, neutral, success, warning, destructive
18216
18284
 
18217
18285
  ## Tokens used
18218
18286
 
18219
- `tone-edition`, `tone-collectible`, `tone-standard`, `tone-info`, `primary`, `muted-foreground`, `success`, `tone-warning`, `destructive`
18287
+ `tone-1`, `tone-2`, `tone-3`, `tone-info`, `primary`, `muted-foreground`, `success`, `tone-warning`, `destructive`
18220
18288
 
18221
18289
  ## Motion
18222
18290
 
@@ -18227,7 +18295,7 @@ Use SegmentedBar when a stat card or row needs to show how a total divides into
18227
18295
 
18228
18296
  ## Accessibility
18229
18297
 
18230
- - Rendered role=img with an aria-label built from `label` and each segment's share ("Revenue by type: Editions 42%, Collectibles 27%"), so the whole breakdown is read in one stop.
18298
+ - Rendered role=img with an aria-label built from `label` and each segment's share ("Revenue by channel: Direct 42%, Partners 27%"), so the whole breakdown is read in one stop.
18231
18299
  - Colour alone does not identify a segment: always show a legend with each segment's label beside the bar.
18232
18300
 
18233
18301
  ## Related
@@ -18243,13 +18311,13 @@ Use SegmentedBar when a stat card or row needs to show how a total divides into
18243
18311
  import { SegmentedBar, Stack, StatusDot } from "@sableui/react"
18244
18312
 
18245
18313
  const sales = [
18246
- { label: "Editions", value: 42, tone: "edition" },
18247
- { label: "Collectibles", value: 27, tone: "collectible" },
18248
- { label: "Standard", value: 19, tone: "standard" },
18314
+ { label: "Direct", value: 42, tone: "1" },
18315
+ { label: "Partners", value: 27, tone: "2" },
18316
+ { label: "Referrals", value: 19, tone: "3" },
18249
18317
  ] as const
18250
18318
 
18251
18319
  <Stack gap={3}>
18252
- <SegmentedBar label="Revenue by type" segments={[...sales]} size="lg" />
18320
+ <SegmentedBar label="Revenue by channel" segments={[...sales]} size="lg" />
18253
18321
  <ul className="flex flex-col gap-1.5">
18254
18322
  {sales.map((s) => (
18255
18323
  <li key={s.label} className="flex items-center gap-2 text-body-sm">
@@ -18375,7 +18443,7 @@ import { SelectContent, SelectGroup, SelectGroupLabel, SelectItem } from "@sable
18375
18443
  ```tsx
18376
18444
  import { Field, Select, SelectContent, SelectItem, SelectTrigger } from "@sableui/react"
18377
18445
 
18378
- <Field label="Default visibility" description="Controls how new editions appear."><Select defaultValue="public"><SelectTrigger placeholder="Choose visibility" /><SelectContent><SelectItem value="public">Public</SelectItem><SelectItem value="private">Private</SelectItem><SelectItem value="unlisted">Unlisted</SelectItem></SelectContent></Select></Field>
18446
+ <Field label="Default visibility" description="Controls how new projects appear."><Select defaultValue="public"><SelectTrigger placeholder="Choose visibility" /><SelectContent><SelectItem value="public">Public</SelectItem><SelectItem value="private">Private</SelectItem><SelectItem value="unlisted">Unlisted</SelectItem></SelectContent></Select></Field>
18379
18447
  ```
18380
18448
 
18381
18449
  ### Rich item labels
@@ -18383,7 +18451,7 @@ import { Field, Select, SelectContent, SelectItem, SelectTrigger } from "@sableu
18383
18451
  ```tsx
18384
18452
  import { Badge, Select, SelectContent, SelectItem, SelectTrigger } from "@sableui/react"
18385
18453
 
18386
- <Select defaultValue="edition" items={[{ value: "edition", label: "Edition" }, { value: "collectible", label: "Collectible" }]}><SelectTrigger /><SelectContent><SelectItem value="edition" label="Edition"><span className="flex items-center gap-2"><Badge tone="edition">Edition</Badge><span>Edition drop</span></span></SelectItem><SelectItem value="collectible" label="Collectible"><span className="flex items-center gap-2"><Badge tone="collectible">Collectible</Badge><span>Collectible</span></span></SelectItem></SelectContent></Select>
18454
+ <Select defaultValue="business" items={[{ value: "business", label: "Business" }, { value: "studio", label: "Studio" }]}><SelectTrigger /><SelectContent><SelectItem value="business" label="Business"><span className="flex items-center gap-2"><Badge tone="1">Business</Badge><span>Business plan</span></span></SelectItem><SelectItem value="studio" label="Studio"><span className="flex items-center gap-2"><Badge tone="2">Studio</Badge><span>Studio</span></span></SelectItem></SelectContent></Select>
18387
18455
  ```
18388
18456
 
18389
18457
  ---
@@ -18708,7 +18776,7 @@ import { Button, Sheet, SheetClose, SheetContent, SheetFooter, SheetHeader, Shee
18708
18776
  ```tsx
18709
18777
  import { Button, Sheet, SheetClose, SheetContent, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from "@sableui/react"
18710
18778
 
18711
- <Sheet><SheetTrigger render={<Button variant="secondary">Details</Button>} /><SheetContent side="bottom"><SheetHeader><SheetTitle>Edition details</SheetTitle></SheetHeader><SheetFooter><SheetClose render={<Button variant="secondary">Close</Button>} /></SheetFooter></SheetContent></Sheet>
18779
+ <Sheet><SheetTrigger render={<Button variant="secondary">Details</Button>} /><SheetContent side="bottom"><SheetHeader><SheetTitle>Order details</SheetTitle></SheetHeader><SheetFooter><SheetClose render={<Button variant="secondary">Close</Button>} /></SheetFooter></SheetContent></Sheet>
18712
18780
  ```
18713
18781
 
18714
18782
  ---
@@ -18844,7 +18912,7 @@ Use Sidebar for persistent desktop navigation or application chrome outside the
18844
18912
  import { Icon, NotificationBadge, Sidebar, SidebarBrand, SidebarFooter, SidebarHeader, SidebarItem, SidebarNav } from "@sableui/react"
18845
18913
  // <Link> = your router's link (react-router, next/link, @tanstack/router, …)
18846
18914
 
18847
- <Sidebar><SidebarHeader><SidebarBrand mark={<Icon name="music" variant="solid" />} label="Undertow" render={<Link to="/" />} /></SidebarHeader><SidebarNav><SidebarItem icon={<Icon name="bell" />} label="Notifications" badge={<NotificationBadge count={3} />} render={<Link to="/notifications" />} /></SidebarNav><SidebarFooter>{wallet}</SidebarFooter></Sidebar>
18915
+ <Sidebar><SidebarHeader><SidebarBrand mark={<Icon name="music" variant="solid" />} label="Undertow" render={<Link to="/" />} /></SidebarHeader><SidebarNav><SidebarItem icon={<Icon name="bell" />} label="Notifications" badge={<NotificationBadge count={3} />} render={<Link to="/notifications" />} /></SidebarNav><SidebarFooter>{account}</SidebarFooter></Sidebar>
18848
18916
  ```
18849
18917
 
18850
18918
  ### Collapsible (icon rail)
@@ -18953,7 +19021,7 @@ import { SideNav, SideNavGroup, SideNavItem } from "@sableui/react"
18953
19021
  import { SideNav, SideNavItem } from "@sableui/react"
18954
19022
  // <Link> = your router's link (react-router, next/link, @tanstack/router, …)
18955
19023
 
18956
- <SideNav size="lg"><SideNavItem label="Profile" active render={<Link to="/settings/profile" />} /><SideNavItem label="Wallets" render={<Link to="/settings/wallets" />} /><SideNavItem label="Security" render={<Link to="/settings/security" />} /></SideNav>
19024
+ <SideNav size="lg"><SideNavItem label="Profile" active render={<Link to="/settings/profile" />} /><SideNavItem label="Billing" render={<Link to="/settings/billing" />} /><SideNavItem label="Security" render={<Link to="/settings/security" />} /></SideNav>
18957
19025
  ```
18958
19026
 
18959
19027
  ### Inside Sidebar chrome
@@ -19196,7 +19264,7 @@ Use Sparkline next to a number to show where it has been — a KPI's last 30 day
19196
19264
  | data | number[] | — | The values, oldest first. |
19197
19265
  | label | string | — | What the line tracks ("Revenue, last 30 days"). The spoken summary adds the first and last values, low and high. |
19198
19266
  | variant | "line" \| "area" \| "bar" | "line" | A line, a line over a soft fill, or columns. |
19199
- | tone | "edition" \| "collectible" \| "standard" \| "info" \| "warning" \| "primary" \| "neutral" \| "success" \| "destructive" | "primary" | Colour by role. |
19267
+ | tone | "1" \| "2" \| "3" \| "info" \| "warning" \| "primary" \| "neutral" \| "success" \| "destructive" | "primary" | Colour by role. |
19200
19268
  | className | string | "h-8 w-24" | Sizes it; the drawing stretches to fit. |
19201
19269
  | animate | boolean | true | Draw on (or rise) when it scrolls into view. |
19202
19270
  | locale | string | runtime locale | BCP 47 locale for default number and date formatting. |
@@ -19207,7 +19275,7 @@ Use Sparkline next to a number to show where it has been — a KPI's last 30 day
19207
19275
 
19208
19276
  ## Tokens used
19209
19277
 
19210
- `primary`, `tone-edition`, `tone-collectible`, `tone-standard`, `tone-info`, `tone-warning`, `muted-foreground`, `success`, `destructive`
19278
+ `primary`, `tone-1`, `tone-2`, `tone-3`, `tone-info`, `tone-warning`, `muted-foreground`, `success`, `destructive`
19211
19279
 
19212
19280
  ## Motion
19213
19281
 
@@ -19338,7 +19406,7 @@ import { Spinner } from "@sableui/react"
19338
19406
  ```tsx
19339
19407
  import { Button } from "@sableui/react"
19340
19408
 
19341
- <Button loading>Collecting</Button>
19409
+ <Button loading>Saving</Button>
19342
19410
  ```
19343
19411
 
19344
19412
  ---
@@ -19467,18 +19535,18 @@ Use StatusDot as a compact visual state cue next to text, avatars, rows, or labe
19467
19535
 
19468
19536
  | Name | Type | Default | Description |
19469
19537
  | --- | --- | --- | --- |
19470
- | tone | "neutral" \| "standard" \| "success" \| "warning" \| "info" \| "edition" \| "collectible" \| "destructive" | "neutral" | Semantic color of the dot. |
19538
+ | tone | "neutral" \| "1" \| "2" \| "3" \| "success" \| "warning" \| "info" \| "destructive" \| "red" \| "orange" \| "yellow" \| "green" \| "teal" \| "blue" \| "purple" \| "pink" | "neutral" | Semantic color of the dot, or a palette family (red … pink) for a category legend. |
19471
19539
  | size | "sm" \| "md" \| "lg" | "md" | Dot diameter. |
19472
19540
  | pulse | boolean | false | Wrap the dot in an animated ping ring to signal a live state. |
19473
19541
 
19474
19542
  ## Variants
19475
19543
 
19476
- - **tone**: neutral, standard, success, warning, info, edition, collectible, destructive
19544
+ - **tone**: neutral, 1, 2, 3, success, warning, info, destructive, red, orange, yellow, green, teal, blue, purple, pink
19477
19545
  - **size**: sm, md, lg
19478
19546
 
19479
19547
  ## Tokens used
19480
19548
 
19481
- `muted-foreground`, `tone-standard`, `success`, `tone-warning`, `tone-info`, `tone-edition`, `tone-collectible`, `destructive`
19549
+ `palette-dot`, `muted-foreground`, `tone-3`, `success`, `tone-warning`, `tone-info`, `tone-1`, `tone-2`, `destructive`
19482
19550
 
19483
19551
  ## Accessibility
19484
19552
 
@@ -19497,7 +19565,7 @@ Use StatusDot as a compact visual state cue next to text, avatars, rows, or labe
19497
19565
  ```tsx
19498
19566
  import { Row, StatusDot } from "@sableui/react"
19499
19567
 
19500
- <Row gap={2}><StatusDot tone="standard" /><StatusDot tone="info" /><StatusDot tone="warning" /><StatusDot tone="destructive" /></Row>
19568
+ <Row gap={2}><StatusDot tone="3" /><StatusDot tone="info" /><StatusDot tone="warning" /><StatusDot tone="destructive" /></Row>
19501
19569
  ```
19502
19570
 
19503
19571
  ### With label — use Badge
@@ -19882,25 +19950,25 @@ Use Table for structured data with consistent columns and rows. Use DataTable wh
19882
19950
 
19883
19951
  ## Examples
19884
19952
 
19885
- ### Collectors
19953
+ ### Orders
19886
19954
 
19887
19955
  ```tsx
19888
19956
  import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@sableui/react"
19889
19957
 
19890
19958
  <Table>
19891
- <TableCaption>Recent collectors</TableCaption>
19959
+ <TableCaption>Recent orders</TableCaption>
19892
19960
  <TableHeader>
19893
19961
  <TableRow>
19894
- <TableHead>Collector</TableHead>
19895
- <TableHead>Edition</TableHead>
19962
+ <TableHead>Customer</TableHead>
19963
+ <TableHead>Order</TableHead>
19896
19964
  <TableHead>Paid</TableHead>
19897
19965
  </TableRow>
19898
19966
  </TableHeader>
19899
19967
  <TableBody>
19900
19968
  <TableRow>
19901
19969
  <TableCell>@nova</TableCell>
19902
- <TableCell>#012</TableCell>
19903
- <TableCell>0.5 SOL</TableCell>
19970
+ <TableCell>1042</TableCell>
19971
+ <TableCell>$48.00</TableCell>
19904
19972
  </TableRow>
19905
19973
  </TableBody>
19906
19974
  </Table>
@@ -19919,7 +19987,7 @@ import { Avatar, Badge, Button, Row, Table, TableBody, TableCaption, TableCell,
19919
19987
  ```tsx
19920
19988
  import { Card, CardContent, CardHeader, CardTitle, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@sableui/react"
19921
19989
 
19922
- <Card><CardHeader><CardTitle>Collectors</CardTitle></CardHeader><CardContent flush><Table><TableHeader><TableRow><TableHead>Collector</TableHead><TableHead>Paid</TableHead></TableRow></TableHeader><TableBody><TableRow><TableCell>@nova</TableCell><TableCell>0.5 SOL</TableCell></TableRow></TableBody></Table></CardContent></Card>
19990
+ <Card><CardHeader><CardTitle>Customers</CardTitle></CardHeader><CardContent flush><Table><TableHeader><TableRow><TableHead>Customer</TableHead><TableHead>Paid</TableHead></TableRow></TableHeader><TableBody><TableRow><TableCell>@nova</TableCell><TableCell>$48.00</TableCell></TableRow></TableBody></Table></CardContent></Card>
19923
19991
  ```
19924
19992
 
19925
19993
  ### Summary footer
@@ -19927,7 +19995,7 @@ import { Card, CardContent, CardHeader, CardTitle, Table, TableBody, TableCell,
19927
19995
  ```tsx
19928
19996
  import { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "@sableui/react"
19929
19997
 
19930
- <Table><TableHeader><TableRow><TableHead>Item</TableHead><TableHead className="text-end">Amount</TableHead></TableRow></TableHeader><TableBody><TableRow><TableCell>Primary sale</TableCell><TableCell className="text-end">42 SOL</TableCell></TableRow></TableBody><TableFooter><TableRow><TableCell>Total</TableCell><TableCell className="text-end">42 SOL</TableCell></TableRow></TableFooter></Table>
19998
+ <Table><TableHeader><TableRow><TableHead>Item</TableHead><TableHead className="text-end">Amount</TableHead></TableRow></TableHeader><TableBody><TableRow><TableCell>Studio plan</TableCell><TableCell className="text-end">$192.00</TableCell></TableRow></TableBody><TableFooter><TableRow><TableCell>Total</TableCell><TableCell className="text-end">$192.00</TableCell></TableRow></TableFooter></Table>
19931
19999
  ```
19932
20000
 
19933
20001
  ---
@@ -20014,7 +20082,7 @@ import { Tabs, TabsList, TabsPanel, TabsTrigger } from "@sableui/react"
20014
20082
  ```tsx
20015
20083
  import { Tabs, TabsList, TabsPanel, TabsTrigger } from "@sableui/react"
20016
20084
 
20017
- <Tabs defaultValue="overview"><TabsList><TabsTrigger value="overview">Overview</TabsTrigger><TabsTrigger value="holders" disabled>Holders</TabsTrigger></TabsList><TabsPanel value="overview">Overview content</TabsPanel></Tabs>
20085
+ <Tabs defaultValue="overview"><TabsList><TabsTrigger value="overview">Overview</TabsTrigger><TabsTrigger value="billing" disabled>Billing</TabsTrigger></TabsList><TabsPanel value="overview">Overview content</TabsPanel></Tabs>
20018
20086
  ```
20019
20087
 
20020
20088
  ---
@@ -20081,7 +20149,7 @@ import { Textarea } from "@sableui/react"
20081
20149
  ```tsx
20082
20150
  import { Textarea } from "@sableui/react"
20083
20151
 
20084
- <Textarea rows={6} placeholder="Tell the story behind this edition…" />
20152
+ <Textarea rows={6} placeholder="Tell your team what this project is for…" />
20085
20153
  ```
20086
20154
 
20087
20155
  ### Auto-resize
@@ -20105,7 +20173,7 @@ import { Textarea } from "@sableui/react"
20105
20173
  ```tsx
20106
20174
  import { Textarea } from "@sableui/react"
20107
20175
 
20108
- <Textarea disabled defaultValue="Captions lock once the edition is minted." />
20176
+ <Textarea disabled defaultValue="Notes lock once the invoice is sent." />
20109
20177
  ```
20110
20178
 
20111
20179
  ---
@@ -20522,7 +20590,7 @@ toast.promise(publish(), { loading: "Publishing…", success: "Published", error
20522
20590
  ```tsx
20523
20591
  import { toast } from "@sableui/react"
20524
20592
 
20525
- const id = toast.loading("Pinning to Arweave…"); /* later */ toast.update(id, { title: "Pinned", type: "success", timeout: 4000 })
20593
+ const id = toast.loading("Uploading report…"); /* later */ toast.update(id, { title: "Uploaded", type: "success", timeout: 4000 })
20526
20594
  ```
20527
20595
 
20528
20596
  ### Custom toast markup (composed parts)
@@ -20904,7 +20972,7 @@ Use Tooltip for brief nonessential hints tied to a specific control or piece of
20904
20972
  ```tsx
20905
20973
  import { Button, Icon, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@sableui/react"
20906
20974
 
20907
- <TooltipProvider><Tooltip><TooltipTrigger render={<Button variant="ghost" size="icon-sm" aria-label="Info"><Icon name="info" /></Button>} /><TooltipContent>Stored permanently on Arweave.</TooltipContent></Tooltip></TooltipProvider>
20975
+ <TooltipProvider><Tooltip><TooltipTrigger render={<Button variant="ghost" size="icon-sm" aria-label="Info"><Icon name="info" /></Button>} /><TooltipContent>Saved to your workspace.</TooltipContent></Tooltip></TooltipProvider>
20908
20976
  ```
20909
20977
 
20910
20978
  ### Sides
@@ -20920,7 +20988,7 @@ import { Button, Tooltip, TooltipContent, TooltipTrigger } from "@sableui/react"
20920
20988
  ```tsx
20921
20989
  import { Button, Icon, SimpleTooltip } from "@sableui/react"
20922
20990
 
20923
- <SimpleTooltip content="Stored permanently on Arweave."><Button variant="ghost" size="icon-sm" aria-label="Info"><Icon name="info" /></Button></SimpleTooltip>
20991
+ <SimpleTooltip content="Saved to your workspace."><Button variant="ghost" size="icon-sm" aria-label="Info"><Icon name="info" /></Button></SimpleTooltip>
20924
20992
  ```
20925
20993
 
20926
20994
  ---
@@ -21417,23 +21485,23 @@ Which token to reach for, and what it is most often confused with. Generated fro
21417
21485
  - **Not** `tone-info`. Success asserts an outcome; info asserts a fact. 'Saved' is success, '3 items' is info.
21418
21486
  - **`success-foreground`** — Ink paired with `success`. Evaluate the rendered contrast together; never choose it independently. *Use for:* Whenever the background is `success`. Use them as a pair — `bg-success text-success-foreground` — so a theme can retune the surface and the ink together.
21419
21487
  - **Not** `foreground`. That is the ink for `background` only; on any other surface it is a contrast gamble that happens to pass in the default theme and fails in another.
21420
- - **`tone-standard`** — Domain tone: the baseline classification in a set. Part of the `tone-*` family, which exists so a product can classify content without inventing colours per feature. *Use for:* The default category of a classified item — the one that means 'nothing special'.
21488
+ - **`tone-3`** — Extra colour 3: the third, and usually the quietest, category in a classified set. *Use for:* A third category, or the baseline one that means 'nothing special'.
21421
21489
  - **Not** `muted`. A tone is *semantic classification* that a reader is meant to decode; `muted` is de-emphasis. If a legend would help the user, it is a tone.
21422
- - **`tone-collectible`** — Domain tone: a scarce or owned item. *Use for:* Marking an item as collectible/owned in a classified set.
21423
- - **Not** `primary`. A tone classifies the *thing*; primary drives an *action*. A collectible badge is not a button.
21424
- - **`tone-edition`** — Domain tone: an edition or release. *Use for:* Marking editioned content in a classified set.
21425
- - **Not** `tone-collectible`. Distinct classifications must stay visually distinct.
21490
+ - **`tone-2`** — Extra colour 2: the second category in a classified set. *Use for:* A second category, distinct from tone-1, in a badge, dot, card or chart.
21491
+ - **Not** `tone-1`. Distinct categories must stay visually distinct.
21492
+ - **`tone-1`** — Extra colour 1, the lead of the three a theme defines beyond its status colours. The numbered tones exist so a product can classify content (categories, plans, series) without inventing colours per feature; the product decides what each one means. *Use for:* The most prominent category in a classified set, or the first series of a chart.
21493
+ - **Not** `primary`. A tone classifies the *thing*; primary drives an *action*. A category badge is not a button.
21426
21494
  - **`tone-info`** — Domain tone: neutral, factual information. *Use for:* An informational note or banner, a neutral count, a 'did you know' surface.
21427
21495
  - **Not** `success`. Info states a fact; success states an outcome. Also not `primary` — an info banner is not an action.
21428
21496
  - **`tone-warning`** — Domain tone: proceed with care, nothing is broken yet. *Use for:* A caution banner, a nearing-limit indicator, a soloed/overridden state in a tool.
21429
21497
  - **Not** `destructive`. Warning is *advisory*; destructive is *consequential*. If nothing is lost by continuing, it is a warning.
21430
21498
  - **`highlight`** — A marker on content the user is meant to notice — search hits, diffs, annotations. *Use for:* Highlighting matched text, a changed line, a called-out passage.
21431
21499
  - **Not** `accent`. `accent` is UI state that follows the pointer or selection; `highlight` marks the *content* and persists independently of interaction.
21432
- - **`tone-standard-foreground`** — Ink paired with `tone-standard`. Evaluate the rendered contrast together; never choose it independently. *Use for:* Whenever the background is `tone-standard`. Use them as a pair — `bg-tone-standard text-tone-standard-foreground` — so a theme can retune the surface and the ink together.
21500
+ - **`tone-3-foreground`** — Ink paired with `tone-3`. Evaluate the rendered contrast together; never choose it independently. *Use for:* Whenever the background is `tone-3`. Use them as a pair — `bg-tone-3 text-tone-3-foreground` — so a theme can retune the surface and the ink together.
21433
21501
  - **Not** `foreground`. That is the ink for `background` only; on any other surface it is a contrast gamble that happens to pass in the default theme and fails in another.
21434
- - **`tone-collectible-foreground`** — Ink paired with `tone-collectible`. Evaluate the rendered contrast together; never choose it independently. *Use for:* Whenever the background is `tone-collectible`. Use them as a pair — `bg-tone-collectible text-tone-collectible-foreground` — so a theme can retune the surface and the ink together.
21502
+ - **`tone-2-foreground`** — Ink paired with `tone-2`. Evaluate the rendered contrast together; never choose it independently. *Use for:* Whenever the background is `tone-2`. Use them as a pair — `bg-tone-2 text-tone-2-foreground` — so a theme can retune the surface and the ink together.
21435
21503
  - **Not** `foreground`. That is the ink for `background` only; on any other surface it is a contrast gamble that happens to pass in the default theme and fails in another.
21436
- - **`tone-edition-foreground`** — Ink paired with `tone-edition`. Evaluate the rendered contrast together; never choose it independently. *Use for:* Whenever the background is `tone-edition`. Use them as a pair — `bg-tone-edition text-tone-edition-foreground` — so a theme can retune the surface and the ink together.
21504
+ - **`tone-1-foreground`** — Ink paired with `tone-1`. Evaluate the rendered contrast together; never choose it independently. *Use for:* Whenever the background is `tone-1`. Use them as a pair — `bg-tone-1 text-tone-1-foreground` — so a theme can retune the surface and the ink together.
21437
21505
  - **Not** `foreground`. That is the ink for `background` only; on any other surface it is a contrast gamble that happens to pass in the default theme and fails in another.
21438
21506
  - **`tone-info-foreground`** — Ink paired with `tone-info`. Evaluate the rendered contrast together; never choose it independently. *Use for:* Whenever the background is `tone-info`. Use them as a pair — `bg-tone-info text-tone-info-foreground` — so a theme can retune the surface and the ink together.
21439
21507
  - **Not** `foreground`. That is the ink for `background` only; on any other surface it is a contrast gamble that happens to pass in the default theme and fails in another.
@@ -21540,11 +21608,11 @@ Which token to reach for, and what it is most often confused with. Generated fro
21540
21608
 
21541
21609
  ### layout (11)
21542
21610
 
21543
- - **`rhythm-unit`** — The vertical rhythm base — two spacing steps (8px at the default scale). Every vertical measure is a multiple of it, and it follows the density control with the spacing scale. *Use for:* Through `Stack`/`Row`/`Grid` gap numbers, which are rhythm units rather than pixels. A gap of 2 is 16px at the default scale, and grows or tightens with density.
21611
+ - **`rhythm-unit`** — The vertical rhythm base — one baseline step (8px at the default scale). Every vertical measure is a multiple of it, and it follows the type and the density control. *Use for:* Through `Stack`/`Row`/`Grid` gap numbers, which are rhythm units rather than pixels. A gap of 2 is 16px at the default scale, and grows or tightens with density.
21544
21612
  - **Not** arbitrary vertical spacing. The rhythm is what makes unrelated sections of a page feel like one document.
21545
21613
  - **`grid-columns`** — The column count of the content grid — 12. *Use for:* `Grid`/`Columns`, WITHIN a region, for genuinely multi-column content.
21546
21614
  - **Not** using a column span as the page's reading width. A span is not a measure — see `measure-prose`.
21547
- - **`grid-gutter`** — The gap between grid columns. *Use for:* `Grid`/`Columns` spacing.
21615
+ - **`grid-gutter`** — The gap between grid columns and rows — 2 baseline steps on phones, 3 on desktop, snapped to the step. *Use for:* `Grid`/`Columns` spacing.
21548
21616
  - **`page-inset`** — The page's outer breathing room, left and right. *Use for:* The gutter between content and the viewport edge, at every breakpoint.
21549
21617
  - **Not** a global max-width. Sable caps width per REGION by role, never once for the whole page.
21550
21618
  - **`measure-prose`** — The maximum comfortable line length for prose, in `ch`. *Use for:* Any long-form text column.
@@ -21704,6 +21772,20 @@ Which token to reach for, and what it is most often confused with. Generated fro
21704
21772
  - **`border-floating`** — The edge of a floating surface. Fainter than `border`: the shadow carries the elevation. *Use for:* Every floating surface — popover, menu, select, tooltip, dialog, sheet, toast, a floating panel — as `border-border-floating` or `ring-border-floating`, together with `bg-popover` and a `shadow-md`+ shadow. Derived for every theme (a faint alpha of the foreground); a theme may tune it with `--theme-border-floating`.
21705
21773
  - **Not** `border`. A full hairline plus a shadow stacks into a heavy outline around anything that floats. `border` is for resting edges: inputs, cards, badges.
21706
21774
 
21775
+ ### Generated palette
21776
+
21777
+ More colour for apps, generated from the active theme: neutral and eight hue families, plus a 50–950 scale under every tone. Each family keeps its hue and leans toward the brand (half the gap, at most 15°; --theme-palette-harmony scales it, 0 = own hues). Neutral is the theme's --background mixed toward its --foreground. Steps follow the mode: low steps sit near the page and high ones far from it in light and dark alike, so a pair needs no dark: variant. For decorative and categorical colour (tags, avatars, labels, legends, illustration); surfaces, text and actions take roles. Components never name a step.
21778
+
21779
+ - **Families:** `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, each 50–950.
21780
+ - **Tone scales:** `tone-1-*`, `tone-2-*`, `tone-3-*`, `tone-info-*`, `tone-warning-*`, `success-*`, `destructive-*`; step 500 is the tone itself.
21781
+ - **Use as:** bg-blue-100, text-blue-800, border-neutral-200, bg-tone-1-100 — or var(--teal-500). Tailwind's other families (slate, gray, zinc, stone, amber, lime, emerald, cyan, sky, indigo, violet, fuchsia, rose) are switched off.
21782
+ - **tag / label:** bg-<family>-100 text-<family>-800 (held to 4.5:1 in every theme and mode)
21783
+ - **text on the page:** text-<family>-700
21784
+ - **solid fill:** bg-<family>-700 text-<family>-50
21785
+ - **hairline:** border-<family>-300
21786
+ - **Components:** Badge, Pill and StatusDot take a family as tone (tone="blue"): a category with no meaning attached.
21787
+ - **Slots:** data-palette="<family>" on an element fills these from that family, so one set of classes paints any family: bg-palette-wash text-palette-text.
21788
+
21707
21789
  ---
21708
21790
 
21709
21791
  ## Utilities
@@ -21809,7 +21891,7 @@ The `@utility` and `@custom-variant` layer in `styles.css`: what each one is, wh
21809
21891
  - `<ol className="sable-list-numbered"><li>Install</li><li>Import the styles</li></ol>`
21810
21892
  - **`sable-list-dl`** — Label/value pairs on a native `<dl>`: muted label-tier `<dt>`s and body-tier `<dd>`s on a baseline-aligned two-column grid; long values wrap inside their column. Colour a status value with a `text-*` utility on its `<dd>`. *Use for:* Read-only details: an order summary, a record's metadata, a settings summary, profile stats.
21811
21893
  - **Instead of** A grid of `<div>`s (screen readers lose the term/value pairing), or Table for a single record. For editable values use Form and Field.
21812
- - `<dl className="sable-list-dl"><dt>Network</dt><dd>Solana</dd><dt>Status</dt><dd className="text-success">Active</dd></dl>`
21894
+ - `<dl className="sable-list-dl"><dt>Plan</dt><dd>Studio</dd><dt>Status</dt><dd className="text-success">Active</dd></dl>`
21813
21895
  - **`sable-list-dl-2`** — Two label/value pairs a row once the nearest `@container` ancestor is at least 28rem wide. Add to `sable-list-dl`. *Use for:* A compact metadata block in a wide card or panel. Put `@container` on a parent so it measures the room it actually has.
21814
21896
  - **Instead of** A viewport breakpoint, which gives a narrow card on a wide screen four columns it cannot fit.
21815
21897
  - `<div className="@container"><dl className="sable-list-dl sable-list-dl-2">…</dl></div>`