@boost33/ui 0.7.1 → 0.8.0-next.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 (64) hide show
  1. package/CHANGELOG.md +183 -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 +85 -17
  5. package/dist/components/AppShell/AppShell.js +75 -50
  6. package/dist/components/AppShell/AppShell.js.map +1 -1
  7. package/dist/components/AppShell/AppShell.module.css.js +22 -16
  8. package/dist/components/AppShell/AppShell.module.css.js.map +1 -1
  9. package/dist/components/AppSwitcher/AppSwitcher.d.ts +90 -45
  10. package/dist/components/AppSwitcher/AppSwitcher.js +71 -66
  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 +11 -22
  17. package/dist/components/AppSwitcher/labels.js.map +1 -1
  18. package/dist/components/CommandPalette/CommandPalette.d.ts +8 -2
  19. package/dist/components/CommandPalette/CommandPalette.js.map +1 -1
  20. package/dist/components/NavItem/NavItem.d.ts +15 -2
  21. package/dist/components/NavItem/NavItem.js +14 -11
  22. package/dist/components/NavItem/NavItem.js.map +1 -1
  23. package/dist/components/NavItem/NavItem.module.css.js +6 -2
  24. package/dist/components/NavItem/NavItem.module.css.js.map +1 -1
  25. package/dist/components/NavSection/NavSection.module.css.js +5 -5
  26. package/dist/components/NotificationButton/NotificationButton.d.ts +29 -0
  27. package/dist/components/NotificationButton/NotificationButton.js +23 -0
  28. package/dist/components/NotificationButton/NotificationButton.js.map +1 -0
  29. package/dist/components/NotificationButton/NotificationButton.module.css.js +14 -0
  30. package/dist/components/NotificationButton/NotificationButton.module.css.js.map +1 -0
  31. package/dist/components/NotificationButton/labels.d.ts +12 -0
  32. package/dist/components/NotificationButton/labels.js +15 -0
  33. package/dist/components/NotificationButton/labels.js.map +1 -0
  34. package/dist/components/NotificationCenter/NotificationCenter.js +37 -36
  35. package/dist/components/NotificationCenter/NotificationCenter.js.map +1 -1
  36. package/dist/components/NotificationCenter/NotificationCenter.module.css.js +43 -43
  37. package/dist/components/Sidebar/NavHeightBudgetWarning.js +2 -2
  38. package/dist/components/Sidebar/NavHeightBudgetWarning.js.map +1 -1
  39. package/dist/components/Sidebar/Sidebar.module.css.js +1 -1
  40. package/dist/components/TopBar/TopBar.d.ts +54 -4
  41. package/dist/components/TopBar/TopBar.js +22 -7
  42. package/dist/components/TopBar/TopBar.js.map +1 -1
  43. package/dist/components/TopBar/TopBar.module.css.js +12 -8
  44. package/dist/components/TopBar/TopBar.module.css.js.map +1 -1
  45. package/dist/components/TopBar/labels.d.ts +20 -0
  46. package/dist/components/TopBar/labels.js +15 -0
  47. package/dist/components/TopBar/labels.js.map +1 -0
  48. package/dist/components/WalletChip/WalletChip.d.ts +77 -2
  49. package/dist/components/WalletChip/WalletChip.js +142 -50
  50. package/dist/components/WalletChip/WalletChip.js.map +1 -1
  51. package/dist/components/WalletChip/WalletChip.module.css.js +93 -47
  52. package/dist/components/WalletChip/WalletChip.module.css.js.map +1 -1
  53. package/dist/components/WalletChip/labels.d.ts +63 -1
  54. package/dist/components/WalletChip/labels.js +21 -6
  55. package/dist/components/WalletChip/labels.js.map +1 -1
  56. package/dist/index.d.ts +9 -3
  57. package/dist/index.js +55 -49
  58. package/dist/index.js.map +1 -1
  59. package/dist/nav-height-budget.d.ts +12 -9
  60. package/dist/nav-height-budget.js +3 -3
  61. package/dist/nav-height-budget.js.map +1 -1
  62. package/dist/style.css +1 -1
  63. package/dist/theme.css +1 -1
  64. package/package.json +1 -1
