@sableui/react 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +95 -0
  3. package/CONTRIBUTING.md +2 -2
  4. package/DESIGN.md +55 -16
  5. package/README.md +12 -0
  6. package/dist/{_contract-C2M1SjBf.d.ts → _contract-CkqXDVww.d.ts} +25 -2
  7. package/dist/blocks/danger-zone-block.js +3 -3
  8. package/dist/blocks/danger-zone-block.meta.js +1 -1
  9. package/dist/blocks/faq-block.js +3 -3
  10. package/dist/blocks/goal-progress-block.js +1 -1
  11. package/dist/blocks/notification-list-block.js +1 -1
  12. package/dist/blocks/pricing-table-block.js +12 -12
  13. package/dist/blocks/product-card-block.js +3 -3
  14. package/dist/blocks/product-card-block.meta.js +3 -3
  15. package/dist/blocks/profile-card-block.js +1 -1
  16. package/dist/blocks/profile-form-block.js +3 -3
  17. package/dist/blocks/revenue-breakdown-block.js +5 -5
  18. package/dist/blocks/stat-row-block.js +3 -3
  19. package/dist/blocks/task-list-block.js +2 -2
  20. package/dist/blocks/team-members-block.js +1 -1
  21. package/dist/blocks/wallet-block.js +4 -4
  22. package/dist/blocks/wallet-block.meta.js +3 -3
  23. package/dist/components/accordion/accordion.meta.js +2 -2
  24. package/dist/components/app-header/app-header.meta.js +1 -1
  25. package/dist/components/app-shell/app-shell.meta.js +2 -2
  26. package/dist/components/area-chart/area-chart.meta.js +3 -3
  27. package/dist/components/badge/badge.js +13 -4
  28. package/dist/components/badge/badge.meta.js +24 -13
  29. package/dist/components/badge/badge.variants.js +37 -23
  30. package/dist/components/banner/banner.meta.js +8 -8
  31. package/dist/components/banner/banner.variants.js +3 -3
  32. package/dist/components/bar-chart/bar-chart.meta.js +4 -4
  33. package/dist/components/breadcrumbs/breadcrumbs.meta.js +1 -1
  34. package/dist/components/button/button.meta.js +3 -3
  35. package/dist/components/card/card.js +16 -7
  36. package/dist/components/card/card.meta.js +4 -4
  37. package/dist/components/checkbox-card/checkbox-card.meta.js +10 -10
  38. package/dist/components/checkbox-card/checkbox-card.variants.js +15 -15
  39. package/dist/components/code-block/code-block.meta.js +1 -1
  40. package/dist/components/command-menu/command-menu.meta.js +1 -1
  41. package/dist/components/context-card/context-card.meta.js +2 -2
  42. package/dist/components/destructive-action-modal/destructive-action-modal.meta.js +1 -1
  43. package/dist/components/dialog/dialog.meta.js +2 -2
  44. package/dist/components/disclosure-picker/disclosure-picker.js +48 -2
  45. package/dist/components/disclosure-picker/disclosure-picker.meta.js +3 -2
  46. package/dist/components/empty-state/empty-state.meta.js +4 -4
  47. package/dist/components/gradient-art/gradient-art.meta.js +3 -3
  48. package/dist/components/label/label.meta.js +1 -1
  49. package/dist/components/line-chart/line-chart.meta.js +3 -3
  50. package/dist/components/meter/meter.js +1 -1
  51. package/dist/components/meter/meter.meta.js +2 -2
  52. package/dist/components/meter/meter.variants.js +1 -1
  53. package/dist/components/middle-truncate/middle-truncate.meta.js +4 -4
  54. package/dist/components/note/note.meta.js +3 -3
  55. package/dist/components/page-header/page-header.js +1 -1
  56. package/dist/components/pagination/pagination.meta.js +2 -2
  57. package/dist/components/phone/phone.meta.js +1 -1
  58. package/dist/components/pie-chart/pie-chart.meta.js +3 -3
  59. package/dist/components/pill/pill.js +4 -2
  60. package/dist/components/pill/pill.meta.js +19 -2
  61. package/dist/components/pill/pill.variants.js +23 -2
  62. package/dist/components/popover/popover.meta.js +2 -2
  63. package/dist/components/radar-chart/radar-chart.meta.js +3 -3
  64. package/dist/components/radial-chart/radial-chart.meta.js +3 -3
  65. package/dist/components/radio/radio.meta.js +2 -2
  66. package/dist/components/radio-card/radio-card.meta.js +6 -6
  67. package/dist/components/radio-card/radio-card.variants.js +15 -15
  68. package/dist/components/scroller/scroller.meta.js +1 -1
  69. package/dist/components/search-input/search-input.meta.js +1 -1
  70. package/dist/components/section-header/section-header.js +1 -1
  71. package/dist/components/segmented-bar/segmented-bar.js +1 -1
  72. package/dist/components/segmented-bar/segmented-bar.meta.js +6 -6
  73. package/dist/components/segmented-bar/segmented-bar.variants.js +4 -4
  74. package/dist/components/select/select.meta.js +2 -2
  75. package/dist/components/sheet/sheet.meta.js +1 -1
  76. package/dist/components/side-nav/side-nav.meta.js +1 -1
  77. package/dist/components/sidebar/sidebar.meta.js +1 -1
  78. package/dist/components/sparkline/sparkline.meta.js +4 -4
  79. package/dist/components/spinner/spinner.meta.js +1 -1
  80. package/dist/components/status-dot/status-dot.js +4 -1
  81. package/dist/components/status-dot/status-dot.meta.js +8 -16
  82. package/dist/components/status-dot/status-dot.variants.js +13 -4
  83. package/dist/components/table/table.meta.js +4 -4
  84. package/dist/components/tabs/tabs.meta.js +1 -1
  85. package/dist/components/textarea/textarea.meta.js +2 -2
  86. package/dist/components/toast/toast.meta.js +1 -1
  87. package/dist/components/tooltip/tooltip.meta.js +2 -2
  88. package/dist/file-hashes.json +120 -118
  89. package/dist/foundations/spacing.js +8 -8
  90. package/dist/index.d.ts +137 -72
  91. package/dist/index.js +12 -1
  92. package/dist/lib/chart/types.js +3 -3
  93. package/dist/lib/palette.js +103 -0
  94. package/dist/lib/selection-card.js +4 -4
  95. package/dist/palette.css +669 -0
  96. package/dist/patterns/auth-page.js +6 -6
  97. package/dist/patterns/auth-page.meta.js +3 -3
  98. package/dist/patterns/brand-landing-page.js +2 -2
  99. package/dist/patterns/chat-panel.js +2 -2
  100. package/dist/patterns/dashboard-page.js +71 -71
  101. package/dist/patterns/dashboard-page.meta.js +5 -5
  102. package/dist/patterns/feed-page.js +25 -25
  103. package/dist/patterns/feed-page.meta.js +3 -3
  104. package/dist/patterns/landing-page.js +1 -1
  105. package/dist/patterns/list-detail-page.js +17 -17
  106. package/dist/patterns/settings-page.js +17 -17
  107. package/dist/presets/index.d.ts +1 -1
  108. package/dist/presets/index.js +17 -2
  109. package/dist/styles.css +115 -50
  110. package/dist/{theme-provider-S0NVJs2Y.d.ts → theme-provider-CdSicAad.d.ts} +1 -1
  111. package/dist/themes/_contract.js +22 -8
  112. package/dist/themes/atelier/atelier.css +13 -13
  113. package/dist/themes/atelier/index.d.ts +1 -1
  114. package/dist/themes/ember/ember.css +13 -13
  115. package/dist/themes/ember/index.d.ts +1 -1
  116. package/dist/themes/index.d.ts +2 -2
  117. package/dist/themes/meridian/index.d.ts +1 -1
  118. package/dist/themes/meridian/meridian.css +13 -13
  119. package/dist/themes/poppy/index.d.ts +1 -1
  120. package/dist/themes/poppy/poppy.css +19 -16
  121. package/dist/themes/prism/index.d.ts +1 -1
  122. package/dist/themes/prism/prism.css +18 -15
  123. package/dist/themes/sable/index.d.ts +1 -1
  124. package/dist/themes/sable/sable.css +18 -22
  125. package/dist/themes/verdant/index.d.ts +1 -1
  126. package/dist/themes/verdant/verdant.css +15 -15
  127. package/llms/blocks/danger-zone-block.md +4 -4
  128. package/llms/blocks/faq-block.md +3 -3
  129. package/llms/blocks/goal-progress-block.md +1 -1
  130. package/llms/blocks/notification-list-block.md +1 -1
  131. package/llms/blocks/pricing-table-block.md +12 -12
  132. package/llms/blocks/product-card-block.md +6 -6
  133. package/llms/blocks/profile-card-block.md +1 -1
  134. package/llms/blocks/profile-form-block.md +3 -3
  135. package/llms/blocks/revenue-breakdown-block.md +5 -5
  136. package/llms/blocks/stat-row-block.md +3 -3
  137. package/llms/blocks/task-list-block.md +2 -2
  138. package/llms/blocks/team-members-block.md +1 -1
  139. package/llms/blocks/wallet-block.md +7 -7
  140. package/llms/components/accordion.md +2 -2
  141. package/llms/components/app-header.md +1 -1
  142. package/llms/components/app-shell.md +2 -2
  143. package/llms/components/area-chart.md +1 -1
  144. package/llms/components/badge.md +15 -7
  145. package/llms/components/banner.md +6 -6
  146. package/llms/components/bar-chart.md +2 -2
  147. package/llms/components/breadcrumbs.md +1 -1
  148. package/llms/components/button.md +3 -3
  149. package/llms/components/card.md +4 -4
  150. package/llms/components/checkbox-card.md +5 -5
  151. package/llms/components/code-block.md +1 -1
  152. package/llms/components/command-menu.md +1 -1
  153. package/llms/components/context-card.md +2 -2
  154. package/llms/components/destructive-action-modal.md +1 -1
  155. package/llms/components/dialog.md +2 -2
  156. package/llms/components/disclosure-picker.md +2 -1
  157. package/llms/components/empty-state.md +4 -4
  158. package/llms/components/gradient-art.md +3 -3
  159. package/llms/components/label.md +1 -1
  160. package/llms/components/line-chart.md +1 -1
  161. package/llms/components/meter.md +2 -2
  162. package/llms/components/middle-truncate.md +5 -5
  163. package/llms/components/note.md +3 -3
  164. package/llms/components/pagination.md +2 -2
  165. package/llms/components/phone.md +1 -1
  166. package/llms/components/pie-chart.md +1 -1
  167. package/llms/components/pill.md +12 -2
  168. package/llms/components/popover.md +2 -2
  169. package/llms/components/radar-chart.md +1 -1
  170. package/llms/components/radial-chart.md +1 -1
  171. package/llms/components/radio-card.md +4 -4
  172. package/llms/components/radio.md +2 -2
  173. package/llms/components/scroller.md +1 -1
  174. package/llms/components/search-input.md +1 -1
  175. package/llms/components/segmented-bar.md +7 -7
  176. package/llms/components/select.md +2 -2
  177. package/llms/components/sheet.md +1 -1
  178. package/llms/components/side-nav.md +1 -1
  179. package/llms/components/sidebar.md +1 -1
  180. package/llms/components/sparkline.md +2 -2
  181. package/llms/components/spinner.md +1 -1
  182. package/llms/components/status-dot.md +4 -4
  183. package/llms/components/table.md +8 -8
  184. package/llms/components/tabs.md +1 -1
  185. package/llms/components/textarea.md +2 -2
  186. package/llms/components/toast.md +1 -1
  187. package/llms/components/tooltip.md +2 -2
  188. package/llms/foundations/spacing.md +7 -7
  189. package/llms/layout/grid.md +1 -1
  190. package/llms/layout/page.md +1 -1
  191. package/llms/layout/region.md +1 -1
  192. package/llms/layout/split-pane.md +1 -1
  193. package/llms/patterns/auth-page.md +11 -11
  194. package/llms/patterns/brand-landing-page.md +2 -2
  195. package/llms/patterns/chat-panel.md +2 -2
  196. package/llms/patterns/dashboard-page.md +78 -78
  197. package/llms/patterns/feed-page.md +31 -31
  198. package/llms/patterns/landing-page.md +1 -1
  199. package/llms/patterns/list-detail-page.md +18 -18
  200. package/llms/patterns/settings-page.md +18 -18
  201. package/llms/reference/token-intent.md +25 -11
  202. package/llms/reference/utilities.md +1 -1
  203. package/llms/tasks/adding-a-component.md +4 -1
  204. package/llms/tasks/building-a-page.md +40 -0
  205. package/llms/tasks/converting-an-app.md +3 -1
  206. package/llms/tasks/using-presets.md +4 -0
  207. package/llms-full.txt +429 -347
  208. package/llms.txt +7 -7
  209. package/package.json +4 -4
  210. package/registry.json +369 -233
  211. package/scripts/lint-rules.mjs +26 -5
  212. package/skills/sable/SKILL.md +14 -3
