@boost33/ui 0.7.1 → 0.8.0-next.1

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 (68) hide show
  1. package/CHANGELOG.md +257 -0
  2. package/dist/buttons.module.css.js +10 -0
  3. package/dist/buttons.module.css.js.map +1 -0
  4. package/dist/components/AppShell/AppShell.d.ts +129 -17
  5. package/dist/components/AppShell/AppShell.js +78 -50
  6. package/dist/components/AppShell/AppShell.js.map +1 -1
  7. package/dist/components/AppShell/AppShell.module.css.js +22 -14
  8. package/dist/components/AppShell/AppShell.module.css.js.map +1 -1
  9. package/dist/components/AppSwitcher/AppSwitcher.d.ts +92 -45
  10. package/dist/components/AppSwitcher/AppSwitcher.js +76 -68
  11. package/dist/components/AppSwitcher/AppSwitcher.js.map +1 -1
  12. package/dist/components/AppSwitcher/AppSwitcher.module.css.js +42 -36
  13. package/dist/components/AppSwitcher/AppSwitcher.module.css.js.map +1 -1
  14. package/dist/components/AppSwitcher/icons.js +10 -10
  15. package/dist/components/AppSwitcher/icons.js.map +1 -1
  16. package/dist/components/AppSwitcher/labels.d.ts +15 -23
  17. package/dist/components/AppSwitcher/labels.js +3 -4
  18. package/dist/components/AppSwitcher/labels.js.map +1 -1
  19. package/dist/components/CommandPalette/CommandPalette.d.ts +8 -2
  20. package/dist/components/CommandPalette/CommandPalette.js.map +1 -1
  21. package/dist/components/NavItem/NavItem.d.ts +15 -2
  22. package/dist/components/NavItem/NavItem.js +14 -11
  23. package/dist/components/NavItem/NavItem.js.map +1 -1
  24. package/dist/components/NavItem/NavItem.module.css.js +6 -2
  25. package/dist/components/NavItem/NavItem.module.css.js.map +1 -1
  26. package/dist/components/NavSection/NavSection.module.css.js +5 -5
  27. package/dist/components/NotificationButton/NotificationButton.d.ts +29 -0
  28. package/dist/components/NotificationButton/NotificationButton.js +23 -0
  29. package/dist/components/NotificationButton/NotificationButton.js.map +1 -0
  30. package/dist/components/NotificationButton/NotificationButton.module.css.js +14 -0
  31. package/dist/components/NotificationButton/NotificationButton.module.css.js.map +1 -0
  32. package/dist/components/NotificationButton/labels.d.ts +12 -0
  33. package/dist/components/NotificationButton/labels.js +15 -0
  34. package/dist/components/NotificationButton/labels.js.map +1 -0
  35. package/dist/components/NotificationCenter/NotificationCenter.js +37 -36
  36. package/dist/components/NotificationCenter/NotificationCenter.js.map +1 -1
  37. package/dist/components/NotificationCenter/NotificationCenter.module.css.js +43 -43
  38. package/dist/components/Sidebar/NavHeightBudgetWarning.js +10 -10
  39. package/dist/components/Sidebar/NavHeightBudgetWarning.js.map +1 -1
  40. package/dist/components/Sidebar/Sidebar.module.css.js +1 -1
  41. package/dist/components/TopBar/TopBar.d.ts +55 -4
  42. package/dist/components/TopBar/TopBar.js +24 -7
  43. package/dist/components/TopBar/TopBar.js.map +1 -1
  44. package/dist/components/TopBar/TopBar.module.css.js +12 -8
  45. package/dist/components/TopBar/TopBar.module.css.js.map +1 -1
  46. package/dist/components/TopBar/labels.d.ts +20 -0
  47. package/dist/components/TopBar/labels.js +15 -0
  48. package/dist/components/TopBar/labels.js.map +1 -0
  49. package/dist/components/WalletChip/WalletChip.d.ts +77 -2
  50. package/dist/components/WalletChip/WalletChip.js +142 -50
  51. package/dist/components/WalletChip/WalletChip.js.map +1 -1
  52. package/dist/components/WalletChip/WalletChip.module.css.js +93 -47
  53. package/dist/components/WalletChip/WalletChip.module.css.js.map +1 -1
  54. package/dist/components/WalletChip/labels.d.ts +63 -1
  55. package/dist/components/WalletChip/labels.js +21 -6
  56. package/dist/components/WalletChip/labels.js.map +1 -1
  57. package/dist/deprecated-prop-warning.d.ts +5 -0
  58. package/dist/deprecated-prop-warning.js +11 -0
  59. package/dist/deprecated-prop-warning.js.map +1 -0
  60. package/dist/index.d.ts +9 -3
  61. package/dist/index.js +55 -49
  62. package/dist/index.js.map +1 -1
  63. package/dist/nav-height-budget.d.ts +12 -9
  64. package/dist/nav-height-budget.js +3 -3
  65. package/dist/nav-height-budget.js.map +1 -1
  66. package/dist/style.css +1 -1
  67. package/dist/theme.css +1 -1
  68. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -31,6 +31,263 @@ Three components are built to Stage A on main and are not releasable until