@@ -1 +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;"}
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 // Four filled squares, the last in the brand orange -- the hub mark as board 21\n // draws it on the panel's dark Hub row (B33-1711). It was four outlined\n // squares on the Wallet-era light row.\n return (\n <svg className={className} viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <rect x=\"3\" y=\"3\" width=\"8\" height=\"8\" rx=\"2\" fill=\"currentColor\" />\n <rect x=\"13\" y=\"3\" width=\"8\" height=\"8\" rx=\"2\" fill=\"currentColor\" />\n <rect x=\"3\" y=\"13\" width=\"8\" height=\"8\" rx=\"2\" fill=\"currentColor\" />\n <rect x=\"13\" y=\"13\" width=\"8\" height=\"8\" rx=\"2\" fill=\"var(--color-primary)\" />\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","jsx","Chevron","ChevronRight","Check"],"mappings":";AAWO,SAASA,EAAQ,EAAE,WAAAC,KAAqC;AAI7D,2BACG,OAAA,EAAI,WAAAA,GAAsB,SAAQ,aAAY,eAAY,QACzD,UAAA;AAAA,IAAA,gBAAAC,EAAC,QAAA,EAAK,GAAE,KAAI,GAAE,KAAI,OAAM,KAAI,QAAO,KAAI,IAAG,KAAI,MAAK,gBAAe;AAAA,IAClE,gBAAAA,EAAC,QAAA,EAAK,GAAE,MAAK,GAAE,KAAI,OAAM,KAAI,QAAO,KAAI,IAAG,KAAI,MAAK,gBAAe;AAAA,IACnE,gBAAAA,EAAC,QAAA,EAAK,GAAE,KAAI,GAAE,MAAK,OAAM,KAAI,QAAO,KAAI,IAAG,KAAI,MAAK,gBAAe;AAAA,IACnE,gBAAAA,EAAC,QAAA,EAAK,GAAE,MAAK,GAAE,MAAK,OAAM,KAAI,QAAO,KAAI,IAAG,KAAI,MAAK,uBAAA,CAAuB;AAAA,EAAA,GAC9E;AAEJ;AAGO,SAASC,EAAQ,EAAE,WAAAF,KAAqC;AAC7D,SACE,gBAAAC,EAAC,SAAI,WAAAD,GAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QACxJ,UAAA,gBAAAC,EAAC,QAAA,EAAK,GAAE,eAAA,CAAe,EAAA,CACzB;AAEJ;AAGO,SAASE,EAAa,EAAE,WAAAH,KAAqC;AAClE,SACE,gBAAAC,EAAC,SAAI,WAAAD,GAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QACxJ,UAAA,gBAAAC,EAAC,QAAA,EAAK,GAAE,eAAA,CAAe,EAAA,CACzB;AAEJ;AAGO,SAASG,EAAM,EAAE,WAAAJ,KAAqC;AAC3D,SACE,gBAAAC,EAAC,SAAI,WAAAD,GAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QAC1J,UAAA,gBAAAC,EAAC,QAAA,EAAK,GAAE,iBAAA,CAAiB,EAAA,CAC3B;AAEJ;"}
@@ -13,23 +13,17 @@ export interface AppSwitcherLabels {
13
13
  */
14
14
  notOnPlan: string;
15
15
  /**
16
- * The tag on a product that does not exist yet -- `live: false` on the row.
17
- * Default "Soon".
16
+ * **Unused since 0.8.0, and kept so a consumer's labels object still
17
+ * compiles.** It was the tag on a product that does not exist yet -- a row
18
+ * carrying `live: false`. B33-1711 took those rows off the switcher, so no row
19
+ * draws it any more.
18
20
  *
19
- * It takes the same slot as `notOnPlan` and never sits beside it, because the
20
- * two answer different questions and only one of them is worth a person's
21
- * attention at a time. `live` is whether the product exists at all, globally;
22
- * `notOnPlan` is whether this account holds it. A product that has not shipped
23
- * is held by nobody, so an unbuilt row is *always* also an unentitled one --
24
- * which is why the unbuilt word wins the slot rather than stacking with it.
21
+ * It stays in the type rather than going, because a consumer that passes
22
+ * `labels={{ soon: 'Bientôt' }}` as an object literal would otherwise fail its
23
+ * typecheck on an excess property, and that is a breaking change across four
24
+ * products for a word nothing renders. Drop it from your catalogue at leisure.
25
25
  *
26
- * Collapsing the two is the confusion Identity added the `live` field to
27
- * prevent (B33-561). With only `notOnPlan`, a product nobody can buy yet
28
- * renders as one this client has not bought, and the row invites them to go
29
- * and ask about something that does not exist.
30
- *
31
- * Like every other label here it is a prop with an English default, so the two
32
- * French-default consumers translate it rather than reading "Soon".
26
+ * @deprecated Rendered nowhere since 0.8.0 (B33-1711).
33
27
  */
34
28
  soon: string;
35
29
  /**
@@ -57,13 +51,8 @@ export interface AppSwitcherLabels {
57
51
  * `currentSlug` matches nothing, and all three baselines were captured with the
58
52
  * Wallet current.
59
53
  *
60
- * `soon` is no exception either, for the same shape of reason: it reaches the
61
- * screen only on a row carrying `live: false`, and `live` defaults to true, so
62
- * the four rows the baselines were captured from still draw exactly the tag they
63
- * drew before the field existed. The Wallet never rendered a "Soon" tag at all
64
- * -- it had no registry to read `live` from -- so there is no captured baseline
65
- * for this state and `scripts/screenshot-states.ts` records the specimen row
66
- * that shows it as ungated, pending a human capture (CLAUDE.md).
54
+ * `soon` reaches the screen nowhere since 0.8.0 (B33-1711); its default stays
55
+ * so the type keeps the field. See the field.
67
56
  */
68
57
  export declare const defaultAppSwitcherLabels: AppSwitcherLabels;
69
58
  /**
@@ -1 +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// No label here carries a value, so this module names no placeholder. The one a\n// future field would use is `%name%` and not `{name}`: a brace is ICU\n// MessageFormat argument syntax, and a consumer keeps these strings in an ICU\n// catalogue (B33-1277; the reasoning is in `src/fill-template.ts`). Written down\n// because B33-1007 is open about this module quietly diverging from the chip's\n// contract, and a delimiter is the easiest thing to re-decide wrongly.\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 tag on a product that does not exist yet -- `live: false` on the row.\n * Default \"Soon\".\n *\n * It takes the same slot as `notOnPlan` and never sits beside it, because the\n * two answer different questions and only one of them is worth a person's\n * attention at a time. `live` is whether the product exists at all, globally;\n * `notOnPlan` is whether this account holds it. A product that has not shipped\n * is held by nobody, so an unbuilt row is *always* also an unentitled one --\n * which is why the unbuilt word wins the slot rather than stacking with it.\n *\n * Collapsing the two is the confusion Identity added the `live` field to\n * prevent (B33-561). With only `notOnPlan`, a product nobody can buy yet\n * renders as one this client has not bought, and the row invites them to go\n * and ask about something that does not exist.\n *\n * Like every other label here it is a prop with an English default, so the two\n * French-default consumers translate it rather than reading \"Soon\".\n */\n soon: 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 *\n * `soon` is no exception either, for the same shape of reason: it reaches the\n * screen only on a row carrying `live: false`, and `live` defaults to true, so\n * the four rows the baselines were captured from still draw exactly the tag they\n * drew before the field existed. The Wallet never rendered a \"Soon\" tag at all\n * -- it had no registry to read `live` from -- so there is no captured baseline\n * for this state and `scripts/screenshot-states.ts` records the specimen row\n * that shows it as ungated, pending a human capture (CLAUDE.md).\n */\nexport const defaultAppSwitcherLabels: AppSwitcherLabels = {\n panel: 'Boost33 products',\n notOnPlan: 'Not on plan',\n soon: 'Soon',\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":"AAmFO,MAAMA,IAA8C;AAAA,EACzD,OAAO;AAAA,EACP,WAAW;AAAA,EACX,MAAM;AAAA,EACN,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;"}
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// No label here carries a value, so this module names no placeholder. The one a\n// future field would use is `%name%` and not `{name}`: a brace is ICU\n// MessageFormat argument syntax, and a consumer keeps these strings in an ICU\n// catalogue (B33-1277; the reasoning is in `src/fill-template.ts`). Written down\n// because B33-1007 is open about this module quietly diverging from the chip's\n// contract, and a delimiter is the easiest thing to re-decide wrongly.\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 * **Unused since 0.8.0, and kept so a consumer's labels object still\n * compiles.** It was the tag on a product that does not exist yet -- a row\n * carrying `live: false`. B33-1711 took those rows off the switcher, so no row\n * draws it any more.\n *\n * It stays in the type rather than going, because a consumer that passes\n * `labels={{ soon: 'Bientôt' }}` as an object literal would otherwise fail its\n * typecheck on an excess property, and that is a breaking change across four\n * products for a word nothing renders. Drop it from your catalogue at leisure.\n *\n * @deprecated Rendered nowhere since 0.8.0 (B33-1711).\n */\n soon: 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 *\n * `soon` reaches the screen nowhere since 0.8.0 (B33-1711); its default stays\n * so the type keeps the field. See the field.\n */\nexport const defaultAppSwitcherLabels: AppSwitcherLabels = {\n panel: 'Boost33 products',\n notOnPlan: 'Not on plan',\n soon: 'Soon',\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":"AAwEO,MAAMA,IAA8C;AAAA,EACzD,OAAO;AAAA,EACP,WAAW;AAAA,EACX,MAAM;AAAA,EACN,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;"}
@@ -108,8 +108,14 @@ export interface CommandPaletteProps {
108
108
  linkComponent?: ElementType;
109
109
  }