@@ -1,6 +1,6 @@
1
1
  # AuthPage
2
2
 
3
- 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.
3
+ 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.
4
4
 
5
5
  **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).
6
6
 
@@ -11,14 +11,14 @@ Split sign-in page: a form column (brand mark, heading, email + password, primar
11
11
  - The heading is `text-heading-1` in an h1 — it is the page outline, so it takes a heading tier, not a card title.
12
12
  - 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.
13
13
  - 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.
14
- - One primary action. Passkey and wallet are outline buttons under an `or` Separator; switching to sign-up is a quiet `link-underline` link.
14
+ - One primary action. Passkey and email link are outline buttons under an `or` Separator; switching to sign-up is a quiet `link-underline` link.
15
15
  - 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.
16
16
  - 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.
17
17
  - The panel is `hidden lg:flex`: on a phone the form is the whole screen, with no decorative scroll above it.
18
18
 
19
19
  ## Accessibility
20
20
 
21
- - The route renders inside <main> with one h1; the brand panel is an <aside> labelled "Featured release".
21
+ - The route renders inside <main> with one h1; the brand panel is an <aside> labelled "Customer story".
22
22
  - Field auto-wires labels, describedby, and invalid state onto the inputs; on submit, the first invalid field is focused.
23
23
  - Autocomplete attributes (`email webauthn`, `current-password`) keep password managers and passkey autofill working.
24
24
  - The Forgot password and Create one links use `hit-target` to meet the 44px touch floor without changing layout.
@@ -49,7 +49,7 @@ export function AuthPage() {
49
49
  <Stack gap={1.5}>
50
50
  <h1 className="text-heading-1">Welcome back</h1>
51
51
  <p className="text-body-lg text-muted-foreground">
52
- Sign in to collect new releases and manage your editions.
52
+ Sign in to pick up where you and your team left off.
53
53
  </p>
54
54
  </Stack>
55
55
 
@@ -87,8 +87,8 @@ export function AuthPage() {
87
87
  <Button variant="outline" size="cta" className="w-full" iconStart={<Icon name="key" />}>
88
88
  Sign in with a passkey
89
89
  </Button>
90
- <Button variant="outline" size="cta" className="w-full" iconStart={<Icon name="wallet" />}>
91
- Connect a wallet
90
+ <Button variant="outline" size="cta" className="w-full" iconStart={<Icon name="mail" />}>
91
+ Email me a sign-in link
92
92
  </Button>
93
93
  </Stack>
94
94
 
@@ -115,23 +115,23 @@ export function AuthPage() {
115
115
  </Region>
116
116
 
117
117
  {/* The brand panel is decoration: on a phone the form is the whole screen. */}
118
- <aside aria-label="Featured release" className="hidden p-3 lg:flex">
118
+ <aside aria-label="Customer story" className="hidden p-3 lg:flex">
119
119
  {/* Art painted from the theme's tones over its brand, so it follows every look. */}
120
120
  <GradientArt seed="Untitled (gold rush)" className="flex flex-1 flex-col justify-end rounded-xl p-8">
121
121
  <Card className="max-w-md">
122
122
  <CardContent>
123
123
  <Stack gap={3}>
124
- <Badge tone="edition" className="self-start">
125
- Edition of 50
124
+ <Badge tone="1" className="self-start">
125
+ Customer story
126
126
  </Badge>
127
127
  <p className="text-title-lg">
128
- “Every edition is a conversation with whoever holds it next.”
128
+ “We moved three teams over in a week, and nobody asked for the old tool back.”
129
129
  </p>
130
130
  <Entity
131
131
  className="p-0"
132
132
  leading={<Avatar variant="art" fallback="MO" />}
133
133
  title="Mara Okafor"
134
- subtitle="Untitled (gold rush), 2026"
134
+ subtitle="Head of Design, Northwind"
135
135
  />
136
136
  </Stack>
137
137
  </CardContent>
@@ -45,7 +45,7 @@ const spaces = [
45
45
 
46
46
  const routes = [
47
47
  { label: "Intake", state: "Open", tone: "info" as const },
48
- { label: "Review", state: "Live", tone: "edition" as const },
48
+ { label: "Review", state: "Live", tone: "1" as const },
49
49
  { label: "Archive", state: "Safe", tone: "success" as const },
50
50
  ]
51
51
 
@@ -223,7 +223,7 @@ export function BrandLandingPage() {
223
223
  <div className="flex items-center justify-between border-b border-border px-4 py-3">
224
224
  <Row gap={1}>
225
225
  <StatusDot tone="warning" />
226
- <StatusDot tone="edition" />
226
+ <StatusDot tone="1" />
227
227
  <StatusDot tone="success" />
228
228
  </Row>
229
229
  <span className="text-label-sm text-muted-foreground">Live workspace</span>
@@ -95,7 +95,7 @@ export function ChatPanel() {
95
95
  render={<section aria-labelledby="chat-title" />}
96
96
  className="flex h-[calc(100dvh_-_--spacing(12))] flex-col overflow-hidden"
97
97
  >
98
- <CardHeader className="border-b border-border pb-(--card-p)">
98
+ <CardHeader className="pb-(--card-p) shadow-[inset_0_-1px_0_var(--border)]">
99
99
  {/* CardTitle carries the type; the h1 inside it is the page heading. */}
100
100
  <CardTitle>
101
101
  <h1 id="chat-title">Scroll behaviour</h1>
@@ -234,7 +234,7 @@ export function ChatPanel() {
234
234
  </Message>
235
235
  </MessageScroller>
236
236
 
237
- <CardFooter className="border-t border-border pt-(--card-p)">
237
+ <CardFooter className="pt-(--card-p) shadow-[inset_0_1px_0_var(--border)]">
238
238
  <ChatComposer
239
239
  className="flex-1"
240
240
  placeholder="Ask a follow-up…"
@@ -1,17 +1,17 @@
1
1
  # DashboardPage
2
2
 
3
- 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.
3
+ 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.
4
4
 
5
- **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).
5
+ **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).
6
6
 
7
7
  ## Notes
8
8
 
9
9
  - Region cap is `wide` (1280px): dashboards hold tables and multi-column grids, never a reading column.
10
- - 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.
10
+ - 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.
11
11
  - 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.
12
12
  - 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.
13
13
  - 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.
14
- - 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.
14
+ - 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.
15
15
  - 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`.
16
16
  - 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.
17
17
  - The page has exactly one heading-1 (PageHeader); card titles are Title tier, not headings.
@@ -23,7 +23,7 @@ App-shell creator dashboard: a branded Sidebar with an account footer (AppHeader
23
23
  - AppShell provides the skip link and <main id="main-content"> landmark.
24
24
  - The active SidebarItem and BottomNavItem carry aria-current via `active`.
25
25
  - DataTable headers are real <th>; sortable columns are announced.
26
- - Each sell-through Meter has an aria-label naming the edition and aria-valuetext reading "184 of 250 sold".
26
+ - Each seat-usage Meter has an aria-label naming the account and aria-valuetext reading "184 of 250 seats used".
27
27
  - Avatars beside a visible name use an empty alt so the name is not read twice.
28
28
 
29
29
  ## Source
@@ -31,11 +31,11 @@ App-shell creator dashboard: a branded Sidebar with an account footer (AppHeader
31
31
  ```tsx
32
32
  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"
33
33
 
34
- // Edition status → Badge tone. Sable has no status vocabulary of its own;
34
+ // Account status → Badge tone. Sable has no status vocabulary of its own;
35
35
  // an app maps ITS states onto tones once, here, and every badge follows.
36
- const STATUS_TONE = { live: "success", scheduled: "info", "sold-out": "edition", closed: "neutral" } as const
37
- const STATUS_LABEL = { live: "Live", scheduled: "Scheduled", "sold-out": "Sold out", closed: "Closed" } as const
38
- type EditionStatus = keyof typeof STATUS_TONE
36
+ const STATUS_TONE = { active: "success", onboarding: "info", full: "1", cancelled: "neutral" } as const
37
+ const STATUS_LABEL = { active: "Active", onboarding: "Onboarding", full: "Seats full", cancelled: "Cancelled" } as const
38
+ type AccountStatus = keyof typeof STATUS_TONE
39
39
 
40
40
  const MIN = 60 * 1000
41
41
  const HOUR = 60 * MIN
@@ -48,54 +48,54 @@ const shortDate = (offset: number) =>
48
48
 
49
49
  const stats = [
50
50
  { label: "Revenue", value: "$18,240", delta: "+8.2%", up: true, note: "vs. $16,857" },
51
- { label: "Collects", value: "1,284", delta: "+3.1%", up: true, note: "182 first-time" },
52
- { label: "Collectors", value: "312", delta: "+12.4%", up: true, note: "Best month yet" },
53
- { label: "Sell-through", value: "64%", delta: "−1.8%", up: false, note: "6 open editions" },
51
+ { label: "Active seats", value: "1,284", delta: "+3.1%", up: true, note: "182 added" },
52
+ { label: "Accounts", value: "312", delta: "+12.4%", up: true, note: "Best month yet" },
53
+ { label: "Seat usage", value: "64%", delta: "−1.8%", up: false, note: "6 near their limit" },
54
54
  ]
55
55
 
56
- const editions = [
57
- { id: "ed_01", title: "Gold Rush", status: "live" as const, sold: 184, supply: 250, price: 48, released: -6 * DAY },
58
- { id: "ed_02", title: "Quiet Harbor", status: "sold-out" as const, sold: 100, supply: 100, price: 120, released: -19 * DAY },
59
- { id: "ed_03", title: "Dusk, no. 4", status: "live" as const, sold: 61, supply: 150, price: 64, released: -2 * DAY },
60
- { id: "ed_04", title: "Tidewater", status: "live" as const, sold: 212, supply: 300, price: 36, released: -27 * DAY },
61
- { id: "ed_05", title: "Salt Study", status: "closed" as const, sold: 38, supply: 75, price: 90, released: -44 * DAY },
62
- { id: "ed_06", title: "Paper Moon", status: "sold-out" as const, sold: 50, supply: 50, price: 220, released: -58 * DAY },
63
- { id: "ed_07", title: "Night Signal", status: "scheduled" as const, sold: 0, supply: 200, price: 56, released: 3 * DAY },
64
- { id: "ed_08", title: "Low Tide, Blue", status: "closed" as const, sold: 72, supply: 120, price: 42, released: -71 * DAY },
65
- { id: "ed_09", title: "Ember Field", status: "sold-out" as const, sold: 80, supply: 80, price: 140, released: -86 * DAY },
56
+ const accounts = [
57
+ { id: "ac_01", title: "Northwind", status: "active" as const, used: 184, seats: 250, price: 16, started: -6 * DAY },
58
+ { id: "ac_02", title: "Harbor Labs", status: "full" as const, used: 100, seats: 100, price: 20, started: -19 * DAY },
59
+ { id: "ac_03", title: "Brightline", status: "active" as const, used: 61, seats: 150, price: 16, started: -2 * DAY },
60
+ { id: "ac_04", title: "Tidewater Co.", status: "active" as const, used: 212, seats: 300, price: 12, started: -27 * DAY },
61
+ { id: "ac_05", title: "Saltworks", status: "cancelled" as const, used: 38, seats: 75, price: 20, started: -44 * DAY },
62
+ { id: "ac_06", title: "Paper Moon", status: "full" as const, used: 50, seats: 50, price: 24, started: -58 * DAY },
63
+ { id: "ac_07", title: "Signal Studio", status: "onboarding" as const, used: 0, seats: 200, price: 16, started: 3 * DAY },
64
+ { id: "ac_08", title: "Bluewater", status: "cancelled" as const, used: 72, seats: 120, price: 12, started: -71 * DAY },
65
+ { id: "ac_09", title: "Ember & Field", status: "full" as const, used: 80, seats: 80, price: 20, started: -86 * DAY },
66
66
  ]
67
- const editionCount = 24
67
+ const accountCount = 312
68
68
 
69
69
  const payout = {
70
- amount: 8214.6,
70
+ amount: 8862.64,
71
71
  date: 5 * DAY,
72
72
  lines: [
73
- { label: "Sales", value: 9127.33 },
74
- { label: "Platform fee (10%)", value: -912.73 },
73
+ { label: "Subscriptions", value: 9127.33 },
74
+ { label: "Processing (2.9%)", value: -264.69 },
75
75
  { label: "Deposits to", value: "Checking ••4821" },
76
76
  ],
77
77
  }
78
78
 
79
- const collectors = [
80
- { id: "co_01", name: "Nina Simone", handle: "@nina", initials: "NS", pieces: 14, spent: 1460 },
81
- { id: "co_02", name: "Grace Hopper", handle: "@grace", initials: "GH", pieces: 11, spent: 1128 },
82
- { id: "co_03", name: "Frida Kahlo", handle: "@frida", initials: "FK", pieces: 9, spent: 942 },
83
- { id: "co_04", name: "Mary Shelley", handle: "@mary", initials: "MS", pieces: 6, spent: 588 },
79
+ const customers = [
80
+ { id: "cu_01", name: "Northwind", handle: "northwind.io", initials: "NW", seats: 184, spent: 2944 },
81
+ { id: "cu_02", name: "Tidewater Co.", handle: "tidewater.co", initials: "TC", seats: 212, spent: 2544 },
82
+ { id: "cu_03", name: "Harbor Labs", handle: "harborlabs.com", initials: "HL", seats: 100, spent: 2000 },
83
+ { id: "cu_04", name: "Ember & Field", handle: "emberfield.com", initials: "EF", seats: 80, spent: 1600 },
84
84
  ]
85
85
 
86
86
  const upcoming = [
87
- { id: "up_01", title: "Night Signal", detail: "Edition of 200 · $56", at: 3 * DAY },
88
- { id: "up_02", title: "Tidewater, second printing", detail: "Edition of 100 · $40", at: 12 * DAY },
89
- { id: "up_03", title: "Dusk, no. 5", detail: "Edition of 150 · $64", at: 26 * DAY },
90
- { id: "up_04", title: "Quiet Harbor, artist proofs", detail: "Edition of 10 · $480", at: 34 * DAY },
87
+ { id: "up_01", title: "Version 2.4", detail: "Shared reports · All plans", at: 3 * DAY },
88
+ { id: "up_02", title: "Audit log export", detail: "Business plan · Beta", at: 12 * DAY },
89
+ { id: "up_03", title: "Version 2.5", detail: "Offline mode · All plans", at: 26 * DAY },
90
+ { id: "up_04", title: "Usage-based billing", detail: "Studio and Business · Beta", at: 34 * DAY },
91
91
  ]
92
92
 
93
93
  const activity = [
94
- { id: "ac_01", who: "Nina Simone", initials: "NS", what: "collected Gold Rush · 185 of 250", when: 2 * MIN },
95
- { id: "ac_02", who: "Grace Hopper", initials: "GH", what: "collected Dusk, no. 4 · 61 of 150", when: 26 * MIN },
96
- { id: "ac_03", who: "Frida Kahlo", initials: "FK", what: "tipped $20 on Tidewater", when: 1 * HOUR },
97
- { id: "ac_04", who: "Mary Shelley", initials: "MS", what: "started following you", when: 3 * HOUR },
98
- { id: "ac_05", who: "Nina Simone", initials: "NS", what: "set a reminder for Night Signal", when: 6 * HOUR },
94
+ { id: "ev_01", who: "Nina Simone", initials: "NS", what: "added 12 seats to Northwind · 184 of 250", when: 2 * MIN },
95
+ { id: "ev_02", who: "Grace Hopper", initials: "GH", what: "upgraded Brightline to Studio", when: 26 * MIN },
96
+ { id: "ev_03", who: "Frida Kahlo", initials: "FK", what: "paid invoice INV-0042 for Tidewater Co.", when: 1 * HOUR },
97
+ { id: "ev_04", who: "Mary Shelley", initials: "MS", what: "joined the Harbor Labs workspace", when: 3 * HOUR },
98
+ { id: "ev_05", who: "Nina Simone", initials: "NS", what: "booked onboarding for Signal Studio", when: 6 * HOUR },
99
99
  ]
100
100
 
101
101
  export function DashboardPage() {
@@ -117,8 +117,8 @@ export function DashboardPage() {
117
117
  bottomNav={
118
118
  <BottomNav aria-label="Primary">
119
119
  <BottomNavItem icon={<Icon name="grid" />} label="Overview" showLabel active render={<a href="/" />} />
120
- <BottomNavItem icon={<Icon name="image-stack" />} label="Editions" showLabel render={<a href="/editions" />} />
121
- <BottomNavItem icon={<Icon name="user" />} label="Collectors" showLabel render={<a href="/collectors" />} />
120
+ <BottomNavItem icon={<Icon name="folder" />} label="Accounts" showLabel render={<a href="/accounts" />} />
121
+ <BottomNavItem icon={<Icon name="user" />} label="Customers" showLabel render={<a href="/customers" />} />
122
122
  <BottomNavItem icon={<Icon name="bell" />} label="Activity" showLabel render={<a href="/activity" />} />
123
123
  <BottomNavItem icon={<Icon name="gear" />} label="Settings" showLabel render={<a href="/settings" />} />
124
124
  </BottomNav>
@@ -130,8 +130,8 @@ export function DashboardPage() {
130
130
  </SidebarHeader>
131
131
  <SidebarNav>
132
132
  <SidebarItem icon={<Icon name="grid" />} label="Overview" active render={<a href="/" />} />
133
- <SidebarItem icon={<Icon name="image-stack" />} label="Editions" render={<a href="/editions" />} />
134
- <SidebarItem icon={<Icon name="user" />} label="Collectors" render={<a href="/collectors" />} />
133
+ <SidebarItem icon={<Icon name="folder" />} label="Accounts" render={<a href="/accounts" />} />
134
+ <SidebarItem icon={<Icon name="user" />} label="Customers" render={<a href="/customers" />} />
135
135
  <SidebarItem
136
136
  icon={<Icon name="bell" />}
137
137
  label="Activity"
@@ -162,7 +162,7 @@ export function DashboardPage() {
162
162
  <Page max="wide" gap={4} className="py-6">
163
163
  <PageHeader
164
164
  title="Overview"
165
- description="Sales and collector activity for the last 30 days."
165
+ description="Revenue and account activity for the last 30 days."
166
166
  actions={
167
167
  <Row gap={2}>
168
168
  <Button variant="outline">
@@ -171,7 +171,7 @@ export function DashboardPage() {
171
171
  </Button>
172
172
  <Button>
173
173
  <Icon name="plus" />
174
- New edition
174
+ New account
175
175
  </Button>
176
176
  </Row>
177
177
  }
@@ -201,43 +201,43 @@ export function DashboardPage() {
201
201
  <Grid cols={{ base: 1, xl: 3 }} gap={3}>
202
202
  <Card className="flex flex-col xl:col-span-2">
203
203
  <CardHeader>
204
- <CardTitle>Editions</CardTitle>
205
- <CardDescription>Sales across your releases, newest first.</CardDescription>
204
+ <CardTitle>Accounts</CardTitle>
205
+ <CardDescription>Seats across your customers, newest first.</CardDescription>
206
206
  </CardHeader>
207
207
  <CardContent flush className="mt-4 flex-1">
208
208
  <DataTable
209
- data={editions}
209
+ data={accounts}
210
210
  rowKey={(row) => row.id}
211
- defaultSort={{ key: "released", direction: "desc" }}
211
+ defaultSort={{ key: "started", direction: "desc" }}
212
212
  columns={[
213
- { key: "title", header: "Edition", sortable: true, className: "text-label-md whitespace-nowrap" },
213
+ { key: "title", header: "Account", sortable: true, className: "text-label-md whitespace-nowrap" },
214
214
  {
215
215
  key: "status",
216
216
  header: "Status",
217
217
  cell: (row) => (
218
- <Badge appearance="plain" tone={STATUS_TONE[row.status as EditionStatus]} dot>
219
- {STATUS_LABEL[row.status as EditionStatus]}
218
+ <Badge appearance="plain" tone={STATUS_TONE[row.status as AccountStatus]} dot>
219
+ {STATUS_LABEL[row.status as AccountStatus]}
220
220
  </Badge>
221
221
  ),
222
222
  },
223
223
  {
224
- key: "sold",
225
- header: "Sold",
224
+ key: "used",
225
+ header: "Seats",
226
226
  sortable: true,
227
- sortValue: (row) => row.sold / row.supply,
227
+ sortValue: (row) => row.used / row.seats,
228
228
  cell: (row) => (
229
229
  <Row gap={3} align="center" className="min-w-40">
230
230
  <Meter
231
- value={row.sold}
232
- max={row.supply}
231
+ value={row.used}
232
+ max={row.seats}
233
233
  size="sm"
234
- tone={row.sold === row.supply ? "success" : "primary"}
235
- aria-label={`${row.title} sold`}
236
- getAriaValueText={(_, v) => `${v} of ${row.supply} sold`}
234
+ tone={row.used === row.seats ? "success" : "primary"}
235
+ aria-label={`${row.title} seats used`}
236
+ getAriaValueText={(_, v) => `${v} of ${row.seats} seats used`}
237
237
  className="flex-1"
238
238
  />
239
239
  <span className="text-mono-caption whitespace-nowrap text-muted-foreground">
240
- {row.sold}/{row.supply}
240
+ {row.used}/{row.seats}
241
241
  </span>
242
242
  </Row>
243
243
  ),
@@ -247,21 +247,21 @@ export function DashboardPage() {
247
247
  header: "Revenue",
248
248
  align: "end",
249
249
  sortable: true,
250
- sortValue: (row) => row.sold * row.price,
250
+ sortValue: (row) => row.used * row.price,
251
251
  cell: (row) =>
252
- row.sold > 0 ? (
253
- <span className="text-mono-sm">{usd.format(row.sold * row.price)}</span>
252
+ row.used > 0 ? (
253
+ <span className="text-mono-sm">{usd.format(row.used * row.price)}</span>
254
254
  ) : (
255
255
  <span className="text-mono-sm text-muted-foreground">—</span>
256
256
  ),
257
257
  },
258
258
  {
259
- key: "released",
260
- header: "Release",
259
+ key: "started",
260
+ header: "Started",
261
261
  align: "end",
262
262
  sortable: true,
263
263
  cell: (row) => (
264
- <span className="text-body-sm whitespace-nowrap text-muted-foreground">{shortDate(row.released)}</span>
264
+ <span className="text-body-sm whitespace-nowrap text-muted-foreground">{shortDate(row.started)}</span>
265
265
  ),
266
266
  },
267
267
  ]}
@@ -269,10 +269,10 @@ export function DashboardPage() {
269
269
  </CardContent>
270
270
  <CardFooter className="justify-between">
271
271
  <span className="text-caption text-muted-foreground">
272
- Showing {editions.length} of {editionCount} editions
272
+ Showing {accounts.length} of {accountCount} accounts
273
273
  </span>
274
- <Button variant="ghost" size="sm" bleed="end" render={<a href="/editions" />}>
275
- View all editions
274
+ <Button variant="ghost" size="sm" bleed="end" render={<a href="/accounts" />}>
275
+ View all accounts
276
276
  <Icon name="arrow-right" />
277
277
  </Button>
278
278
  </CardFooter>
@@ -303,21 +303,21 @@ export function DashboardPage() {
303
303
  </Card>
304
304
  <Card>
305
305
  <CardHeader>
306
- <CardTitle>Top collectors</CardTitle>
306
+ <CardTitle>Top customers</CardTitle>
307
307
  <CardDescription>By spend this month.</CardDescription>
308
308
  <CardAction>
309
- <Button variant="ghost" size="sm" render={<a href="/collectors" />}>
309
+ <Button variant="ghost" size="sm" render={<a href="/customers" />}>
310
310
  View all
311
311
  </Button>
312
312
  </CardAction>
313
313
  </CardHeader>
314
314
  <CardList>
315
- {collectors.map((person) => (
315
+ {customers.map((person) => (
316
316
  <Entity
317
317
  key={person.id}
318
318
  leading={<Avatar variant="art" size="sm" fallback={person.initials} alt="" />}
319
319
  title={person.name}
320
- subtitle={`${person.handle} · ${person.pieces} pieces`}
320
+ subtitle={`${person.handle} · ${person.seats} seats`}
321
321
  action={<span className="text-mono-sm">{usd.format(person.spent)}</span>}
322
322
  />
323
323
  ))}
@@ -329,7 +329,7 @@ export function DashboardPage() {
329
329
  <Card className="flex flex-col">
330
330
  <CardHeader>
331
331
  <CardTitle>Upcoming releases</CardTitle>
332
- <CardDescription>Scheduled to open to collectors.</CardDescription>
332
+ <CardDescription>Scheduled to ship to customers.</CardDescription>
333
333
  </CardHeader>
334
334
  <CardList>
335
335
  {upcoming.map((release) => (
@@ -342,7 +342,7 @@ export function DashboardPage() {
342
342
  ))}
343
343
  </CardList>
344
344
  <CardFooter className="mt-auto">
345
- <Button variant="outline" size="sm" render={<a href="/editions/new" />}>
345
+ <Button variant="outline" size="sm" render={<a href="/releases/new" />}>
346
346
  <Icon name="calendar" />
347
347
  Schedule a release
348
348
  </Button>
@@ -351,7 +351,7 @@ export function DashboardPage() {
351
351
  <Card className="xl:col-span-2">
352
352
  <CardHeader>
353
353
  <CardTitle>Activity</CardTitle>
354
- <CardDescription>Collects, tips, and follows.</CardDescription>
354
+ <CardDescription>Seats, upgrades, and invoices.</CardDescription>
355
355
  <CardAction>
356
356
  <Button variant="ghost" size="sm" render={<a href="/activity" />}>
357
357
  View all
@@ -1,6 +1,6 @@
1
1
  # FeedPage
2
2
 
3
- 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.
3
+ 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.
4
4
 
5
5
  **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).
6
6
 
@@ -10,7 +10,7 @@ Social reading feed with a side rail: a feed-width main column (composer, For yo
10
10
  - 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.
11
11
  - 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.
12
12
  - 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.
13
- - 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.
13
+ - 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.
14
14
  - 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.
15
15
  - 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.
16
16
 
@@ -19,7 +19,7 @@ Social reading feed with a side rail: a feed-width main column (composer, For yo
19
19
  - Each post is an <article>, so AT users can jump between posts; the rail is a labelled <aside> landmark.
20
20
  - Rail card titles render as h2 (CardTitle `render={<h2 />}`) under the page's one h1.
21
21
  - Icon-only buttons and every Follow / Remind button carry aria-labels naming their target; counts stay in the accessible name.
22
- - The supply Meter is role=meter with a getAriaValueText reading ("62 of 100 collected").
22
+ - The capacity Meter is role=meter with a getAriaValueText reading ("28 of 40 seats taken").
23
23
  - The Skeleton card is aria-busy; the loading Button keeps its label ("Loading posts").
24
24
 
25
25
  ## Source
@@ -36,7 +36,7 @@ type Post = {
36
36
  following: boolean
37
37
  body: string
38
38
  media?: { src: string; alt: string }
39
- drop?: { title: string; price: string; collected: number; supply: number }
39
+ event?: { title: string; price: string; registered: number; capacity: number }
40
40
  likes: number
41
41
  comments: number
42
42
  }
@@ -49,9 +49,9 @@ const posts: Post[] = [
49
49
  initials: "AL",
50
50
  time: "2h",
51
51
  following: true,
52
- body: "Gold Rush is live. 100 signed prints, each paired with the on-chain original. Thank you for waiting.",
53
- media: { src: "/media/edition-cover.svg", alt: "Gold Rush, a gold-leaf study in overlapping arcs" },
54
- drop: { title: "Gold Rush", price: "$120", collected: 62, supply: 100 },
52
+ body: "Registration for the spring workshop is open. Two days of plotter drawing in a small group. See you there.",
53
+ media: { src: "/media/edition-cover.svg", alt: "Workshop poster, a gold-leaf study in overlapping arcs" },
54
+ event: { title: "Spring plotter workshop", price: "$120", registered: 28, capacity: 40 },
55
55
  likes: 128,
56
56
  comments: 24,
57
57
  },
@@ -74,7 +74,7 @@ const posts: Post[] = [
74
74
  initials: "NS",
75
75
  time: "8h",
76
76
  following: false,
77
- body: "Collectors of Dusk, no. 3 — your unlockable score is now in your library. Headphones on for this one.",
77
+ body: "Thanks to everyone at the Dusk listening session — the full recording is now in your library. Headphones on.",
78
78
  likes: 214,
79
79
  comments: 31,
80
80
  },
@@ -86,8 +86,8 @@ const suggestions = [
86
86
  ]
87
87
 
88
88
  const upcoming = [
89
- { id: "d_01", title: "Quiet Harbor", artist: "Mary Shelley", initials: "MS", date: "Sat, Oct 12" },
90
- { id: "d_02", title: "Dusk, no. 4", artist: "Nina Simone", initials: "NS", date: "Tue, Oct 15" },
89
+ { id: "e_01", title: "Quiet Harbor talk", host: "Mary Shelley", initials: "MS", date: "Sat, Oct 12" },
90
+ { id: "e_02", title: "Dusk, live", host: "Nina Simone", initials: "NS", date: "Tue, Oct 15" },
91
91
  ]
92
92
 
93
93
  const renderPost = (post: Post) => (
@@ -113,35 +113,35 @@ const renderPost = (post: Post) => (
113
113
  {post.media ? (
114
114
  <AspectBox ratio={16 / 9} className="overflow-hidden rounded-concentric-lg/6 bg-muted">
115
115
  <img src={post.media.src} alt={post.media.alt} className="size-full object-cover" />
116
- {post.drop ? (
116
+ {post.event ? (
117
117
  <div className="absolute start-3 top-3">
118
118
  <Badge appearance="overlay" size="sm" dot>
119
- Live drop
119
+ Open now
120
120
  </Badge>
121
121
  </div>
122
122
  ) : null}
123
123
  </AspectBox>
124
124
  ) : null}
125
- {post.drop ? (
125
+ {post.event ? (
126
126
  <Stack gap={1.5}>
127
127
  <Row gap={2} align="center" justify="between">
128
128
  <div className="min-w-0">
129
- <p className="text-title-md text-foreground">{post.drop.title}</p>
129
+ <p className="text-title-md text-foreground">{post.event.title}</p>
130
130
  <p className="text-body-sm text-muted-foreground">
131
- {`${post.drop.price} · Edition of ${post.drop.supply}`}
131
+ {`${post.event.price} · ${post.event.capacity} seats`}
132
132
  </p>
133
133
  </div>
134
- <Button>{`Collect for ${post.drop.price}`}</Button>
134
+ <Button>{`Register for ${post.event.price}`}</Button>
135
135
  </Row>
136
136
  <Meter
137
- value={post.drop.collected}
138
- max={post.drop.supply}
137
+ value={post.event.registered}
138
+ max={post.event.capacity}
139
139
  size="sm"
140
- getAriaValueText={(_, value) => `${value} of ${post.drop?.supply} collected`}
140
+ getAriaValueText={(_, value) => `${value} of ${post.event?.capacity} seats taken`}
141
141
  >
142
142
  <Row gap={2} align="center" justify="between" className="mb-2">
143
- <MeterLabel>{`${post.drop.collected} collected`}</MeterLabel>
144
- <MeterValue>{(_, value) => `${(post.drop?.supply ?? 0) - value} left`}</MeterValue>
143
+ <MeterLabel>{`${post.event.registered} registered`}</MeterLabel>
144
+ <MeterValue>{(_, value) => `${(post.event?.capacity ?? 0) - value} left`}</MeterValue>
145
145
  </Row>
146
146
  <MeterTrack />
147
147
  </Meter>
@@ -182,15 +182,15 @@ export function FeedPage() {
182
182
  autoResize
183
183
  rows={2}
184
184
  aria-label="New post"
185
- placeholder="Share a process shot or an upcoming drop"
185
+ placeholder="Share a process shot or an upcoming event"
186
186
  />
187
187
  <Row gap={1} align="center" justify="between">
188
188
  <Row gap={0.5}>
189
189
  <Button variant="ghost" size="icon-sm" aria-label="Add images">
190
190
  <Icon name="image-stack" />
191
191
  </Button>
192
- <Button variant="ghost" size="icon-sm" aria-label="Attach an edition">
193
- <Icon name="gem" />
192
+ <Button variant="ghost" size="icon-sm" aria-label="Add an event">
193
+ <Icon name="calendar" />
194
194
  </Button>
195
195
  </Row>
196
196
  <Button variant="secondary" size="sm">
@@ -257,17 +257,17 @@ export function FeedPage() {
257
257
  </Card>
258
258
  <Card>
259
259
  <CardHeader>
260
- <CardTitle render={<h2 />}>Upcoming drops</CardTitle>
260
+ <CardTitle render={<h2 />}>Upcoming events</CardTitle>
261
261
  </CardHeader>
262
262
  <CardList>
263
- {upcoming.map((drop) => (
263
+ {upcoming.map((event) => (
264
264
  <Entity
265
- key={drop.id}
266
- leading={<Avatar variant="art" fallback={drop.initials} alt={drop.artist} />}
267
- title={drop.title}
268
- subtitle={`${drop.artist} · ${drop.date}`}
265
+ key={event.id}
266
+ leading={<Avatar variant="art" fallback={event.initials} alt={event.host} />}
267
+ title={event.title}
268
+ subtitle={`${event.host} · ${event.date}`}
269
269
  action={
270
- <Button variant="ghost" size="icon-sm" aria-label={`Remind me about ${drop.title}`}>
270
+ <Button variant="ghost" size="icon-sm" aria-label={`Remind me about ${event.title}`}>
271
271
  <Icon name="bell" />
272
272
  </Button>
273
273
  }
@@ -285,7 +285,7 @@ export function LandingPage() {
285
285
  <CardDescription>Team plan</CardDescription>
286
286
  </CardHeader>
287
287
  <CardContent>
288
- <Meter value={64} tone="standard">
288
+ <Meter value={64} tone="3">
289
289
  <Row justify="between" wrap className="mb-2">
290
290
  <MeterLabel>Used</MeterLabel>
291
291
  <MeterValue>{(_, value) => `${(value * 30).toLocaleString("en-US")} of 3,000`}</MeterValue>