31
31
  each passes its fidelity gate. Their entries wait here for the release that
32
32
  ships them.
33
33
 
34
+ ### Changed — the shell redesign, part 1 of 3: the sidebar and the app switcher (B33-1711)
35
+
36
+ The 3 Nov 2026 navigation redesign (B33-1710; Paper file Boost Products, page
37
+ 101 Hub, row V0.7, boards 20 and 21). **This changes how every consumer's
38
+ sidebar and switcher look, and every gated screenshot of them.** No new
39
+ baseline has been captured: the person approving the redesign captures them
40
+ (CLAUDE.md). Parts 2 and 3 (B33-1712, B33-1713) land in the same release.
41
+
42
+ What a consumer sees, and what it has to do:
43
+
44
+ - **`AppShell` takes `appName`.** The product's name, drawn as plain text at
45
+ the top of the sidebar with no icon. Optional: omit it and nothing draws
46
+ there. Pass your product's display name, in your language. It names no
47
+ landmark (`label` still does that).
48
+ - **`AppSwitcher` no longer draws products that do not exist yet.** A row with
49
+ `live: false` is left out, unless it is the current product. Keep passing the
50
+ registry whole -- do not filter it yourself. A product the account does not
51
+ hold (`entitled: false`) is still drawn, with its "Not on plan" tag: hiding it
52
+ would tell a client their product does not exist. This overrules the
53
+ Vocabulary document's "the switcher shows every product regardless of state",
54
+ which is amended to match.
55
+ - **The "Soon" tag is gone with them.** `labels.soon` stays in the type, now
56
+ optional and deprecated, and is ignored: a labels object that sets it still
57
+ compiles, and a catalogue typed as a full `AppSwitcherLabels` no longer has to
58
+ carry it. Drop it when convenient.
59
+ - **The switcher's account row is gone.** `account` is now optional and
60
+ ignored, and `AppSwitcherAccount` is deprecated. The organisation moves to the
61
+ top bar in part 2. Nothing breaks if you keep passing it; stop when
62
+ convenient. Its `onSelect` is never called. In development, passing
63
+ `account` logs a console warning once (`process.env.NODE_ENV` other than
64
+ `production`); production builds are silent.
65
+ - **New, optional, per product row:** `group` (a section header, e.g. "Your
66
+ tools" -- rows are grouped in the order served; no groups means one plain
67
+ list) and `trailing` (`{ kind: 'value', value: '8,940' }` or
68
+ `{ kind: 'dot', label: '2 new' }` -- the dot's `label` is what a screen reader
69
+ hears). Both are data you are served; the switcher fetches nothing.
70
+ - **The switcher is redrawn to board 21.** A 32px tinted trigger with no border
71
+ and the product name in the display face; a 360px borderless panel; a dark
72
+ Hub row with a chevron; 28px product tiles; the current row semibold on a
73
+ muted ground. The product marks are unchanged (the set approved 29 Oct 2026).
74
+ - **Nav rows:** the active label is regular weight now, not semibold (the white
75
+ row and full-ink colour carry "you are here"). Counts are 12px medium and
76
+ right-aligned. `countUrgent` draws a filled pill instead of orange text --
77
+ white on `--color-orange-600`, 5.18:1, because the old orange text was 3.45:1
78
+ on the sidebar and failed AA. New `dot` on `NavItem` and `AppShellNavItem`: a
79
+ 7px attention dot, its string read by a screen reader.
80
+ - **Section labels** track at 0.09em (`--tracking-wider`), up from 0.05em.
81
+ - **Three new component-private tokens** (in `style.css`, no Tailwind
82
+ utility): `--container-app-switcher` (360px), `--spacing-2_75` (11px),
83
+ `--spacing-1_75` (7px).
84
+
85
+ The 56px rail keeps working, icon-only. The redesign draws no rail, so it gets
86
+ no new design.
87
+
88
+ Repo-only: the specimen's unarmed-switcher route is removed (the row it showed
89
+ is gone), and a new "Shell redesign — B33-1710" route at the bottom of the
90
+ specimen assembles boards 20 and 21 for review.
91
+
92
+ ### Changed — the shell redesign, part 3 of 3: the sidebar action, and every button on three tiers (B33-1713)
93
+
94
+ Board 20 of the same redesign, and the consolidation its review asked for.
95
+ **Every button the package draws changes look**; no new baseline has been
96
+ captured (the person approving the redesign captures them).
97
+
98
+ What a consumer sees, and what it has to do:
99
+
100
+ - **New `AppShell.action`** (`AppShellAction`: `label`, optional `icon`, and
101
+ `href` or `onClick`): your product's main action, drawn at the foot of the
102
+ sidebar directly above the footer's divider -- board 20's "New scan". Near-black,
103
+ full width, 40px. Not drawn with neither `href` nor `onClick`. On the rail it
104
+ shows its icon alone, its label kept as its accessible name, and is not drawn
105
+ if it has no icon. If you drew your own action button elsewhere in the shell,
106
+ pass it here instead.
107
+ - **The footer sits under a hairline when there is an action**, 10px above its
108
+ rows, and its rows are 2px apart (the nav's own pitch), down from 16px.
109
+ - **Three button tiers, written down in ADR-0016:** primary (filled, 40px, ink or
110
+ accent), outlined (32px, white, 1px edge) and quiet (a word, no box). Every
111
+ package button now takes one.
112
+ - **`WalletChip`'s "Top up" is no longer orange text.** At rest it is the quiet
113
+ tier in full ink; past the threshold it is the primary tier in the accent tone.
114
+ The orange text was 3.69:1 on white and failed AA at 12px.
115
+ - **The accent tone is `--color-orange-600`**, not the brand orange: white on the
116
+ brand orange is 3.69:1. "Buy tokens" and the grown "Top up" both use it.
117
+ Decided by Jamal on 4 Nov 2026.
118
+ - **`NotificationCenter`'s "Mark all as read" and "View all"** are the quiet tier.
119
+ The component is still Stage A and not releasable.
120
+ - **If you pass `action`, add 67px to `consumerSlotsPx`** when you call
121
+ `navBudgetForViewport` yourself: the 40px button, 16px clear above it, and the
122
+ hairline and 10px the footer then takes. The development warning measures the
123
+ real column and needs nothing.
124
+
125
+ Not done here, and why: board 20's Settings row carries an external-link arrow,
126
+ which no ticket in Wave 4 asks for and `NavItem` has no slot for. It wants its
127
+ own ticket.
128
+
129
+ Repo-only: `src/buttons.module.css` holds the tiers. The "Shell redesign —
130
+ B33-1710" specimen route's shell gains "New scan" and a Settings / Help footer.
131
+
132
+ ### Changed — the shell redesign, part 2 of 3: the top bar, the token chip, the bell (B33-1712)
133
+
134
+ Boards 20, 21 and 22 of the same redesign. **This is a breaking layout change
135
+ for every consumer of `AppShell`: the switcher and the wallet chip move out of
136
+ the sidebar into the top bar, and the breadcrumb is no longer drawn.** Every
137
+ gated screenshot of the shell, the top bar and the sidebar changes; no new
138
+ baseline has been captured (the person approving the redesign captures them).
139
+
140
+ What a consumer sees, and what it has to do:
141
+
142
+ - **The top bar has three regions.** Left: the Boost33 mark (linked home
143
+ through `hubHref`, as the sidebar wordmark was) and the new `org` slot.
144
+ Centre: "Switch Tools →" then the switcher, "Check Your Tokens →" then the
145
+ token chip. Right: `topBar.actions` -- pass `NotificationButton` and your
146
+ avatar there.
147
+ - **`AppShell.switcher` and `AppShell.wallet` keep their names and move.**
148
+ Both now render in the top bar's centre group, not the sidebar. Each label is
149
+ drawn only when its control is passed. Pass `WalletChip` with
150
+ `placement="topbar"` in `wallet`; the sidebar card is not designed for the
151
+ bar.
152
+ - **The sidebar no longer draws the wordmark, the switcher or the wallet
153
+ chip.** It holds `appName`, the nav and `footer`, with 16px top and bottom
154
+ padding of its own.
155
+ - **New `AppShell.org`**: a slot for your organisation pill, beside the mark.
156
+ The package draws no organisation menu (that is AccountMenu, B33-493, which
157
+ has not passed its gate).
158
+ - **`topBar` is optional, and `topBar.breadcrumb` is deprecated and not
159
+ drawn.** It still compiles; stop passing it. `PageHeader` names the page.
160
+ **If you put `SidebarToggle` in `topBar.breadcrumb` (the Wallet does), move it
161
+ to the new `topBar.sidebarToggle` slot, or your rail loses its toggle.** The
162
+ screenshot gate does not catch this: the bar passed its baseline at 0.19%
163
+ with the toggle gone. So in development, passing `breadcrumb` -- to `TopBar`
164
+ or through AppShell `topBar` -- logs a console warning once, naming where a
165
+ toggle and an organisation switcher go instead; production builds are silent.
166
+ - **New `topBar.sidebarToggle`** (and `TopBar.sidebarToggle`): your rail
167
+ toggle, drawn at the far left of the bar before the mark. No board draws it;
168
+ judged at the gate.
169
+ `topBar.labels` (`TopBarLabels`, `defaultTopBarLabels`) translates the two
170
+ centre labels.
171
+ - **No search, and no `⌘K`.** Search was removed from the top bar on 3 Nov
172
+ 2026 until it is built, and nothing replaces it. `CommandPalette` stays in the
173
+ package, Stage A and unreleasable, with no trigger in the shell.
174
+ - **New `WalletChip placement="topbar"`**: a 32px pill drawn like the
175
+ switcher -- the balance in the mono face, a 44 x 6 two-segment bar, a
176
+ chevron -- that opens a 340px popover with the balance, the bar at width,
177
+ granted and purchased with what each means, your `burnNote`, an "Open" link
178
+ (`walletHref`, `linkComponent`) and a "Buy tokens" button (`onTopUp`). A zero
179
+ segment is drawn at 0px with the 2px gap kept, never as a sliver (B33-590).
180
+ `unknown` shows an em dash, no bar and no split. The default
181
+ `placement="sidebar"` card is unchanged.
182
+ - **New `WalletChip totalOnly` with a signed `balance`** (top bar only, enforced
183
+ by the type), for a balance the Ledger reports with a bucket below zero, which
184
+ no two-segment bar can draw honestly. Below zero: the distance past zero in
185
+ `--color-danger` with the words "past zero", as the Wallet's past-zero screen
186
+ says it. At zero or above (a bucket below zero, a positive total -- decided by
187
+ Jamal on 3 Nov 2026): the plain figure. Neither draws a bar or a
188
+ granted / purchased legend. New labels: `pastZero`, `pastZeroLong`,
189
+ `pastZeroBalance`. **No board draws this state; it is judged at the gate.**
190
+ - **New labels for the popover:** `popover`, `grantedFigure`,
191
+ `purchasedFigure`, `grantedNote`, `grantedNoteUndated`, `purchasedNote`,
192
+ `open`, `buyTokens`. Each defaults to English.
193
+ - **New `NotificationButton`**: the 32px bell for `topBar.actions`, with an
194
+ `unread` dot. It fetches nothing and renders nothing without `onClick` or
195
+ `href`. The NotificationCenter panel stays Stage A and is not wired.
196
+ - **"Buy tokens" is white on `--color-orange-600`** (5.18:1), not the brand
197
+ orange board 21 draws (3.69:1, fails AA at 13px). Decided by Jamal on 4 Nov
198
+ 2026: orange that carries text is `--color-orange-600`.
199
+ - **The top bar spans the full width,** above the sidebar and the product's
200
+ column alike (board 20). Until now it sat over the content column only and
201
+ the sidebar ran the full height.
202
+ - **Two new component-private tokens:** `--container-token-popover` (340px),
203
+ `--spacing-0_75` (3px).
204
+ - **The nav height budget moves, because the sidebar's furniture moved.**
205
+ `navBudgetForViewport(900)` and `checkNavHeightBudget`'s default budget rise
206
+ from 629 to 730px: the top bar (48) is now counted above the sidebar, and the
207
+ wordmark, switcher and wallet chip no longer take sidebar height. The default
208
+ `consumerSlotsPx` is now 44 (the footer alone), down from 86 (switcher plus
209
+ footer). If you pass your own `consumerSlotsPx`, drop your switcher's height
210
+ from it. A 13-item nav that scrolled at 900px now fits; the dev warning in
211
+ `Sidebar` follows the same numbers.
212
+ - **The app name no longer shrinks** when the nav is long enough to scroll
213
+ (it lost 2px of its 38).
214
+
215
+ The 56px rail keeps working, icon-only, and gets no new design. The top bar
216
+ does not collapse.
217
+
218
+ Repo-only: the specimen's "Shell redesign — B33-1710" route grows the top bar,
219
+ the open token popover, board 22's nothing-purchased chip, and the unknown,
220
+ past-zero and total-only chips.
221
+
222
+ ### Added — `AppShell` gets out of the way on a phone: `chromeFrom` and `phoneChrome`
223
+
224
+ AppShell has no phone layout, and the 0.8 top bar runs off a 390px screen.
225
+ Products keep their own phone chrome below a breakpoint, and until now the only
226
+ ways to do that were a `matchMedia` switch (the desktop shell flashes on a phone
227
+ before it swaps) or CSS reaching into the shell's DOM shape. Both options are opt-in;
228
+ a shell that sets neither renders exactly as before.
229
+
230
+ - **`chromeFrom?: 'md' | 'lg' | 'xl'`** (new type `AppShellBreakpoint`). Below
231
+ that viewport width the shell hides its own top bar and sidebar, the page
232
+ body takes the full width, and the document scrolls instead of the shell's
233
+ content box. Done in the shell's own `@media` CSS, so the first paint is
234
+ already right, with no flash and no second render of the page. The widths are
235
+ Tailwind v4's defaults -- `md` 48rem (768px), `lg` 64rem (1024px), `xl` 80rem
236
+ (1280px) -- so `lg` here and `lg:` in your own markup flip at the same pixel.
237
+ It is a name and not a number because a media query cannot read a value
238
+ passed at runtime.
239
+ - **`phoneChrome?: ReactNode`**: your phone header and navigation. The shell
240
+ draws it above the page body, outside `<main>`, and shows it only below
241
+ `chromeFrom`. Ignored without `chromeFrom`. Pass your phone chrome here and
242
+ drop any `lg:hidden` and any selector that hid the kit's bar or `<aside>` by
243
+ position: those depended on a DOM shape this package does not promise.
244
+ - Not a mobile shell. Nothing is redesigned below the breakpoint; the shell only
245
+ stops drawing its desktop chrome. A phone design needs a board first.
246
+ - The sidebar's development-only nav-height warning no longer fires when the
247
+ sidebar is hidden this way.
248
+
249
+ ### Note — in a Tailwind v4 app, `style.css` alone re-sizes the app's own utilities
250
+
251
+ **Importing `@boost33/ui/style.css` changes what a Tailwind v4 app already has
252
+ on screen, with or without `theme.css`.** Off Market and Brokers measured it
253
+ adopting 0.8.0-next.0. This is not new in 0.8, but 0.8 is when Tailwind v4
254
+ apps first take the shell.
255
+
256
+ Why: `style.css` carries `tokens.css`, which declares this package's tokens on
257
+ `:root`, unlayered, under the names Tailwind v4 itself uses (`--text-*`,
258
+ `--leading-*`, `--tracking-*`, `--radius-*`, `--font-*`, `--font-weight-*`,
259
+ `--color-*`). Tailwind's default utilities read those names through `var()`
260
+ (`.text-sm { font-size: var(--text-sm) }`), and an unlayered declaration beats
261
+ Tailwind's `@layer theme`. So in the app, `text-sm` becomes 13px (Tailwind's
262
+ 14px) with a 13px line height, `text-base` 15px (16px), and `rounded-sm`, `-md`,
263
+ `-lg`, `-xl`, `-2xl` become 3/4/6/8/12px (4/6/8/12/16px). The `leading-*` and
264
+ `tracking-*` steps move too.
265
+
266
+ To keep your own scale, tell Tailwind to write literal values into its
267
+ utilities so they never read `:root`. Either import Tailwind's theme inline:
268
+
269
+ ```css
270
+ @layer theme, base, components, utilities;
271
+ @import "tailwindcss/theme.css" layer(theme) theme(inline);
272
+ @import "tailwindcss/preflight.css" layer(base);
273
+ @import "tailwindcss/utilities.css" layer(utilities);
274
+ @import "@boost33/ui/style.css";
275
+ ```
276
+
277
+ or pin each colliding name you use under `@theme inline { … }` with Tailwind's
278
+ value. Either way the kit's components keep reading `:root` and render as
279
+ designed. Brokers measured identical computed styles on all 12 of its pages
280
+ with the first recipe.
281
+
282
+ `theme.css` is a separate, optional step on top of this. It adds utilities for
283
+ names only this package has (`bg-surface`, `border-border`, …) and makes the
284
+ kit's values the app's whole scale (ADR-0011, ADR-0012). Every kit component
285
+ renders correctly without it (the specimen imports no `theme.css`). An app that
286
+ used those utilities and drops `theme.css` loses them.
287
+
288
+ The shell also sets `font-family: var(--font-sans)` (Inter) on its frame, so
289
+ `children` inherit Inter unless the page sets its own family.
290
+
34
291
  ### Changed — `CHANGELOG.md` now ships inside the package
