@sableui/react 0.9.0 → 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 (221) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +124 -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.js +13 -2
  35. package/dist/components/button/button.meta.js +3 -3
  36. package/dist/components/card/card.js +16 -7
  37. package/dist/components/card/card.meta.js +4 -4
  38. package/dist/components/checkbox-card/checkbox-card.meta.js +10 -10
  39. package/dist/components/checkbox-card/checkbox-card.variants.js +15 -15
  40. package/dist/components/code-block/code-block.meta.js +1 -1
  41. package/dist/components/command-menu/command-menu.js +2 -0
  42. package/dist/components/command-menu/command-menu.meta.js +1 -1
  43. package/dist/components/context-card/context-card.meta.js +2 -2
  44. package/dist/components/destructive-action-modal/destructive-action-modal.meta.js +1 -1
  45. package/dist/components/dialog/dialog.meta.js +2 -2
  46. package/dist/components/disclosure-picker/disclosure-picker.js +48 -2
  47. package/dist/components/disclosure-picker/disclosure-picker.meta.js +3 -2
  48. package/dist/components/empty-state/empty-state.meta.js +4 -4
  49. package/dist/components/form/form.js +10 -5
  50. package/dist/components/gradient-art/gradient-art.meta.js +3 -3
  51. package/dist/components/label/label.meta.js +1 -1
  52. package/dist/components/line-chart/line-chart.meta.js +3 -3
  53. package/dist/components/meter/meter.js +1 -1
  54. package/dist/components/meter/meter.meta.js +2 -2
  55. package/dist/components/meter/meter.variants.js +1 -1
  56. package/dist/components/middle-truncate/middle-truncate.meta.js +4 -4
  57. package/dist/components/note/note.meta.js +3 -3
  58. package/dist/components/page-header/page-header.js +1 -1
  59. package/dist/components/pagination/pagination.meta.js +2 -2
  60. package/dist/components/phone/phone.meta.js +1 -1
  61. package/dist/components/pie-chart/pie-chart.meta.js +3 -3
  62. package/dist/components/pill/pill.js +4 -2
  63. package/dist/components/pill/pill.meta.js +19 -2
  64. package/dist/components/pill/pill.variants.js +23 -2
  65. package/dist/components/popover/popover.meta.js +2 -2
  66. package/dist/components/radar-chart/radar-chart.meta.js +3 -3
  67. package/dist/components/radial-chart/radial-chart.meta.js +3 -3
  68. package/dist/components/radio/radio.meta.js +2 -2
  69. package/dist/components/radio-card/radio-card.meta.js +6 -6
  70. package/dist/components/radio-card/radio-card.variants.js +15 -15
  71. package/dist/components/scroller/scroller.meta.js +1 -1
  72. package/dist/components/search-input/search-input.meta.js +1 -1
  73. package/dist/components/section-header/section-header.js +1 -1
  74. package/dist/components/segmented-bar/segmented-bar.js +1 -1
  75. package/dist/components/segmented-bar/segmented-bar.meta.js +6 -6
  76. package/dist/components/segmented-bar/segmented-bar.variants.js +4 -4
  77. package/dist/components/select/select.meta.js +2 -2
  78. package/dist/components/sheet/sheet.js +15 -1
  79. package/dist/components/sheet/sheet.meta.js +1 -1
  80. package/dist/components/side-nav/side-nav.meta.js +1 -1
  81. package/dist/components/sidebar/sidebar.meta.js +1 -1
  82. package/dist/components/sparkline/sparkline.meta.js +4 -4
  83. package/dist/components/spinner/spinner.meta.js +1 -1
  84. package/dist/components/status-dot/status-dot.js +4 -1
  85. package/dist/components/status-dot/status-dot.meta.js +8 -16
  86. package/dist/components/status-dot/status-dot.variants.js +13 -4
  87. package/dist/components/stepper/stepper.js +11 -2
  88. package/dist/components/table/table.meta.js +4 -4
  89. package/dist/components/tabs/tabs.meta.js +1 -1
  90. package/dist/components/textarea/textarea.meta.js +2 -2
  91. package/dist/components/toast/toast.js +1 -0
  92. package/dist/components/toast/toast.meta.js +1 -1
  93. package/dist/components/tooltip/tooltip.meta.js +2 -2
  94. package/dist/file-hashes.json +127 -124
  95. package/dist/foundations/spacing.js +8 -8
  96. package/dist/index.d.ts +138 -73
  97. package/dist/index.js +12 -1
  98. package/dist/lib/chart/types.js +3 -3
  99. package/dist/lib/is-promise.js +5 -0
  100. package/dist/lib/palette.js +103 -0
  101. package/dist/lib/selection-card.js +4 -4
  102. package/dist/palette.css +669 -0
  103. package/dist/patterns/auth-page.js +6 -6
  104. package/dist/patterns/auth-page.meta.js +3 -3
  105. package/dist/patterns/brand-landing-page.js +2 -2
  106. package/dist/patterns/chat-panel.js +2 -2
  107. package/dist/patterns/dashboard-page.js +71 -71
  108. package/dist/patterns/dashboard-page.meta.js +5 -5
  109. package/dist/patterns/feed-page.js +25 -25
  110. package/dist/patterns/feed-page.meta.js +3 -3
  111. package/dist/patterns/landing-page.js +1 -1
  112. package/dist/patterns/list-detail-page.js +17 -17
  113. package/dist/patterns/settings-page.js +17 -17
  114. package/dist/presets/index.d.ts +1 -1
  115. package/dist/presets/index.js +17 -2
  116. package/dist/styles.css +163 -55
  117. package/dist/{theme-provider-S0NVJs2Y.d.ts → theme-provider-CdSicAad.d.ts} +1 -1
  118. package/dist/themes/_contract.js +22 -8
  119. package/dist/themes/atelier/atelier.css +13 -13
  120. package/dist/themes/atelier/index.d.ts +1 -1
  121. package/dist/themes/ember/ember.css +13 -13
  122. package/dist/themes/ember/index.d.ts +1 -1
  123. package/dist/themes/index.d.ts +2 -2
  124. package/dist/themes/meridian/index.d.ts +1 -1
  125. package/dist/themes/meridian/meridian.css +13 -13
  126. package/dist/themes/poppy/index.d.ts +1 -1
  127. package/dist/themes/poppy/poppy.css +19 -16
  128. package/dist/themes/prism/index.d.ts +1 -1
  129. package/dist/themes/prism/prism.css +18 -15
  130. package/dist/themes/sable/index.d.ts +1 -1
  131. package/dist/themes/sable/sable.css +18 -22
  132. package/dist/themes/verdant/index.d.ts +1 -1
  133. package/dist/themes/verdant/verdant.css +15 -15
  134. package/llms/blocks/danger-zone-block.md +4 -4
  135. package/llms/blocks/faq-block.md +3 -3
  136. package/llms/blocks/goal-progress-block.md +1 -1
  137. package/llms/blocks/notification-list-block.md +1 -1
  138. package/llms/blocks/pricing-table-block.md +12 -12
  139. package/llms/blocks/product-card-block.md +6 -6
  140. package/llms/blocks/profile-card-block.md +1 -1
  141. package/llms/blocks/profile-form-block.md +3 -3
  142. package/llms/blocks/revenue-breakdown-block.md +5 -5
  143. package/llms/blocks/stat-row-block.md +3 -3
  144. package/llms/blocks/task-list-block.md +2 -2
  145. package/llms/blocks/team-members-block.md +1 -1
  146. package/llms/blocks/wallet-block.md +7 -7
  147. package/llms/components/accordion.md +2 -2
  148. package/llms/components/app-header.md +1 -1
  149. package/llms/components/app-shell.md +2 -2
  150. package/llms/components/area-chart.md +1 -1
  151. package/llms/components/badge.md +15 -7
  152. package/llms/components/banner.md +6 -6
  153. package/llms/components/bar-chart.md +2 -2
  154. package/llms/components/breadcrumbs.md +1 -1
  155. package/llms/components/button.md +3 -3
  156. package/llms/components/card.md +4 -4
  157. package/llms/components/checkbox-card.md +5 -5
  158. package/llms/components/code-block.md +1 -1
  159. package/llms/components/command-menu.md +1 -1
  160. package/llms/components/context-card.md +2 -2
  161. package/llms/components/destructive-action-modal.md +1 -1
  162. package/llms/components/dialog.md +2 -2
  163. package/llms/components/disclosure-picker.md +2 -1
  164. package/llms/components/empty-state.md +4 -4
  165. package/llms/components/gradient-art.md +3 -3
  166. package/llms/components/label.md +1 -1
  167. package/llms/components/line-chart.md +1 -1
  168. package/llms/components/meter.md +2 -2
  169. package/llms/components/middle-truncate.md +5 -5
  170. package/llms/components/note.md +3 -3
  171. package/llms/components/pagination.md +2 -2
  172. package/llms/components/phone.md +1 -1
  173. package/llms/components/pie-chart.md +1 -1
  174. package/llms/components/pill.md +12 -2
  175. package/llms/components/popover.md +2 -2
  176. package/llms/components/radar-chart.md +1 -1
  177. package/llms/components/radial-chart.md +1 -1
  178. package/llms/components/radio-card.md +4 -4
  179. package/llms/components/radio.md +2 -2
  180. package/llms/components/scroller.md +1 -1
  181. package/llms/components/search-input.md +1 -1
  182. package/llms/components/segmented-bar.md +7 -7
  183. package/llms/components/select.md +2 -2
  184. package/llms/components/sheet.md +1 -1
  185. package/llms/components/side-nav.md +1 -1
  186. package/llms/components/sidebar.md +1 -1
  187. package/llms/components/sparkline.md +2 -2
  188. package/llms/components/spinner.md +1 -1
  189. package/llms/components/status-dot.md +4 -4
  190. package/llms/components/table.md +8 -8
  191. package/llms/components/tabs.md +1 -1
  192. package/llms/components/textarea.md +2 -2
  193. package/llms/components/toast.md +1 -1
  194. package/llms/components/tooltip.md +2 -2
  195. package/llms/foundations/spacing.md +7 -7
  196. package/llms/layout/grid.md +1 -1
  197. package/llms/layout/page.md +1 -1
  198. package/llms/layout/region.md +1 -1
  199. package/llms/layout/split-pane.md +1 -1
  200. package/llms/patterns/auth-page.md +11 -11
  201. package/llms/patterns/brand-landing-page.md +2 -2
  202. package/llms/patterns/chat-panel.md +2 -2
  203. package/llms/patterns/dashboard-page.md +78 -78
  204. package/llms/patterns/feed-page.md +31 -31
  205. package/llms/patterns/landing-page.md +1 -1
  206. package/llms/patterns/list-detail-page.md +18 -18
  207. package/llms/patterns/settings-page.md +18 -18
  208. package/llms/reference/token-intent.md +25 -11
  209. package/llms/reference/utilities.md +1 -1
  210. package/llms/tasks/adding-a-component.md +4 -1
  211. package/llms/tasks/building-a-page.md +40 -0
  212. package/llms/tasks/building-with-sable.md +4 -0
  213. package/llms/tasks/converting-an-app.md +3 -1
  214. package/llms/tasks/using-presets.md +4 -0
  215. package/llms-full.txt +433 -347
  216. package/llms.txt +7 -7
  217. package/package.json +4 -4
  218. package/registry.json +369 -233
  219. package/scripts/lint-rules.mjs +26 -5
  220. package/scripts/sources.mjs +4 -1
  221. 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
@@ -1040,6 +1083,10 @@ for each recognizable element with `sable search "<need>" --json`; inspect the
1040
1083
  matching `component <Name> --props` and `--examples` before implementation.
1041
1084
  Use Sable's composed anatomy and supported variants. A hand-built button, field,
1042
1085
  menu, dialog, table, or card that imitates a shipped component violates the contract.
1086
+ That binds behaviour and parts, not layouts. Results marked ◇ (patterns, blocks,
1087
+ and compositions such as MediaTile) are references: use one when its layout fits
1088
+ the brief, otherwise compose your own from Sable's parts. The brief's signature
1089
+ surfaces, where the product's identity lives, are designed from its direction.
1043
1090
 
1044
1091
  Resolve styling through component variants, documented utilities/recipes, then
1045
1092
  semantic tokens. App-owned CSS handles uncovered content and composition; it
@@ -1220,7 +1267,9 @@ loudest and least clearly ("invalid hook call").
1220
1267
 
1221
1268
  The most reliable signal that a conversion is real: the app's own CSS variable
1222
1269
  block shrinks to almost nothing. Anything that remains should be *domain* colour,
1223
- 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.
1224
1273
 
1225
1274
  Grep for orphaned references afterwards. A CSS variable that no longer exists
1226
1275
  resolves to nothing and the declaration is dropped — **silently**. A checkerboard
@@ -2506,6 +2555,10 @@ prints a complete starting record; the public `PRESET_TOKEN_NAMES` is authoritat
2506
2555
  Font stacks are requirements, not downloaded fonts. Declare matching stacks in
2507
2556
  the font metadata and tokens. Resolve internal palette variables before saving;
2508
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.
2509
2562
  The validator rejects unknown tokens and CSS that can escape its declaration.
2510
2563
  `sable preset --check` also holds labels on brand and status fills to 4.5:1 and APCA Lc 60
2511
2564
  and suggests a pair that reads; a preset may keep a pair deliberately through
@@ -2821,9 +2874,9 @@ muted-foreground is contrast-tested on every surface; a 60% alpha is not.
2821
2874
 
2822
2875
  # Spacing & sizing
2823
2876
 
2824
- 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.
2825
2878
 
2826
- **4px base · 8px rhythm · 3 roles · 1 control ladder**
2879
+ **Baseline from the type · 8px steps · 3 roles · 1 control ladder**
2827
2880
 
2828
2881
  ## Spacing by role
2829
2882
 
@@ -2832,8 +2885,8 @@ Steps below 2 (8px) are optical correction only.
2832
2885
  | Name | Value | Use |
2833
2886
  |---|---|---|