110
110
  /**
111
- * The command palette: a search field over grouped results, opened from the top
112
- * bar's search button or `⌘K` (B33-495).
111
+ * The command palette: a search field over grouped results (B33-495).
112
+ *
113
+ * **It has no trigger in the shell.** The top bar's search button it was drawn
114
+ * to open from was removed on 3 Nov 2026 (B33-1710), and the palette is left out
115
+ * of 0.8.0 entirely: the shell neither renders it nor registers `⌘K`, because a
116
+ * shortcut that opens a Stage A drawing does nothing a person can use. It
117
+ * returns, its top-bar trigger and `⌘K` together as one change, when search
118
+ * ships.
113
119
  *
114
120
  * **Stage A. Static, and not yet through its fidelity gate.** It holds no state,
115
121
  * wires no handler and registers no shortcut. It is not the modal variant yet
@@ -1 +1 @@
1
- {"version":3,"file":"CommandPalette.js","sources":["../../../src/components/CommandPalette/CommandPalette.tsx"],"sourcesContent":["// No \"use client\" directive, and that is a decision rather than an omission.\n//\n// Stage A holds no state, wires no handler and attaches no listener, so there is\n// nothing here a Server Component cannot render -- and tests/guards.test.ts\n// asserts the built package carries the directive on exactly the three modules\n// that need it (B33-846). Adding a fourth now would make that guard say\n// something untrue about this file. `useId` is one of the four hooks React 19's\n// react-server build exports, so the id generation below is server-safe.\n//\n// Stage B changes that: the modal variant of useAnchoredOverlay holds open\n// state, traps focus and marks the background inert, none of which survives on\n// the server. The directive and the guard's expectation move together then.\n\nimport { useId, type ElementType, type ReactNode } from 'react'\nimport { SearchGlyph } from './icons'\nimport { resolveCommandPaletteLabels, type CommandPaletteLabels } from './labels'\nimport styles from './CommandPalette.module.css'\n\n/**\n * One row in the palette. The host owns everything about it: its words, where it\n * goes, and the glyph in its leading slot.\n *\n * There is no `kind` field and there will not be one. The artboard draws three\n * kinds of row -- a page, a product, an action -- but the groups they sit in are\n * the host's and arbitrary, so a `kind` would be this package deciding what a\n * host's group *means*, which is the product meaning CLAUDE.md keeps out. A row\n * that draws a product tile draws it because the host passed\n * `icon={<ProductMark slug=\"boostscore\" />}`, not because the palette recognised\n * anything.\n */\nexport interface CommandPaletteResult {\n /**\n * Stable within one palette. It is the row's React key and, composed with the\n * palette's own `useId` base, the DOM id `aria-activedescendant` points at --\n * so two results sharing an id would leave the highlight ambiguous.\n */\n id: string\n /** The row's visible text, e.g. \"Buy tokens\". The host's, never defaulted. */\n label: string\n /**\n * Where choosing this row goes. A result with an `href` navigates and renders\n * through `linkComponent`; one without is an action and renders as a button.\n *\n * The button has no handler in Stage A. Activation -- Enter in the input, or a\n * click -- is interaction, and interaction is Stage B.\n */\n href?: string\n /**\n * The glyph in the row's 22px leading slot. Supplied by the host, because what\n * a row *is* is the host's business (see the note on this interface).\n *\n * The slot is drawn whether or not a row has one, so labels line up in a\n * single lane down the panel -- the same reason NavItem's trailing slot is\n * fixed-width (CONTEXT.md, \"Trailing slot\").\n */\n icon?: ReactNode\n}\n\n/** One titled run of results -- the artboard's Pages, Products and Actions. */\nexport interface CommandPaletteGroup {\n /** Stable within one palette; the group's React key and its heading's DOM id. */\n id: string\n /**\n * The group's uppercase label, e.g. \"Pages\". The host's words, not this\n * package's: the palette owns the grouping chrome and not the grouping.\n *\n * **Omit it and the group draws its rows with no label at all.** That is the\n * state the artboard's annotation describes -- \"before a search starts, the\n * same list shows Recent pages instead of grouped results, same shell, no\n * group labels needed\" -- expressed as the absence of a heading rather than as\n * a second mode. One prop, not a variant (CLAUDE.md, \"build-once components\").\n */\n heading?: string\n /** The rows, in the order the host wants them read. An empty group is skipped. */\n results: CommandPaletteResult[]\n}\n\nexport interface CommandPaletteProps {\n /**\n * The grouped results, in the order they should appear.\n *\n * **The palette takes these as props and owns no index.** That is the decision\n * recorded on B33-495 on 2026-10-06: Pages, Products and Actions are all the\n * host's, there is no cross-suite search index in any repo, and this component\n * must not imply one. It is scope-agnostic by construction -- four products'\n * worth of results render exactly as one product's do -- so a real index later\n * changes what is passed in and not what is here.\n */\n groups: CommandPaletteGroup[]\n /**\n * What is in the search field. Rendered as the input's initial value.\n *\n * Uncontrolled on purpose for now: a `value` with no `onChange` is a React\n * warning and a field nobody can type in, and the handler is Stage B. The\n * element and its markup do not change when the wiring arrives.\n */\n query?: string\n /**\n * The `id` of the highlighted result, if any.\n *\n * Settable from day one for the same reason `useAnchoredOverlay` takes `open`:\n * it is what lets a specimen route draw the highlighted row statically, which\n * is Stage A rather than wiring (ADR-0009). What *moves* the highlight is\n * Stage B. That the palette has a highlight at all, rather than moving DOM\n * focus between rows, is settled -- ADR-0010 puts it on the combobox pattern\n * with `aria-activedescendant`, and B33-693 is closed.\n *\n * An id matching no result is a supported state and simply draws no highlight.\n */\n activeResultId?: string\n /**\n * Every user-facing string that is not result data. Each field defaults to\n * English, and a field left out -- or supplied as `undefined`, which is what a\n * missed next-intl key looks like -- keeps its default (B33-873).\n */\n labels?: Partial<CommandPaletteLabels>\n /**\n * The element a navigating result renders through. Defaults to a plain `a`; a\n * router-owning consumer passes its own so rows route client-side with the\n * locale prefix intact (ADR-0011, B33-847).\n *\n * Consulted only for the linked form, as ADR-0011 requires: a result with no\n * `href` is an action and stays a `<button>`, and `linkComponent` is ignored\n * on it rather than rendered.\n */\n linkComponent?: ElementType\n}\n\n/**\n * The command palette: a search field over grouped results, opened from the top\n * bar's search button or `⌘K` (B33-495).\n *\n * **Stage A. Static, and not yet through its fidelity gate.** It holds no state,\n * wires no handler and registers no shortcut. It is not the modal variant yet\n * either: ADR-0009 folds this component into the one dismissal contract behind a\n * `modal` flag and it is the only one of the four anchored overlays that sets\n * it, but modality *is* interaction -- the focus trap, the `inert` background\n * and backdrop dismissal all arrive with the wiring.\n *\n * A combobox over a listbox, not a disclosure (ADR-0010): the input carries\n * `role=\"combobox\"` and names the highlighted row through\n * `aria-activedescendant`, the list is a `listbox`, each result an `option` and\n * each group a `group` with its own label. That markup is what the ADR settles;\n * what the arrow keys do inside it is Stage B's to implement.\n */\nexport function CommandPalette({\n groups,\n query,\n activeResultId,\n labels,\n linkComponent,\n}: CommandPaletteProps) {\n const t = resolveCommandPaletteLabels(labels)\n const Link: ElementType = linkComponent ?? 'a'\n\n // One base per mounted palette, so the specimen can stack several on a page\n // without two listboxes sharing an id and leaving aria-controls ambiguous.\n const base = useId()\n const listId = `${base}-results`\n const optionId = (resultId: string) => `${base}-result-${resultId}`\n const headingId = (groupId: string) => `${base}-heading-${groupId}`\n\n // A group with no rows is not drawn -- a heading over nothing is a promise the\n // panel does not keep.\n const populated = groups.filter((group) => group.results.length > 0)\n\n // The highlight is only announced if it names a row that is actually on\n // screen. An `aria-activedescendant` pointing at a missing id is worse than\n // none: it tells a screen reader there is a selection and then gives it\n // nothing to read.\n const highlighted =\n activeResultId !== undefined &&\n populated.some((group) => group.results.some((result) => result.id === activeResultId))\n ? activeResultId\n : undefined\n\n return (\n <div className={styles.panel}>\n <div className={styles.searchRow}>\n <SearchGlyph className={styles.searchGlyph} />\n <input\n type=\"text\"\n className={styles.input}\n // ADR-0010. The input is the combobox; the list it controls is the\n // popup. `aria-expanded` describes that popup and nothing else, which\n // is why it is false when there is no list to point at -- the panel\n // being on screen is a separate fact, and in Stage B it is the dialog\n // that carries it.\n role=\"combobox\"\n aria-label={t.search}\n aria-expanded={populated.length > 0}\n aria-controls={populated.length > 0 ? listId : undefined}\n aria-activedescendant={highlighted === undefined ? undefined : optionId(highlighted)}\n aria-autocomplete=\"list\"\n placeholder={t.placeholder}\n defaultValue={query}\n />\n <span className={styles.dismissKey}>{t.dismissKey}</span>\n </div>\n\n {populated.length > 0 ? (\n <div id={listId} className={styles.results} role=\"listbox\" aria-label={t.results}>\n {populated.map((group) => (\n <div\n key={group.id}\n className={styles.group}\n role=\"group\"\n aria-labelledby={group.heading === undefined ? undefined : headingId(group.id)}\n >\n {group.heading !== undefined && (\n <p id={headingId(group.id)} className={styles.groupHeading}>\n {group.heading}\n </p>\n )}\n {group.results.map((result) => {\n const selected = result.id === highlighted\n // Children of an `option` are presentational in ARIA, so a\n // focusable descendant is a conflict rather than a convenience:\n // the row's own text is what gets announced either way. Taking\n // the row out of the tab sequence is what the role already\n // implies, not an anticipation of traversal -- focus stays in\n // the input under ADR-0010's combobox model.\n const rowProps = {\n id: optionId(result.id),\n className: styles.row,\n role: 'option',\n 'aria-selected': selected,\n tabIndex: -1,\n }\n const body = (\n <>\n <span className={styles.icon}>{result.icon}</span>\n <span className={styles.label}>{result.label}</span>\n </>\n )\n return result.href === undefined ? (\n <button key={result.id} type=\"button\" {...rowProps}>\n {body}\n </button>\n ) : (\n <Link key={result.id} href={result.href} {...rowProps}>\n {body}\n </Link>\n )\n })}\n </div>\n ))}\n </div>\n ) : (\n <p className={styles.empty}>{t.empty}</p>\n )}\n\n <div className={styles.footer}>\n <span className={styles.hints}>{t.footerHints}</span>\n </div>\n </div>\n )\n}\n"],"names":["CommandPalette","groups","query","activeResultId","labels","linkComponent","t","resolveCommandPaletteLabels","Link","base","useId","listId","optionId","resultId","headingId","groupId","populated","group","highlighted","result","jsxs","styles","jsx","SearchGlyph","selected","rowProps","body","Fragment"],"mappings":"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"}
1
+ {"version":3,"file":"CommandPalette.js","sources":["../../../src/components/CommandPalette/CommandPalette.tsx"],"sourcesContent":["// No \"use client\" directive, and that is a decision rather than an omission.\n//\n// Stage A holds no state, wires no handler and attaches no listener, so there is\n// nothing here a Server Component cannot render -- and tests/guards.test.ts\n// asserts the built package carries the directive on exactly the three modules\n// that need it (B33-846). Adding a fourth now would make that guard say\n// something untrue about this file. `useId` is one of the four hooks React 19's\n// react-server build exports, so the id generation below is server-safe.\n//\n// Stage B changes that: the modal variant of useAnchoredOverlay holds open\n// state, traps focus and marks the background inert, none of which survives on\n// the server. The directive and the guard's expectation move together then.\n\nimport { useId, type ElementType, type ReactNode } from 'react'\nimport { SearchGlyph } from './icons'\nimport { resolveCommandPaletteLabels, type CommandPaletteLabels } from './labels'\nimport styles from './CommandPalette.module.css'\n\n/**\n * One row in the palette. The host owns everything about it: its words, where it\n * goes, and the glyph in its leading slot.\n *\n * There is no `kind` field and there will not be one. The artboard draws three\n * kinds of row -- a page, a product, an action -- but the groups they sit in are\n * the host's and arbitrary, so a `kind` would be this package deciding what a\n * host's group *means*, which is the product meaning CLAUDE.md keeps out. A row\n * that draws a product tile draws it because the host passed\n * `icon={<ProductMark slug=\"boostscore\" />}`, not because the palette recognised\n * anything.\n */\nexport interface CommandPaletteResult {\n /**\n * Stable within one palette. It is the row's React key and, composed with the\n * palette's own `useId` base, the DOM id `aria-activedescendant` points at --\n * so two results sharing an id would leave the highlight ambiguous.\n */\n id: string\n /** The row's visible text, e.g. \"Buy tokens\". The host's, never defaulted. */\n label: string\n /**\n * Where choosing this row goes. A result with an `href` navigates and renders\n * through `linkComponent`; one without is an action and renders as a button.\n *\n * The button has no handler in Stage A. Activation -- Enter in the input, or a\n * click -- is interaction, and interaction is Stage B.\n */\n href?: string\n /**\n * The glyph in the row's 22px leading slot. Supplied by the host, because what\n * a row *is* is the host's business (see the note on this interface).\n *\n * The slot is drawn whether or not a row has one, so labels line up in a\n * single lane down the panel -- the same reason NavItem's trailing slot is\n * fixed-width (CONTEXT.md, \"Trailing slot\").\n */\n icon?: ReactNode\n}\n\n/** One titled run of results -- the artboard's Pages, Products and Actions. */\nexport interface CommandPaletteGroup {\n /** Stable within one palette; the group's React key and its heading's DOM id. */\n id: string\n /**\n * The group's uppercase label, e.g. \"Pages\". The host's words, not this\n * package's: the palette owns the grouping chrome and not the grouping.\n *\n * **Omit it and the group draws its rows with no label at all.** That is the\n * state the artboard's annotation describes -- \"before a search starts, the\n * same list shows Recent pages instead of grouped results, same shell, no\n * group labels needed\" -- expressed as the absence of a heading rather than as\n * a second mode. One prop, not a variant (CLAUDE.md, \"build-once components\").\n */\n heading?: string\n /** The rows, in the order the host wants them read. An empty group is skipped. */\n results: CommandPaletteResult[]\n}\n\nexport interface CommandPaletteProps {\n /**\n * The grouped results, in the order they should appear.\n *\n * **The palette takes these as props and owns no index.** That is the decision\n * recorded on B33-495 on 2026-10-06: Pages, Products and Actions are all the\n * host's, there is no cross-suite search index in any repo, and this component\n * must not imply one. It is scope-agnostic by construction -- four products'\n * worth of results render exactly as one product's do -- so a real index later\n * changes what is passed in and not what is here.\n */\n groups: CommandPaletteGroup[]\n /**\n * What is in the search field. Rendered as the input's initial value.\n *\n * Uncontrolled on purpose for now: a `value` with no `onChange` is a React\n * warning and a field nobody can type in, and the handler is Stage B. The\n * element and its markup do not change when the wiring arrives.\n */\n query?: string\n /**\n * The `id` of the highlighted result, if any.\n *\n * Settable from day one for the same reason `useAnchoredOverlay` takes `open`:\n * it is what lets a specimen route draw the highlighted row statically, which\n * is Stage A rather than wiring (ADR-0009). What *moves* the highlight is\n * Stage B. That the palette has a highlight at all, rather than moving DOM\n * focus between rows, is settled -- ADR-0010 puts it on the combobox pattern\n * with `aria-activedescendant`, and B33-693 is closed.\n *\n * An id matching no result is a supported state and simply draws no highlight.\n */\n activeResultId?: string\n /**\n * Every user-facing string that is not result data. Each field defaults to\n * English, and a field left out -- or supplied as `undefined`, which is what a\n * missed next-intl key looks like -- keeps its default (B33-873).\n */\n labels?: Partial<CommandPaletteLabels>\n /**\n * The element a navigating result renders through. Defaults to a plain `a`; a\n * router-owning consumer passes its own so rows route client-side with the\n * locale prefix intact (ADR-0011, B33-847).\n *\n * Consulted only for the linked form, as ADR-0011 requires: a result with no\n * `href` is an action and stays a `<button>`, and `linkComponent` is ignored\n * on it rather than rendered.\n */\n linkComponent?: ElementType\n}\n\n/**\n * The command palette: a search field over grouped results (B33-495).\n *\n * **It has no trigger in the shell.** The top bar's search button it was drawn\n * to open from was removed on 3 Nov 2026 (B33-1710), and the palette is left out\n * of 0.8.0 entirely: the shell neither renders it nor registers `⌘K`, because a\n * shortcut that opens a Stage A drawing does nothing a person can use. It\n * returns, its top-bar trigger and `⌘K` together as one change, when search\n * ships.\n *\n * **Stage A. Static, and not yet through its fidelity gate.** It holds no state,\n * wires no handler and registers no shortcut. It is not the modal variant yet\n * either: ADR-0009 folds this component into the one dismissal contract behind a\n * `modal` flag and it is the only one of the four anchored overlays that sets\n * it, but modality *is* interaction -- the focus trap, the `inert` background\n * and backdrop dismissal all arrive with the wiring.\n *\n * A combobox over a listbox, not a disclosure (ADR-0010): the input carries\n * `role=\"combobox\"` and names the highlighted row through\n * `aria-activedescendant`, the list is a `listbox`, each result an `option` and\n * each group a `group` with its own label. That markup is what the ADR settles;\n * what the arrow keys do inside it is Stage B's to implement.\n */\nexport function CommandPalette({\n groups,\n query,\n activeResultId,\n labels,\n linkComponent,\n}: CommandPaletteProps) {\n const t = resolveCommandPaletteLabels(labels)\n const Link: ElementType = linkComponent ?? 'a'\n\n // One base per mounted palette, so the specimen can stack several on a page\n // without two listboxes sharing an id and leaving aria-controls ambiguous.\n const base = useId()\n const listId = `${base}-results`\n const optionId = (resultId: string) => `${base}-result-${resultId}`\n const headingId = (groupId: string) => `${base}-heading-${groupId}`\n\n // A group with no rows is not drawn -- a heading over nothing is a promise the\n // panel does not keep.\n const populated = groups.filter((group) => group.results.length > 0)\n\n // The highlight is only announced if it names a row that is actually on\n // screen. An `aria-activedescendant` pointing at a missing id is worse than\n // none: it tells a screen reader there is a selection and then gives it\n // nothing to read.\n const highlighted =\n activeResultId !== undefined &&\n populated.some((group) => group.results.some((result) => result.id === activeResultId))\n ? activeResultId\n : undefined\n\n return (\n <div className={styles.panel}>\n <div className={styles.searchRow}>\n <SearchGlyph className={styles.searchGlyph} />\n <input\n type=\"text\"\n className={styles.input}\n // ADR-0010. The input is the combobox; the list it controls is the\n // popup. `aria-expanded` describes that popup and nothing else, which\n // is why it is false when there is no list to point at -- the panel\n // being on screen is a separate fact, and in Stage B it is the dialog\n // that carries it.\n role=\"combobox\"\n aria-label={t.search}\n aria-expanded={populated.length > 0}\n aria-controls={populated.length > 0 ? listId : undefined}\n aria-activedescendant={highlighted === undefined ? undefined : optionId(highlighted)}\n aria-autocomplete=\"list\"\n placeholder={t.placeholder}\n defaultValue={query}\n />\n <span className={styles.dismissKey}>{t.dismissKey}</span>\n </div>\n\n {populated.length > 0 ? (\n <div id={listId} className={styles.results} role=\"listbox\" aria-label={t.results}>\n {populated.map((group) => (\n <div\n key={group.id}\n className={styles.group}\n role=\"group\"\n aria-labelledby={group.heading === undefined ? undefined : headingId(group.id)}\n >\n {group.heading !== undefined && (\n <p id={headingId(group.id)} className={styles.groupHeading}>\n {group.heading}\n </p>\n )}\n {group.results.map((result) => {\n const selected = result.id === highlighted\n // Children of an `option` are presentational in ARIA, so a\n // focusable descendant is a conflict rather than a convenience:\n // the row's own text is what gets announced either way. Taking\n // the row out of the tab sequence is what the role already\n // implies, not an anticipation of traversal -- focus stays in\n // the input under ADR-0010's combobox model.\n const rowProps = {\n id: optionId(result.id),\n className: styles.row,\n role: 'option',\n 'aria-selected': selected,\n tabIndex: -1,\n }\n const body = (\n <>\n <span className={styles.icon}>{result.icon}</span>\n <span className={styles.label}>{result.label}</span>\n </>\n )\n return result.href === undefined ? (\n <button key={result.id} type=\"button\" {...rowProps}>\n {body}\n </button>\n ) : (\n <Link key={result.id} href={result.href} {...rowProps}>\n {body}\n </Link>\n )\n })}\n </div>\n ))}\n </div>\n ) : (\n <p className={styles.empty}>{t.empty}</p>\n )}\n\n <div className={styles.footer}>\n <span className={styles.hints}>{t.footerHints}</span>\n </div>\n </div>\n )\n}\n"],"names":["CommandPalette","groups","query","activeResultId","labels","linkComponent","t","resolveCommandPaletteLabels","Link","base","useId","listId","optionId","resultId","headingId","groupId","populated","group","highlighted","result","jsxs","styles","jsx","SearchGlyph","selected","rowProps","body","Fragment"],"mappings":"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"}
@@ -14,8 +14,21 @@ export interface NavItemProps {
14
14
  collapsed?: boolean;
15
15
  /** Trailing slot content. Omit to leave the slot empty -- it still reserves its width so counts line up across rows. */
16
16
  count?: number;
17
- /** Renders `count` in the primary color instead of muted. */
17
+ /**
18
+ * Draws `count` as a filled pill rather than muted text -- the "needs you"
19
+ * count (board 20 of the 3 Nov 2026 redesign, B33-1711). Until 0.8 it drew the
20
+ * count in --color-primary text, which was 3.45:1 on the grey sidebar and
21
+ * failed AA at the row's size; the pill is white on --color-orange-600, 5.18:1.
22
+ */
18
23
  countUrgent?: boolean;
24
+ /**
25
+ * A 7px dot in the trailing slot, saying the row needs attention -- an
26
+ * unfinished profile, say (board 20, B33-1711). The dot is colour alone, so
27
+ * the string is what a screen reader hears in its place, in the consumer's
28
+ * language ("Incomplete"). Ignored when `count` is set: the slot holds one
29
+ * thing.
30
+ */
31
+ dot?: string;
19
32
  href?: string;
20
33
  /**
21
34
  * The element the linked form renders through. Defaults to a plain `a`; a
@@ -29,4 +42,4 @@ export interface NavItemProps {
29
42
  linkComponent?: ElementType;
30
43
  onClick?: () => void;
31
44
  }
32
- export declare function NavItem({ children, icon, active, collapsed, count, countUrgent, href, linkComponent, onClick }: NavItemProps): import("react").JSX.Element;
45
+ export declare function NavItem({ children, icon, active, collapsed, count, countUrgent, dot, href, linkComponent, onClick }: NavItemProps): import("react").JSX.Element;
@@ -1,18 +1,21 @@
1
- import { jsx as e, jsxs as p, Fragment as N } from "react/jsx-runtime";
1
+ import { jsx as e, jsxs as d, Fragment as m } from "react/jsx-runtime";
2
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: [
3
+ function b({ children: u, icon: n, active: s = !1, collapsed: p = !1, count: l, countUrgent: h = !1, dot: r, href: t, linkComponent: N, onClick: c }) {
4
+ const i = {
5
+ "data-active": s ? "true" : void 0,
6
+ "data-collapsed": p ? "true" : void 0,
7
+ "aria-current": s ? "page" : void 0
8
+ }, o = /* @__PURE__ */ d(m, { children: [
9
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 }) })
10
+ /* @__PURE__ */ e("span", { className: a.label, children: u }),
11
+ /* @__PURE__ */ e("span", { className: a.trailingSlot, children: l != null ? /* @__PURE__ */ e("span", { className: h ? a.countUrgent : a.count, children: l }) : r != null && /* @__PURE__ */ d(m, { children: [
12
+ /* @__PURE__ */ e("span", { className: a.dot, "aria-hidden": "true" }),
13
+ /* @__PURE__ */ e("span", { className: a.srOnly, children: r })
14
+ ] }) })
12
15
  ] });
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 });
16
+ return t ? /* @__PURE__ */ e(N ?? "a", { className: a.row, href: t, onClick: c, ...i, children: o }) : /* @__PURE__ */ e("button", { type: "button", className: a.row, onClick: c, ...i, children: o });
14
17
  }