35
292
 
36
293
  `package.json` `files` adds `CHANGELOG.md` next to `dist`, so this file is in
@@ -0,0 +1,10 @@
1
+ const t = "_primary_qxhao_26", a = "_quiet_qxhao_77", o = {
2
+ primary: t,
3
+ quiet: a
4
+ };
5
+ export {
6
+ o as default,
7
+ t as primary,
8
+ a as quiet
9
+ };
10
+ //# sourceMappingURL=buttons.module.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buttons.module.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -10,12 +10,34 @@ export interface AppShellNavItem {
10
10
  active?: boolean;
11
11
  /** Trailing-slot count. Omit to leave the slot empty; it still reserves its width so counts line up. */
12
12
  count?: number;
13
- /** Renders `count` in the primary color instead of muted. */
13
+ /** Draws `count` as the filled "needs you" pill instead of muted text. */
14
14
  countUrgent?: boolean;
15
+ /** A trailing attention dot; the string is what a screen reader hears. Ignored when `count` is set. */
16
+ dot?: string;
15
17
  /** Where the row goes. A nav row's destination is data; the shell renders it as a link. */
16
18
  href?: string;
17
19
  }
18
20
  /** One nav section: an optional header over a run of rows. */
21
+ /**
22
+ * The product's main action, drawn as a button at the foot of the sidebar,
23
+ * above the footer's divider (B33-1713, board 20's "New scan"). Data, like a
24
+ * nav row: the shell renders it, in the primary tier's ink tone (ADR-0016).
25
+ */
26
+ /**
27
+ * A named width for `AppShell.chromeFrom`, with Tailwind v4's default values:
28
+ * `md` 48rem (768px), `lg` 64rem (1024px), `xl` 80rem (1280px).
29
+ */
30
+ export type AppShellBreakpoint = 'md' | 'lg' | 'xl';
31
+ export interface AppShellAction {
32
+ /** The button's words, in the consumer's language -- "New scan". */
33
+ label: string;
34
+ /** A leading icon, decorative. On the 56px rail it is all that shows. */
35
+ icon?: ReactNode;
36
+ /** Where it goes. Renders a link through `linkComponent`. Wins over `onClick`. */
37
+ href?: string;
38
+ /** What pressing it does. Renders a `<button>`. */
39
+ onClick?: () => void;
40
+ }
19
41
  export interface AppShellNavSection {
20
42
  /** The section header. Omit for a headerless section (the nav's first section has none). On the rail every header drops. */
21
43
  label?: string;
@@ -65,40 +87,130 @@ export interface AppShellProps {
65
87
  * resolved by a consumer passing this.
66
88
  */
67
89
  navLabel?: string;
90
+ /**
91
+ * The product's name, drawn as plain text at the top of the sidebar (B33-1711).
92
+ * The product name only -- "Boost Score", in the consumer's language -- with
93
+ * no icon beside it: the product mark belongs in the app switcher, not next to
94
+ * the name. Omit it and nothing is drawn there.
95
+ *
96
+ * It is visible text and nothing else. It names no landmark (that is `label`)
97
+ * and nothing branches on it. Often the same string as `label`; it is a
98
+ * separate prop because one is read aloud as a landmark's name and the other
99
+ * is read on screen, and a consumer may want them to differ.
100
+ *
101
+ * Hidden on the 56px rail, where there is no room for a word.
102
+ */
103
+ appName?: string;
68
104
  /** The navigation, as data. No product hardcodes rows; the shell composes them. */
69
105
  nav: AppShellNavSection[];
70
- /** The top bar's content -- the breadcrumb and the shell actions on its right. */
71
- topBar: TopBarProps;
72
- /** The Boost33 mark for the wordmark lockup. Passed in, the way Wordmark takes it; on the rail it is all that shows. Pass it unlinked -- `hubHref` links the whole lockup. */
106
+ /**
107
+ * The top bar's own content: its `actions` on the right (the notification
108
+ * button and the avatar) and its `labels`. The shell fills the rest of the bar
109
+ * itself -- the mark and `org` on the left, `switcher` and `wallet` in the
110
+ * centre -- so those are props of the shell, not of this object.
111
+ *
112
+ * `breadcrumb` is still accepted and no longer drawn (B33-1712): the redesign
113
+ * puts the mark and the organisation where it was. A sidebar toggle kept
114
+ * inside it goes in `topBar.sidebarToggle` instead, at the far left of the
115
+ * bar, or the rail loses its toggle.
116
+ */
117
+ topBar?: Omit<TopBarProps, 'start' | 'switcher' | 'tokens'>;
118
+ /**
119
+ * The Boost33 mark, at the left of the top bar (B33-1712; it was the
120
+ * sidebar's wordmark lockup until 0.8.0). Passed in, the way Wordmark takes
121
+ * it. Pass it unlinked -- `hubHref` links it. The word "Boost33" is not drawn
122
+ * beside it; it stays as the link's accessible name.
123
+ */
73
124
  mark?: ReactNode;
74
125
  /**
75
- * Where the hub lives. Set it and the wordmark in the sidebar's top-left
76
- * corner becomes a link home, rendered through `linkComponent`; omit it and
77
- * the wordmark is the plain lockup it always was (B33-1699). The consumer
126
+ * Where the hub lives. Set it and the mark at the left of the top bar becomes
127
+ * a link home, rendered through `linkComponent` (B33-1699). The consumer
78
128
  * supplies the URL -- Identity owns where the hub is and it differs per
79
129
  * environment, so the package never hardcodes it. See `Wordmark.hubHref`.
80
130
  */
81
131
  hubHref?: string;
82
132
  /**
83
- * The product switcher, sitting under the wordmark. A slot: the registry it
84
- * draws is served by Identity (ADR-0003), not owned here. Render its own
85
- * collapsed form for the rail.
133
+ * The organisation, beside the mark at the left of the top bar (B33-1712,
134
+ * board 20: "AC Acme Coaching ⌄"). A slot -- the product's own pill. The
135
+ * package draws no organisation menu: that is AccountMenu (B33-493), which has
136
+ * not passed its gate.
137
+ */
138
+ org?: ReactNode;
139
+ /**
140
+ * The app switcher, at the centre of the top bar after its "Switch Tools →"
141
+ * label (B33-1712; it sat under the wordmark in the sidebar until 0.8.0). A
142
+ * slot: pass `AppSwitcher`. The registry it draws is served by Identity
143
+ * (ADR-0003), not owned here. It no longer has a rail form to render -- the
144
+ * top bar does not collapse.
86
145
  */
87
146
  switcher?: ReactNode;
88
147
  /**
89
- * The wallet chip, pinned above the footer. A slot: the balance comes from
90
- * `@boost33/ledger-client`, never read here. Render its own collapsed form
91
- * for the rail.
148
+ * The token chip, at the centre of the top bar after the switcher and its
149
+ * "Check Your Tokens →" label (B33-1712; it was pinned above the sidebar's
150
+ * footer until 0.8.0). A slot: pass `WalletChip` with `placement="topbar"`.
151
+ * The balance comes from `@boost33/ledger-client`, never read here.
92
152
  */
93
153
  wallet?: ReactNode;
94
- /** The sidebar footer, below the wallet chip. A slot -- the product's own. */
154
+ /**
155
+ * The product's main action, at the foot of the sidebar directly above the
156
+ * footer's divider (B33-1713). Omit it and nothing is drawn. Given neither
157
+ * `href` nor `onClick`, it is not drawn either: a button that does nothing is
158
+ * a control that lies (B33-1270). On the rail it shows its icon alone, or
159
+ * nothing if it has none.
160
+ */
161
+ action?: AppShellAction;
162
+ /**
163
+ * The sidebar footer, at the foot of the sidebar, under a divider -- Settings
164
+ * and Help on board 20. A slot -- the product's own.
165
+ */
95
166
  footer?: ReactNode;
96
167
  /**
97
168
  * The sidebar collapsed to the 56px rail. A state of the shell: the sidebar
98
- * narrows and every piece it owns collapses off this one flag. The slots
99
- * (switcher, wallet, footer) render their own collapsed form.
169
+ * narrows to icon-only nav and every piece it owns collapses off this one
170
+ * flag. The footer slot renders its own collapsed form. The switcher and the
171
+ * token chip live in the top bar since 0.8.0 (B33-1712) and do not collapse;
172
+ * the rail has no redesign of its own and keeps working as it did.
100
173
  */
101
174
  collapsed?: boolean;
175
+ /**
176
+ * The width from which the shell draws its own chrome -- the top bar and the
177
+ * sidebar. Below it the shell hides both and the page body takes the full
178
+ * width, so a product renders its own phone header and navigation around the
179
+ * shell. Omit it and the chrome is drawn at every width, as before.
180
+ *
181
+ * Not a mobile shell: nothing is redesigned below the breakpoint, the shell
182
+ * only gets out of the way. AppShell has no phone layout of its own, and the
183
+ * 0.8 top bar runs off a 390px screen.
184
+ *
185
+ * Done in the shell's own CSS, `@media (width < …)`, so the first paint is
186
+ * already right and a server-rendered page does not flash the desktop chrome
187
+ * on a phone, which a `matchMedia` switch in the product does. That is also
188
+ * why it is a name and not a number: a media query cannot read a value passed
189
+ * at runtime. The names and widths are Tailwind v4's defaults, so `lg` here
190
+ * and `lg:` in a product's own markup flip at the same pixel:
191
+ * `md` 48rem (768px), `lg` 64rem (1024px), `xl` 80rem (1280px).
192
+ *
193
+ * Below the breakpoint the page scrolls the document instead of the shell's
194
+ * content box, so a product header above the shell scrolls with the page. The
195
+ * `<main>` landmark stays; the hidden chrome is `display: none`, so it leaves
196
+ * the accessibility tree too.
197
+ */
198
+ chromeFrom?: AppShellBreakpoint;
199
+ /**
200
+ * The product's own phone chrome -- its header, its navigation -- drawn by
201
+ * the shell above the page body and shown only below `chromeFrom`, the
202
+ * inverse of the top bar and sidebar. The shell's own CSS does both halves,
203
+ * so a product passes its phone header here and writes no selector and no
204
+ * `lg:hidden` of its own.
205
+ *
206
+ * Above `<main>`, not inside it: "jump to main" skips the chrome on a phone
207
+ * the way it skips the top bar and sidebar on a desktop. A slot -- the
208
+ * product's own markup; the shell draws no phone design.
209
+ *
210
+ * Ignored without `chromeFrom`: with no breakpoint there is no width at which
211
+ * it would show.
212
+ */
213
+ phoneChrome?: ReactNode;
102
214
  /**
103
215
  * The element every linked nav row renders through. Defaults to a plain `a`;
104
216
  * a consumer whose router owns navigation passes its own here and the shell
@@ -140,4 +252,4 @@ export interface AppShellProps {
140
252
  */
141
253
  children: ReactNode;
142
254
  }
143
- export declare function AppShell({ label, navLabel, nav, topBar, mark, hubHref, switcher, wallet, footer, collapsed, linkComponent, children, }: AppShellProps): import("react").JSX.Element;
255
+ export declare function AppShell({ label, appName, navLabel, nav, topBar, mark, hubHref, org, switcher, wallet, action, footer, collapsed, chromeFrom, phoneChrome, linkComponent, children, }: AppShellProps): import("react").JSX.Element;
@@ -1,58 +1,86 @@
1
- import { jsxs as i, jsx as r } from "react/jsx-runtime";
2
- import { Sidebar as x } from "../Sidebar/Sidebar.js";
3
- import { NavSection as S } from "../NavSection/NavSection.js";
4
- import { NavItem as g } from "../NavItem/NavItem.js";
5
- import { TopBar as j } from "../TopBar/TopBar.js";
6
- import { Wordmark as k } from "../Wordmark/Wordmark.js";
7
- import a from "./AppShell.module.css.js";
8
- function W({
9
- label: e,
10
- navLabel: t,
11
- nav: h,
12
- topBar: v,
13
- mark: f,
14
- hubHref: p,
15
- switcher: m,
16
- wallet: c,
17
- footer: o,
18
- collapsed: n = !1,
19
- linkComponent: s,
20
- children: u
1
+ import { jsxs as c, jsx as r, Fragment as N } from "react/jsx-runtime";
2
+ import { Sidebar as A } from "../Sidebar/Sidebar.js";
3
+ import { NavSection as C } from "../NavSection/NavSection.js";
4
+ import { NavItem as L } from "../NavItem/NavItem.js";
5
+ import { TopBar as U } from "../TopBar/TopBar.js";
6
+ import { Wordmark as $ } from "../Wordmark/Wordmark.js";
7
+ import B from "../../buttons.module.css.js";
8
+ import n from "./AppShell.module.css.js";
9
+ function F({
10
+ label: l,
11
+ appName: a,
12
+ navLabel: s,
13
+ nav: o,
14
+ topBar: t,
15
+ mark: d,
16
+ hubHref: m,
17
+ org: b,
18
+ switcher: S,
19
+ wallet: k,
20
+ action: y,
21
+ footer: u,
22
+ collapsed: i = !1,
23
+ chromeFrom: f,
24
+ phoneChrome: v,
25
+ linkComponent: h,
26
+ children: g
21
27
  }) {
22
- return /* @__PURE__ */ i("div", { className: a.shell, children: [
23
- /* @__PURE__ */ i(x, { landmarkLabel: e, collapsed: n, children: [
24
- /* @__PURE__ */ r("div", { className: a.wordmark, children: /* @__PURE__ */ r(k, { mark: f, collapsed: n, hubHref: p, linkComponent: s }) }),
25
- m != null && /* @__PURE__ */ r("div", { className: a.switcher, children: m }),
26
- /* @__PURE__ */ r("nav", { "aria-label": t ?? e, className: a.nav, children: h.map((d, N) => (
27
- // No room for a header at 56px, so on the rail every section renders
28
- // headerless -- its rows collapse to their icons and line up.
29
- /* @__PURE__ */ r(S, { label: n ? void 0 : d.label, children: d.items.map((l, b) => /* @__PURE__ */ r(
30
- g,
31
- {
32
- icon: l.icon,
33
- active: l.active,
34
- collapsed: n,
35
- count: l.count,
36
- countUrgent: l.countUrgent,
37
- href: l.href,
38
- linkComponent: s,
39
- children: l.label
40
- },
28
+ return /* @__PURE__ */ c("div", { className: n.shell, "data-chrome-from": f, children: [
29
+ f != null && v != null && /* @__PURE__ */ r("div", { className: n.phoneChrome, children: v }),
30
+ /* @__PURE__ */ r(
31
+ U,
32
+ {
33
+ ...t,
34
+ start: /* @__PURE__ */ c(N, { children: [
35
+ /* @__PURE__ */ r($, { mark: d, collapsed: !0, hubHref: m, linkComponent: h }),
41
36
  b
42
- )) }, N)
43
- )) }),
44
- (c != null || o != null) && /* @__PURE__ */ i("div", { className: a.foot, children: [
45
- c,
46
- o
47
- ] })
48
- ] }),
49
- /* @__PURE__ */ i("div", { className: a.main, children: [
50
- /* @__PURE__ */ r(j, { ...v }),
51
- /* @__PURE__ */ r("main", { className: a.content, children: u })
37
+ ] }),
38
+ switcher: S,
39
+ tokens: k
40
+ }
41
+ ),
42
+ /* @__PURE__ */ c("div", { className: n.body, children: [
43
+ /* @__PURE__ */ c(A, { landmarkLabel: l, collapsed: i, children: [
44
+ a != null && !i && /* @__PURE__ */ r("p", { className: n.appName, children: a }),
45
+ /* @__PURE__ */ r("nav", { "aria-label": s ?? l, className: n.nav, children: o.map((p, x) => (
46
+ // No room for a header at 56px, so on the rail every section renders
47
+ // headerless -- its rows collapse to their icons and line up.
48
+ /* @__PURE__ */ r(C, { label: i ? void 0 : p.label, children: p.items.map((e, j) => /* @__PURE__ */ r(
49
+ L,
50
+ {
51
+ icon: e.icon,
52
+ active: e.active,
53
+ collapsed: i,
54
+ count: e.count,
55
+ countUrgent: e.countUrgent,
56
+ dot: e.dot,
57
+ href: e.href,
58
+ linkComponent: h,
59
+ children: e.label
60
+ },
61
+ j
62
+ )) }, x)
63
+ )) }),
64
+ /* @__PURE__ */ r(I, { action: y, collapsed: i, linkComponent: h }),
65
+ u != null && /* @__PURE__ */ r("div", { className: n.foot, children: u })
66
+ ] }),
67
+ /* @__PURE__ */ r("div", { className: n.main, children: /* @__PURE__ */ r("main", { className: n.content, children: g }) })
52
68
  ] })
53
69
  ] });
54
70
  }
71
+ function I({ action: l, collapsed: a, linkComponent: s }) {
72
+ if (l === void 0 || l.href === void 0 && l.onClick === void 0 || a && l.icon == null) return null;
73
+ const o = /* @__PURE__ */ c(N, { children: [
74
+ l.icon != null && /* @__PURE__ */ r("span", { "aria-hidden": "true", children: l.icon }),
75
+ /* @__PURE__ */ r("span", { className: a ? n.srOnly : void 0, children: l.label })
76
+ ] }), t = `${B.primary} ${n.action}`, d = a ? "true" : void 0;
77
+ if (l.href !== void 0) {
78
+ const m = s ?? "a";
79
+ return /* @__PURE__ */ r("div", { className: n.actionSlot, children: /* @__PURE__ */ r(m, { className: t, href: l.href, "data-collapsed": d, children: o }) });
80
+ }
81
+ return /* @__PURE__ */ r("div", { className: n.actionSlot, children: /* @__PURE__ */ r("button", { type: "button", className: t, onClick: l.onClick, "data-collapsed": d, children: o }) });
82
+ }
55
83
  export {
56
- W as AppShell
84
+ F as AppShell
57
85
  };
58
86
  //# sourceMappingURL=AppShell.js.map