2834
2887
  | `Inset p-4` | 16px | Padding inside a surface. p-3 for a dense card, px-3 py-2 for a row. |
2835
- | `Stack, within a group gap-1.5 – gap-2` | 6–8px | Label to field, title to description. |
2836
- | `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. |
2837
2890
  | `Stack, between sections gap-12+` | 48px+ | Major regions of a page. |
2838
2891
  | `Inline gap-2 – gap-3` | 8–12px | Icon to label, control to control. |
2839
2892
 
@@ -2859,8 +2912,8 @@ Comfortable values; data-density="compact" (or <Density>) redefines every step f
2859
2912
 
2860
2913
  ## Rules
2861
2914
 
2862
- 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.
2863
- 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).*
2864
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.
2865
2918
  4. **Space lives on the container (gap), not on children (margins).** gap survives reordering, conditional children and RTL; per-child margins do not.
2866
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.*
@@ -2898,7 +2951,7 @@ Every control, menu and popup inside retunes, including portaled overlays.
2898
2951
  <Stack gap={3}> between groups (24px), <Stack gap={1}> inside a group (8px)
2899
2952
  ```
2900
2953
 
2901
- 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.
2902
2955
 
2903
2956
  **Related:** `sable docs shape` · `sable docs typography` · `sable docs building-a-tool` · `sable token layout`
2904
2957
 
@@ -3261,7 +3314,7 @@ The region is in the accessibility tree before its text changes, so the change i
3261
3314
 
3262
3315
  # AuthPage
3263
3316
 
3264
- 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.
3265
3318
 
3266
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).
3267
3320
 