15
18
  export {
16
- v as NavItem
19
+ b as NavItem
17
20
  };
18
21
  //# sourceMappingURL=NavItem.js.map
@@ -1 +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;"}
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 /**\n * Draws `count` as a filled pill rather than muted text -- the \"needs you\"\n * count (board 20 of the 3 Nov 2026 redesign, B33-1711). Until 0.8 it drew the\n * count in --color-primary text, which was 3.45:1 on the grey sidebar and\n * failed AA at the row's size; the pill is white on --color-orange-600, 5.18:1.\n */\n countUrgent?: boolean\n /**\n * A 7px dot in the trailing slot, saying the row needs attention -- an\n * unfinished profile, say (board 20, B33-1711). The dot is colour alone, so\n * the string is what a screen reader hears in its place, in the consumer's\n * language (\"Incomplete\"). Ignored when `count` is set: the slot holds one\n * thing.\n */\n dot?: string\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, dot, 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 ? (\n <span className={countUrgent ? styles.countUrgent : styles.count}>{count}</span>\n ) : (\n dot != null && (\n <>\n <span className={styles.dot} aria-hidden=\"true\" />\n <span className={styles.srOnly}>{dot}</span>\n </>\n )\n )}\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","dot","href","linkComponent","onClick","dataProps","content","jsxs","Fragment","styles","jsx"],"mappings":";;AA+CO,SAASA,EAAQ,EAAE,UAAAC,GAAU,MAAAC,GAAM,QAAAC,IAAS,IAAO,WAAAC,IAAY,IAAO,OAAAC,GAAO,aAAAC,IAAc,IAAO,KAAAC,GAAK,MAAAC,GAAM,eAAAC,GAAe,SAAAC,KAAyB;AAC1J,QAAMC,IAAY;AAAA,IAChB,eAAeR,IAAU,SAAmB;AAAA,IAC5C,kBAAkBC,IAAa,SAAmB;AAAA,IAClD,gBAAgBD,IAAU,SAAmB;AAAA,EAAA,GAGzCS,IACJ,gBAAAC,EAAAC,GAAA,EACG,UAAA;AAAA,IAAAZ,KAAQ,0BACN,QAAA,EAAK,WAAWa,EAAO,MAAM,eAAY,QACvC,UAAAb,EAAA,CACH;AAAA,IAEF,gBAAAc,EAAC,QAAA,EAAK,WAAWD,EAAO,OAAQ,UAAAd,GAAS;AAAA,sBACxC,QAAA,EAAK,WAAWc,EAAO,cACrB,UAAAV,KAAS,OACR,gBAAAW,EAAC,QAAA,EAAK,WAAWV,IAAcS,EAAO,cAAcA,EAAO,OAAQ,aAAM,IAEzER,KAAO,QACL,gBAAAM,EAAAC,GAAA,EACE,UAAA;AAAA,MAAA,gBAAAE,EAAC,QAAA,EAAK,WAAWD,EAAO,KAAK,eAAY,QAAO;AAAA,MAChD,gBAAAC,EAAC,QAAA,EAAK,WAAWD,EAAO,QAAS,UAAAR,EAAA,CAAI;AAAA,IAAA,EAAA,CACvC,EAAA,CAGN;AAAA,EAAA,GACF;AAGF,SAAIC,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;"}
@@ -1,18 +1,22 @@
1
- const t = "_row_w21mf_4", o = "_icon_w21mf_41", n = "_label_w21mf_66", _ = "_trailingSlot_w21mf_92", c = "_count_w21mf_106", l = "_countUrgent_w21mf_110", r = {
1
+ const t = "_row_1vu2t_4", o = "_icon_1vu2t_41", n = "_label_1vu2t_66", _ = "_trailingSlot_1vu2t_106", c = "_count_1vu2t_122", l = "_countUrgent_1vu2t_134", s = "_dot_1vu2t_152", u = "_srOnly_1vu2t_151", r = {
2
2
  row: t,
3
3
  icon: o,
4
4
  label: n,
5
5
  trailingSlot: _,
6
6
  count: c,
7
- countUrgent: l
7
+ countUrgent: l,
8
+ dot: s,
9
+ srOnly: u
8
10
  };
9
11
  export {
10
12
  c as count,
11
13
  l as countUrgent,
12
14
  r as default,
15
+ s as dot,
13
16
  o as icon,
14
17
  n as label,
15
18
  t as row,
19
+ u as srOnly,
16
20
  _ as trailingSlot
17
21
  };
18
22
  //# sourceMappingURL=NavItem.module.css.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"NavItem.module.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
1
+ {"version":3,"file":"NavItem.module.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
@@ -1,10 +1,10 @@
1
- const s = "_section_3sxsg_5", e = "_header_3sxsg_13", t = {
2
- section: s,
3
- header: e
1
+ const e = "_section_wg5c6_5", s = "_header_wg5c6_13", t = {
2
+ section: e,
3
+ header: s
4
4
  };
5
5
  export {
6
6
  t as default,
7
- e as header,
8
- s as section
7
+ s as header,
8
+ e as section
9
9
  };
10
10
  //# sourceMappingURL=NavSection.module.css.js.map
@@ -0,0 +1,29 @@
1
+ import { ElementType } from 'react';
2
+ import { NotificationButtonLabels } from './labels';
3
+ export interface NotificationButtonProps {
4
+ /**
5
+ * Draws the unread dot, and tells a screen reader (`labels.unread`). The
6
+ * consumer decides what counts as unread; the button fetches nothing.
7
+ */
8
+ unread?: boolean;
9
+ /** What pressing it does -- opening the product's notifications. Renders a `<button>`. */
10
+ onClick?: () => void;
11
+ /** Where it goes, when it navigates rather than opens something. Renders a link. Wins over `onClick`. */
12
+ href?: string;
13
+ /** The element the link form renders through (ADR-0011). Defaults to `a`. */
14
+ linkComponent?: ElementType;
15
+ /** The button's words. Each field defaults to English. */
16
+ labels?: Partial<NotificationButtonLabels>;
17
+ }
18
+ /**
19
+ * The bell in the top bar, next to the avatar (B33-1712, board 20 of the 3 Nov
20
+ * 2026 redesign): a 32px square on the muted fill, a 17px bell, and an orange
21
+ * dot when something is unread.
22
+ *
23
+ * It is the button and nothing more. NotificationCenter, the panel it would
24
+ * open, is Stage A and not releasable (B33-494), so this opens whatever the
25
+ * consumer wires to it -- or nothing is drawn at all: with neither `onClick` nor
26
+ * `href` the bell would be a control that does nothing, so it does not render
27
+ * (the rule B33-1270 set for the wallet's top-up and the switcher's account row).
28
+ */
29
+ export declare function NotificationButton({ unread, onClick, href, linkComponent, labels }: NotificationButtonProps): import("react").JSX.Element | null;
@@ -0,0 +1,23 @@
1
+ import { jsx as t, jsxs as r, Fragment as d } from "react/jsx-runtime";
2
+ import { resolveNotificationButtonLabels as u } from "./labels.js";
3
+ import o from "./NotificationButton.module.css.js";
4
+ function h({ unread: n = !1, onClick: i, href: e, linkComponent: c, labels: l }) {
5
+ const s = u(l);
6
+ if (e === void 0 && i === void 0) return null;
7
+ const a = /* @__PURE__ */ r(d, { children: [
8
+ /* @__PURE__ */ r("svg", { className: o.bell, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
9
+ /* @__PURE__ */ t("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }),
10
+ /* @__PURE__ */ t("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" }),
11
+ n && /* @__PURE__ */ t("circle", { className: o.dot, cx: "18", cy: "6.5", r: "3", stroke: "none" })
12
+ ] }),
13
+ /* @__PURE__ */ r("span", { className: o.srOnly, children: [
14
+ s.name,
15
+ n && `, ${s.unread}`
16
+ ] })
17
+ ] });
18
+ return e !== void 0 ? /* @__PURE__ */ t(c ?? "a", { className: o.button, href: e, "data-unread": n ? "true" : void 0, children: a }) : /* @__PURE__ */ t("button", { type: "button", className: o.button, onClick: i, "data-unread": n ? "true" : void 0, children: a });
19
+ }
20
+ export {
21
+ h as NotificationButton
22
+ };
23
+ //# sourceMappingURL=NotificationButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NotificationButton.js","sources":["../../../src/components/NotificationButton/NotificationButton.tsx"],"sourcesContent":["// Not a client module, on the same footing as NavItem: it holds no state and\n// calls no hook. A consumer passing `onClick` is already a client component; one\n// passing `href` can render this from the server (B33-846).\n\nimport type { ElementType } from 'react'\nimport { resolveNotificationButtonLabels, type NotificationButtonLabels } from './labels'\nimport styles from './NotificationButton.module.css'\n\nexport interface NotificationButtonProps {\n /**\n * Draws the unread dot, and tells a screen reader (`labels.unread`). The\n * consumer decides what counts as unread; the button fetches nothing.\n */\n unread?: boolean\n /** What pressing it does -- opening the product's notifications. Renders a `<button>`. */\n onClick?: () => void\n /** Where it goes, when it navigates rather than opens something. Renders a link. Wins over `onClick`. */\n href?: string\n /** The element the link form renders through (ADR-0011). Defaults to `a`. */\n linkComponent?: ElementType\n /** The button's words. Each field defaults to English. */\n labels?: Partial<NotificationButtonLabels>\n}\n\n/**\n * The bell in the top bar, next to the avatar (B33-1712, board 20 of the 3 Nov\n * 2026 redesign): a 32px square on the muted fill, a 17px bell, and an orange\n * dot when something is unread.\n *\n * It is the button and nothing more. NotificationCenter, the panel it would\n * open, is Stage A and not releasable (B33-494), so this opens whatever the\n * consumer wires to it -- or nothing is drawn at all: with neither `onClick` nor\n * `href` the bell would be a control that does nothing, so it does not render\n * (the rule B33-1270 set for the wallet's top-up and the switcher's account row).\n */\nexport function NotificationButton({ unread = false, onClick, href, linkComponent, labels }: NotificationButtonProps) {\n const t = resolveNotificationButtonLabels(labels)\n if (href === undefined && onClick === undefined) return null\n\n const content = (\n <>\n <svg className={styles.bell} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9\" />\n <path d=\"M10.3 21a1.94 1.94 0 0 0 3.4 0\" />\n {unread && <circle className={styles.dot} cx=\"18\" cy=\"6.5\" r=\"3\" stroke=\"none\" />}\n </svg>\n <span className={styles.srOnly}>\n {t.name}\n {unread && `, ${t.unread}`}\n </span>\n </>\n )\n\n if (href !== undefined) {\n const Link = linkComponent ?? 'a'\n return (\n <Link className={styles.button} href={href} data-unread={unread ? 'true' : undefined}>\n {content}\n </Link>\n )\n }\n\n return (\n <button type=\"button\" className={styles.button} onClick={onClick} data-unread={unread ? 'true' : undefined}>\n {content}\n </button>\n )\n}\n"],"names":["NotificationButton","unread","onClick","href","linkComponent","labels","t","resolveNotificationButtonLabels","content","jsxs","Fragment","styles","jsx"],"mappings":";;;AAmCO,SAASA,EAAmB,EAAE,QAAAC,IAAS,IAAO,SAAAC,GAAS,MAAAC,GAAM,eAAAC,GAAe,QAAAC,KAAmC;AACpH,QAAMC,IAAIC,EAAgCF,CAAM;AAChD,MAAIF,MAAS,UAAaD,MAAY,OAAW,QAAO;AAExD,QAAMM,IACJ,gBAAAC,EAAAC,GAAA,EACE,UAAA;AAAA,IAAA,gBAAAD,EAAC,SAAI,WAAWE,EAAO,MAAM,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAY,QAC1J,UAAA;AAAA,MAAA,gBAAAC,EAAC,QAAA,EAAK,GAAE,4CAAA,CAA4C;AAAA,MACpD,gBAAAA,EAAC,QAAA,EAAK,GAAE,iCAAA,CAAiC;AAAA,MACxCX,KAAU,gBAAAW,EAAC,UAAA,EAAO,WAAWD,EAAO,KAAK,IAAG,MAAK,IAAG,OAAM,GAAE,KAAI,QAAO,OAAA,CAAO;AAAA,IAAA,GACjF;AAAA,IACA,gBAAAF,EAAC,QAAA,EAAK,WAAWE,EAAO,QACrB,UAAA;AAAA,MAAAL,EAAE;AAAA,MACFL,KAAU,KAAKK,EAAE,MAAM;AAAA,IAAA,EAAA,CAC1B;AAAA,EAAA,GACF;AAGF,SAAIH,MAAS,SAGT,gBAAAS,EAFWR,KAAiB,KAE3B,EAAK,WAAWO,EAAO,QAAQ,MAAAR,GAAY,eAAaF,IAAS,SAAS,QACxE,UAAAO,EAAA,CACH,IAKF,gBAAAI,EAAC,UAAA,EAAO,MAAK,UAAS,WAAWD,EAAO,QAAQ,SAAAT,GAAkB,eAAaD,IAAS,SAAS,QAC9F,UAAAO,GACH;AAEJ;"}
@@ -0,0 +1,14 @@
1
+ const o = "_button_oj9om_6", t = "_bell_oj9om_33", _ = "_dot_oj9om_42", l = "_srOnly_oj9om_41", n = {
2
+ button: o,
3
+ bell: t,
4
+ dot: _,
5
+ srOnly: l
6
+ };
7
+ export {
8
+ t as bell,
9
+ o as button,
10
+ n as default,
11
+ _ as dot,
12
+ l as srOnly
13
+ };
14
+ //# sourceMappingURL=NotificationButton.module.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NotificationButton.module.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
@@ -0,0 +1,12 @@
1
+ export interface NotificationButtonLabels {
2
+ /** The button's accessible name. It draws no text, so this is its whole name. Default "Notifications". */
3
+ name: string;
4
+ /**
5
+ * Appended for a screen reader when `unread` is set, because the unread dot is
6
+ * colour alone. Default "unread".
7
+ */
8
+ unread: string;
9
+ }
10
+ export declare const defaultNotificationButtonLabels: NotificationButtonLabels;
11
+ /** Merge partial labels onto the defaults, skipping explicit `undefined`s (B33-873). */
12
+ export declare function resolveNotificationButtonLabels(labels?: Partial<NotificationButtonLabels>): NotificationButtonLabels;
@@ -0,0 +1,15 @@
1
+ const n = {
2
+ name: "Notifications",
3
+ unread: "unread"
4
+ };
5
+ function o(t) {
6
+ const e = Object.fromEntries(
7
+ Object.entries(t ?? {}).filter(([, i]) => i !== void 0)
8
+ );
9
+ return { ...n, ...e };
10
+ }
11
+ export {
12
+ n as defaultNotificationButtonLabels,
13
+ o as resolveNotificationButtonLabels
14
+ };
15
+ //# sourceMappingURL=labels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labels.js","sources":["../../../src/components/NotificationButton/labels.ts"],"sourcesContent":["// The notification button's words, each a prop with an English default -- the\n// pattern every labels module here follows (B33-849).\n\nexport interface NotificationButtonLabels {\n /** The button's accessible name. It draws no text, so this is its whole name. Default \"Notifications\". */\n name: string\n /**\n * Appended for a screen reader when `unread` is set, because the unread dot is\n * colour alone. Default \"unread\".\n */\n unread: string\n}\n\nexport const defaultNotificationButtonLabels: NotificationButtonLabels = {\n name: 'Notifications',\n unread: 'unread',\n}\n\n/** Merge partial labels onto the defaults, skipping explicit `undefined`s (B33-873). */\nexport function resolveNotificationButtonLabels(labels?: Partial<NotificationButtonLabels>): NotificationButtonLabels {\n const supplied = Object.fromEntries(\n Object.entries(labels ?? {}).filter(([, value]) => value !== undefined),\n ) as Partial<NotificationButtonLabels>\n return { ...defaultNotificationButtonLabels, ...supplied }\n}\n"],"names":["defaultNotificationButtonLabels","resolveNotificationButtonLabels","labels","supplied","value"],"mappings":"AAaO,MAAMA,IAA4D;AAAA,EACvE,MAAM;AAAA,EACN,QAAQ;AACV;AAGO,SAASC,EAAgCC,GAAsE;AACpH,QAAMC,IAAW,OAAO;AAAA,IACtB,OAAO,QAAQD,KAAU,EAAE,EAAE,OAAO,CAAC,CAAA,EAAGE,CAAK,MAAMA,MAAU,MAAS;AAAA,EAAA;AAExE,SAAO,EAAE,GAAGJ,GAAiC,GAAGG,EAAA;AAClD;"}
@@ -1,67 +1,68 @@
1
- import { jsxs as r, jsx as a, Fragment as u } from "react/jsx-runtime";
2
- import { useId as f } from "react";
3
- import { ProductMark as b } from "../ProductMark/ProductMark.js";
4
- import { EmptyBell as v, SourceBellMark as T } from "./icons.js";
5
- import { resolveNotificationCenterLabels as g } from "./labels.js";
1
+ import { jsxs as s, jsx as a, Fragment as u } from "react/jsx-runtime";
2
+ import { useId as b } from "react";
3
+ import { ProductMark as v } from "../ProductMark/ProductMark.js";
4
+ import { EmptyBell as T, SourceBellMark as g } from "./icons.js";
5
+ import { resolveNotificationCenterLabels as k } from "./labels.js";
6
+ import h from "../../buttons.module.css.js";
6
7
  import e from "./NotificationCenter.module.css.js";
7
- const h = 22;
8
- function C({
9
- items: n,
10
- viewAllHref: t,
11
- labels: p,
12
- linkComponent: N
8
+ const p = 22;
9
+ function $({
10
+ items: t,
11
+ viewAllHref: n,
12
+ labels: N,
13
+ linkComponent: f
13
14
  }) {
14
- const s = g(p), c = N ?? "a", d = f(), i = n.filter((l) => l.unread === !0).length, o = n.length === 0;
15
+ const r = k(N), c = f ?? "a", i = b(), d = t.filter((l) => l.unread === !0).length, o = t.length === 0;
15
16
  return (
16
17
  // Named by the heading it already draws rather than by a second copy of the
17
18
  // same word in an aria-label (ADR-0010; see `title` in labels.ts).
18
- /* @__PURE__ */ r("div", { className: e.panel, "aria-labelledby": d, children: [
19
- /* @__PURE__ */ r("div", { className: e.header, children: [
20
- /* @__PURE__ */ a("h2", { className: e.title, id: d, children: s.title }),
21
- !o && /* @__PURE__ */ a("button", { type: "button", className: e.markAll, children: s.markAllAsRead })
19
+ /* @__PURE__ */ s("div", { className: e.panel, "aria-labelledby": i, children: [
20
+ /* @__PURE__ */ s("div", { className: e.header, children: [
21
+ /* @__PURE__ */ a("h2", { className: e.title, id: i, children: r.title }),
22
+ !o && /* @__PURE__ */ a("button", { type: "button", className: `${h.quiet} ${e.markAll}`, children: r.markAllAsRead })
22
23
  ] }),
23
- o ? /* @__PURE__ */ r("div", { className: e.empty, children: [
24
- /* @__PURE__ */ a(v, {}),
25
- /* @__PURE__ */ r("div", { className: e.emptyText, children: [
26
- /* @__PURE__ */ a("p", { className: e.emptyTitle, children: s.emptyTitle }),
27
- /* @__PURE__ */ a("p", { className: e.emptyBody, children: s.emptyBody })
24
+ o ? /* @__PURE__ */ s("div", { className: e.empty, children: [
25
+ /* @__PURE__ */ a(T, {}),
26
+ /* @__PURE__ */ s("div", { className: e.emptyText, children: [
27
+ /* @__PURE__ */ a("p", { className: e.emptyTitle, children: r.emptyTitle }),
28
+ /* @__PURE__ */ a("p", { className: e.emptyBody, children: r.emptyBody })
28
29
  ] })
29
- ] }) : /* @__PURE__ */ r(u, { children: [
30
- /* @__PURE__ */ r("div", { className: e.tabs, children: [
31
- /* @__PURE__ */ a("button", { type: "button", className: e.tab, "aria-pressed": "true", children: s.all }),
32
- /* @__PURE__ */ r("button", { type: "button", className: e.tab, "aria-pressed": "false", children: [
33
- s.unread,
34
- i > 0 && /* @__PURE__ */ a("span", { className: e.count, children: i })
30
+ ] }) : /* @__PURE__ */ s(u, { children: [
31
+ /* @__PURE__ */ s("div", { className: e.tabs, children: [
32
+ /* @__PURE__ */ a("button", { type: "button", className: e.tab, "aria-pressed": "true", children: r.all }),
33
+ /* @__PURE__ */ s("button", { type: "button", className: e.tab, "aria-pressed": "false", children: [
34
+ r.unread,
35
+ d > 0 && /* @__PURE__ */ a("span", { className: e.count, children: d })
35
36
  ] })
36
37
  ] }),
37
- /* @__PURE__ */ a("ul", { className: e.list, children: n.map((l) => {
38
+ /* @__PURE__ */ a("ul", { className: e.list, children: t.map((l) => {
38
39
  const m = l.unread === !0, y = l.href != null ? c : "div";
39
- return /* @__PURE__ */ a("li", { children: /* @__PURE__ */ r(
40
+ return /* @__PURE__ */ a("li", { children: /* @__PURE__ */ s(
40
41
  y,
41
42
  {
42
43
  className: e.row,
43
44
  href: l.href,
44
45
  "data-unread": m ? "true" : void 0,
45
46
  children: [
46
- l.slug != null ? /* @__PURE__ */ a(b, { slug: l.slug, size: h, className: e.sourceMark }) : /* @__PURE__ */ a(T, { size: h }),
47
- /* @__PURE__ */ r("span", { className: e.rowText, children: [
47
+ l.slug != null ? /* @__PURE__ */ a(v, { slug: l.slug, size: p, className: e.sourceMark }) : /* @__PURE__ */ a(g, { size: p }),
48
+ /* @__PURE__ */ s("span", { className: e.rowText, children: [
48
49
  /* @__PURE__ */ a("span", { className: e.rowTitle, children: l.title }),
49
- /* @__PURE__ */ a("span", { className: e.rowMeta, children: l.dateTime != null ? /* @__PURE__ */ a("time", { dateTime: l.dateTime, children: s.meta(l.source, l.time) }) : s.meta(l.source, l.time) })
50
+ /* @__PURE__ */ a("span", { className: e.rowMeta, children: l.dateTime != null ? /* @__PURE__ */ a("time", { dateTime: l.dateTime, children: r.meta(l.source, l.time) }) : r.meta(l.source, l.time) })
50
51
  ] }),
51
- /* @__PURE__ */ a("span", { className: e.unreadSlot, children: m && /* @__PURE__ */ r(u, { children: [
52
+ /* @__PURE__ */ a("span", { className: e.unreadSlot, children: m && /* @__PURE__ */ s(u, { children: [
52
53
  /* @__PURE__ */ a("span", { className: e.dot }),
53
- /* @__PURE__ */ a("span", { className: e.srOnly, children: s.unreadItem })
54
+ /* @__PURE__ */ a("span", { className: e.srOnly, children: r.unreadItem })
54
55
  ] }) })
55
56
  ]
56
57
  }
57
58
  ) }, l.id);
58
59
  }) }),
59
- t != null && /* @__PURE__ */ a("div", { className: e.footer, children: /* @__PURE__ */ a(c, { className: e.viewAll, href: t, children: s.viewAll }) })
60
+ n != null && /* @__PURE__ */ a("div", { className: e.footer, children: /* @__PURE__ */ a(c, { className: `${h.quiet} ${e.viewAll}`, href: n, children: r.viewAll }) })
60
61
  ] })
61
62
  ] })
62
63
  );
63
64
  }
64
65
  export {
65
- C as NotificationCenter
66
+ $ as NotificationCenter
66
67
  };
67
68
  //# sourceMappingURL=NotificationCenter.js.map