@boost33/ui 0.1.0 → 0.3.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 (86) hide show
  1. package/README.md +5 -2
  2. package/dist/anchored-overlay.d.ts +86 -0
  3. package/dist/anchored-overlay.js +125 -0
  4. package/dist/anchored-overlay.js.map +1 -0
  5. package/dist/components/AppShell/AppShell.d.ts +80 -0
  6. package/dist/components/AppShell/AppShell.js +59 -0
  7. package/dist/components/AppShell/AppShell.js.map +1 -0
  8. package/dist/components/AppShell/AppShell.module.css.js +20 -0
  9. package/dist/components/AppShell/AppShell.module.css.js.map +1 -0
  10. package/dist/components/AppSwitcher/AppSwitcher.d.ts +96 -0
  11. package/dist/components/AppSwitcher/AppSwitcher.js +81 -0
  12. package/dist/components/AppSwitcher/AppSwitcher.js.map +1 -0
  13. package/dist/components/AppSwitcher/AppSwitcher.module.css.js +44 -0
  14. package/dist/components/AppSwitcher/AppSwitcher.module.css.js.map +1 -0
  15. package/dist/components/AppSwitcher/icons.d.ts +20 -0
  16. package/dist/components/AppSwitcher/icons.js +25 -0
  17. package/dist/components/AppSwitcher/icons.js.map +1 -0
  18. package/dist/components/AppSwitcher/labels.d.ts +60 -0
  19. package/dist/components/AppSwitcher/labels.js +16 -0
  20. package/dist/components/AppSwitcher/labels.js.map +1 -0
  21. package/dist/components/Boost33Mark/Boost33Mark.d.ts +65 -0
  22. package/dist/components/Boost33Mark/Boost33Mark.js +62 -0
  23. package/dist/components/Boost33Mark/Boost33Mark.js.map +1 -0
  24. package/dist/components/Boost33Mark/Boost33Mark.module.css.js +12 -0
  25. package/dist/components/Boost33Mark/Boost33Mark.module.css.js.map +1 -0
  26. package/dist/components/NavItem/NavItem.d.ts +18 -2
  27. package/dist/components/NavItem/NavItem.js +18 -0
  28. package/dist/components/NavItem/NavItem.js.map +1 -0
  29. package/dist/components/NavItem/NavItem.module.css.js +18 -0
  30. package/dist/components/NavItem/NavItem.module.css.js.map +1 -0
  31. package/dist/components/NavSection/NavSection.d.ts +12 -0
  32. package/dist/components/NavSection/NavSection.js +14 -0
  33. package/dist/components/NavSection/NavSection.js.map +1 -0
  34. package/dist/components/NavSection/NavSection.module.css.js +10 -0
  35. package/dist/components/NavSection/NavSection.module.css.js.map +1 -0
  36. package/dist/components/PageHeader/PageHeader.d.ts +10 -0
  37. package/dist/components/PageHeader/PageHeader.js +15 -0
  38. package/dist/components/PageHeader/PageHeader.js.map +1 -0
  39. package/dist/components/PageHeader/PageHeader.module.css.js +16 -0
  40. package/dist/components/PageHeader/PageHeader.module.css.js.map +1 -0
  41. package/dist/components/ProductMark/ProductMark.d.ts +63 -0
  42. package/dist/components/ProductMark/ProductMark.js +73 -0
  43. package/dist/components/ProductMark/ProductMark.js.map +1 -0
  44. package/dist/components/ProductMark/ProductMark.module.css.js +10 -0
  45. package/dist/components/ProductMark/ProductMark.module.css.js.map +1 -0
  46. package/dist/components/Sidebar/NavHeightBudgetWarning.d.ts +3 -0
  47. package/dist/components/Sidebar/NavHeightBudgetWarning.js +28 -0
  48. package/dist/components/Sidebar/NavHeightBudgetWarning.js.map +1 -0
  49. package/dist/components/Sidebar/Sidebar.d.ts +19 -0
  50. package/dist/components/Sidebar/Sidebar.js +14 -0
  51. package/dist/components/Sidebar/Sidebar.js.map +1 -0
  52. package/dist/components/Sidebar/Sidebar.module.css.js +8 -0
  53. package/dist/components/Sidebar/Sidebar.module.css.js.map +1 -0
  54. package/dist/components/TopBar/TopBar.d.ts +8 -0
  55. package/dist/components/TopBar/TopBar.js +12 -0
  56. package/dist/components/TopBar/TopBar.js.map +1 -0
  57. package/dist/components/TopBar/TopBar.module.css.js +12 -0
  58. package/dist/components/TopBar/TopBar.module.css.js.map +1 -0
  59. package/dist/components/WalletChip/WalletChip.d.ts +60 -0
  60. package/dist/components/WalletChip/WalletChip.js +68 -0
  61. package/dist/components/WalletChip/WalletChip.js.map +1 -0
  62. package/dist/components/WalletChip/WalletChip.module.css.js +56 -0
  63. package/dist/components/WalletChip/WalletChip.module.css.js.map +1 -0
  64. package/dist/components/WalletChip/format.d.ts +47 -0
  65. package/dist/components/WalletChip/format.js +29 -0
  66. package/dist/components/WalletChip/format.js.map +1 -0
  67. package/dist/components/WalletChip/icons.d.ts +8 -0
  68. package/dist/components/WalletChip/icons.js +18 -0
  69. package/dist/components/WalletChip/icons.js.map +1 -0
  70. package/dist/components/WalletChip/labels.d.ts +143 -0
  71. package/dist/components/WalletChip/labels.js +38 -0
  72. package/dist/components/WalletChip/labels.js.map +1 -0
  73. package/dist/components/Wordmark/Wordmark.d.ts +12 -0
  74. package/dist/components/Wordmark/Wordmark.js +12 -0
  75. package/dist/components/Wordmark/Wordmark.js.map +1 -0
  76. package/dist/components/Wordmark/Wordmark.module.css.js +12 -0
  77. package/dist/components/Wordmark/Wordmark.module.css.js.map +1 -0
  78. package/dist/index.d.ts +26 -2
  79. package/dist/index.js +41 -35
  80. package/dist/index.js.map +1 -1
  81. package/dist/nav-height-budget.d.ts +58 -1
  82. package/dist/nav-height-budget.js +38 -0
  83. package/dist/nav-height-budget.js.map +1 -0
  84. package/dist/style.css +1 -1
  85. package/dist/theme.css +189 -0
  86. package/package.json +17 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppSwitcher.js","sources":["../../../src/components/AppSwitcher/AppSwitcher.tsx"],"sourcesContent":["'use client'\n\n// A client module: the switcher holds open/closed state and wires clicks, focus\n// and Escape through useAnchoredOverlay, none of which a Server Component can\n// carry. The rest of the shell stays on the server -- see WalletChip, Sidebar\n// and B33-846. AppShell renders this as a slot (`switcher`), so it imports no\n// client code by taking it.\n\nimport type { ElementType } from 'react'\nimport { useAnchoredOverlay } from '../../anchored-overlay'\nimport { ProductMark } from '../ProductMark/ProductMark'\nimport { Check, Chevron, ChevronRight, HubMark } from './icons'\nimport { resolveAppSwitcherLabels, type AppSwitcherLabels } from './labels'\nimport styles from './AppSwitcher.module.css'\n\n/**\n * One product the switcher can jump to. It is Identity's registry row, supplied\n * by the consuming app (ADR-0003) -- the switcher fetches nothing and hardcodes\n * no list. Every product is rendered, entitled or not: hiding an unowned one\n * means nobody learns the family exists (B33-479).\n */\nexport interface AppSwitcherProduct {\n /** Identity's product slug, which selects the ProductMark artwork. */\n slug: string\n /** The product's display name, e.g. \"Boost Score\". Identity's, not the slug. */\n name: string\n /** Where selecting this product goes -- the product's own URL, never the hub. */\n href: string\n /**\n * Whether the account holds this product. Defaults to `true`. `false` dims the\n * mark and shows the \"Not on plan\" tag, but the row stays an ordinary focusable\n * link: entitlement is a rendering hint, and the switcher is not the thing\n * deciding what someone may open (ADR-0010, \"Not entitled is not disabled\").\n */\n entitled?: boolean\n}\n\n/** The \"all products\" row above the divider. Its blurb and URL are the consumer's. */\nexport interface AppSwitcherHub {\n /** The row's name, e.g. \"Hub\". */\n name: string\n /** The line beneath it, e.g. \"All Boost33 products\". */\n description: string\n /** The hub URL. */\n href: string\n}\n\n/**\n * The account/workspace row at the foot of the panel. A row that opens a second\n * panel (a later ticket owns that panel), so it is a button rather than a link;\n * this ticket renders the row and calls `onSelect` when it is chosen.\n */\nexport interface AppSwitcherAccount {\n /** The organisation's name, e.g. \"Acme Coaching\". */\n name: string\n /** The avatar's initials, e.g. \"AC\". */\n initials: string\n /** The line beneath the name, preformatted, e.g. \"Admin · 2 workspaces\". */\n description: string\n /** Chosen -- opens the workspace panel. */\n onSelect?: () => void\n}\n\nexport interface AppSwitcherProps {\n /** The products, in the order Identity serves them. Rendered whole, entitled or not. */\n products: AppSwitcherProduct[]\n /**\n * The slug of the product this shell is rendering for. It names the trigger\n * and is the row that carries the check -- the product you are in.\n *\n * A slug matching nothing in `products` is a supported state, not a caller\n * error: the registry is Identity's and can run ahead of this package\n * (ADR-0003). The mark degrades to a bare tile and the trigger falls back to\n * `labels.switchProduct` for its name (B33-919).\n */\n currentSlug: string\n /** The \"all products\" row above the products. */\n hub: AppSwitcherHub\n /** The account/workspace row below the products. */\n account: AppSwitcherAccount\n /**\n * Every user-facing string that is not row data. Each field defaults to\n * English, and a field left out -- or supplied as `undefined`, which is what\n * a missed next-intl key looks like -- keeps its default (B33-873).\n */\n labels?: Partial<AppSwitcherLabels>\n /**\n * The element the product and hub rows render through. Defaults to a plain\n * `a`; a router-owning consumer passes its own so the rows route client-side\n * with the locale prefix intact (ADR-0011, B33-847). The account row stays a\n * button -- it opens a panel, it does not navigate.\n */\n linkComponent?: ElementType\n /** The 56px rail. The trigger collapses to the current product's mark alone. */\n collapsed?: boolean\n /** Render the panel open on first paint -- the specimen's Stage-A open state. */\n defaultOpen?: boolean\n /** Every open and close, so a consumer can un-clip an ancestor's overflow while the panel overhangs. */\n onOpenChange?: (open: boolean) => void\n}\n\n/**\n * The product switcher under the wordmark. It opens, lists every Boost33 product\n * with the current one checked, and takes you straight into the one you pick --\n * the real switcher the Wallet's chevron-that-opens-nothing never was (B33-479).\n *\n * A disclosure, not a menu (ADR-0010): the panel carries no widget role, the\n * rows are the real links and buttons they already are, and Tab is the\n * document's own tab order -- so tabbing through the products does not close the\n * panel on the first row. Dismissal, focus restoration and trigger ARIA are the\n * shared anchored-overlay contract's (ADR-0009); it never becomes a modal.\n */\nexport function AppSwitcher({\n products,\n currentSlug,\n hub,\n account,\n labels,\n linkComponent,\n collapsed = false,\n defaultOpen,\n onOpenChange,\n}: AppSwitcherProps) {\n const overlay = useAnchoredOverlay({ defaultOpen, onOpenChange })\n const t = resolveAppSwitcherLabels(labels)\n const Link: ElementType = linkComponent ?? 'a'\n const current = products.find((p) => p.slug === currentSlug)\n\n // The name the trigger carries, in both its shapes. `current` is `undefined`\n // whenever `currentSlug` matches nothing in the served registry -- a fifth\n // Identity product, or a slug left stale by a rename -- which ADR-0003\n // contemplates and `AppSwitcherProduct.slug` being `string` rather than\n // `ProductSlug` deliberately allows.\n //\n // Degrading the artwork to a bare tile there is right. Degrading the name is\n // not, and that is what happened: the mark is `aria-hidden` unless it is\n // given a title, so a collapsed trigger handed `title={undefined}` had no\n // accessible name at all. A screen reader reached the only control in the\n // 56px rail and announced \"button\" -- no product, no purpose, nothing to act\n // on. Expanded it was the same absence with a visible face: an empty\n // `.triggerName` between a blank tile and a chevron. CLAUDE.md puts labels in\n // the definition of done, so the fallback is a label prop rather than a\n // literal (B33-919, B33-849).\n const triggerName = current?.name ?? t.switchProduct\n\n return (\n <div className={styles.root}>\n <button\n type=\"button\"\n className={collapsed ? styles.railTrigger : styles.trigger}\n onClick={overlay.toggle}\n {...overlay.triggerProps}\n >\n {/* On the rail the mark is the button's only content, so it carries the\n accessible name (title); expanded, the name sits beside it and the\n mark is decorative, or a screen reader says the product twice.\n\n No `size`. ProductMark's default is 20px -- the Wallet's own size at\n its call sites, and the size all three appswitcher-* baselines were\n captured at. This passed 28 expanded and 32 collapsed until B33-479's\n fidelity pass: a mark 8px over budget in every row pushed the account\n row out of the open panel's frame entirely and truncated \"Boost\n Outbound\" to \"Boos...\". The rail is not a larger mark; it is the same\n mark with the label and chevron gone. */}\n <ProductMark slug={currentSlug} title={collapsed ? triggerName : undefined} />\n {!collapsed && (\n <>\n <span className={styles.triggerName}>{triggerName}</span>\n <Chevron className={styles.chevron} />\n </>\n )}\n </button>\n\n {overlay.open && (\n <div {...overlay.panelProps} className={styles.panel} aria-label={t.panel}>\n <Link className={styles.hubRow} href={hub.href} onClick={overlay.close}>\n <HubMark className={styles.hubMark} />\n <span className={styles.rowText}>\n <span className={styles.rowName}>{hub.name}</span>\n <span className={styles.rowSub}>{hub.description}</span>\n </span>\n </Link>\n\n <hr className={styles.divider} />\n\n <ul className={styles.products}>\n {products.map((product) => {\n const isCurrent = product.slug === currentSlug\n const notEntitled = product.entitled === false\n return (\n <li key={product.slug}>\n <Link\n className={styles.productRow}\n href={product.href}\n aria-current={isCurrent ? 'true' : undefined}\n data-current={isCurrent ? 'true' : undefined}\n data-entitled={notEntitled ? 'false' : undefined}\n onClick={overlay.close}\n >\n {/* Default 20px, as in the trigger above -- see the note there. */}\n <ProductMark slug={product.slug} muted={notEntitled} />\n <span className={styles.rowName}>{product.name}</span>\n {notEntitled && <span className={styles.tag}>{t.notOnPlan}</span>}\n {isCurrent && <Check className={styles.check} />}\n </Link>\n </li>\n )\n })}\n </ul>\n\n <hr className={styles.divider} />\n\n <button type=\"button\" className={styles.accountRow} onClick={account.onSelect}>\n <span className={styles.avatar} aria-hidden=\"true\">\n {account.initials}\n </span>\n <span className={styles.rowText}>\n <span className={styles.rowName}>{account.name}</span>\n <span className={styles.rowSub}>{account.description}</span>\n </span>\n <ChevronRight className={styles.chevronRight} />\n </button>\n </div>\n )}\n </div>\n )\n}\n"],"names":["AppSwitcher","products","currentSlug","hub","account","labels","linkComponent","collapsed","defaultOpen","onOpenChange","overlay","useAnchoredOverlay","t","resolveAppSwitcherLabels","Link","current","p","triggerName","jsxs","styles","jsx","ProductMark","Fragment","Chevron","HubMark","product","isCurrent","notEntitled","Check","ChevronRight"],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ const o = "_root_qm5nw_8", _ = "_trigger_qm5nw_17", r = "_railTrigger_qm5nw_43", t = "_triggerName_qm5nw_53", n = "_chevron_qm5nw_64", c = "_panel_qm5nw_93", w = "_hubRow_qm5nw_113", e = "_productRow_qm5nw_114", a = "_accountRow_qm5nw_115", s = "_products_qm5nw_146", m = "_rowName_qm5nw_160", g = "_rowText_qm5nw_164", i = "_rowSub_qm5nw_184", q = "_hubMark_qm5nw_192", u = "_tag_qm5nw_201", h = "_check_qm5nw_212", d = "_avatar_qm5nw_222", v = "_chevronRight_qm5nw_235", R = "_divider_qm5nw_244", p = {