@@ -3272,14 +3325,14 @@ Split sign-in page: a form column (brand mark, heading, email + password, primar
3272
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.
3273
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.
3274
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.
3275
- - 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.
3276
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.
3277
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.
3278
3331
  - The panel is `hidden lg:flex`: on a phone the form is the whole screen, with no decorative scroll above it.
3279
3332
 
3280
3333
  ## Accessibility
3281
3334
 
3282
- - 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".
3283
3336
  - Field auto-wires labels, describedby, and invalid state onto the inputs; on submit, the first invalid field is focused.
3284
3337
  - Autocomplete attributes (`email webauthn`, `current-password`) keep password managers and passkey autofill working.
3285
3338
  - The Forgot password and Create one links use `hit-target` to meet the 44px touch floor without changing layout.
@@ -3310,7 +3363,7 @@ export function AuthPage() {
3310
3363
  <Stack gap={1.5}>
3311
3364
  <h1 className="text-heading-1">Welcome back</h1>
3312
3365
  <p className="text-body-lg text-muted-foreground">
3313
- Sign in to collect new releases and manage your editions.
3366
+ Sign in to pick up where you and your team left off.
3314
3367
  </p>
3315
3368
  </Stack>
3316
3369
 
@@ -3348,8 +3401,8 @@ export function AuthPage() {
3348
3401
  <Button variant="outline" size="cta" className="w-full" iconStart={<Icon name="key" />}>
3349
3402
  Sign in with a passkey
3350
3403
  </Button>
3351
- <Button variant="outline" size="cta" className="w-full" iconStart={<Icon name="wallet" />}>
3352
- Connect a wallet
3404
+ <Button variant="outline" size="cta" className="w-full" iconStart={<Icon name="mail" />}>
3405
+ Email me a sign-in link
3353
3406
  </Button>
3354
3407
  </Stack>
3355
3408
 
@@ -3376,23 +3429,23 @@ export function AuthPage() {
3376
3429
  </Region>
3377
3430
 
3378
3431
  {/* The brand panel is decoration: on a phone the form is the whole screen. */}
3379
- <aside aria-label="Featured release" className="hidden p-3 lg:flex">
3432
+ <aside aria-label="Customer story" className="hidden p-3 lg:flex">
3380
3433
  {/* Art painted from the theme's tones over its brand, so it follows every look. */}
3381
3434
  <GradientArt seed="Untitled (gold rush)" className="flex flex-1 flex-col justify-end rounded-xl p-8">
3382
3435
  <Card className="max-w-md">
3383
3436
  <CardContent>
3384
3437
  <Stack gap={3}>
3385
- <Badge tone="edition" className="self-start">
3386
- Edition of 50
3438
+ <Badge tone="1" className="self-start">
3439
+ Customer story
3387
3440
  </Badge>
3388
3441
  <p className="text-title-lg">
3389
- “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.”
3390
3443
  </p>
3391
3444
  <Entity
3392
3445
  className="p-0"
3393
3446
  leading={<Avatar variant="art" fallback="MO" />}
3394
3447
  title="Mara Okafor"
3395
- subtitle="Untitled (gold rush), 2026"
3448
+ subtitle="Head of Design, Northwind"
3396
3449
  />
3397
3450
  </Stack>
3398
3451
  </CardContent>
@@ -3453,7 +3506,7 @@ const spaces = [
3453
3506
 
3454
3507
  const routes = [
3455
3508
  { label: "Intake", state: "Open", tone: "info" as const },
3456
- { label: "Review", state: "Live", tone: "edition" as const },
3509
+ { label: "Review", state: "Live", tone: "1" as const },
3457
3510
  { label: "Archive", state: "Safe", tone: "success" as const },
3458
3511
  ]
3459
3512
 
@@ -3631,7 +3684,7 @@ export function BrandLandingPage() {
3631
3684
  <div className="flex items-center justify-between border-b border-border px-4 py-3">
3632
3685
  <Row gap={1}>
3633
3686
  <StatusDot tone="warning" />
3634
- <StatusDot tone="edition" />
3687
+ <StatusDot tone="1" />
3635
3688
  <StatusDot tone="success" />
3636
3689
  </Row>
3637
3690
  <span className="text-label-sm text-muted-foreground">Live workspace</span>
@@ -4025,7 +4078,7 @@ export function ChatPanel() {
4025
4078
  render={<section aria-labelledby="chat-title" />}
4026
4079
  className="flex h-[calc(100dvh_-_--spacing(12))] flex-col overflow-hidden"
4027
4080
  >
4028
- <CardHeader className="border-b border-border pb-(--card-p)">
4081
+ <CardHeader className="pb-(--card-p) shadow-[inset_0_-1px_0_var(--border)]">
4029
4082
  {/* CardTitle carries the type; the h1 inside it is the page heading. */}
4030
4083
  <CardTitle>
4031
4084
  <h1 id="chat-title">Scroll behaviour</h1>
@@ -4164,7 +4217,7 @@ export function ChatPanel() {
4164
4217
  </Message>
4165
4218
  </MessageScroller>
4166
4219
 
4167
- <CardFooter className="border-t border-border pt-(--card-p)">
4220
+ <CardFooter className="pt-(--card-p) shadow-[inset_0_1px_0_var(--border)]">
4168
4221
  <ChatComposer
4169
4222
  className="flex-1"
4170
4223
  placeholder="Ask a follow-up…"
@@ -4224,18 +4277,18 @@ export function ChatPanel() {
4224
4277
 
4225
4278
  # DashboardPage
4226
4279
 
4227
- 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.
4228
4281
 
4229
- **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).
4230
4283
 
4231
4284
  ## Notes
4232
4285
 
4233
4286
  - Region cap is `wide` (1280px): dashboards hold tables and multi-column grids, never a reading column.
4234
- - 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.
4235
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.
4236
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.
4237
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.
4238
- - 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.
4239
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`.
4240
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.
4241
4294
  - The page has exactly one heading-1 (PageHeader); card titles are Title tier, not headings.
@@ -4247,7 +4300,7 @@ App-shell creator dashboard: a branded Sidebar with an account footer (AppHeader
4247
4300
  - AppShell provides the skip link and <main id="main-content"> landmark.
4248
4301
  - The active SidebarItem and BottomNavItem carry aria-current via `active`.
4249
4302
  - DataTable headers are real <th>; sortable columns are announced.
4250
- - 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".
4251
4304
  - Avatars beside a visible name use an empty alt so the name is not read twice.
4252
4305
 
4253
4306
  ## Source
@@ -4255,11 +4308,11 @@ App-shell creator dashboard: a branded Sidebar with an account footer (AppHeader
4255
4308
  ```tsx
4256
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"
4257
4310
 
4258
- // 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;
4259
4312
  // an app maps ITS states onto tones once, here, and every badge follows.
4260
- const STATUS_TONE = { live: "success", scheduled: "info", "sold-out": "edition", closed: "neutral" } as const
4261
- const STATUS_LABEL = { live: "Live", scheduled: "Scheduled", "sold-out": "Sold out", closed: "Closed" } as const
4262
- 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
4263
4316
 
4264
4317
  const MIN = 60 * 1000
4265
4318
  const HOUR = 60 * MIN
@@ -4272,54 +4325,54 @@ const shortDate = (offset: number) =>
4272
4325
 
4273
4326
  const stats = [
4274
4327
  { label: "Revenue", value: "$18,240", delta: "+8.2%", up: true, note: "vs. $16,857" },
4275
- { label: "Collects", value: "1,284", delta: "+3.1%", up: true, note: "182 first-time" },
4276
- { label: "Collectors", value: "312", delta: "+12.4%", up: true, note: "Best month yet" },
4277
- { 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" },
4278
4331
  ]
4279
4332
 
4280
- const editions = [
4281
- { id: "ed_01", title: "Gold Rush", status: "live" as const, sold: 184, supply: 250, price: 48, released: -6 * DAY },
4282
- { id: "ed_02", title: "Quiet Harbor", status: "sold-out" as const, sold: 100, supply: 100, price: 120, released: -19 * DAY },
4283
- { id: "ed_03", title: "Dusk, no. 4", status: "live" as const, sold: 61, supply: 150, price: 64, released: -2 * DAY },
4284
- { id: "ed_04", title: "Tidewater", status: "live" as const, sold: 212, supply: 300, price: 36, released: -27 * DAY },
4285
- { id: "ed_05", title: "Salt Study", status: "closed" as const, sold: 38, supply: 75, price: 90, released: -44 * DAY },
4286
- { id: "ed_06", title: "Paper Moon", status: "sold-out" as const, sold: 50, supply: 50, price: 220, released: -58 * DAY },
4287
- { id: "ed_07", title: "Night Signal", status: "scheduled" as const, sold: 0, supply: 200, price: 56, released: 3 * DAY },
4288
- { id: "ed_08", title: "Low Tide, Blue", status: "closed" as const, sold: 72, supply: 120, price: 42, released: -71 * DAY },
4289
- { 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 },
4290
4343
  ]
4291
- const editionCount = 24
4344
+ const accountCount = 312
4292
4345
 
4293
4346
  const payout = {
4294
- amount: 8214.6,
4347
+ amount: 8862.64,
4295
4348
  date: 5 * DAY,
4296
4349
  lines: [
4297
- { label: "Sales", value: 9127.33 },
4298
- { label: "Platform fee (10%)", value: -912.73 },
4350
+ { label: "Subscriptions", value: 9127.33 },
4351
+ { label: "Processing (2.9%)", value: -264.69 },
4299
4352
  { label: "Deposits to", value: "Checking ••4821" },
4300
4353
  ],
4301
4354
  }
4302
4355
 
4303
- const collectors = [
4304
- { id: "co_01", name: "Nina Simone", handle: "@nina", initials: "NS", pieces: 14, spent: 1460 },
4305
- { id: "co_02", name: "Grace Hopper", handle: "@grace", initials: "GH", pieces: 11, spent: 1128 },
4306
- { id: "co_03", name: "Frida Kahlo", handle: "@frida", initials: "FK", pieces: 9, spent: 942 },
4307
- { 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 },
4308
4361
  ]
4309
4362
 
4310
4363
  const upcoming = [
4311
- { id: "up_01", title: "Night Signal", detail: "Edition of 200 · $56", at: 3 * DAY },
4312
- { id: "up_02", title: "Tidewater, second printing", detail: "Edition of 100 · $40", at: 12 * DAY },
4313
- { id: "up_03", title: "Dusk, no. 5", detail: "Edition of 150 · $64", at: 26 * DAY },
4314
- { 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 },
4315
4368
  ]
4316
4369
 
4317
4370
  const activity = [
4318
- { id: "ac_01", who: "Nina Simone", initials: "NS", what: "collected Gold Rush · 185 of 250", when: 2 * MIN },
4319
- { id: "ac_02", who: "Grace Hopper", initials: "GH", what: "collected Dusk, no. 4 · 61 of 150", when: 26 * MIN },
4320
- { id: "ac_03", who: "Frida Kahlo", initials: "FK", what: "tipped $20 on Tidewater", when: 1 * HOUR },
4321
- { id: "ac_04", who: "Mary Shelley", initials: "MS", what: "started following you", when: 3 * HOUR },
4322
- { 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 },
4323
4376
  ]
4324
4377
 
4325
4378
  export function DashboardPage() {
@@ -4341,8 +4394,8 @@ export function DashboardPage() {
4341
4394
  bottomNav={
4342
4395
  <BottomNav aria-label="Primary">
4343
4396
  <BottomNavItem icon={<Icon name="grid" />} label="Overview" showLabel active render={<a href="/" />} />
4344
- <BottomNavItem icon={<Icon name="image-stack" />} label="Editions" showLabel render={<a href="/editions" />} />
4345
- <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" />} />
4346
4399
  <BottomNavItem icon={<Icon name="bell" />} label="Activity" showLabel render={<a href="/activity" />} />
4347
4400
  <BottomNavItem icon={<Icon name="gear" />} label="Settings" showLabel render={<a href="/settings" />} />
4348
4401
  </BottomNav>
@@ -4354,8 +4407,8 @@ export function DashboardPage() {
4354
4407
  </SidebarHeader>
4355
4408
  <SidebarNav>
4356
4409
  <SidebarItem icon={<Icon name="grid" />} label="Overview" active render={<a href="/" />} />
4357
- <SidebarItem icon={<Icon name="image-stack" />} label="Editions" render={<a href="/editions" />} />
4358
- <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" />} />
4359
4412
  <SidebarItem
4360
4413
  icon={<Icon name="bell" />}
4361
4414
  label="Activity"
@@ -4386,7 +4439,7 @@ export function DashboardPage() {
4386
4439
  <Page max="wide" gap={4} className="py-6">
4387
4440
  <PageHeader
4388
4441
  title="Overview"
4389
- description="Sales and collector activity for the last 30 days."
4442
+ description="Revenue and account activity for the last 30 days."
4390
4443
  actions={
4391
4444
  <Row gap={2}>
4392
4445
  <Button variant="outline">
@@ -4395,7 +4448,7 @@ export function DashboardPage() {
4395
4448
  </Button>
4396
4449
  <Button>
4397
4450
  <Icon name="plus" />
4398
- New edition
4451
+ New account
4399
4452
  </Button>
4400
4453
  </Row>
4401
4454
  }
@@ -4425,43 +4478,43 @@ export function DashboardPage() {
4425
4478
  <Grid cols={{ base: 1, xl: 3 }} gap={3}>
4426
4479
  <Card className="flex flex-col xl:col-span-2">
4427
4480
  <CardHeader>
4428
- <CardTitle>Editions</CardTitle>
4429
- <CardDescription>Sales across your releases, newest first.</CardDescription>
4481
+ <CardTitle>Accounts</CardTitle>
4482
+ <CardDescription>Seats across your customers, newest first.</CardDescription>
4430
4483
  </CardHeader>
4431
4484
  <CardContent flush className="mt-4 flex-1">
4432
4485
  <DataTable
4433
- data={editions}
4486
+ data={accounts}
4434
4487
  rowKey={(row) => row.id}
4435
- defaultSort={{ key: "released", direction: "desc" }}
4488
+ defaultSort={{ key: "started", direction: "desc" }}
4436
4489
  columns={[
4437
- { 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" },
4438
4491
  {
4439
4492
  key: "status",
4440
4493
  header: "Status",
4441
4494
  cell: (row) => (
4442
- <Badge appearance="plain" tone={STATUS_TONE[row.status as EditionStatus]} dot>
4443
- {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]}
4444
4497
  </Badge>
4445
4498
  ),
4446
4499
  },
4447
4500
  {
4448
- key: "sold",
4449
- header: "Sold",
4501
+ key: "used",
4502
+ header: "Seats",
4450
4503
  sortable: true,
4451
- sortValue: (row) => row.sold / row.supply,
4504
+ sortValue: (row) => row.used / row.seats,
4452
4505
  cell: (row) => (
4453
4506
  <Row gap={3} align="center" className="min-w-40">
4454
4507
  <Meter
4455
- value={row.sold}
4456
- max={row.supply}
4508
+ value={row.used}
4509
+ max={row.seats}
4457
4510
  size="sm"
4458
- tone={row.sold === row.supply ? "success" : "primary"}
4459
- aria-label={`${row.title} sold`}
4460
- 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`}
4461
4514
  className="flex-1"
4462
4515
  />
4463
4516
  <span className="text-mono-caption whitespace-nowrap text-muted-foreground">
4464
- {row.sold}/{row.supply}
4517
+ {row.used}/{row.seats}
4465
4518
  </span>
4466
4519
  </Row>
4467
4520
  ),
@@ -4471,21 +4524,21 @@ export function DashboardPage() {
4471
4524
  header: "Revenue",
4472
4525
  align: "end",
4473
4526
  sortable: true,
4474
- sortValue: (row) => row.sold * row.price,
4527
+ sortValue: (row) => row.used * row.price,
4475
4528
  cell: (row) =>
4476
- row.sold > 0 ? (
4477
- <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>
4478
4531
  ) : (
4479
4532
  <span className="text-mono-sm text-muted-foreground">—</span>
4480
4533
  ),
4481
4534
  },
4482
4535
  {
4483
- key: "released",
4484
- header: "Release",
4536
+ key: "started",
4537
+ header: "Started",
4485
4538
  align: "end",
4486
4539
  sortable: true,
4487
4540
  cell: (row) => (
4488
- <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>
4489
4542
  ),
4490
4543
  },
4491
4544
  ]}
@@ -4493,10 +4546,10 @@ export function DashboardPage() {
4493
4546
  </CardContent>
4494
4547
  <CardFooter className="justify-between">
4495
4548
  <span className="text-caption text-muted-foreground">
4496
- Showing {editions.length} of {editionCount} editions
4549
+ Showing {accounts.length} of {accountCount} accounts
4497
4550
  </span>
4498
- <Button variant="ghost" size="sm" bleed="end" render={<a href="/editions" />}>
4499
- View all editions
4551
+ <Button variant="ghost" size="sm" bleed="end" render={<a href="/accounts" />}>
4552
+ View all accounts
4500
4553
  <Icon name="arrow-right" />
4501
4554
  </Button>
4502
4555
  </CardFooter>
@@ -4527,21 +4580,21 @@ export function DashboardPage() {
4527
4580
  </Card>
4528
4581
  <Card>
4529
4582
  <CardHeader>
4530
- <CardTitle>Top collectors</CardTitle>
4583
+ <CardTitle>Top customers</CardTitle>
4531
4584
  <CardDescription>By spend this month.</CardDescription>
4532
4585
  <CardAction>
4533
- <Button variant="ghost" size="sm" render={<a href="/collectors" />}>
4586
+ <Button variant="ghost" size="sm" render={<a href="/customers" />}>
4534
4587
  View all
4535
4588
  </Button>
4536
4589
  </CardAction>
4537
4590
  </CardHeader>
4538
4591
  <CardList>
4539
- {collectors.map((person) => (
4592
+ {customers.map((person) => (
4540
4593
  <Entity
4541
4594
  key={person.id}
4542
4595
  leading={<Avatar variant="art" size="sm" fallback={person.initials} alt="" />}
4543
4596
  title={person.name}
4544
- subtitle={`${person.handle} · ${person.pieces} pieces`}
4597
+ subtitle={`${person.handle} · ${person.seats} seats`}
4545
4598
  action={<span className="text-mono-sm">{usd.format(person.spent)}</span>}
4546
4599
  />
4547
4600
  ))}
@@ -4553,7 +4606,7 @@ export function DashboardPage() {
4553
4606
  <Card className="flex flex-col">
4554
4607
  <CardHeader>
4555
4608
  <CardTitle>Upcoming releases</CardTitle>
4556
- <CardDescription>Scheduled to open to collectors.</CardDescription>
4609
+ <CardDescription>Scheduled to ship to customers.</CardDescription>
4557
4610
  </CardHeader>
4558
4611
  <CardList>
4559
4612
  {upcoming.map((release) => (
@@ -4566,7 +4619,7 @@ export function DashboardPage() {
4566
4619
  ))}
4567
4620
  </CardList>
4568
4621
  <CardFooter className="mt-auto">
4569
- <Button variant="outline" size="sm" render={<a href="/editions/new" />}>
4622
+ <Button variant="outline" size="sm" render={<a href="/releases/new" />}>
4570
4623
  <Icon name="calendar" />
4571
4624
  Schedule a release
4572
4625
  </Button>
@@ -4575,7 +4628,7 @@ export function DashboardPage() {
4575
4628
  <Card className="xl:col-span-2">
4576
4629
  <CardHeader>
4577
4630
  <CardTitle>Activity</CardTitle>
4578
- <CardDescription>Collects, tips, and follows.</CardDescription>
4631
+ <CardDescription>Seats, upgrades, and invoices.</CardDescription>
4579
4632
  <CardAction>
4580
4633
  <Button variant="ghost" size="sm" render={<a href="/activity" />}>
4581
4634
  View all
@@ -4605,7 +4658,7 @@ export function DashboardPage() {
4605
4658
 
4606
4659
  # FeedPage
4607
4660
 
4608
- 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.
4609
4662
 
4610
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).
4611
4664
 
@@ -4615,7 +4668,7 @@ Social reading feed with a side rail: a feed-width main column (composer, For yo
4615
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.
4616
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.
4617
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.
4618
- - 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.
4619
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.
4620
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.
4621
4674
 
@@ -4624,7 +4677,7 @@ Social reading feed with a side rail: a feed-width main column (composer, For yo
4624
4677
  - Each post is an <article>, so AT users can jump between posts; the rail is a labelled <aside> landmark.
4625
4678
  - Rail card titles render as h2 (CardTitle `render={<h2 />}`) under the page's one h1.
4626
4679
  - Icon-only buttons and every Follow / Remind button carry aria-labels naming their target; counts stay in the accessible name.
4627
- - 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").
4628
4681
  - The Skeleton card is aria-busy; the loading Button keeps its label ("Loading posts").
4629
4682
 
4630
4683
  ## Source
@@ -4641,7 +4694,7 @@ type Post = {
4641
4694
  following: boolean
4642
4695
  body: string
4643
4696
  media?: { src: string; alt: string }
4644
- drop?: { title: string; price: string; collected: number; supply: number }
4697
+ event?: { title: string; price: string; registered: number; capacity: number }
4645
4698
  likes: number
4646
4699
  comments: number
4647
4700
  }
@@ -4654,9 +4707,9 @@ const posts: Post[] = [
4654
4707
  initials: "AL",
4655
4708
  time: "2h",
4656
4709
  following: true,
4657
- body: "Gold Rush is live. 100 signed prints, each paired with the on-chain original. Thank you for waiting.",
4658
- media: { src: "/media/edition-cover.svg", alt: "Gold Rush, a gold-leaf study in overlapping arcs" },
4659
- 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 },
4660
4713
  likes: 128,
4661
4714
  comments: 24,
4662
4715
  },
@@ -4679,7 +4732,7 @@ const posts: Post[] = [
4679
4732
  initials: "NS",
4680
4733
  time: "8h",
4681
4734
  following: false,
4682
- 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.",
4683
4736
  likes: 214,
4684
4737
  comments: 31,
4685
4738
  },
@@ -4691,8 +4744,8 @@ const suggestions = [
4691
4744
  ]
4692
4745
 
4693
4746
  const upcoming = [
4694
- { id: "d_01", title: "Quiet Harbor", artist: "Mary Shelley", initials: "MS", date: "Sat, Oct 12" },
4695
- { 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" },
4696
4749
  ]
4697
4750
 
4698
4751
  const renderPost = (post: Post) => (
@@ -4718,35 +4771,35 @@ const renderPost = (post: Post) => (
4718
4771
  {post.media ? (
4719
4772
  <AspectBox ratio={16 / 9} className="overflow-hidden rounded-concentric-lg/6 bg-muted">
4720
4773
  <img src={post.media.src} alt={post.media.alt} className="size-full object-cover" />
4721
- {post.drop ? (
4774
+ {post.event ? (
4722
4775
  <div className="absolute start-3 top-3">
4723
4776
  <Badge appearance="overlay" size="sm" dot>
4724
- Live drop
4777
+ Open now
4725
4778
  </Badge>
4726
4779
  </div>
4727
4780
  ) : null}
4728
4781
  </AspectBox>
4729
4782
  ) : null}
4730
- {post.drop ? (
4783
+ {post.event ? (
4731
4784
  <Stack gap={1.5}>
4732
4785
  <Row gap={2} align="center" justify="between">
4733
4786
  <div className="min-w-0">
4734
- <p className="text-title-md text-foreground">{post.drop.title}</p>
4787
+ <p className="text-title-md text-foreground">{post.event.title}</p>
4735
4788
  <p className="text-body-sm text-muted-foreground">
4736
- {`${post.drop.price} · Edition of ${post.drop.supply}`}
4789
+ {`${post.event.price} · ${post.event.capacity} seats`}
4737
4790
  </p>
4738
4791
  </div>
4739
- <Button>{`Collect for ${post.drop.price}`}</Button>
4792
+ <Button>{`Register for ${post.event.price}`}</Button>
4740
4793
  </Row>
4741
4794
  <Meter
4742
- value={post.drop.collected}
4743
- max={post.drop.supply}
4795
+ value={post.event.registered}
4796
+ max={post.event.capacity}
4744
4797
  size="sm"
4745
- getAriaValueText={(_, value) => `${value} of ${post.drop?.supply} collected`}
4798
+ getAriaValueText={(_, value) => `${value} of ${post.event?.capacity} seats taken`}
4746
4799
  >
4747
4800
  <Row gap={2} align="center" justify="between" className="mb-2">
4748
- <MeterLabel>{`${post.drop.collected} collected`}</MeterLabel>
4749
- <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>
4750
4803
  </Row>
4751
4804
  <MeterTrack />
4752
4805
  </Meter>
@@ -4787,15 +4840,15 @@ export function FeedPage() {
4787
4840
  autoResize
4788
4841
  rows={2}
4789
4842
  aria-label="New post"
4790
- placeholder="Share a process shot or an upcoming drop"
4843
+ placeholder="Share a process shot or an upcoming event"
4791
4844
  />
4792
4845
  <Row gap={1} align="center" justify="between">
4793
4846
  <Row gap={0.5}>
4794
4847
  <Button variant="ghost" size="icon-sm" aria-label="Add images">
4795
4848
  <Icon name="image-stack" />
4796
4849
  </Button>
4797
- <Button variant="ghost" size="icon-sm" aria-label="Attach an edition">
4798
- <Icon name="gem" />
4850
+ <Button variant="ghost" size="icon-sm" aria-label="Add an event">
4851
+ <Icon name="calendar" />
4799
4852
  </Button>
4800
4853
  </Row>
4801
4854
  <Button variant="secondary" size="sm">
@@ -4862,17 +4915,17 @@ export function FeedPage() {
4862
4915
  </Card>
4863
4916
  <Card>
4864
4917
  <CardHeader>
4865
- <CardTitle render={<h2 />}>Upcoming drops</CardTitle>
4918
+ <CardTitle render={<h2 />}>Upcoming events</CardTitle>
4866
4919
  </CardHeader>
4867
4920
  <CardList>
4868
- {upcoming.map((drop) => (
4921
+ {upcoming.map((event) => (
4869
4922
  <Entity
4870
- key={drop.id}
4871
- leading={<Avatar variant="art" fallback={drop.initials} alt={drop.artist} />}
4872
- title={drop.title}
4873
- 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}`}
4874
4927
  action={
4875
- <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}`}>
4876
4929
  <Icon name="bell" />
4877
4930
  </Button>
4878
4931
  }
@@ -5177,7 +5230,7 @@ export function LandingPage() {
5177
5230
  <CardDescription>Team plan</CardDescription>
5178
5231
  </CardHeader>
5179
5232
  <CardContent>
5180
- <Meter value={64} tone="standard">
5233
+ <Meter value={64} tone="3">
5181
5234
  <Row justify="between" wrap className="mb-2">
5182
5235
  <MeterLabel>Used</MeterLabel>
5183
5236
  <MeterValue>{(_, value) => `${(value * 30).toLocaleString("en-US")} of 3,000`}</MeterValue>
@@ -5460,9 +5513,9 @@ const roleLabel: Record<Role, string> = {
5460
5513
 
5461
5514
  const roleSummary: Record<Role, string> = {
5462
5515
  owner: "Full access, including payouts and billing. Transfer ownership in studio settings.",
5463
- admin: "Manages members, editions and payouts.",
5464
- editor: "Drafts, prices and releases editions.",
5465
- 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.",
5466
5519
  }
5467
5520
 
5468
5521
  type Activity = { id: string; icon: string; text: string; when: string }
@@ -5477,7 +5530,7 @@ type Member = {
5477
5530
  handle?: string
5478
5531
  joined?: string
5479
5532
  lastActive?: string
5480
- editions?: number
5533
+ projects?: number
5481
5534
  invitedBy?: string
5482
5535
  sent?: string
5483
5536
  expires?: string
@@ -5490,40 +5543,40 @@ const seats = 10
5490
5543
  const initialMembers: Member[] = [
5491
5544
  {
5492
5545
  id: "u_01", name: "Ada Lovelace", handle: "@ada", email: "ada@atelier.art", initials: "AL", role: "owner", pending: false,
5493
- joined: "Mar 4, 2024", lastActive: "Online now", editions: 12,
5546
+ joined: "Mar 4, 2024", lastActive: "Online now", projects: 12,
5494
5547
  activity: [
5495
- { 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" },
5496
5549
  { id: "a2", icon: "mail", text: "Invited louise@bourgeois.studio as Editor", when: "2 days ago" },
5497
5550
  ],
5498
5551
  },
5499
5552
  {
5500
5553
  id: "u_02", name: "Grace Hopper", handle: "@grace", email: "grace@atelier.art", initials: "GH", role: "admin", pending: false,
5501
- joined: "Mar 9, 2024", lastActive: "20 minutes ago", editions: 4,
5554
+ joined: "Mar 9, 2024", lastActive: "20 minutes ago", projects: 4,
5502
5555
  activity: [
5503
5556
  { id: "a1", icon: "user", text: "Changed Frida Kahlo's role to Viewer", when: "Yesterday" },
5504
- { id: "a2", icon: "edit", text: "Updated pricing on First Compiler — edition of 10", when: "3 days ago" },
5505
- { 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" },
5506
5559
  ],
5507
5560
  },
5508
5561
  {
5509
5562
  id: "u_03", name: "Mary Shelley", handle: "@mary", email: "mary@atelier.art", initials: "MS", role: "editor", pending: false,
5510
- joined: "Jun 18, 2024", lastActive: "3 hours ago", editions: 7,
5563
+ joined: "Jun 18, 2024", lastActive: "3 hours ago", projects: 7,
5511
5564
  activity: [
5512
5565
  { id: "a1", icon: "calendar", text: "Scheduled Frankenstein Studies for Oct 14", when: "3 hours ago" },
5513
- { 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" },
5514
5567
  ],
5515
5568
  },
5516
5569
  {
5517
5570
  id: "u_04", name: "Nina Simone", handle: "@nina", email: "nina@atelier.art", initials: "NS", role: "editor", pending: false,
5518
- joined: "Jan 12, 2025", lastActive: "Yesterday", editions: 3,
5571
+ joined: "Jan 12, 2025", lastActive: "Yesterday", projects: 3,
5519
5572
  activity: [
5520
- { id: "a1", icon: "upload", text: "Uploaded artwork for Little Girl Blue", when: "Yesterday" },
5521
- { 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" },
5522
5575
  ],
5523
5576
  },
5524
5577
  {
5525
5578
  id: "u_05", name: "Frida Kahlo", handle: "@frida", email: "frida@atelier.art", initials: "FK", role: "viewer", pending: false,
5526
- joined: "Jul 2, 2025", lastActive: "4 days ago", editions: 0,
5579
+ joined: "Jul 2, 2025", lastActive: "4 days ago", projects: 0,
5527
5580
  activity: [{ id: "a1", icon: "comment", text: "Commented on the Las Dos Fridas draft", when: "4 days ago" }],
5528
5581
  },
5529
5582
  {
@@ -5756,8 +5809,8 @@ export function ListDetailPage() {
5756
5809
  <dd className={selected.lastActive === "Online now" ? "text-success" : undefined}>
5757
5810
  {selected.lastActive}
5758
5811
  </dd>
5759
- <dt>Editions released</dt>
5760
- <dd>{selected.editions}</dd>
5812
+ <dt>Projects owned</dt>
5813
+ <dd>{selected.projects}</dd>
5761
5814
  </dl>
5762
5815
  )}
5763
5816
  </div>
@@ -5810,7 +5863,7 @@ export function ListDetailPage() {
5810
5863
  description={
5811
5864
  selected.pending
5812
5865
  ? `${selected.email} won't be able to join with the link they were sent.`
5813
- : "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."
5814
5867
  }
5815
5868
  confirmLabel={selected.pending ? "Revoke invite" : "Remove member"}
5816
5869
  onConfirm={() => remove(selected.id)}
@@ -5875,17 +5928,17 @@ const timezones = [
5875
5928
  ]
5876
5929
 
5877
5930
  const notifications = [
5878
- { name: "notifyCollects", title: "New collects", description: "When someone collects your work", on: true },
5879
- { name: "notifySoldOut", title: "Sold out", description: "When an edition sells its last copy", on: true },
5880
- { name: "notifyFollowers", title: "New followers", description: "When a collector follows you", on: false },
5881
- { 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 },
5882
5935
  { name: "notifyProduct", title: "Product updates", description: "New Atelier features", on: false },
5883
5936
  ]
5884
5937
 
5885
5938
  const payouts = [
5886
5939
  { label: "Next payout", value: "$1,284.60", note: "Friday, Oct 16" },
5887
5940
  { label: "Pending", value: "$412.00", note: "Clears in 7 days" },
5888
- { 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" },
5889
5942
  ]
5890
5943
 
5891
5944
  export function SettingsPage() {
@@ -5910,8 +5963,8 @@ export function SettingsPage() {
5910
5963
  bottomNav={
5911
5964
  <BottomNav aria-label="Primary">
5912
5965
  <BottomNavItem icon={<Icon name="grid" />} label="Overview" showLabel render={<a href="/" />} />
5913
- <BottomNavItem icon={<Icon name="image-stack" />} label="Editions" showLabel render={<a href="/editions" />} />
5914
- <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" />} />
5915
5968
  <BottomNavItem icon={<Icon name="bell" />} label="Activity" showLabel render={<a href="/activity" />} />
5916
5969
  <BottomNavItem icon={<Icon name="gear" />} label="Settings" showLabel active render={<a href="/settings" />} />
5917
5970
  </BottomNav>
@@ -5923,8 +5976,8 @@ export function SettingsPage() {
5923
5976
  </SidebarHeader>
5924
5977
  <SidebarNav>
5925
5978
  <SidebarItem icon={<Icon name="grid" />} label="Overview" render={<a href="/" />} />
5926
- <SidebarItem icon={<Icon name="image-stack" />} label="Editions" render={<a href="/editions" />} />
5927
- <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" />} />
5928
5981
  <SidebarItem
5929
5982
  icon={<Icon name="bell" />}
5930
5983
  label="Activity"
@@ -5978,7 +6031,7 @@ export function SettingsPage() {
5978
6031
  <Card id="profile" className="scroll-mt-6">
5979
6032
  <CardHeader>
5980
6033
  <CardTitle render={<h2 />}>Profile</CardTitle>
5981
- <CardDescription>How collectors see you on Atelier.</CardDescription>
6034
+ <CardDescription>How your team sees you on Atelier.</CardDescription>
5982
6035
  </CardHeader>
5983
6036
  {/* The one explicit save on the page: profile text is drafted, then
5984
6037
  committed. Toggles below apply the moment they change. */}
@@ -6009,14 +6062,14 @@ export function SettingsPage() {
6009
6062
  <Input defaultValue="ada" autoComplete="username" spellCheck={false} />
6010
6063
  </Field>
6011
6064
  </Grid>
6012
- <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.">
6013
6066
  <Textarea
6014
6067
  rows={3}
6015
- 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."
6016
6069
  />
6017
6070
  </Field>
6018
6071
  <Grid cols={{ base: 1, sm: 2 }} gap={3}>
6019
- <Field label="Timezone" name="timezone" description="Release times use this zone.">
6072
+ <Field label="Timezone" name="timezone" description="Due dates use this zone.">
6020
6073
  <Select defaultValue="europe-london" items={timezones}>
6021
6074
  <SelectTrigger />
6022
6075
  <SelectContent>
@@ -6041,7 +6094,7 @@ export function SettingsPage() {
6041
6094
  <CardHeader>
6042
6095
  <CardTitle render={<h2 />}>Notifications</CardTitle>
6043
6096
  <CardDescription>
6044
- Email updates about your editions. Changes save automatically.
6097
+ Email updates about your projects. Changes save automatically.
6045
6098
  </CardDescription>
6046
6099
  </CardHeader>
6047
6100
  <CardList>
@@ -6104,7 +6157,7 @@ export function SettingsPage() {
6104
6157
  <Entity
6105
6158
  leading={<Icon name="download" className="text-lg text-muted-foreground" />}
6106
6159
  title="Export data"
6107
- subtitle="Editions, sales, and collectors"
6160
+ subtitle="Projects, invoices, and members"
6108
6161
  action={
6109
6162
  <Button variant="outline" size="sm">
6110
6163
  Export
@@ -6118,8 +6171,8 @@ export function SettingsPage() {
6118
6171
  <CardHeader>
6119
6172
  <CardTitle render={<h2 />}>Delete account</CardTitle>
6120
6173
  <CardDescription>
6121
- Permanently remove your profile, editions, and sales history. Collectors keep
6122
- the editions they own.
6174
+ Permanently remove your profile, projects, and billing history. Files you shared
6175
+ stay with your team.
6123
6176
  </CardDescription>
6124
6177
  </CardHeader>
6125
6178
  <CardFooter className="justify-end">
@@ -6136,7 +6189,7 @@ export function SettingsPage() {
6136
6189
  open={deleteOpen}
6137
6190
  onOpenChange={setDeleteOpen}
6138
6191
  title="Delete your account?"
6139
- 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."
6140
6193
  confirmLabel="Delete account"
6141
6194
  confirmingLabel="Deleting…"
6142
6195
  confirmText="ada"
@@ -6413,7 +6466,7 @@ A delete-account card: what deleting removes and what survives, one destructive
6413
6466
 
6414
6467
  - Copied from the SettingsPage page shell's Delete account section.
6415
6468
  - The button never acts directly: it opens the modal, whose confirm stays disabled until the handle is typed.
6416
- - 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.
6417
6470
 
6418
6471
  ## Accessibility
6419
6472
 
@@ -6426,7 +6479,7 @@ A delete-account card: what deleting removes and what survives, one destructive
6426
6479
  import { useState } from "react"
6427
6480
  import { Button, Card, CardDescription, CardFooter, CardHeader, CardTitle, cn, DestructiveActionModal } from "@sableui/react"
6428
6481
 
6429
- const account = { handle: "ada", editions: 12 }
6482
+ const account = { handle: "ada", projects: 12 }
6430
6483
 
6431
6484
  export interface DangerZoneBlockProps {
6432
6485
  className?: string
@@ -6444,7 +6497,7 @@ export function DangerZoneBlock({ className, framed = true, onDelete }: DangerZo
6444
6497
  <CardHeader>
6445
6498
  <CardTitle render={<h2 />}>Delete account</CardTitle>
6446
6499
  <CardDescription>
6447
- 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.
6448
6501
  </CardDescription>
6449
6502
  </CardHeader>
6450
6503
  <CardFooter actions className="justify-end">
@@ -6457,7 +6510,7 @@ export function DangerZoneBlock({ className, framed = true, onDelete }: DangerZo
6457
6510
  open={open}
6458
6511
  onOpenChange={setOpen}
6459
6512
  title="Delete your account?"
6460
- 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.`}
6461
6514
  confirmLabel="Delete account"
6462
6515
  confirmingLabel="Deleting…"
6463
6516
  confirmText={account.handle}
@@ -6686,10 +6739,10 @@ const intro = {
6686
6739
  }
6687
6740
 
6688
6741
  const questions = [
6689
- { 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." },
6690
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." },
6691
- { 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." },
6692
- { 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." },
6693
6746
  ]
6694
6747
 
6695
6748
  export interface FaqBlockProps {
@@ -6902,7 +6955,7 @@ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,
6902
6955
 
6903
6956
  const goals = [
6904
6957
  { goal: "Releases", progress: 82 },
6905
- { goal: "Collectors", progress: 64 },
6958
+ { goal: "Customers", progress: 64 },
6906
6959
  { goal: "Revenue", progress: 91 },
6907
6960
  ]
6908
6961
 
@@ -7466,7 +7519,7 @@ An activity list: Entity rows with the actor's Avatar, who did what, and a compa
7466
7519
  import { Avatar, Card, CardList, cn, Entity, StatusDot } from "@sableui/react"
7467
7520
 
7468
7521
  const notifications = [
7469
- { 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 },
7470
7523
  { initials: "SR", name: "Sam Rivera", what: "left a comment on Dusk", when: "1h", unread: false },
7471
7524
  ]
7472
7525
 
@@ -7743,32 +7796,32 @@ const tiers = [
7743
7796
  {
7744
7797
  id: "starter",
7745
7798
  name: "Starter",
7746
- blurb: "For one person shipping their first editions.",
7799
+ blurb: "For one person getting a first project going.",
7747
7800
  monthly: 0,
7748
7801
  yearly: 0,
7749
7802
  cta: "Start for free",
7750
7803
  featured: false,
7751
- features: ["3 live editions", "Collector profiles", "Community support"],
7804
+ features: ["3 active projects", "Shared docs", "Community support"],
7752
7805
  },
7753
7806
  {
7754
7807
  id: "studio",
7755
7808
  name: "Studio",
7756
- blurb: "For working artists with a steady release schedule.",
7809
+ blurb: "For small teams shipping on a steady schedule.",
7757
7810
  monthly: 20,
7758
7811
  yearly: 16,
7759
7812
  cta: "Start free trial",
7760
7813
  featured: true,
7761
- features: ["Unlimited editions", "Scheduled drops", "Sales analytics", "Priority support"],
7814
+ features: ["Unlimited projects", "Scheduled reports", "Usage analytics", "Priority support"],
7762
7815
  },
7763
7816
  {
7764
- id: "collective",
7765
- name: "Collective",
7766
- blurb: "For galleries and teams releasing together.",
7817
+ id: "business",
7818
+ name: "Business",
7819
+ blurb: "For companies running several teams at once.",
7767
7820
  monthly: 48,
7768
7821
  yearly: 40,
7769
7822
  cta: "Talk to sales",
7770
7823
  featured: false,
7771
- 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"],
7772
7825
  },
7773
7826
  ]
7774
7827
 
@@ -7788,10 +7841,10 @@ export function PricingTableBlock({ className, onChoose }: PricingTableBlockProp
7788
7841
  <Stack gap={10}>
7789
7842
  <Stack gap={4} align="center" className="text-center">
7790
7843
  <h2 id={titleId} className="text-balance text-heading-1">
7791
- Pricing that grows with your practice
7844
+ Pricing that grows with your team
7792
7845
  </h2>
7793
7846
  <p className="max-w-(--measure-prose) text-body-lg text-muted-foreground">
7794
- 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.
7795
7848
  </p>
7796
7849
  <ToggleGroup value={[period]} onValueChange={(v: unknown[]) => v[0] && setPeriod(v[0] as Period)} aria-label="Billing period">
7797
7850
  <Toggle value="monthly">Monthly</Toggle>
@@ -7803,14 +7856,14 @@ export function PricingTableBlock({ className, onChoose }: PricingTableBlockProp
7803
7856
  <Card
7804
7857
  key={tier.id}
7805
7858
  render={<article />}
7806
- 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")}
7807
7860
  >
7808
7861
  <CardHeader>
7809
7862
  <CardTitle render={<h3 />}>{tier.name}</CardTitle>
7810
7863
  <CardDescription>{tier.blurb}</CardDescription>
7811
7864
  {tier.featured ? (
7812
7865
  <CardAction>
7813
- <Badge tone="edition" size="sm">
7866
+ <Badge tone="1" size="sm">
7814
7867
  Most popular
7815
7868
  </Badge>
7816
7869
  </CardAction>
@@ -7848,15 +7901,15 @@ export function PricingTableBlock({ className, onChoose }: PricingTableBlockProp
7848
7901
 
7849
7902
  # ProductCardBlock
7850
7903
 
7851
- 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.
7852
7905
 
7853
- **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.
7854
7907
 
7855
7908
  **Block:** Commerce · card · designed for 16rem–20rem wide (fills its container up to the max)
7856
7909
 
7857
7910
  ## Notes
7858
7911
 
7859
- - 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).
7860
7913
  - Prices are `text-mono-lg` — the metric mono tier with tabular figures — never a heading tier.
7861
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.
7862
7915
 
@@ -7871,7 +7924,7 @@ A product card: edge-to-edge artwork, title and maker, an edition Badge, and the
7871
7924
  import { AspectBox, Badge, Button, Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle, cn, MediaImage } from "@sableui/react"
7872
7925
 
7873
7926
  // `image` is the product shot URL; left empty, it paints art seeded by the title.
7874
- 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" }
7875
7928
 
7876
7929
 
7877
7930
  export interface ProductCardBlockProps {
@@ -7892,12 +7945,12 @@ export function ProductCardBlock({ className, framed = true, onCollect }: Produc
7892
7945
  <CardTitle>{product.title}</CardTitle>
7893
7946
  <CardDescription>{product.maker}</CardDescription>
7894
7947
  <CardAction>
7895
- <Badge tone="edition">{product.edition}</Badge>
7948
+ <Badge tone="1">{product.badge}</Badge>
7896
7949
  </CardAction>
7897
7950
  </CardHeader>
7898
7951
  <CardFooter className="justify-between pt-4">
7899
7952
  <span className="text-mono-lg">{product.price}</span>
7900
- <Button onClick={onCollect}>Collect</Button>
7953
+ <Button onClick={onCollect}>Add to cart</Button>
7901
7954
  </CardFooter>
7902
7955
  </Card>
7903
7956
  )
@@ -7935,7 +7988,7 @@ const profile = {
7935
7988
  name: "Juno Avery",
7936
7989
  initials: "JA",
7937
7990
  handle: "@juno · Sound and light",
7938
- bio: "Field recordings, slowed down. New edition every full moon.",
7991
+ bio: "Field recordings, slowed down. New track every full moon.",
7939
7992
  stats: [
7940
7993
  { value: "128", label: "posts" },
7941
7994
  { value: "4.2k", label: "followers" },
@@ -8027,7 +8080,7 @@ const profile = {
8027
8080
  name: "Ada Lovelace",
8028
8081
  initials: "AL",
8029
8082
  handle: "ada",
8030
- 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.",
8031
8084
  timezone: "europe-london",
8032
8085
  }
8033
8086
 
@@ -8052,7 +8105,7 @@ export function ProfileFormBlock({ className, framed = true, onSave }: ProfileFo
8052
8105
  <Card variant={framed ? "default" : "flat"} className={cn("w-full max-w-2xl", className)}>
8053
8106
  <CardHeader>
8054
8107
  <CardTitle render={<h2 />}>Profile</CardTitle>
8055
- <CardDescription>How collectors see you.</CardDescription>
8108
+ <CardDescription>How your team sees you.</CardDescription>
8056
8109
  </CardHeader>
8057
8110
  {/* The one explicit save: profile text is drafted, then committed. */}
8058
8111
  <Form
@@ -8086,7 +8139,7 @@ export function ProfileFormBlock({ className, framed = true, onSave }: ProfileFo
8086
8139
  <Input defaultValue={profile.handle} autoComplete="username" spellCheck={false} />
8087
8140
  </Field>
8088
8141
  </Grid>
8089
- <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.">
8090
8143
  <Textarea rows={3} defaultValue={profile.bio} />
8091
8144
  </Field>
8092
8145
  <Grid cols={{ base: 1, sm: 2 }} gap={3}>
@@ -8143,10 +8196,10 @@ import { Badge, Card, CardAction, CardContent, CardDescription, CardHeader, cn,
8143
8196
  const revenue = { label: "Revenue this month", total: "$12,480", change: "+8.2%" }
8144
8197
 
8145
8198
  const segments = [
8146
- { label: "Editions", value: 42, tone: "edition" },
8147
- { label: "Collectibles", value: 27, tone: "collectible" },
8148
- { label: "Standard", value: 19, tone: "standard" },
8149
- { 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" },
8150
8203
  ] as const
8151
8204
 
8152
8205
  export interface RevenueBreakdownBlockProps {
@@ -8168,7 +8221,7 @@ export function RevenueBreakdownBlock({ className, framed = true }: RevenueBreak
8168
8221
  </CardHeader>
8169
8222
  <CardContent className="flex flex-col gap-4 pt-2">
8170
8223
  <span className="text-mono-xl">{revenue.total}</span>
8171
- <SegmentedBar label="Revenue by type" segments={[...segments]} size="lg" />
8224
+ <SegmentedBar label="Revenue by channel" segments={[...segments]} size="lg" />
8172
8225
  {/* Two columns while each entry fits whole, one when they would not:
8173
8226
  sized in em, so a larger type or a narrower card drops to one.
8174
8227
  aria-hidden: the bar already reads every share aloud. */}
@@ -8463,9 +8516,9 @@ import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle, cn, G
8463
8516
 
8464
8517
  const stats = [
8465
8518
  { label: "Revenue", value: "$18,240", delta: "+8.2%", up: true, note: "vs. $16,857" },
8466
- { label: "Collects", value: "1,284", delta: "+3.1%", up: true, note: "182 first-time" },
8467
- { label: "Collectors", value: "312", delta: "+12.4%", up: true, note: "Best month yet" },
8468
- { 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" },
8469
8522
  ]
8470
8523
 
8471
8524
  export interface StatRowBlockProps {
@@ -8600,9 +8653,9 @@ import { useState, useId } from "react"
8600
8653
  import { Badge, Button, Card, CardAction, CardContent, CardFooter, CardHeader, CardTitle, Checkbox, cn, Icon, Label } from "@sableui/react"
8601
8654
 
8602
8655
  const tasks = [
8603
- { title: "Pick the cover type", tag: "Design", tone: "collectible", done: true },
8656
+ { title: "Pick the cover type", tag: "Design", tone: "2", done: true },
8604
8657
  { title: "Wire up checkout", tag: "Engineering", tone: "info", done: false },
8605
- { title: "Draft launch post", tag: "Launch", tone: "edition", done: false },
8658
+ { title: "Draft launch post", tag: "Launch", tone: "1", done: false },
8606
8659
  ] as const
8607
8660
 
8608
8661
  export interface TaskListBlockProps {
@@ -8719,7 +8772,7 @@ export function TeamMembersBlock({ className, framed = true, onInvite, onMemberA
8719
8772
  subtitle={m.email}
8720
8773
  action={
8721
8774
  <div className="flex items-center gap-2">
8722
- <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">
8723
8776
  {m.role}
8724
8777
  </Badge>
8725
8778
  {/* The owner is not managed from here; a placeholder keeps the column aligned. */}
@@ -9134,7 +9187,7 @@ export function VerifyCodeBlock({ className, framed = true, onVerify, onResend }
9134
9187
 
9135
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.
9136
9189
 
9137
- **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.
9138
9191
 
9139
9192
  **Block:** Commerce · card · designed for 18rem–20rem wide (fills its container up to the max)
9140
9193
 
@@ -9142,24 +9195,24 @@ A balance card: the balance in the metric mono tier, Send and Receive as an equa
9142
9195
 
9143
9196
  - The balance is CardTitle at `text-mono-xl` — tabular figures, never a heading tier — under its label as CardDescription.
9144
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.
9145
- - 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.
9146
9199
 
9147
9200
  ## Accessibility
9148
9201
 
9149
9202
  - Each activity row's leading swatch is aria-hidden; the title and detail carry the meaning.
9150
- - 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.
9151
9204
 
9152
9205
  ## Source
9153
9206
 
9154
9207
  ```tsx
9155
9208
  import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle, cn, Entity, Icon } from "@sableui/react"
9156
9209
 
9157
- const balance = "14.62 SOL"
9210
+ const balance = "$2,418.50"
9158
9211
 
9159
9212
  const activity = [
9160
- { title: "Sold “Dusk, no. 4”", detail: "Edition · 2h ago", amount: "+0.80", tone: "edition" },
9161
- { title: "Royalty", detail: "Collectible · Yesterday", amount: "+0.12", tone: "collectible" },
9162
- { 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" },
9163
9216
  ]
9164
9217
 
9165
9218
  export interface WalletBlockProps {
@@ -9269,7 +9322,7 @@ import { PageHeader, Region } from "@sableui/react"
9269
9322
  ```tsx
9270
9323
  import { Card, CardHeader, CardTitle, Grid, Region } from "@sableui/react"
9271
9324
 
9272
- <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>
9273
9326
  ```
9274
9327
 
9275
9328
  ### Fluid workspace: the queue's table takes the extra width
@@ -9319,7 +9372,7 @@ Use Page as a route content shell that combines Region sizing with vertical rhyt
9319
9372
  ```tsx
9320
9373
  import { Card, CardContent, Page, PageHeader } from "@sableui/react"
9321
9374
 
9322
- <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>
9323
9376
  ```
9324
9377
 
9325
9378
  ---
@@ -9390,7 +9443,7 @@ import { Card, CardDescription, CardHeader, Grid } from "@sableui/react"
9390
9443
  ```tsx
9391
9444
  import { Badge, Grid } from "@sableui/react"
9392
9445
 
9393
- <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>
9394
9447
  ```
9395
9448
 
9396
9449
  ---
@@ -9729,7 +9782,7 @@ Use SplitPane for responsive two-pane layouts where a primary pane and supportin
9729
9782
  ```tsx
9730
9783
  import { AspectBox, Button, SplitPane, Stack } from "@sableui/react"
9731
9784
 
9732
- <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>} />
9733
9786
  ```
9734
9787
 
9735
9788
  ---
@@ -9892,7 +9945,7 @@ Use Accordion to reveal a vertical set of related sections while keeping the sur
9892
9945
  ```tsx
9893
9946
  import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger } from "@sableui/react"
9894
9947
 
9895
- <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>
9896
9949
  ```
9897
9950
 
9898
9951
  ### Multiple open
@@ -9908,7 +9961,7 @@ import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger } from "@sab
9908
9961
  ```tsx
9909
9962
  import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger, Icon } from "@sableui/react"
9910
9963
 
9911
- <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>
9912
9965
  ```
9913
9966
 
9914
9967
  ---
@@ -9938,7 +9991,7 @@ Use AppHeader for compact application chrome at the top of a screen, usually wit
9938
9991
  | --- | --- | --- | --- |
9939
9992
  | leading | ReactNode | — | Leading slot — back button, create action, or menu trigger. |
9940
9993
  | title | ReactNode | — | Center slot — brand wordmark or contextual page title. |
9941
- | actions | ReactNode | — | Trailing slot — wallet trigger, action buttons. |
9994
+ | actions | ReactNode | — | Trailing slot — account menu, action buttons. |
9942
9995
 
9943
9996
  ## Tokens used
9944
9997
 
@@ -10060,7 +10113,7 @@ import { AppHeader, AppShell, Banner, BottomNav, BottomNavItem, Icon, PageHeader
10060
10113
  // <Link> = your router's link (react-router, next/link, @tanstack/router, …)
10061
10114
 
10062
10115
  <AppShell
10063
- 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>}
10064
10117
  header={<AppHeader title="Studio" />}
10065
10118
  sidebar={
10066
10119
  <Sidebar>
@@ -10079,7 +10132,7 @@ import { AppHeader, AppShell, Banner, BottomNav, BottomNavItem, Icon, PageHeader
10079
10132
  }
10080
10133
  >
10081
10134
  <Region max="feed">
10082
- <PageHeader title="Home" description="Latest posts from artists you follow." />
10135
+ <PageHeader title="Home" description="Latest posts from people you follow." />
10083
10136
  </Region>
10084
10137
  </AppShell>
10085
10138
  ```
@@ -10181,7 +10234,7 @@ Use AreaChart for volume over time — visitors, revenue, usage — where the fi
10181
10234
 
10182
10235
  ## Tokens used
10183
10236
 
10184
- `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`
10185
10238
 
10186
10239
  ## Motion
10187
10240
 
@@ -10540,7 +10593,7 @@ import { Avatar, Entity, StatusDot } from "@sableui/react"
10540
10593
 
10541
10594
  **Status:** stable
10542
10595
 
10543
- 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.
10544
10597
 
10545
10598
  **Keywords:** badge, tag, label, status, metadata, chip, pill, tone, category
10546
10599
 
@@ -10551,7 +10604,7 @@ Use Badge for any short static label: metadata, categories, tone-coded labels, a
10551
10604
  ### Best practices
10552
10605
 
10553
10606
  - Do: Keep badge text short: one to three words or a compact count.
10554
- - 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.
10555
10608
  - Do: Pair icons with text; the icon reinforces meaning but should not be the only cue.
10556
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.
10557
10610
  - Don't: Use a Badge as a button. If it triggers an action, use Button, Toggle, or a menu item.
@@ -10570,7 +10623,7 @@ Use Badge for any short static label: metadata, categories, tone-coded labels, a
10570
10623
  | Name | Type | Default | Description |
10571
10624
  | --- | --- | --- | --- |
10572
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. |
10573
- | 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. |
10574
10627
  | size | "sm" \| "md" | "md" | "sm" uses tighter padding + text-label-sm; "md" is the default look. |
10575
10628
  | icon | ReactNode | — | Optional leading glyph (e.g. <Icon name="lock" />), rendered before the children with the base gap. Ignored when dot is set. |
10576
10629
  | dot | boolean | false | A leading status dot in the badge's tone. With appearance="plain" it is a status line (● Ready). |
@@ -10579,12 +10632,12 @@ Use Badge for any short static label: metadata, categories, tone-coded labels, a
10579
10632
  ## Variants
10580
10633
 
10581
10634
  - **appearance**: soft, outline, solid, overlay, plain
10582
- - **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
10583
10636
  - **size**: sm, md
10584
10637
 
10585
10638
  ## Tokens used
10586
10639
 
10587
- `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`
10588
10641
 
10589
10642
  ## Accessibility
10590
10643
 
@@ -10603,7 +10656,15 @@ Use Badge for any short static label: metadata, categories, tone-coded labels, a
10603
10656
  ```tsx
10604
10657
  import { Badge, Row } from "@sableui/react"
10605
10658
 
10606
- <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>
10607
10668
  ```
10608
10669
 
10609
10670
  ### Solid
@@ -10643,7 +10704,7 @@ import { Badge } from "@sableui/react"
10643
10704
  ```tsx
10644
10705
  import { Badge, Icon, Row } from "@sableui/react"
10645
10706
 
10646
- <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>
10647
10708
  ```
10648
10709
 
10649
10710
  ### Semantic state
@@ -10660,7 +10721,7 @@ import { Badge, Row } from "@sableui/react"
10660
10721
 
10661
10722
  **Status:** stable
10662
10723
 
10663
- 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.
10664
10725
 
10665
10726
  **Keywords:** banner, announcement, alert, status, message, warning, success, offline, sticky
10666
10727
 
@@ -10689,7 +10750,7 @@ Use Banner for page-level or app-level messages that affect the current experien
10689
10750
 
10690
10751
  | Name | Type | Default | Description |
10691
10752
  | --- | --- | --- | --- |
10692
- | 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). |
10693
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). |
10694
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. |
10695
10756
  | action | ReactNode | — | A trailing action (e.g. a Button). Switches the bar to leading text + trailing action instead of centered text. |
@@ -10700,12 +10761,12 @@ Use Banner for page-level or app-level messages that affect the current experien
10700
10761
 
10701
10762
  ## Variants
10702
10763
 
10703
- - **tone**: neutral, info, success, warning, edition, destructive
10764
+ - **tone**: neutral, info, success, warning, 1, destructive
10704
10765
  - **appearance**: soft, solid
10705
10766
 
10706
10767
  ## Tokens used
10707
10768
 
10708
- `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`
10709
10770
 
10710
10771
  ## Accessibility
10711
10772
 
@@ -10733,7 +10794,7 @@ import { Banner } from "@sableui/react"
10733
10794
  ```tsx
10734
10795
  import { Banner } from "@sableui/react"
10735
10796
 
10736
- <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>
10737
10798
  ```
10738
10799
 
10739
10800
  ### Success
@@ -10765,7 +10826,7 @@ import { Banner } from "@sableui/react"
10765
10826
  ```tsx
10766
10827
  import { Banner, Button } from "@sableui/react"
10767
10828
 
10768
- <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>
10769
10830
  ```
10770
10831
 
10771
10832
  ### App-shell outage
@@ -10822,13 +10883,13 @@ Use BarChart to compare amounts across categories — the default chart for "how
10822
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. |
10823
10884
  | labels | boolean \| "inside" | false | Print each bar's value at its end, or (`"inside"`, horizontal) the category name inside the bar as well. |
10824
10885
  | colorBy | "series" \| "index" | "series" | Colour by series, or give each category its own tone (one series). |
10825
- | 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. |
10826
10887
  | radius | number | 4 | Corner radius of each bar's far end, in px. Stacks round only the outer segment. |
10827
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. |
10828
10889
 
10829
10890
  ## Tokens used
10830
10891
 
10831
- `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`
10832
10893
 
10833
10894
  ## Motion
10834
10895
 
@@ -11060,7 +11121,7 @@ Use Breadcrumbs to show the user's location inside a hierarchy and provide light
11060
11121
  ```tsx
11061
11122
  import { BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, Breadcrumbs, BreadcrumbSeparator } from "@sableui/react"
11062
11123
 
11063
- <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>
11064
11125
  ```
11065
11126
 
11066
11127
  ### Custom separator
@@ -11355,7 +11416,7 @@ Use Button when the user commits an action, submits a form, opens UI, or confirm
11355
11416
  ```tsx
11356
11417
  import { Button } from "@sableui/react"
11357
11418
 
11358
- <Button>Collect</Button>
11419
+ <Button>Save changes</Button>
11359
11420
  ```
11360
11421
 
11361
11422
  ### Secondary + ghost
@@ -11412,7 +11473,7 @@ import { Button, Row } from "@sableui/react"
11412
11473
  ```tsx
11413
11474
  import { Button, Row } from "@sableui/react"
11414
11475
 
11415
- <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>
11416
11477
  ```
11417
11478
 
11418
11479
  ### Leading icon
@@ -11495,7 +11556,7 @@ import { Button } from "@sableui/react"
11495
11556
  ```tsx
11496
11557
  import { Banner, Button } from "@sableui/react"
11497
11558
 
11498
- <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>
11499
11560
  ```
11500
11561
 
11501
11562
  ---
@@ -11663,7 +11724,7 @@ Use Card for a discrete thing with its own boundary, such as one product, one no
11663
11724
  ```tsx
11664
11725
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@sableui/react"
11665
11726
 
11666
- <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>
11667
11728
  ```
11668
11729
 
11669
11730
  ### With a header action
@@ -11671,7 +11732,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@sabl
11671
11732
  ```tsx
11672
11733
  import { Button, Card, CardAction, CardContent, CardHeader, CardTitle, Icon } from "@sableui/react"
11673
11734
 
11674
- <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>
11675
11736
  ```
11676
11737
 
11677
11738
  ### Compact section panel
@@ -11687,7 +11748,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@sableui/react"
11687
11748
  ```tsx
11688
11749
  import { Badge, Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@sableui/react"
11689
11750
 
11690
- <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>
11691
11752
  ```
11692
11753
 
11693
11754
  ### Whole card as a link
@@ -11703,7 +11764,7 @@ import { Card, CardContent, CardDescription, CardTitle, Icon } from "@sableui/re
11703
11764
  ```tsx
11704
11765
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@sableui/react"
11705
11766
 
11706
- <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>
11707
11768
  ```
11708
11769
 
11709
11770
  ### List card
@@ -12071,17 +12132,17 @@ Use CheckboxCard when an option needs more visual weight, supporting text, or ic
12071
12132
  | title | ReactNode | — | The card's primary label (text-title-sm). |
12072
12133
  | description | ReactNode | — | Optional supporting copy beneath the title (text-body-sm, muted). |
12073
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. |
12074
- | 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. |
12075
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. |
12076
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. |
12077
12138
 
12078
12139
  ## Variants
12079
12140
 
12080
- - **tone**: neutral, standard, collectible, edition, info, warning
12141
+ - **tone**: neutral, 3, 2, 1, info, warning
12081
12142
 
12082
12143
  ## Tokens used
12083
12144
 
12084
- `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`
12085
12146
 
12086
12147
  ## Accessibility
12087
12148
 
@@ -12117,7 +12178,7 @@ import { Checkbox, CheckboxCard, CheckboxGroup, Field } from "@sableui/react"
12117
12178
  ```tsx
12118
12179
  import { CheckboxCard } from "@sableui/react"
12119
12180
 
12120
- <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} />
12121
12182
  ```
12122
12183
 
12123
12184
  ### Toned with description
@@ -12125,7 +12186,7 @@ import { CheckboxCard } from "@sableui/react"
12125
12186
  ```tsx
12126
12187
  import { CheckboxCard } from "@sableui/react"
12127
12188
 
12128
- <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 />
12129
12190
  ```
12130
12191
 
12131
12192
  ---
@@ -12211,7 +12272,7 @@ Use CodeBlock for multiline code samples that users may read, inspect, or copy.
12211
12272
 
12212
12273
  - Do: Give the snippet a filename, or a sentence of surrounding copy, when it helps readers and agents place it.
12213
12274
  - Do: Keep examples complete enough to teach the intended pattern.
12214
- - 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.
12215
12276
  - Don't: Do not put short inline identifiers in a CodeBlock; use Code for those.
12216
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.
12217
12278
 
@@ -12704,7 +12765,7 @@ import { CommandMenu, CommandMenuGroup, CommandMenuItem } from "@sableui/react"
12704
12765
  ```tsx
12705
12766
  import { CommandMenu, CommandMenuGroup, CommandMenuItem } from "@sableui/react"
12706
12767
 
12707
- <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>
12708
12769
  ```
12709
12770
 
12710
12771
  ### Consumer-owned ranking
@@ -12755,7 +12816,7 @@ useCommandMenuShortcut(() => setOpen((value) => !value))
12755
12816
 
12756
12817
  **Status:** stable
12757
12818
 
12758
- 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.
12759
12820
 
12760
12821
  **Keywords:** context card, rich tooltip, hover card, preview
12761
12822
 
@@ -12814,7 +12875,7 @@ Use ContextCard to show richer contextual content near a trigger without taking
12814
12875
  ```tsx
12815
12876
  import { Avatar, ContextCard, ContextCardContent, ContextCardTrigger, Entity } from "@sableui/react"
12816
12877
 
12817
- <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>
12818
12879
  ```
12819
12880
 
12820
12881
  ### With stat row
@@ -13300,7 +13361,7 @@ import { DestructiveActionModal } from "@sableui/react"
13300
13361
  ```tsx
13301
13362
  import { DestructiveActionModal, Note } from "@sableui/react"
13302
13363
 
13303
- <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>
13304
13365
  ```
13305
13366
 
13306
13367
  ---
@@ -13414,7 +13475,7 @@ import { Button, Carousel, Dialog, DialogContent, DialogTrigger } from "@sableui
13414
13475
  ```tsx
13415
13476
  import { Button, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@sableui/react"
13416
13477
 
13417
- <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>
13418
13479
  ```
13419
13480
 
13420
13481
  ### Settings form
@@ -13430,7 +13491,7 @@ import { Button, Dialog, DialogClose, DialogContent, DialogDescription, DialogFo
13430
13491
  ```tsx
13431
13492
  import { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, Button } from "@sableui/react"
13432
13493
 
13433
- <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>
13434
13495
  ```
13435
13496
 
13436
13497
  ---
@@ -13478,7 +13539,7 @@ Use DisclosurePicker for one-of-many choices in a docked panel or inspector wher
13478
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. |
13479
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. |
13480
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. |
13481
- | 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. |
13482
13543
  | DisclosurePickerEmpty | { children?: ReactNode } | The no-match line, rendered in place of the list. |
13483
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. |
13484
13545
 
@@ -13510,6 +13571,7 @@ Use DisclosurePicker for one-of-many choices in a docked panel or inspector wher
13510
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.
13511
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.
13512
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.
13513
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.
13514
13576
 
13515
13577
  ## Related
@@ -13620,7 +13682,7 @@ Use EmptyState when a content area has no items, no search results, or no first-
13620
13682
 
13621
13683
  ### Best practices
13622
13684
 
13623
- - 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".
13624
13686
  - Do: Offer one next step when the user can recover, create, clear filters, or start setup.
13625
13687
  - Do: Keep the icon decorative and make the title carry the meaning.
13626
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.
@@ -13693,7 +13755,7 @@ import { Button, EmptyState, Icon } from "@sableui/react"
13693
13755
  ```tsx
13694
13756
  import { Button, EmptyState, Icon, Row } from "@sableui/react"
13695
13757
 
13696
- <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>} />
13697
13759
  ```
13698
13760
 
13699
13761
  ### Page-level error
@@ -13709,7 +13771,7 @@ import { Button, EmptyState } from "@sableui/react"
13709
13771
  ```tsx
13710
13772
  import { Button, EmptyState } from "@sableui/react"
13711
13773
 
13712
- <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>} />
13713
13775
  ```
13714
13776
 
13715
13777
  ### Empty table body
@@ -13717,7 +13779,7 @@ import { Button, EmptyState } from "@sableui/react"
13717
13779
  ```tsx
13718
13780
  import { DataTable, EmptyState } from "@sableui/react"
13719
13781
 
13720
- <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." />} />
13721
13783
  ```
13722
13784
 
13723
13785
  ---
@@ -14455,7 +14517,7 @@ Use GradientArt where a real image belongs but none exists yet, or where the ima
14455
14517
  | Name | Type | Default | Description |
14456
14518
  | --- | --- | --- | --- |
14457
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. |
14458
- | 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. |
14459
14521
  | seed | string \| number | — | Fixes the composition: the same seed paints the same art in every theme. Pass a stable id. |
14460
14522
  | children | ReactNode | — | Rendered above the art — initials, a glyph, a short label. |
14461
14523
  | className | string | — | Size and shape: aspect, height, rounding. |
@@ -14466,7 +14528,7 @@ Use GradientArt where a real image belongs but none exists yet, or where the ima
14466
14528
 
14467
14529
  ## Tokens used
14468
14530
 
14469
- `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`
14470
14532
 
14471
14533
  ## Motion
14472
14534
 
@@ -14499,7 +14561,7 @@ import { GradientArt } from "@sableui/react"
14499
14561
  ```tsx
14500
14562
  import { GradientArt } from "@sableui/react"
14501
14563
 
14502
- <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" />
14503
14565
  ```
14504
14566
 
14505
14567
  ### Avatar with initials
@@ -14850,7 +14912,7 @@ import { Field, Input } from "@sableui/react"
14850
14912
  ```tsx
14851
14913
  import { Label } from "@sableui/react"
14852
14914
 
14853
- <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>
14854
14916
  ```
14855
14917
 
14856
14918
  ---
@@ -14902,7 +14964,7 @@ Use LineChart to compare how two or more measures change over the same sequence,
14902
14964
 
14903
14965
  ## Tokens used
14904
14966
 
14905
- `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`
14906
14968
 
14907
14969
  ## Motion
14908
14970
 
@@ -15809,7 +15871,7 @@ Use Meter to show a static measurement against a known maximum — storage used,
15809
15871
  | value | number | — | The current reading, between min and max. Required. |
15810
15872
  | min | number | 0 | The bottom of the range. |
15811
15873
  | max | number | 100 | The top of the range. |
15812
- | 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. |
15813
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. |
15814
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. |
15815
15877
  | showValue | boolean | — | Print the value in the ring's centre (formatted like MeterValue). |
@@ -15820,7 +15882,7 @@ Use Meter to show a static measurement against a known maximum — storage used,
15820
15882
 
15821
15883
  ## Variants
15822
15884
 
15823
- - **tone**: primary, standard, success, warning, destructive
15885
+ - **tone**: primary, 3, success, warning, destructive
15824
15886
  - **shape**: bar, ring
15825
15887
  - **size**: sm, md, lg
15826
15888
 
@@ -15929,12 +15991,12 @@ Use MiddleTruncate when both the beginning and end of a long identifier must rem
15929
15991
 
15930
15992
  ## Examples
15931
15993
 
15932
- ### Solana address
15994
+ ### API key
15933
15995
 
15934
15996
  ```tsx
15935
15997
  import { MiddleTruncate } from "@sableui/react"
15936
15998
 
15937
- <MiddleTruncate text="5J7sX2QpHt9vK4mNbR8wYcZ3dF6gH1jL0pA8xQpVnTu" />
15999
+ <MiddleTruncate text="demo_key_7f3a9c2e5b8d1f4a6c0e9b3d7a2f5c8e1b4" />
15938
16000
  ```
15939
16001
 
15940
16002
  ### Custom start/end counts
@@ -15942,7 +16004,7 @@ import { MiddleTruncate } from "@sableui/react"
15942
16004
  ```tsx
15943
16005
  import { MiddleTruncate } from "@sableui/react"
15944
16006
 
15945
- <MiddleTruncate text="0xAbC1234567890dEf1234567890aBcDeF12345678" startChars={4} endChars={4} />
16007
+ <MiddleTruncate text="9f2c4e7a1b3d5f60c8e2a4b6d8f0e1c3a5b7d9f2" startChars={4} endChars={4} />
15946
16008
  ```
15947
16009
 
15948
16010
  ### Copyable value
@@ -15950,8 +16012,8 @@ import { MiddleTruncate } from "@sableui/react"
15950
16012
  ```tsx
15951
16013
  import { Button, Icon, MiddleTruncate, Row, useCopy } from "@sableui/react"
15952
16014
 
15953
- const { copy, copied } = useCopy(address)
15954
- <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>
15955
16017
  ```
15956
16018
 
15957
16019
  ---
@@ -16011,7 +16073,7 @@ Use Note for helpful supporting information that should sit near the relevant co
16011
16073
  ```tsx
16012
16074
  import { Note } from "@sableui/react"
16013
16075
 
16014
- <Note tone="info">Editions mint on Solana mainnet.</Note>
16076
+ <Note tone="info">Reports refresh every hour.</Note>
16015
16077
  ```
16016
16078
 
16017
16079
  ### With title
@@ -16019,7 +16081,7 @@ import { Note } from "@sableui/react"
16019
16081
  ```tsx
16020
16082
  import { Note } from "@sableui/react"
16021
16083
 
16022
- <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>
16023
16085
  ```
16024
16086
 
16025
16087
  ### Warning with a remedy, inside a dialog
@@ -16035,7 +16097,7 @@ import { Note } from "@sableui/react"
16035
16097
  ```tsx
16036
16098
  import { Note } from "@sableui/react"
16037
16099
 
16038
- <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>
16039
16101
  ```
16040
16102
 
16041
16103
  ---
@@ -16519,7 +16581,7 @@ Use Pagination when a result set is split into discrete pages and the user needs
16519
16581
  ### Best practices
16520
16582
 
16521
16583
  - Do: Keep page state controlled by the parent route or data layer so URL, filters, and result fetches stay in sync.
16522
- - 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".
16523
16585
  - Do: Reset to page 1 when filters or search terms materially change the result set.
16524
16586
  - Don't: Use Pagination for tiny lists where all items fit comfortably on one page.
16525
16587
  - Don't: Hide disabled previous/next buttons at the bounds; disabled controls make the current position clearer.
@@ -16585,7 +16647,7 @@ const [page, setPage] = useState(8)
16585
16647
  ```tsx
16586
16648
  import { DataTable, Pagination, Stack } from "@sableui/react"
16587
16649
 
16588
- <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>
16589
16651
  ```
16590
16652
 
16591
16653
  ### Server query state
@@ -16895,7 +16957,7 @@ Use Phone to frame a mobile product preview when the device context is useful.
16895
16957
  ```tsx
16896
16958
  import { Phone } from "@sableui/react"
16897
16959
 
16898
- <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>
16899
16961
  ```
16900
16962
 
16901
16963
  ### Mockup, not a responsive preview
@@ -16965,7 +17027,7 @@ Use PieChart for a share of one total across two to six categories — traffic s
16965
17027
 
16966
17028
  ## Tokens used
16967
17029
 
16968
- `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`
16969
17031
 
16970
17032
  ## Motion
16971
17033
 
@@ -17053,6 +17115,7 @@ Use Pill for a row of filters or choices a person clicks to narrow or toggle wha
17053
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. |
17054
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. |
17055
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. |
17056
17119
  | icon | ReactNode | — | Optional leading glyph (e.g. <Icon name="lock" />), rendered before the children with the base gap. |
17057
17120
 
17058
17121
  ## Variants
@@ -17060,10 +17123,11 @@ Use Pill for a row of filters or choices a person clicks to narrow or toggle wha
17060
17123
  - **appearance**: solid, outline
17061
17124
  - **size**: md, sm
17062
17125
  - **selected**: true, false
17126
+ - **tone**: neutral, red, orange, yellow, green, teal, blue, purple, pink
17063
17127
 
17064
17128
  ## Tokens used
17065
17129
 
17066
- `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`
17067
17131
 
17068
17132
  ## Accessibility
17069
17133
 
@@ -17076,12 +17140,20 @@ Use Pill for a row of filters or choices a person clicks to narrow or toggle wha
17076
17140
 
17077
17141
  ## Examples
17078
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
+
17079
17151
  ### Filter row
17080
17152
 
17081
17153
  ```tsx
17082
17154
  import { Pill, Row } from "@sableui/react"
17083
17155
 
17084
- <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>
17085
17157
  ```
17086
17158
 
17087
17159
  ### Outline, small (reactions)
@@ -17171,7 +17243,7 @@ Use Popover for anchored secondary content that needs more room than a tooltip b
17171
17243
  ```tsx
17172
17244
  import { Button, Popover, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger } from "@sableui/react"
17173
17245
 
17174
- <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>
17175
17247
  ```
17176
17248
 
17177
17249
  ### With close
@@ -17179,7 +17251,7 @@ import { Button, Popover, PopoverContent, PopoverDescription, PopoverTitle, Popo
17179
17251
  ```tsx
17180
17252
  import { Button, Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger } from "@sableui/react"
17181
17253
 
17182
- <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>
17183
17255
  ```
17184
17256
 
17185
17257
  ---
@@ -17329,7 +17401,7 @@ Use RadarChart to compare the shape of a profile across five to eight dimensions
17329
17401
 
17330
17402
  ## Tokens used
17331
17403
 
17332
- `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`
17333
17405
 
17334
17406
  ## Motion
17335
17407
 
@@ -17440,7 +17512,7 @@ Use RadialChart for progress toward goals (several rings), a single score or usa
17440
17512
 
17441
17513
  ## Tokens used
17442
17514
 
17443
- `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`
17444
17516
 
17445
17517
  ## Motion
17446
17518
 
@@ -17564,7 +17636,7 @@ import { Field, Fieldset, FieldsetContent, FieldsetLegend, Radio, RadioGroup } f
17564
17636
  <FieldsetContent>
17565
17637
  <RadioGroup defaultValue="standard">
17566
17638
  <Field label="Standard" orientation="horizontal"><Radio value="standard" /></Field>
17567
- <Field label="Edition" orientation="horizontal"><Radio value="edition" /></Field>
17639
+ <Field label="Poll" orientation="horizontal"><Radio value="poll" /></Field>
17568
17640
  </RadioGroup>
17569
17641
  </FieldsetContent>
17570
17642
  </Fieldset>
@@ -17580,7 +17652,7 @@ import { Field, Fieldset, FieldsetContent, FieldsetLegend, Radio, RadioGroup } f
17580
17652
  <FieldsetContent>
17581
17653
  <RadioGroup defaultValue="standard">
17582
17654
  <Field label="Standard" orientation="horizontal"><Radio value="standard" /></Field>
17583
- <Field label="Edition" orientation="horizontal" disabled><Radio value="edition" /></Field>
17655
+ <Field label="Poll" orientation="horizontal" disabled><Radio value="poll" /></Field>
17584
17656
  </RadioGroup>
17585
17657
  </FieldsetContent>
17586
17658
  </Fieldset>
@@ -17629,18 +17701,18 @@ Use RadioCard for visual choice cards when users compare a small set of named op
17629
17701
  | description | ReactNode | — | Optional supporting copy beneath the title (text-body-sm, muted). |
17630
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. |
17631
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. |
17632
- | 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. |
17633
17705
  | disabled | boolean | false | Dims the card and blocks selection. The group skips it during keyboard navigation. |
17634
17706
  | titleClassName | string | — | Merged onto the title span (default text-title-sm) — e.g. to tighten copy in a two-column layout. |
17635
17707
  | descriptionClassName | string | — | Merged onto the description span (default text-body-sm text-muted-foreground). |
17636
17708
 
17637
17709
  ## Variants
17638
17710
 
17639
- - **tone**: neutral, standard, collectible, edition, info, warning
17711
+ - **tone**: neutral, 3, 2, 1, info, warning
17640
17712
 
17641
17713
  ## Tokens used
17642
17714
 
17643
- `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`
17644
17716
 
17645
17717
  ## Accessibility
17646
17718
 
@@ -17661,7 +17733,7 @@ Use RadioCard for visual choice cards when users compare a small set of named op
17661
17733
  ```tsx
17662
17734
  import { Icon, RadioCard, RadioGroup } from "@sableui/react"
17663
17735
 
17664
- <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>
17665
17737
  ```
17666
17738
 
17667
17739
  ### Plan picker (plain)
@@ -17807,7 +17879,7 @@ Use Scroller to create a contained overflow region for content that naturally ex
17807
17879
  ```tsx
17808
17880
  import { Scroller } from "@sableui/react"
17809
17881
 
17810
- <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>
17811
17883
  ```
17812
17884
 
17813
17885
  ### Capped, scrolls past the cap
@@ -18010,7 +18082,7 @@ import { SearchInput } from "@sableui/react"
18010
18082
  ```tsx
18011
18083
  import { Field, SearchInput } from "@sableui/react"
18012
18084
 
18013
- <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>
18014
18086
  ```
18015
18087
 
18016
18088
  ### Disabled
@@ -18208,11 +18280,11 @@ Use SegmentedBar when a stat card or row needs to show how a total divides into
18208
18280
  ## Variants
18209
18281
 
18210
18282
  - **size**: sm, md, lg
18211
- - **tone**: edition, collectible, standard, info, primary, neutral, success, warning, destructive
18283
+ - **tone**: 1, 2, 3, info, primary, neutral, success, warning, destructive
18212
18284
 
18213
18285
  ## Tokens used
18214
18286
 
18215
- `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`
18216
18288
 
18217
18289
  ## Motion
18218
18290
 
@@ -18223,7 +18295,7 @@ Use SegmentedBar when a stat card or row needs to show how a total divides into
18223
18295
 
18224
18296
  ## Accessibility
18225
18297
 
18226
- - 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.
18227
18299
  - Colour alone does not identify a segment: always show a legend with each segment's label beside the bar.
18228
18300
 
18229
18301
  ## Related
@@ -18239,13 +18311,13 @@ Use SegmentedBar when a stat card or row needs to show how a total divides into
18239
18311
  import { SegmentedBar, Stack, StatusDot } from "@sableui/react"
18240
18312
 
18241
18313
  const sales = [
18242
- { label: "Editions", value: 42, tone: "edition" },
18243
- { label: "Collectibles", value: 27, tone: "collectible" },
18244
- { 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" },
18245
18317
  ] as const
18246
18318
 
18247
18319
  <Stack gap={3}>
18248
- <SegmentedBar label="Revenue by type" segments={[...sales]} size="lg" />
18320
+ <SegmentedBar label="Revenue by channel" segments={[...sales]} size="lg" />
18249
18321
  <ul className="flex flex-col gap-1.5">
18250
18322
  {sales.map((s) => (
18251
18323
  <li key={s.label} className="flex items-center gap-2 text-body-sm">
@@ -18371,7 +18443,7 @@ import { SelectContent, SelectGroup, SelectGroupLabel, SelectItem } from "@sable
18371
18443
  ```tsx
18372
18444
  import { Field, Select, SelectContent, SelectItem, SelectTrigger } from "@sableui/react"
18373
18445
 
18374
- <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>
18375
18447
  ```
18376
18448
 
18377
18449
  ### Rich item labels
@@ -18379,7 +18451,7 @@ import { Field, Select, SelectContent, SelectItem, SelectTrigger } from "@sableu
18379
18451
  ```tsx
18380
18452
  import { Badge, Select, SelectContent, SelectItem, SelectTrigger } from "@sableui/react"
18381
18453
 
18382
- <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>
18383
18455
  ```
18384
18456
 
18385
18457
  ---
@@ -18704,7 +18776,7 @@ import { Button, Sheet, SheetClose, SheetContent, SheetFooter, SheetHeader, Shee
18704
18776
  ```tsx
18705
18777
  import { Button, Sheet, SheetClose, SheetContent, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from "@sableui/react"
18706
18778
 
18707
- <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>
18708
18780
  ```
18709
18781
 
18710
18782
  ---
@@ -18840,7 +18912,7 @@ Use Sidebar for persistent desktop navigation or application chrome outside the
18840
18912
  import { Icon, NotificationBadge, Sidebar, SidebarBrand, SidebarFooter, SidebarHeader, SidebarItem, SidebarNav } from "@sableui/react"
18841
18913
  // <Link> = your router's link (react-router, next/link, @tanstack/router, …)
18842
18914
 
18843
- <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>
18844
18916
  ```
18845
18917
 
18846
18918
  ### Collapsible (icon rail)
@@ -18949,7 +19021,7 @@ import { SideNav, SideNavGroup, SideNavItem } from "@sableui/react"
18949
19021
  import { SideNav, SideNavItem } from "@sableui/react"
18950
19022
  // <Link> = your router's link (react-router, next/link, @tanstack/router, …)
18951
19023
 
18952
- <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>
18953
19025
  ```
18954
19026
 
18955
19027
  ### Inside Sidebar chrome
@@ -19192,7 +19264,7 @@ Use Sparkline next to a number to show where it has been — a KPI's last 30 day
19192
19264
  | data | number[] | — | The values, oldest first. |
19193
19265
  | label | string | — | What the line tracks ("Revenue, last 30 days"). The spoken summary adds the first and last values, low and high. |
19194
19266
  | variant | "line" \| "area" \| "bar" | "line" | A line, a line over a soft fill, or columns. |
19195
- | 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. |
19196
19268
  | className | string | "h-8 w-24" | Sizes it; the drawing stretches to fit. |
19197
19269
  | animate | boolean | true | Draw on (or rise) when it scrolls into view. |
19198
19270
  | locale | string | runtime locale | BCP 47 locale for default number and date formatting. |
@@ -19203,7 +19275,7 @@ Use Sparkline next to a number to show where it has been — a KPI's last 30 day
19203
19275
 
19204
19276
  ## Tokens used
19205
19277
 
19206
- `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`
19207
19279
 
19208
19280
  ## Motion
19209
19281
 
@@ -19334,7 +19406,7 @@ import { Spinner } from "@sableui/react"
19334
19406
  ```tsx
19335
19407
  import { Button } from "@sableui/react"
19336
19408
 
19337
- <Button loading>Collecting</Button>
19409
+ <Button loading>Saving</Button>
19338
19410
  ```
19339
19411
 
19340
19412
  ---
@@ -19463,18 +19535,18 @@ Use StatusDot as a compact visual state cue next to text, avatars, rows, or labe
19463
19535
 
19464
19536
  | Name | Type | Default | Description |
19465
19537
  | --- | --- | --- | --- |
19466
- | 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. |
19467
19539
  | size | "sm" \| "md" \| "lg" | "md" | Dot diameter. |
19468
19540
  | pulse | boolean | false | Wrap the dot in an animated ping ring to signal a live state. |
19469
19541
 
19470
19542
  ## Variants
19471
19543
 
19472
- - **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
19473
19545
  - **size**: sm, md, lg
19474
19546
 
19475
19547
  ## Tokens used
19476
19548
 
19477
- `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`
19478
19550
 
19479
19551
  ## Accessibility
19480
19552
 
@@ -19493,7 +19565,7 @@ Use StatusDot as a compact visual state cue next to text, avatars, rows, or labe
19493
19565
  ```tsx
19494
19566
  import { Row, StatusDot } from "@sableui/react"
19495
19567
 
19496
- <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>
19497
19569
  ```
19498
19570
 
19499
19571
  ### With label — use Badge
@@ -19878,25 +19950,25 @@ Use Table for structured data with consistent columns and rows. Use DataTable wh
19878
19950
 
19879
19951
  ## Examples
19880
19952
 
19881
- ### Collectors
19953
+ ### Orders
19882
19954
 
19883
19955
  ```tsx
19884
19956
  import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@sableui/react"
19885
19957
 
19886
19958
  <Table>
19887
- <TableCaption>Recent collectors</TableCaption>
19959
+ <TableCaption>Recent orders</TableCaption>
19888
19960
  <TableHeader>
19889
19961
  <TableRow>
19890
- <TableHead>Collector</TableHead>
19891
- <TableHead>Edition</TableHead>
19962
+ <TableHead>Customer</TableHead>
19963
+ <TableHead>Order</TableHead>
19892
19964
  <TableHead>Paid</TableHead>
19893
19965
  </TableRow>
19894
19966
  </TableHeader>
19895
19967
  <TableBody>
19896
19968
  <TableRow>
19897
19969
  <TableCell>@nova</TableCell>
19898
- <TableCell>#012</TableCell>
19899
- <TableCell>0.5 SOL</TableCell>
19970
+ <TableCell>1042</TableCell>
19971
+ <TableCell>$48.00</TableCell>
19900
19972
  </TableRow>
19901
19973
  </TableBody>
19902
19974
  </Table>
@@ -19915,7 +19987,7 @@ import { Avatar, Badge, Button, Row, Table, TableBody, TableCaption, TableCell,
19915
19987
  ```tsx
19916
19988
  import { Card, CardContent, CardHeader, CardTitle, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@sableui/react"
19917
19989
 
19918
- <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>
19919
19991
  ```
19920
19992
 
19921
19993
  ### Summary footer
@@ -19923,7 +19995,7 @@ import { Card, CardContent, CardHeader, CardTitle, Table, TableBody, TableCell,
19923
19995
  ```tsx
19924
19996
  import { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "@sableui/react"
19925
19997
 
19926
- <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>
19927
19999
  ```
19928
20000
 
19929
20001
  ---
@@ -20010,7 +20082,7 @@ import { Tabs, TabsList, TabsPanel, TabsTrigger } from "@sableui/react"
20010
20082
  ```tsx
20011
20083
  import { Tabs, TabsList, TabsPanel, TabsTrigger } from "@sableui/react"
20012
20084
 
20013
- <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>
20014
20086
  ```
20015
20087
 
20016
20088
  ---
@@ -20077,7 +20149,7 @@ import { Textarea } from "@sableui/react"
20077
20149
  ```tsx
20078
20150
  import { Textarea } from "@sableui/react"
20079
20151
 
20080
- <Textarea rows={6} placeholder="Tell the story behind this edition…" />
20152
+ <Textarea rows={6} placeholder="Tell your team what this project is for…" />
20081
20153
  ```
20082
20154
 
20083
20155
  ### Auto-resize
@@ -20101,7 +20173,7 @@ import { Textarea } from "@sableui/react"
20101
20173
  ```tsx
20102
20174
  import { Textarea } from "@sableui/react"
20103
20175
 
20104
- <Textarea disabled defaultValue="Captions lock once the edition is minted." />
20176
+ <Textarea disabled defaultValue="Notes lock once the invoice is sent." />
20105
20177
  ```
20106
20178
 
20107
20179
  ---
@@ -20518,7 +20590,7 @@ toast.promise(publish(), { loading: "Publishing…", success: "Published", error
20518
20590
  ```tsx
20519
20591
  import { toast } from "@sableui/react"
20520
20592
 
20521
- 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 })
20522
20594
  ```
20523
20595
 
20524
20596
  ### Custom toast markup (composed parts)
@@ -20900,7 +20972,7 @@ Use Tooltip for brief nonessential hints tied to a specific control or piece of
20900
20972
  ```tsx
20901
20973
  import { Button, Icon, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@sableui/react"
20902
20974
 
20903
- <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>
20904
20976
  ```
20905
20977
 
20906
20978
  ### Sides
@@ -20916,7 +20988,7 @@ import { Button, Tooltip, TooltipContent, TooltipTrigger } from "@sableui/react"
20916
20988
  ```tsx
20917
20989
  import { Button, Icon, SimpleTooltip } from "@sableui/react"
20918
20990
 
20919
- <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>
20920
20992
  ```
20921
20993
 
20922
20994
  ---
@@ -21413,23 +21485,23 @@ Which token to reach for, and what it is most often confused with. Generated fro
21413
21485
  - **Not** `tone-info`. Success asserts an outcome; info asserts a fact. 'Saved' is success, '3 items' is info.
21414
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.
21415
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.
21416
- - **`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'.
21417
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.
21418
- - **`tone-collectible`** — Domain tone: a scarce or owned item. *Use for:* Marking an item as collectible/owned in a classified set.
21419
- - **Not** `primary`. A tone classifies the *thing*; primary drives an *action*. A collectible badge is not a button.
21420
- - **`tone-edition`** — Domain tone: an edition or release. *Use for:* Marking editioned content in a classified set.
21421
- - **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.
21422
21494
  - **`tone-info`** — Domain tone: neutral, factual information. *Use for:* An informational note or banner, a neutral count, a 'did you know' surface.
21423
21495
  - **Not** `success`. Info states a fact; success states an outcome. Also not `primary` — an info banner is not an action.
21424
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.
21425
21497
  - **Not** `destructive`. Warning is *advisory*; destructive is *consequential*. If nothing is lost by continuing, it is a warning.
21426
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.
21427
21499
  - **Not** `accent`. `accent` is UI state that follows the pointer or selection; `highlight` marks the *content* and persists independently of interaction.
21428
- - **`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.
21429
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.
21430
- - **`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.
21431
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.
21432
- - **`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.
21433
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.
21434
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.
21435
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.
@@ -21536,11 +21608,11 @@ Which token to reach for, and what it is most often confused with. Generated fro
21536
21608
 
21537
21609
  ### layout (11)
21538
21610
 
21539
- - **`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.
21540
21612
  - **Not** arbitrary vertical spacing. The rhythm is what makes unrelated sections of a page feel like one document.
21541
21613
  - **`grid-columns`** — The column count of the content grid — 12. *Use for:* `Grid`/`Columns`, WITHIN a region, for genuinely multi-column content.
21542
21614
  - **Not** using a column span as the page's reading width. A span is not a measure — see `measure-prose`.
21543
- - **`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.
21544
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.
21545
21617
  - **Not** a global max-width. Sable caps width per REGION by role, never once for the whole page.
21546
21618
  - **`measure-prose`** — The maximum comfortable line length for prose, in `ch`. *Use for:* Any long-form text column.
@@ -21700,6 +21772,20 @@ Which token to reach for, and what it is most often confused with. Generated fro
21700
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`.
21701
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.
21702
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
+
21703
21789
  ---
21704
21790
 
21705
21791
  ## Utilities
@@ -21805,7 +21891,7 @@ The `@utility` and `@custom-variant` layer in `styles.css`: what each one is, wh
21805
21891
  - `<ol className="sable-list-numbered"><li>Install</li><li>Import the styles</li></ol>`
21806
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.
21807
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.
21808
- - `<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>`
21809
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.
21810
21896
  - **Instead of** A viewport breakpoint, which gives a narrow card on a wide screen four columns it cannot fit.
21811
21897
  - `<div className="@container"><dl className="sable-list-dl sable-list-dl-2">…</dl></div>`