2
+ root: o,
3
+ trigger: _,
4
+ railTrigger: r,
5
+ triggerName: t,
6
+ chevron: n,
7
+ panel: c,
8
+ hubRow: w,
9
+ productRow: e,
10
+ accountRow: a,
11
+ products: s,
12
+ rowName: m,
13
+ rowText: g,
14
+ rowSub: i,
15
+ hubMark: q,
16
+ tag: u,
17
+ check: h,
18
+ avatar: d,
19
+ chevronRight: v,
20
+ divider: R
21
+ };
22
+ export {
23
+ a as accountRow,
24
+ d as avatar,
25
+ h as check,
26
+ n as chevron,
27
+ v as chevronRight,
28
+ p as default,
29
+ R as divider,
30
+ q as hubMark,
31
+ w as hubRow,
32
+ c as panel,
33
+ e as productRow,
34
+ s as products,
35
+ r as railTrigger,
36
+ o as root,
37
+ m as rowName,
38
+ i as rowSub,
39
+ g as rowText,
40
+ u as tag,
41
+ _ as trigger,
42
+ t as triggerName
43
+ };
44
+ //# sourceMappingURL=AppSwitcher.module.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppSwitcher.module.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The 2x2 grid that stands for "all Boost33 products" on the Hub row. Not a
3
+ * ProductMark: the Hub is not a product and takes no dark tile -- it is four
4
+ * rounded squares in the row's own ink, the universal "all apps" glyph.
5
+ */
6
+ export declare function HubMark({ className }: {
7
+ className?: string;
8
+ }): import("react").JSX.Element;
9
+ /** The chevron on the trigger. Points down when closed; the CSS flips it up when open. */
10
+ export declare function Chevron({ className }: {
11
+ className?: string;
12
+ }): import("react").JSX.Element;
13
+ /** The chevron on the account row, pointing at the second panel it opens. */
14
+ export declare function ChevronRight({ className }: {
15
+ className?: string;
16
+ }): import("react").JSX.Element;
17
+ /** The check marking the current product -- a check, not a colour (B33-479). */
18
+ export declare function Check({ className }: {
19
+ className?: string;
20
+ }): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import { jsx as r, jsxs as t } from "react/jsx-runtime";
2
+ function o({ className: e }) {
3
+ return /* @__PURE__ */ t("svg", { className: e, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinejoin: "round", "aria-hidden": "true", children: [
4
+ /* @__PURE__ */ r("rect", { x: "3", y: "3", width: "7", height: "7", rx: "2" }),
5
+ /* @__PURE__ */ r("rect", { x: "14", y: "3", width: "7", height: "7", rx: "2" }),
6
+ /* @__PURE__ */ r("rect", { x: "3", y: "14", width: "7", height: "7", rx: "2" }),
7
+ /* @__PURE__ */ r("rect", { x: "14", y: "14", width: "7", height: "7", rx: "2" })
8
+ ] });
9
+ }
10
+ function i({ className: e }) {
11
+ return /* @__PURE__ */ r("svg", { className: e, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ r("path", { d: "M6 9l6 6 6-6" }) });
12
+ }
13
+ function h({ className: e }) {
14
+ return /* @__PURE__ */ r("svg", { className: e, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ r("path", { d: "M9 6l6 6-6 6" }) });
15
+ }
16
+ function d({ className: e }) {
17
+ return /* @__PURE__ */ r("svg", { className: e, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ r("path", { d: "M5 12l5 5 9-10" }) });
18
+ }
19
+ export {
20
+ d as Check,
21
+ i as Chevron,
22
+ h as ChevronRight,
23
+ o as HubMark
24
+ };
25
+ //# sourceMappingURL=icons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons.js","sources":["../../../src/components/AppSwitcher/icons.tsx"],"sourcesContent":["// The switcher's own small glyphs, drawn in `currentColor` so the surrounding\n// text colour carries them and the no-hardcoded-hex guard stays satisfied. All\n// decorative -- the row's own text carries meaning -- so every one is\n// aria-hidden. The four product marks are ProductMark's; these are only the\n// chrome ProductMark does not draw.\n\n/**\n * The 2x2 grid that stands for \"all Boost33 products\" on the Hub row. Not a\n * ProductMark: the Hub is not a product and takes no dark tile -- it is four\n * rounded squares in the row's own ink, the universal \"all apps\" glyph.\n */\nexport function HubMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"2\" />\n <rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"2\" />\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"2\" />\n <rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"2\" />\n </svg>\n )\n}\n\n/** The chevron on the trigger. Points down when closed; the CSS flips it up when open. */\nexport function Chevron({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M6 9l6 6 6-6\" />\n </svg>\n )\n}\n\n/** The chevron on the account row, pointing at the second panel it opens. */\nexport function ChevronRight({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M9 6l6 6-6 6\" />\n </svg>\n )\n}\n\n/** The check marking the current product -- a check, not a colour (B33-479). */\nexport function Check({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M5 12l5 5 9-10\" />\n </svg>\n )\n}\n"],"names":["HubMark","className","jsxs","jsx","Chevron","ChevronRight","Check"],"mappings":";AAWO,SAASA,EAAQ,EAAE,WAAAC,KAAqC;AAC7D,SACE,gBAAAC,EAAC,OAAA,EAAI,WAAAD,GAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,gBAAe,SAAQ,eAAY,QAClI,UAAA;AAAA,IAAA,gBAAAE,EAAC,QAAA,EAAK,GAAE,KAAI,GAAE,KAAI,OAAM,KAAI,QAAO,KAAI,IAAG,IAAA,CAAI;AAAA,IAC9C,gBAAAA,EAAC,QAAA,EAAK,GAAE,MAAK,GAAE,KAAI,OAAM,KAAI,QAAO,KAAI,IAAG,IAAA,CAAI;AAAA,IAC/C,gBAAAA,EAAC,QAAA,EAAK,GAAE,KAAI,GAAE,MAAK,OAAM,KAAI,QAAO,KAAI,IAAG,IAAA,CAAI;AAAA,IAC/C,gBAAAA,EAAC,QAAA,EAAK,GAAE,MAAK,GAAE,MAAK,OAAM,KAAI,QAAO,KAAI,IAAG,IAAA,CAAI;AAAA,EAAA,GAClD;AAEJ;AAGO,SAASC,EAAQ,EAAE,WAAAH,KAAqC;AAC7D,SACE,gBAAAE,EAAC,SAAI,WAAAF,GAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QACxJ,UAAA,gBAAAE,EAAC,QAAA,EAAK,GAAE,eAAA,CAAe,EAAA,CACzB;AAEJ;AAGO,SAASE,EAAa,EAAE,WAAAJ,KAAqC;AAClE,SACE,gBAAAE,EAAC,SAAI,WAAAF,GAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QACxJ,UAAA,gBAAAE,EAAC,QAAA,EAAK,GAAE,eAAA,CAAe,EAAA,CACzB;AAEJ;AAGO,SAASG,EAAM,EAAE,WAAAL,KAAqC;AAC3D,SACE,gBAAAE,EAAC,SAAI,WAAAF,GAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QAC1J,UAAA,gBAAAE,EAAC,QAAA,EAAK,GAAE,iBAAA,CAAiB,EAAA,CAC3B;AAEJ;"}
@@ -0,0 +1,60 @@
1
+ /** Every user-facing string the switcher renders that is not row data. */
2
+ export interface AppSwitcherLabels {
3
+ /**
4
+ * The panel's accessible name. A disclosure panel carries no widget role, so
5
+ * it takes an `aria-label` to be named at all (ADR-0010). Default
6
+ * "Boost33 products".
7
+ */
8
+ panel: string;
9
+ /**
10
+ * The tag on a product the account is not entitled to. Not a gate -- the row
11
+ * stays an ordinary link -- only a hint (ADR-0010: "Not entitled is not
12
+ * disabled"). Default "Not on plan".
13
+ */
14
+ notOnPlan: string;
15
+ /**
16
+ * The trigger's name when `currentSlug` matches no product in the supplied
17
+ * list, so there is no product name to put there. Default "Switch product".
18
+ *
19
+ * That case is reachable by design: `AppSwitcherProduct.slug` is typed
20
+ * `string` rather than `ProductSlug` so an unknown slug degrades to a bare
21
+ * tile instead of failing (ADR-0003 -- Identity owns the registry and this
22
+ * package does not know which slugs exist). A fifth Identity product, or a
23
+ * `currentSlug` left stale by a rename, lands there. Before B33-919 the
24
+ * degradation took the button's accessible name with it and the only control
25
+ * in the collapsed shell announced nothing at all.
26
+ *
27
+ * It names the control rather than describing the missing product, because
28
+ * the product is precisely what this package does not know. "Switch product"
29
+ * is true in every state; a guess at the product's name would not be.
30
+ */
31
+ switchProduct: string;
32
+ }
33
+ /**
34
+ * The English defaults. A consumer that passes nothing renders exactly what the
35
+ * appswitcher-* baselines were captured showing, so those baselines do not move.
36
+ * `switchProduct` is no exception: it only ever reaches the screen when
37
+ * `currentSlug` matches nothing, and all three baselines were captured with the
38
+ * Wallet current.
39
+ */
40
+ export declare const defaultAppSwitcherLabels: AppSwitcherLabels;
41
+ /**
42
+ * Merge a consumer's partial `labels` onto the English defaults, treating an
43
+ * explicitly-`undefined` field as unsupplied -- the same resolution
44
+ * `resolveWalletChipLabels` performs, for the same two reasons.
45
+ *
46
+ * The first is forward compatibility, and it is why this exists now. `labels`
47
+ * used to be a whole `AppSwitcherLabels`, so a consumer wrote the object out in
48
+ * full; adding `switchProduct` to a required-field interface would have failed
49
+ * the typecheck of every app that had done so, turning an accessibility fix
50
+ * into a breaking change across four products on a patch bump (CLAUDE.md,
51
+ * "Versioning"). Taking a `Partial` means a consumer supplies the fields it
52
+ * wants to translate and inherits the rest, today and at the next field.
53
+ *
54
+ * The second is that a missed translation key must fall back to English rather
55
+ * than blank the string. A next-intl `t('...')` returns `undefined` on a miss,
56
+ * so `{ panel: undefined }` is what one missing key looks like at the call
57
+ * site. A plain object spread copies that `undefined` across and unnames the
58
+ * panel; a per-field skip does not (B33-873).
59
+ */
60
+ export declare function resolveAppSwitcherLabels(labels?: Partial<AppSwitcherLabels>): AppSwitcherLabels;
@@ -0,0 +1,16 @@
1
+ const n = {
2
+ panel: "Boost33 products",
3
+ notOnPlan: "Not on plan",
4
+ switchProduct: "Switch product"
5
+ };
6
+ function r(t) {
7
+ const e = Object.fromEntries(
8
+ Object.entries(t ?? {}).filter(([, o]) => o !== void 0)
9
+ );
10
+ return { ...n, ...e };
11
+ }
12
+ export {
13
+ n as defaultAppSwitcherLabels,
14
+ r as resolveAppSwitcherLabels
15
+ };
16
+ //# sourceMappingURL=labels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labels.js","sources":["../../../src/components/AppSwitcher/labels.ts"],"sourcesContent":["// The switcher's own user-facing words, each a prop with an English default --\n// the same pattern WalletChipLabels follows, and for the same reason: the first\n// two consumers are French-default, and the package renders what it is given\n// rather than taking an i18n dependency (CLAUDE.md; B33-849). The product names,\n// the Hub's name and blurb, and the account row's name and subtitle are not here\n// -- they are Identity's data, supplied per row on `products`, `hub` and\n// `account`, not copy this package owns.\n\n/** Every user-facing string the switcher renders that is not row data. */\nexport interface AppSwitcherLabels {\n /**\n * The panel's accessible name. A disclosure panel carries no widget role, so\n * it takes an `aria-label` to be named at all (ADR-0010). Default\n * \"Boost33 products\".\n */\n panel: string\n /**\n * The tag on a product the account is not entitled to. Not a gate -- the row\n * stays an ordinary link -- only a hint (ADR-0010: \"Not entitled is not\n * disabled\"). Default \"Not on plan\".\n */\n notOnPlan: string\n /**\n * The trigger's name when `currentSlug` matches no product in the supplied\n * list, so there is no product name to put there. Default \"Switch product\".\n *\n * That case is reachable by design: `AppSwitcherProduct.slug` is typed\n * `string` rather than `ProductSlug` so an unknown slug degrades to a bare\n * tile instead of failing (ADR-0003 -- Identity owns the registry and this\n * package does not know which slugs exist). A fifth Identity product, or a\n * `currentSlug` left stale by a rename, lands there. Before B33-919 the\n * degradation took the button's accessible name with it and the only control\n * in the collapsed shell announced nothing at all.\n *\n * It names the control rather than describing the missing product, because\n * the product is precisely what this package does not know. \"Switch product\"\n * is true in every state; a guess at the product's name would not be.\n */\n switchProduct: string\n}\n\n/**\n * The English defaults. A consumer that passes nothing renders exactly what the\n * appswitcher-* baselines were captured showing, so those baselines do not move.\n * `switchProduct` is no exception: it only ever reaches the screen when\n * `currentSlug` matches nothing, and all three baselines were captured with the\n * Wallet current.\n */\nexport const defaultAppSwitcherLabels: AppSwitcherLabels = {\n panel: 'Boost33 products',\n notOnPlan: 'Not on plan',\n switchProduct: 'Switch product',\n}\n\n/**\n * Merge a consumer's partial `labels` onto the English defaults, treating an\n * explicitly-`undefined` field as unsupplied -- the same resolution\n * `resolveWalletChipLabels` performs, for the same two reasons.\n *\n * The first is forward compatibility, and it is why this exists now. `labels`\n * used to be a whole `AppSwitcherLabels`, so a consumer wrote the object out in\n * full; adding `switchProduct` to a required-field interface would have failed\n * the typecheck of every app that had done so, turning an accessibility fix\n * into a breaking change across four products on a patch bump (CLAUDE.md,\n * \"Versioning\"). Taking a `Partial` means a consumer supplies the fields it\n * wants to translate and inherits the rest, today and at the next field.\n *\n * The second is that a missed translation key must fall back to English rather\n * than blank the string. A next-intl `t('...')` returns `undefined` on a miss,\n * so `{ panel: undefined }` is what one missing key looks like at the call\n * site. A plain object spread copies that `undefined` across and unnames the\n * panel; a per-field skip does not (B33-873).\n */\nexport function resolveAppSwitcherLabels(labels?: Partial<AppSwitcherLabels>): AppSwitcherLabels {\n const supplied = Object.fromEntries(\n Object.entries(labels ?? {}).filter(([, value]) => value !== undefined),\n ) as Partial<AppSwitcherLabels>\n return { ...defaultAppSwitcherLabels, ...supplied }\n}\n"],"names":["defaultAppSwitcherLabels","resolveAppSwitcherLabels","labels","supplied","value"],"mappings":"AAgDO,MAAMA,IAA8C;AAAA,EACzD,OAAO;AAAA,EACP,WAAW;AAAA,EACX,eAAe;AACjB;AAqBO,SAASC,EAAyBC,GAAwD;AAC/F,QAAMC,IAAW,OAAO;AAAA,IACtB,OAAO,QAAQD,KAAU,EAAE,EAAE,OAAO,CAAC,CAAA,EAAGE,CAAK,MAAMA,MAAU,MAAS;AAAA,EAAA;AAExE,SAAO,EAAE,GAAGJ,GAA0B,GAAGG,EAAA;AAC3C;"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The smallest size the artwork survives, in px. Recorded, not enforced — the
3
+ * charte's brand guide warns the monogram stops working below about 20px: the
4
+ * bowls of the B and the hole in the o close up and the whole thing reads as a
5
+ * dark blob. Anything smaller needs a simplified variant from the parent, not
6
+ * this scaled down. Nothing in this package ships below it.
7
+ *
8
+ * This floor is the **monogram's alone**. The product marks have the opposite
9
+ * requirement — they are drawn on a grid chosen so one path renders correctly
10
+ * from 96px down to 20px — so do not generalise this number into a shared rule
11
+ * when the second mark lands.
12
+ */
13
+ export declare const BOOST33_MARK_MIN_LEGIBLE_PX = 20;
14
+ export interface Boost33MarkProps {
15
+ /**
16
+ * Rendered width and height in px. Read BOOST33_MARK_MIN_LEGIBLE_PX before
17
+ * going small.
18
+ */
19
+ size?: number;
20
+ /**
21
+ * An accessible name. Omit it — the default — and the mark is decorative:
22
+ * `aria-hidden`, contributing nothing to the accessibility tree. That is the
23
+ * right answer in the lockup, where the word "Boost33" sits beside it and a
24
+ * name here would only make a screen reader say it twice. Pass one only where
25
+ * the mark stands alone as the sole carrier of meaning.
26
+ */
27
+ title?: string;
28
+ /** Escape hatch for a consumer that needs to position the mark. */
29
+ className?: string;
30
+ }
31
+ /**
32
+ * The Boost33 mark — the "Bo" squircle monogram.
33
+ *
34
+ * **This artwork is not ours.** The monogram and the wordmark are set by the
35
+ * Boost33 charte graphique and a product cannot change them; the parent's brand
36
+ * documentation lists the monogram as "black tile, orange letterform" under what
37
+ * it owns. The paths below are lifted verbatim out of the official lockup at
38
+ * `docs/brand/logo-of-noir-orange.svg` in the Wallet repo (`boostledger`), by way
39
+ * of its `src/ui/primitives/icons.tsx` — reproduced, not redrawn. If the mark
40
+ * ever changes, it changes in that SVG first and this is re-extracted from it.
41
+ * Nobody here gets to nudge a curve.
42
+ *
43
+ * What stood in for it in this package until B33-556 was a Stage A placeholder in
44
+ * the specimen: an orange tile with a `<text>B</text>` typed on top. The Wallet
45
+ * carried the same mistake — tile and letterform the wrong way round — until
46
+ * 9 Sep 2026, which is why the original fidelity baselines showed it too. It had
47
+ * been wrong against the charte the whole time, not just against Paper.
48
+ *
49
+ * How it is built, since the fills are not obvious from the path list: the first
50
+ * path is the black squircle with the B, the o and the underscore knocked out of
51
+ * it as counter-wound subpaths, so those three show through as white. The orange
52
+ * ring is then drawn into the o, and the last three paths fill the B's two bowls
53
+ * and the o's centre back to black. **Order matters between the ring and the
54
+ * centre** — swap them and the o fills solid orange. Nothing else overlaps.
55
+ *
56
+ * The two `fill: none` paths in the source SVG are omitted. They are export
57
+ * artefacts that render nothing, and dropping them does not affect the winding of
58
+ * the path above them.
59
+ *
60
+ * The viewBox is the monogram's own bounding box **inside the lockup**, which is
61
+ * why it is an offset rectangle rather than starting at the origin. Do not "tidy"
62
+ * it to `0 0 …` without re-deriving the paths; they carry absolute coordinates
63
+ * and translating one without the other silently crops the mark.
64
+ */
65
+ export declare function Boost33Mark({ size, title, className }: Boost33MarkProps): import("react").JSX.Element;
@@ -0,0 +1,62 @@
1
+ import { jsxs as o, jsx as t } from "react/jsx-runtime";
2
+ import { useId as r } from "react";
3
+ import a from "./Boost33Mark.module.css.js";
4
+ const d = 24, p = 20;
5
+ function M({ size: i = d, title: s, className: l }) {
6
+ const c = r(), e = s == null;
7
+ return /* @__PURE__ */ o(
8
+ "svg",
9
+ {
10
+ className: l != null ? `${a.mark} ${l}` : a.mark,
11
+ width: i,
12
+ height: i,
13
+ viewBox: "402.17 34.21 234.9 235",
14
+ role: e ? void 0 : "img",
15
+ "aria-hidden": e ? "true" : void 0,
16
+ "aria-labelledby": e ? void 0 : c,
17
+ children: [
18
+ !e && /* @__PURE__ */ t("title", { id: c, children: s }),
19
+ /* @__PURE__ */ t(
20
+ "path",
21
+ {
22
+ className: a.tile,
23
+ d: "M404.19,97c3.79-36.53,38.18-62.45,74.88-62.79q40-.37,80,0c43.66.4,77.69,34.58,78,78.28q.27,38.63,0,77.27c-.35,45.47-34.21,79.19-79.71,79.45q-38.62.23-77.24,0c-43.46-.34-77.66-34.79-77.95-78.34-.09-13,0-26.07,0-39.1m42.93-56V207c23.4,0,46.31.89,69.1-.33,14.41-.77,25.11-13.75,26.36-28.3,1.37-16-6.92-26.34-20.61-33.54,1.15-2.88,2.34-5.37,3.16-8C529,118.35,517,97.73,497.72,96,480.55,94.49,463.15,95.7,445.11,95.7Zm127.65,63.44a31.89,31.89,0,1,0-31.21-32.47A31.87,31.87,0,0,0,572.76,159.14ZM597.12,183V168.11H550.05V183Z"
24
+ }
25
+ ),
26
+ /* @__PURE__ */ t(
27
+ "path",
28
+ {
29
+ className: a.ring,
30
+ d: "M572.76,159.14a31.89,31.89,0,1,1,32.47-31.31A31.62,31.62,0,0,1,572.76,159.14Zm.56-47.42a15.53,15.53,0,1,0,15.61,16A15.44,15.44,0,0,0,573.32,111.72Z"
31
+ }
32
+ ),
33
+ /* @__PURE__ */ t(
34
+ "path",
35
+ {
36
+ className: a.tile,
37
+ d: "M460.7,190.94V160.11c17.05,0,33.68-.53,50.25.23,8.11.37,13.22,7.7,13,15.5s-5.75,14.69-13.73,14.94C493.9,191.3,477.58,190.94,460.7,190.94Z"
38
+ }
39
+ ),
40
+ /* @__PURE__ */ t(
41
+ "path",
42
+ {
43
+ className: a.tile,
44
+ d: "M460.65,142.62V112.36c12.1,0,24.4-.89,36.5.33,7.46.76,11.4,7.3,11.05,15-.37,8.1-5,14-12.95,14.64C484,143.3,472.52,142.62,460.65,142.62Z"
45
+ }
46
+ ),
47
+ /* @__PURE__ */ t(
48
+ "path",
49
+ {
50
+ className: a.tile,
51
+ d: "M573.32,111.72a15.53,15.53,0,1,1,0,31.06,15.53,15.53,0,1,1,0-31.06Z"
52
+ }
53
+ )
54
+ ]
55
+ }
56
+ );
57
+ }
58
+ export {
59
+ p as BOOST33_MARK_MIN_LEGIBLE_PX,
60
+ M as Boost33Mark
61
+ };
62
+ //# sourceMappingURL=Boost33Mark.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Boost33Mark.js","sources":["../../../src/components/Boost33Mark/Boost33Mark.tsx"],"sourcesContent":["import { useId } from 'react'\nimport styles from './Boost33Mark.module.css'\n\n/**\n * The size the mark renders at unless a caller says otherwise. 24px is the\n * Wallet's own default at both of its call sites, and it is the size\n * tests/baselines/mark-boost33.png, wordmark.png and wordmark-rail.png were\n * captured at.\n */\nconst DEFAULT_SIZE = 24\n\n/**\n * The smallest size the artwork survives, in px. Recorded, not enforced — the\n * charte's brand guide warns the monogram stops working below about 20px: the\n * bowls of the B and the hole in the o close up and the whole thing reads as a\n * dark blob. Anything smaller needs a simplified variant from the parent, not\n * this scaled down. Nothing in this package ships below it.\n *\n * This floor is the **monogram's alone**. The product marks have the opposite\n * requirement — they are drawn on a grid chosen so one path renders correctly\n * from 96px down to 20px — so do not generalise this number into a shared rule\n * when the second mark lands.\n */\nexport const BOOST33_MARK_MIN_LEGIBLE_PX = 20\n\nexport interface Boost33MarkProps {\n /**\n * Rendered width and height in px. Read BOOST33_MARK_MIN_LEGIBLE_PX before\n * going small.\n */\n size?: number\n /**\n * An accessible name. Omit it — the default — and the mark is decorative:\n * `aria-hidden`, contributing nothing to the accessibility tree. That is the\n * right answer in the lockup, where the word \"Boost33\" sits beside it and a\n * name here would only make a screen reader say it twice. Pass one only where\n * the mark stands alone as the sole carrier of meaning.\n */\n title?: string\n /** Escape hatch for a consumer that needs to position the mark. */\n className?: string\n}\n\n/**\n * The Boost33 mark — the \"Bo\" squircle monogram.\n *\n * **This artwork is not ours.** The monogram and the wordmark are set by the\n * Boost33 charte graphique and a product cannot change them; the parent's brand\n * documentation lists the monogram as \"black tile, orange letterform\" under what\n * it owns. The paths below are lifted verbatim out of the official lockup at\n * `docs/brand/logo-of-noir-orange.svg` in the Wallet repo (`boostledger`), by way\n * of its `src/ui/primitives/icons.tsx` — reproduced, not redrawn. If the mark\n * ever changes, it changes in that SVG first and this is re-extracted from it.\n * Nobody here gets to nudge a curve.\n *\n * What stood in for it in this package until B33-556 was a Stage A placeholder in\n * the specimen: an orange tile with a `<text>B</text>` typed on top. The Wallet\n * carried the same mistake — tile and letterform the wrong way round — until\n * 9 Sep 2026, which is why the original fidelity baselines showed it too. It had\n * been wrong against the charte the whole time, not just against Paper.\n *\n * How it is built, since the fills are not obvious from the path list: the first\n * path is the black squircle with the B, the o and the underscore knocked out of\n * it as counter-wound subpaths, so those three show through as white. The orange\n * ring is then drawn into the o, and the last three paths fill the B's two bowls\n * and the o's centre back to black. **Order matters between the ring and the\n * centre** — swap them and the o fills solid orange. Nothing else overlaps.\n *\n * The two `fill: none` paths in the source SVG are omitted. They are export\n * artefacts that render nothing, and dropping them does not affect the winding of\n * the path above them.\n *\n * The viewBox is the monogram's own bounding box **inside the lockup**, which is\n * why it is an offset rectangle rather than starting at the origin. Do not \"tidy\"\n * it to `0 0 …` without re-deriving the paths; they carry absolute coordinates\n * and translating one without the other silently crops the mark.\n */\nexport function Boost33Mark({ size = DEFAULT_SIZE, title, className }: Boost33MarkProps) {\n const titleId = useId()\n const decorative = title == null\n\n return (\n <svg\n className={className != null ? `${styles.mark} ${className}` : styles.mark}\n width={size}\n height={size}\n viewBox=\"402.17 34.21 234.9 235\"\n role={decorative ? undefined : 'img'}\n aria-hidden={decorative ? 'true' : undefined}\n aria-labelledby={decorative ? undefined : titleId}\n >\n {!decorative && <title id={titleId}>{title}</title>}\n {/* Tile, with the B / o / underscore knocked out of it. */}\n <path\n className={styles.tile}\n d=\"M404.19,97c3.79-36.53,38.18-62.45,74.88-62.79q40-.37,80,0c43.66.4,77.69,34.58,78,78.28q.27,38.63,0,77.27c-.35,45.47-34.21,79.19-79.71,79.45q-38.62.23-77.24,0c-43.46-.34-77.66-34.79-77.95-78.34-.09-13,0-26.07,0-39.1m42.93-56V207c23.4,0,46.31.89,69.1-.33,14.41-.77,25.11-13.75,26.36-28.3,1.37-16-6.92-26.34-20.61-33.54,1.15-2.88,2.34-5.37,3.16-8C529,118.35,517,97.73,497.72,96,480.55,94.49,463.15,95.7,445.11,95.7Zm127.65,63.44a31.89,31.89,0,1,0-31.21-32.47A31.87,31.87,0,0,0,572.76,159.14ZM597.12,183V168.11H550.05V183Z\"\n />\n {/* The o, and the only orange in the mark. */}\n <path\n className={styles.ring}\n d=\"M572.76,159.14a31.89,31.89,0,1,1,32.47-31.31A31.62,31.62,0,0,1,572.76,159.14Zm.56-47.42a15.53,15.53,0,1,0,15.61,16A15.44,15.44,0,0,0,573.32,111.72Z\"\n />\n {/* The B's two bowls and the o's centre, returned to the tile's black. */}\n <path\n className={styles.tile}\n d=\"M460.7,190.94V160.11c17.05,0,33.68-.53,50.25.23,8.11.37,13.22,7.7,13,15.5s-5.75,14.69-13.73,14.94C493.9,191.3,477.58,190.94,460.7,190.94Z\"\n />\n <path\n className={styles.tile}\n d=\"M460.65,142.62V112.36c12.1,0,24.4-.89,36.5.33,7.46.76,11.4,7.3,11.05,15-.37,8.1-5,14-12.95,14.64C484,143.3,472.52,142.62,460.65,142.62Z\"\n />\n <path\n className={styles.tile}\n d=\"M573.32,111.72a15.53,15.53,0,1,1,0,31.06,15.53,15.53,0,1,1,0-31.06Z\"\n />\n </svg>\n )\n}\n"],"names":["DEFAULT_SIZE","BOOST33_MARK_MIN_LEGIBLE_PX","Boost33Mark","size","title","className","titleId","useId","decorative","jsxs","styles","jsx"],"mappings":";;;AASA,MAAMA,IAAe,IAcRC,IAA8B;AAsDpC,SAASC,EAAY,EAAE,MAAAC,IAAOH,GAAc,OAAAI,GAAO,WAAAC,KAA+B;AACvF,QAAMC,IAAUC,EAAA,GACVC,IAAaJ,KAAS;AAE5B,SACE,gBAAAK;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWJ,KAAa,OAAO,GAAGK,EAAO,IAAI,IAAIL,CAAS,KAAKK,EAAO;AAAA,MACtE,OAAOP;AAAA,MACP,QAAQA;AAAA,MACR,SAAQ;AAAA,MACR,MAAMK,IAAa,SAAY;AAAA,MAC/B,eAAaA,IAAa,SAAS;AAAA,MACnC,mBAAiBA,IAAa,SAAYF;AAAA,MAEzC,UAAA;AAAA,QAAA,CAACE,KAAc,gBAAAG,EAAC,SAAA,EAAM,IAAIL,GAAU,UAAAF,GAAM;AAAA,QAE3C,gBAAAO;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAWD,EAAO;AAAA,YAClB,GAAE;AAAA,UAAA;AAAA,QAAA;AAAA,QAGJ,gBAAAC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAWD,EAAO;AAAA,YAClB,GAAE;AAAA,UAAA;AAAA,QAAA;AAAA,QAGJ,gBAAAC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAWD,EAAO;AAAA,YAClB,GAAE;AAAA,UAAA;AAAA,QAAA;AAAA,QAEJ,gBAAAC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAWD,EAAO;AAAA,YAClB,GAAE;AAAA,UAAA;AAAA,QAAA;AAAA,QAEJ,gBAAAC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAWD,EAAO;AAAA,YAClB,GAAE;AAAA,UAAA;AAAA,QAAA;AAAA,MACJ;AAAA,IAAA;AAAA,EAAA;AAGN;"}
@@ -0,0 +1,12 @@
1
+ const r = "_tile_5vhrh_12", t = "_ring_5vhrh_16", _ = "_mark_5vhrh_24", n = {
2
+ tile: r,
3
+ ring: t,
4
+ mark: _
5
+ };
6
+ export {
7
+ n as default,
8
+ _ as mark,
9
+ t as ring,
10
+ r as tile
11
+ };
12
+ //# sourceMappingURL=Boost33Mark.module.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Boost33Mark.module.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"}
@@ -1,7 +1,13 @@
1
- import { ReactNode } from 'react';
1
+ import { ElementType, ReactNode } from 'react';
2
2
  export interface NavItemProps {
3
3
  /** The nav label. Truncates with an ellipsis rather than wrapping. */
4
4
  children: ReactNode;
5
+ /**
6
+ * The leading icon. Decorative -- the label carries the name, so pass an
7
+ * `aria-hidden` glyph. On the rail it is the only thing that shows, the
8
+ * label having collapsed to the accessibility tree.
9
+ */
10
+ icon?: ReactNode;
5
11
  /** Marks this row as the current page. Sets `aria-current="page"`. */
6
12
  active?: boolean;
7
13
  /** Sidebar-on-the-rail state. Hides the label and trailing slot visually; the label stays in the accessibility tree. */
@@ -11,6 +17,16 @@ export interface NavItemProps {
11
17
  /** Renders `count` in the primary color instead of muted. */
12
18
  countUrgent?: boolean;
13
19
  href?: string;
20
+ /**
21
+ * The element the linked form renders through. Defaults to a plain `a`; a
22
+ * consumer whose router owns navigation (Next.js, next-intl's localised
23
+ * `Link`) passes its own so the row routes client-side with the locale prefix
24
+ * intact instead of a bare anchor that full-page-reloads (B33-847). Receives
25
+ * `href`, `className`, `onClick` and the row's data/aria attributes unchanged,
26
+ * so the markup is identical to the anchor's. Only used when `href` is set;
27
+ * the action form stays a `button`. See docs/adr/0011.
28
+ */
29
+ linkComponent?: ElementType;
14
30
  onClick?: () => void;
15
31
  }
16
- export declare function NavItem({ children, active, collapsed, count, countUrgent, href, onClick }: NavItemProps): import("react").JSX.Element;
32
+ export declare function NavItem({ children, icon, active, collapsed, count, countUrgent, href, linkComponent, onClick }: NavItemProps): import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { jsx as e, jsxs as p, Fragment as N } from "react/jsx-runtime";
2
+ import a from "./NavItem.module.css.js";
3
+ function v({ children: i, icon: n, active: t = !1, collapsed: d = !1, count: s, countUrgent: m = !1, href: r, linkComponent: u, onClick: l }) {
4
+ const o = {
5
+ "data-active": t ? "true" : void 0,
6
+ "data-collapsed": d ? "true" : void 0,
7
+ "aria-current": t ? "page" : void 0
8
+ }, c = /* @__PURE__ */ p(N, { children: [
9
+ n != null && /* @__PURE__ */ e("span", { className: a.icon, "aria-hidden": "true", children: n }),
10
+ /* @__PURE__ */ e("span", { className: a.label, children: i }),
11
+ /* @__PURE__ */ e("span", { className: a.trailingSlot, children: s != null && /* @__PURE__ */ e("span", { className: m ? a.countUrgent : a.count, children: s }) })
12
+ ] });
13
+ return r ? /* @__PURE__ */ e(u ?? "a", { className: a.row, href: r, onClick: l, ...o, children: c }) : /* @__PURE__ */ e("button", { type: "button", className: a.row, onClick: l, ...o, children: c });
14
+ }
15
+ export {
16
+ v as NavItem
17
+ };
18
+ //# sourceMappingURL=NavItem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavItem.js","sources":["../../../src/components/NavItem/NavItem.tsx"],"sourcesContent":["import type { ElementType, ReactNode } from 'react'\nimport styles from './NavItem.module.css'\n\nexport interface NavItemProps {\n /** The nav label. Truncates with an ellipsis rather than wrapping. */\n children: ReactNode\n /**\n * The leading icon. Decorative -- the label carries the name, so pass an\n * `aria-hidden` glyph. On the rail it is the only thing that shows, the\n * label having collapsed to the accessibility tree.\n */\n icon?: ReactNode\n /** Marks this row as the current page. Sets `aria-current=\"page\"`. */\n active?: boolean\n /** Sidebar-on-the-rail state. Hides the label and trailing slot visually; the label stays in the accessibility tree. */\n collapsed?: boolean\n /** Trailing slot content. Omit to leave the slot empty -- it still reserves its width so counts line up across rows. */\n count?: number\n /** Renders `count` in the primary color instead of muted. */\n countUrgent?: boolean\n href?: string\n /**\n * The element the linked form renders through. Defaults to a plain `a`; a\n * consumer whose router owns navigation (Next.js, next-intl's localised\n * `Link`) passes its own so the row routes client-side with the locale prefix\n * intact instead of a bare anchor that full-page-reloads (B33-847). Receives\n * `href`, `className`, `onClick` and the row's data/aria attributes unchanged,\n * so the markup is identical to the anchor's. Only used when `href` is set;\n * the action form stays a `button`. See docs/adr/0011.\n */\n linkComponent?: ElementType\n onClick?: () => void\n}\n\nexport function NavItem({ children, icon, active = false, collapsed = false, count, countUrgent = false, href, linkComponent, onClick }: NavItemProps) {\n const dataProps = {\n 'data-active': active ? ('true' as const) : undefined,\n 'data-collapsed': collapsed ? ('true' as const) : undefined,\n 'aria-current': active ? ('page' as const) : undefined,\n }\n\n const content = (\n <>\n {icon != null && (\n <span className={styles.icon} aria-hidden=\"true\">\n {icon}\n </span>\n )}\n <span className={styles.label}>{children}</span>\n <span className={styles.trailingSlot}>\n {count != null && <span className={countUrgent ? styles.countUrgent : styles.count}>{count}</span>}\n </span>\n </>\n )\n\n if (href) {\n const Link = linkComponent ?? 'a'\n return (\n <Link className={styles.row} href={href} onClick={onClick} {...dataProps}>\n {content}\n </Link>\n )\n }\n\n return (\n <button type=\"button\" className={styles.row} onClick={onClick} {...dataProps}>\n {content}\n </button>\n )\n}\n"],"names":["NavItem","children","icon","active","collapsed","count","countUrgent","href","linkComponent","onClick","dataProps","content","jsxs","Fragment","styles","jsx"],"mappings":";;AAkCO,SAASA,EAAQ,EAAE,UAAAC,GAAU,MAAAC,GAAM,QAAAC,IAAS,IAAO,WAAAC,IAAY,IAAO,OAAAC,GAAO,aAAAC,IAAc,IAAO,MAAAC,GAAM,eAAAC,GAAe,SAAAC,KAAyB;AACrJ,QAAMC,IAAY;AAAA,IAChB,eAAeP,IAAU,SAAmB;AAAA,IAC5C,kBAAkBC,IAAa,SAAmB;AAAA,IAClD,gBAAgBD,IAAU,SAAmB;AAAA,EAAA,GAGzCQ,IACJ,gBAAAC,EAAAC,GAAA,EACG,UAAA;AAAA,IAAAX,KAAQ,0BACN,QAAA,EAAK,WAAWY,EAAO,MAAM,eAAY,QACvC,UAAAZ,EAAA,CACH;AAAA,IAEF,gBAAAa,EAAC,QAAA,EAAK,WAAWD,EAAO,OAAQ,UAAAb,GAAS;AAAA,sBACxC,QAAA,EAAK,WAAWa,EAAO,cACrB,eAAS,QAAQ,gBAAAC,EAAC,QAAA,EAAK,WAAWT,IAAcQ,EAAO,cAAcA,EAAO,OAAQ,aAAM,EAAA,CAC7F;AAAA,EAAA,GACF;AAGF,SAAIP,IAGA,gBAAAQ,EAFWP,KAAiB,OAEtB,WAAWM,EAAO,KAAK,MAAAP,GAAY,SAAAE,GAAmB,GAAGC,GAC5D,UAAAC,EAAA,CACH,IAKF,gBAAAI,EAAC,UAAA,EAAO,MAAK,UAAS,WAAWD,EAAO,KAAK,SAAAL,GAAmB,GAAGC,GAChE,UAAAC,EAAA,CACH;AAEJ;"}
@@ -0,0 +1,18 @@
1
+ const t = "_row_16hr7_4", o = "_icon_16hr7_41", n = "_label_16hr7_66", _ = "_trailingSlot_16hr7_92", c = "_count_16hr7_106", r = "_countUrgent_16hr7_110", l = {
2
+ row: t,
3
+ icon: o,
4
+ label: n,
5
+ trailingSlot: _,
6
+ count: c,
7
+ countUrgent: r
8
+ };
9
+ export {
10
+ c as count,
11
+ r as countUrgent,
12
+ l as default,
13
+ o as icon,
14
+ n as label,
15
+ t as row,
16
+ _ as trailingSlot
17
+ };
18
+ //# sourceMappingURL=NavItem.module.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavItem.module.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
@@ -0,0 +1,12 @@
1
+ import { ReactNode } from 'react';
2
+ export interface NavSectionProps {
3
+ /**
4
+ * The section header -- the small uppercase label above the rows. Omit for a
5
+ * section with no header (the sidebar's first section has none). Passed in
6
+ * its natural case; the uppercasing is presentational.
7
+ */
8
+ label?: string;
9
+ /** The section's rows -- NavItems, passed in as data, never hardcoded here. */
10
+ children: ReactNode;
11
+ }
12
+ export declare function NavSection({ label, children }: NavSectionProps): import("react").JSX.Element;
@@ -0,0 +1,14 @@
1
+ import { jsx as i, jsxs as t } from "react/jsx-runtime";
2
+ import { useId as a } from "react";
3
+ import e from "./NavSection.module.css.js";
4
+ function l({ label: r, children: s }) {
5
+ const o = a();
6
+ return r == null ? /* @__PURE__ */ i("div", { className: e.section, children: s }) : /* @__PURE__ */ t("div", { className: e.section, role: "group", "aria-labelledby": o, children: [
7
+ /* @__PURE__ */ i("p", { id: o, className: e.header, children: r }),
8
+ s
9
+ ] });
10
+ }
11
+ export {
12
+ l as NavSection
13
+ };
14
+ //# sourceMappingURL=NavSection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavSection.js","sources":["../../../src/components/NavSection/NavSection.tsx"],"sourcesContent":["import { useId, type ReactNode } from 'react'\nimport styles from './NavSection.module.css'\n\nexport interface NavSectionProps {\n /**\n * The section header -- the small uppercase label above the rows. Omit for a\n * section with no header (the sidebar's first section has none). Passed in\n * its natural case; the uppercasing is presentational.\n */\n label?: string\n /** The section's rows -- NavItems, passed in as data, never hardcoded here. */\n children: ReactNode\n}\n\n// A group of nav rows under an optional header. A labelled section is an\n// accessible group named by its header, so the rows announce with their\n// section; a headerless section is just the run of rows.\nexport function NavSection({ label, children }: NavSectionProps) {\n const headerId = useId()\n\n if (label == null) {\n return <div className={styles.section}>{children}</div>\n }\n\n return (\n <div className={styles.section} role=\"group\" aria-labelledby={headerId}>\n <p id={headerId} className={styles.header}>\n {label}\n </p>\n {children}\n </div>\n )\n}\n"],"names":["NavSection","label","children","headerId","useId","jsx","styles","jsxs"],"mappings":";;;AAiBO,SAASA,EAAW,EAAE,OAAAC,GAAO,UAAAC,KAA6B;AAC/D,QAAMC,IAAWC,EAAA;AAEjB,SAAIH,KAAS,OACJ,gBAAAI,EAAC,OAAA,EAAI,WAAWC,EAAO,SAAU,UAAAJ,GAAS,IAIjD,gBAAAK,EAAC,SAAI,WAAWD,EAAO,SAAS,MAAK,SAAQ,mBAAiBH,GAC5D,UAAA;AAAA,IAAA,gBAAAE,EAAC,OAAE,IAAIF,GAAU,WAAWG,EAAO,QAChC,UAAAL,GACH;AAAA,IACCC;AAAA,EAAA,GACH;AAEJ;"}
@@ -0,0 +1,10 @@
1
+ const s = "_section_3sxsg_5", e = "_header_3sxsg_13", t = {
2
+ section: s,
3
+ header: e
4
+ };
5
+ export {
6
+ t as default,
7
+ e as header,
8
+ s as section
9
+ };
10
+ //# sourceMappingURL=NavSection.module.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavSection.module.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -0,0 +1,10 @@
1
+ import { ReactNode } from 'react';
2
+ export interface PageHeaderProps {
3
+ /** The page's name. Renders as the page heading. */
4
+ title: ReactNode;
5
+ /** A line under the title. Omit for a title-only header. */
6
+ subtitle?: ReactNode;
7
+ /** Trailing controls, aligned to the right edge. Omit to leave the row empty. */
8
+ actions?: ReactNode;
9
+ }
10
+ export declare function PageHeader({ title, subtitle, actions }: PageHeaderProps): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { jsxs as r, jsx as l } from "react/jsx-runtime";
2
+ import e from "./PageHeader.module.css.js";
3
+ function n({ title: i, subtitle: s, actions: a }) {
4
+ return /* @__PURE__ */ r("header", { className: e.header, children: [
5
+ /* @__PURE__ */ r("div", { className: e.headings, children: [
6
+ /* @__PURE__ */ l("h1", { className: e.title, children: i }),
7
+ s != null && /* @__PURE__ */ l("p", { className: e.subtitle, children: s })
8
+ ] }),
9
+ a != null && /* @__PURE__ */ l("div", { className: e.actions, children: a })
10
+ ] });
11
+ }
12
+ export {
13
+ n as PageHeader
14
+ };
15
+ //# sourceMappingURL=PageHeader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageHeader.js","sources":["../../../src/components/PageHeader/PageHeader.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport styles from './PageHeader.module.css'\n\nexport interface PageHeaderProps {\n /** The page's name. Renders as the page heading. */\n title: ReactNode\n /** A line under the title. Omit for a title-only header. */\n subtitle?: ReactNode\n /** Trailing controls, aligned to the right edge. Omit to leave the row empty. */\n actions?: ReactNode\n}\n\nexport function PageHeader({ title, subtitle, actions }: PageHeaderProps) {\n return (\n <header className={styles.header}>\n <div className={styles.headings}>\n <h1 className={styles.title}>{title}</h1>\n {subtitle != null && <p className={styles.subtitle}>{subtitle}</p>}\n </div>\n {actions != null && <div className={styles.actions}>{actions}</div>}\n </header>\n )\n}\n"],"names":["PageHeader","title","subtitle","actions","jsxs","styles","jsx"],"mappings":";;AAYO,SAASA,EAAW,EAAE,OAAAC,GAAO,UAAAC,GAAU,SAAAC,KAA4B;AACxE,SACE,gBAAAC,EAAC,UAAA,EAAO,WAAWC,EAAO,QACxB,UAAA;AAAA,IAAA,gBAAAD,EAAC,OAAA,EAAI,WAAWC,EAAO,UACrB,UAAA;AAAA,MAAA,gBAAAC,EAAC,MAAA,EAAG,WAAWD,EAAO,OAAQ,UAAAJ,GAAM;AAAA,MACnCC,KAAY,QAAQ,gBAAAI,EAAC,OAAE,WAAWD,EAAO,UAAW,UAAAH,EAAA,CAAS;AAAA,IAAA,GAChE;AAAA,IACCC,KAAW,QAAQ,gBAAAG,EAAC,SAAI,WAAWD,EAAO,SAAU,UAAAF,EAAA,CAAQ;AAAA,EAAA,GAC/D;AAEJ;"}
@@ -0,0 +1,16 @@
1
+ const t = "_header_i17td_42", e = "_headings_i17td_52", s = "_title_i17td_33", i = "_subtitle_i17td_74", _ = "_actions_i17td_82", d = {
2
+ header: t,
3
+ headings: e,
4
+ title: s,
5
+ subtitle: i,
6
+ actions: _
7
+ };
8
+ export {
9
+ _ as actions,
10
+ d as default,
11
+ t as header,
12
+ e as headings,
13
+ i as subtitle,
14
+ s as title
15
+ };
16
+ //# sourceMappingURL=PageHeader.module.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageHeader.module.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}