@nanisoft/prism-ui 1.2.0 → 1.4.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 (43) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/blocks/index.d.ts +2 -0
  3. package/dist/blocks/index.d.ts.map +1 -1
  4. package/dist/blocks/index.js +1 -0
  5. package/dist/blocks/media-band-01/index.d.ts +3 -0
  6. package/dist/blocks/media-band-01/index.d.ts.map +1 -0
  7. package/dist/blocks/media-band-01/index.js +1 -0
  8. package/dist/blocks/media-band-01/media-band.d.ts +166 -0
  9. package/dist/blocks/media-band-01/media-band.d.ts.map +1 -0
  10. package/dist/blocks/media-band-01/media-band.js +55 -0
  11. package/dist/blocks/site-footer/site-footer.d.ts +23 -8
  12. package/dist/blocks/site-footer/site-footer.d.ts.map +1 -1
  13. package/dist/blocks/site-footer/site-footer.js +10 -4
  14. package/dist/blocks/site-navbar/index.d.ts +1 -1
  15. package/dist/blocks/site-navbar/index.d.ts.map +1 -1
  16. package/dist/blocks/site-navbar/mobile-nav.d.ts +25 -4
  17. package/dist/blocks/site-navbar/mobile-nav.d.ts.map +1 -1
  18. package/dist/blocks/site-navbar/mobile-nav.js +11 -4
  19. package/dist/blocks/site-navbar/nav-menu.d.ts +50 -0
  20. package/dist/blocks/site-navbar/nav-menu.d.ts.map +1 -0
  21. package/dist/blocks/site-navbar/nav-menu.js +44 -0
  22. package/dist/blocks/site-navbar/navbar-controls.d.ts +9 -3
  23. package/dist/blocks/site-navbar/navbar-controls.d.ts.map +1 -1
  24. package/dist/blocks/site-navbar/site-navbar.d.ts +54 -13
  25. package/dist/blocks/site-navbar/site-navbar.d.ts.map +1 -1
  26. package/dist/blocks/site-navbar/site-navbar.js +45 -10
  27. package/dist/catalog.d.ts.map +1 -1
  28. package/dist/catalog.js +10 -0
  29. package/dist/components/ui/calendar.d.ts.map +1 -1
  30. package/dist/components/ui/calendar.js +7 -2
  31. package/dist/components/ui/mini-calendar.js +1 -1
  32. package/dist/components/ui/product-mark.d.ts +26 -7
  33. package/dist/components/ui/product-mark.d.ts.map +1 -1
  34. package/dist/components/ui/product-mark.js +6 -4
  35. package/dist/components/ui/pulse-series.d.ts.map +1 -1
  36. package/dist/components/ui/pulse-series.js +1 -1
  37. package/dist/components/ui/section.d.ts +31 -1
  38. package/dist/components/ui/section.d.ts.map +1 -1
  39. package/dist/components/ui/section.js +22 -2
  40. package/dist/components/ui/sparkline.d.ts.map +1 -1
  41. package/dist/components/ui/sparkline.js +1 -2
  42. package/dist/styles.css +19 -0
  43. package/package.json +1 -1
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ChevronDown } from 'lucide-react';
4
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '../../components/ui/dropdown-menu.js';
5
+ /**
6
+ * One group of the site's own destinations, as a menu.
7
+ *
8
+ * A bar whose navigation is a row of links clubs into groups the moment a site
9
+ * outgrows the row: five destinations that are all "the parts of the library"
10
+ * beside four that are each a body of knowledge is a row a reader scans
11
+ * word by word rather than recognises at a glance. This is the second half of
12
+ * that row, and it is the one control in the row, because a group is not a
13
+ * destination: it is a place other destinations live behind.
14
+ *
15
+ * **The items are links, not commands, for the reason the sites menu states.**
16
+ * A menu item that navigated would be a command wearing a link's clothes, and
17
+ * a reader who middle-clicks a row that only looks like a link gets no new tab.
18
+ * Each item is rendered as the anchor itself rather than wrapping one, so the
19
+ * row is one tab stop and one target.
20
+ *
21
+ * **The menu is a closed group, and a closed group shows nothing.** A reader
22
+ * with scripting off sees the trigger and cannot open it, which is why the bar
23
+ * renders a group as a menu only on the row above the panel threshold: below
24
+ * it, the sheet lists every destination flat, and the bar's caller keeps
25
+ * whatever routes it cannot afford to hide out of a group. The trigger is
26
+ * filled when the reader is inside the group, so the place they are is on the
27
+ * row even with the menu shut.
28
+ *
29
+ * **The chevron rotates on the open state and on nothing else.** Something
30
+ * happened and the reader can see that it did, at the authored duration, and
31
+ * the rotation is the only motion here.
32
+ */
33
+ export function NavMenu({ label, links, current }) {
34
+ return (_jsxs(DropdownMenu, { children: [_jsxs(DropdownMenuTrigger, { "data-slot": "site-navbar-nav-trigger", "data-current": current ? 'true' : undefined, className: current
35
+ ? 'group data-[popup-open]:bg-accent data-[popup-open]:text-accent-foreground bg-accent text-accent-foreground focus-visible:border-ring focus-visible:ring-ring gap-1 rounded-md px-2 py-1.5 text-sm font-medium transition-colors focus-visible:ring-[3px] focus-visible:outline-none'
36
+ : 'group data-[popup-open]:bg-accent data-[popup-open]:text-accent-foreground text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:border-ring focus-visible:ring-ring gap-1 rounded-md px-2 py-1.5 text-sm font-medium transition-colors focus-visible:ring-[3px] focus-visible:outline-none', children: [label, _jsx(ChevronDown, { "aria-hidden": true, className: "group-data-[popup-open]:rotate-180 size-4 transition-transform duration-base ease-out" })] }), _jsx(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-44", children: links.map((link) => (_jsx(DropdownMenuItem, {
37
+ /*
38
+ * Rendered AS the link rather than wrapping one. A focusable menu
39
+ * item with an anchor inside it is two tab stops for one row, and a
40
+ * reader who middle-clicks a row that only looks like a link gets no
41
+ * new tab and no way to tell that the row was a link at all.
42
+ */
43
+ render: _jsx("a", { href: link.href, target: link.newTab ? '_blank' : undefined, rel: link.newTab ? 'noopener noreferrer' : undefined, "aria-current": link.current ? 'page' : undefined, "data-current": link.current ? 'true' : undefined }), className: "data-[current]:bg-accent data-[current]:text-accent-foreground data-[current]:font-medium", children: link.label }, link.href))) })] }));
44
+ }
@@ -1,18 +1,24 @@
1
+ import type { ReactNode } from 'react';
1
2
  import type { Mode, PackId } from '../../theming/index.js';
2
3
  import type { SearchDialogMessages } from '../../components/ui/search-dialog.js';
3
4
  import { type ThemePack } from './theme-menu.js';
4
5
  import type { SwitcherProduct } from '../../components/ui/product-switcher.js';
5
- import type { MobileNavLink } from './mobile-nav.js';
6
+ import type { MobileNavEntry } from './mobile-nav.js';
6
7
  /** What the control cluster was told to render. Every field is optional. */
7
8
  export type NavbarControlsProps = {
8
- /** The product whose mark heads the mobile panel. */
9
+ /**
10
+ * The product whose mark heads the mobile panel. The drawn `mark` crosses
11
+ * this client boundary as rendered content, the same way a slot does, so a
12
+ * consumer's drawing stays server-drawn while the panel around it is not.
13
+ */
9
14
  product: {
10
15
  id: string;
11
16
  name: string;
12
17
  pack?: PackId | null;
18
+ mark?: ReactNode;
13
19
  };
14
20
  /** The site's own destinations, for the panel below the row's threshold. */
15
- nav: readonly MobileNavLink[];
21
+ nav: readonly MobileNavEntry[];
16
22
  /** The accessible name of the panel and of its trigger. */
17
23
  navLabel: string;
18
24
  /** The set of sites, or nothing for a site that is the only one. */
@@ -1 +1 @@
1
- {"version":3,"file":"navbar-controls.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-navbar/navbar-controls.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAA;AAI7E,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAA;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAIjD,4EAA4E;AAC5E,MAAM,MAAM,mBAAmB,GAAG;IAChC,qDAAqD;IACrD,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;IAC3D,4EAA4E;IAC5E,GAAG,EAAE,SAAS,aAAa,EAAE,CAAA;IAC7B,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAA;IAChB,oEAAoE;IACpE,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;IAClC,6CAA6C;IAC7C,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uEAAuE;IACvE,MAAM,CAAC,EAAE;QACP,QAAQ,EAAE,MAAM,CAAA;QAChB,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,QAAQ,EAAE,oBAAoB,CAAA;KAC/B,CAAA;IACD,+DAA+D;IAC/D,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,SAAS,SAAS,EAAE,CAAA;KAAE,CAAA;IACvD,6EAA6E;IAC7E,IAAI,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAChD,2CAA2C;IAC3C,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7C,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAA;IACnB,oDAAoD;IACpD,WAAW,EAAE,IAAI,CAAA;IACjB,mEAAmE;IACnE,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,sCAsDxD"}
1
+ {"version":3,"file":"navbar-controls.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-navbar/navbar-controls.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAA;AAI7E,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAA;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAIlD,4EAA4E;AAC5E,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;;OAIG;IACH,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,SAAS,CAAA;KAAE,CAAA;IAC7E,4EAA4E;IAC5E,GAAG,EAAE,SAAS,cAAc,EAAE,CAAA;IAC9B,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAA;IAChB,oEAAoE;IACpE,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;IAClC,6CAA6C;IAC7C,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uEAAuE;IACvE,MAAM,CAAC,EAAE;QACP,QAAQ,EAAE,MAAM,CAAA;QAChB,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,QAAQ,EAAE,oBAAoB,CAAA;KAC/B,CAAA;IACD,+DAA+D;IAC/D,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,SAAS,SAAS,EAAE,CAAA;KAAE,CAAA;IACvD,6EAA6E;IAC7E,IAAI,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAChD,2CAA2C;IAC3C,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7C,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAA;IACnB,oDAAoD;IACpD,WAAW,EAAE,IAAI,CAAA;IACjB,mEAAmE;IACnE,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,sCAsDxD"}
@@ -31,7 +31,41 @@ export type SiteNavLink = {
31
31
  * external: a cross-origin destination is not by itself a reason to open a tab.
32
32
  */
33
33
  newTab?: boolean;
34
+ /** Forbidden: a destination carrying children is a `SiteNavGroup`, not a link. */
35
+ children?: never;
34
36
  };
37
+ /**
38
+ * A group of destinations in the bar's own navigation, rendered as one trigger
39
+ * that opens a menu of them.
40
+ *
41
+ * The group is a place in the row rather than a route: it carries no `href`, and
42
+ * a caller whose group has an index page of its own lists that page among the
43
+ * `children` rather than on the group, so nothing is inventing a route. That is
44
+ * the same shape the site's own sidebar holds a folder to, and for the same
45
+ * reason: a group is where destinations live, not one.
46
+ *
47
+ * **A group hides its destinations until it is opened**, on the row above the
48
+ * panel threshold and nowhere else. The bar's narrow-viewport panel lists every
49
+ * destination flat, so a reader below the threshold meets no group at all, and a
50
+ * caller who cannot afford to hide a route behind a click leaves it out of a
51
+ * group: the search index, the sitemap and the page's own links are outside the
52
+ * bar and unaffected either way.
53
+ */
54
+ export type SiteNavGroup = {
55
+ /** The word on the trigger, which is the group's own name. */
56
+ label: string;
57
+ /** The destinations behind the trigger, in the order a reader should meet them. */
58
+ children: readonly SiteNavLink[];
59
+ /**
60
+ * Marks the group as holding the page the reader is on, for a caller that
61
+ * composes the bar per page and marks each entry itself. A caller that passes
62
+ * `currentPath` leaves this off and the bar resolves it against the group's
63
+ * children.
64
+ */
65
+ current?: boolean;
66
+ };
67
+ /** One entry in the bar's own navigation: a link, or a group of them. */
68
+ export type SiteNavEntry = SiteNavLink | SiteNavGroup;
35
69
  /** The search the bar offers, which is a URL rather than an index. */
36
70
  export type SiteNavSearch = {
37
71
  /**
@@ -79,12 +113,12 @@ export type SiteNavMobileLabels = {
79
113
  /** The props a SiteNavbar takes. */
80
114
  export type SiteNavbarProps = {
81
115
  /**
82
- * The product this bar belongs to: the id, name and pack its `ProductMark` is
83
- * drawn from, and the mark that heads the mobile panel. The bar renders the mark
84
- * itself rather than a slot, because a bar whose brand lockup is a slot is a bar
85
- * every consumer has to reimplement.
116
+ * The product this bar belongs to: the id, name, pack and drawn mark its
117
+ * `ProductMark` is drawn from, and the mark that heads the mobile panel. The
118
+ * bar renders the mark itself rather than a slot, because a bar whose brand
119
+ * lockup is a slot is a bar every consumer has to reimplement.
86
120
  */
87
- product: Pick<ProductMarkProps, 'id' | 'name' | 'pack'>;
121
+ product: Pick<ProductMarkProps, 'id' | 'name' | 'pack' | 'mark'>;
88
122
  /** Where the brand lockup goes. @defaultValue '/' */
89
123
  homeHref?: string;
90
124
  /**
@@ -99,8 +133,13 @@ export type SiteNavbarProps = {
99
133
  * control whose icon is a guess until it is pressed once.
100
134
  */
101
135
  defaultMode: Mode;
102
- /** This site's own destinations. Omit for a bar that carries only the lockup. */
103
- nav?: readonly SiteNavLink[];
136
+ /**
137
+ * This site's own destinations: links, groups of links, or both in the order
138
+ * a reader should meet them. A link renders as itself; a group renders as one
139
+ * trigger opening its children as a menu. Omit for a bar that carries only
140
+ * the lockup.
141
+ */
142
+ nav?: readonly SiteNavEntry[];
104
143
  /**
105
144
  * The accessible name of the site's navigation. Required, and required rather
106
145
  * than defaulted because it is a word a reader hears: a Block that ships no copy
@@ -234,12 +273,14 @@ export type SiteNavbarProps = {
234
273
  * with no release. This package ships no `will-change` anywhere, and the bar is not
235
274
  * the place to start one.
236
275
  *
237
- * **It is a server Component with one client island inside it.** The lockup and the
238
- * navigation are rendered on the server and ship no JavaScript; the controls are a
239
- * single boundary, because a control's state is a reader's state. A site that
240
- * passes no `search`, `sites`, `theme`, `mode` or `nav` renders the whole bar on the
241
- * server and ships none, which is the arrangement a static site wants and the reason
242
- * those props are optional rather than required.
276
+ * **It is a server Component whose client islands are the ones a reader opens.** The
277
+ * lockup and the row's links are rendered on the server and ship no JavaScript; the
278
+ * controls are a single boundary, because a control's state is a reader's state, and
279
+ * a group in the navigation adds one island of its own for the same reason. A site
280
+ * that passes no `search`, `sites`, `theme`, `mode` or `nav`, and groups none of
281
+ * its destinations, renders the whole bar on the server and ships none, which is
282
+ * the arrangement a static site wants and the reason those props are optional
283
+ * rather than required.
243
284
  */
244
285
  export declare function SiteNavbar({ product, homeHref, defaultPack, defaultMode, nav, navLabel, currentPath, sites, currentSiteId, sitesLabel, search, theme, mode, mobileLabels, actions, sticky, className, }: SiteNavbarProps): import("react").JSX.Element;
245
286
  export default SiteNavbar;
@@ -1 +1 @@
1
- {"version":3,"file":"site-navbar.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-navbar/site-navbar.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"site-navbar.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-navbar/site-navbar.tsx"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ProductMark } from '../../components/ui/product-mark.js';
3
3
  import { cn } from '../../lib/utils.js';
4
4
  import { NavbarControls } from './navbar-controls.js';
5
+ import { NavMenu } from './nav-menu.js';
5
6
  /**
6
7
  * The bar at the top of a product site, with the controls a reader needs on every
7
8
  * page of it.
@@ -73,18 +74,52 @@ import { NavbarControls } from './navbar-controls.js';
73
74
  * with no release. This package ships no `will-change` anywhere, and the bar is not
74
75
  * the place to start one.
75
76
  *
76
- * **It is a server Component with one client island inside it.** The lockup and the
77
- * navigation are rendered on the server and ship no JavaScript; the controls are a
78
- * single boundary, because a control's state is a reader's state. A site that
79
- * passes no `search`, `sites`, `theme`, `mode` or `nav` renders the whole bar on the
80
- * server and ships none, which is the arrangement a static site wants and the reason
81
- * those props are optional rather than required.
77
+ * **It is a server Component whose client islands are the ones a reader opens.** The
78
+ * lockup and the row's links are rendered on the server and ship no JavaScript; the
79
+ * controls are a single boundary, because a control's state is a reader's state, and
80
+ * a group in the navigation adds one island of its own for the same reason. A site
81
+ * that passes no `search`, `sites`, `theme`, `mode` or `nav`, and groups none of
82
+ * its destinations, renders the whole bar on the server and ships none, which is
83
+ * the arrangement a static site wants and the reason those props are optional
84
+ * rather than required.
82
85
  */
83
86
  export function SiteNavbar({ product, homeHref = '/', defaultPack, defaultMode, nav, navLabel, currentPath, sites, currentSiteId, sitesLabel, search, theme, mode, mobileLabels, actions, sticky = true, className, }) {
84
- const links = nav ?? [];
85
- const isCurrent = (link) => link.current ?? (currentPath !== undefined && link.href === currentPath);
86
- return (_jsx("header", { "data-slot": "site-navbar", className: cn('border-border bg-background w-full border-b', sticky && 'sticky top-0 z-20', className), children: _jsxs("div", { className: "mx-auto flex h-14 w-full max-w-page items-center gap-2 px-6", children: [_jsx("a", { href: homeHref, className: "focus-visible:border-ring focus-visible:ring-ring shrink-0 rounded-sm focus-visible:ring-[3px] focus-visible:outline-none", children: _jsx(ProductMark, { id: product.id, name: product.name, pack: product.pack, size: "sm" }) }), links.length > 0 ? (_jsx("nav", { "aria-label": navLabel, className: "hidden items-center lg:flex", children: links.map((link) => (_jsx("a", { href: link.href, "aria-current": isCurrent(link) ? 'page' : undefined, rel: link.newTab ? 'noopener noreferrer' : undefined, target: link.newTab ? '_blank' : undefined, className: isCurrent(link)
87
+ const entries = nav ?? [];
88
+ /*
89
+ * The current mark reads the route as a place rather than as a string, so a
90
+ * reader on a page inside a destination is still on it: `/components/button`
91
+ * marks Components on the row, which is the question a reader asking "where
92
+ * am I" is asking. The boundary is the slash, so a Section whose segment is a
93
+ * prefix of another's (`/content` beside a hypothetical `/content-x`) never
94
+ * claims the other's pages.
95
+ */
96
+ const under = (href) => currentPath !== undefined && (currentPath === href || currentPath.startsWith(`${href}/`));
97
+ const leafCurrent = (link) => link.current ?? under(link.href);
98
+ /**
99
+ * A group is current when the reader is on it or inside any of its children.
100
+ * A group is told apart from a link by `href`, which a link must carry and a
101
+ * group must not, so the narrowing is on the one key the two never share.
102
+ */
103
+ const entryCurrent = (entry) => entry.current ??
104
+ ('href' in entry
105
+ ? under(entry.href)
106
+ : entry.children.some((child) => leafCurrent(child)));
107
+ return (_jsx("header", { "data-slot": "site-navbar", className: cn('border-border bg-background w-full border-b', sticky && 'sticky top-0 z-20', className), children: _jsxs("div", { className: "mx-auto flex h-14 w-full max-w-page items-center gap-2 px-6", children: [_jsx("a", { href: homeHref, className: "focus-visible:border-ring focus-visible:ring-ring shrink-0 rounded-sm focus-visible:ring-[3px] focus-visible:outline-none", children: _jsx(ProductMark, { id: product.id, name: product.name, pack: product.pack, mark: product.mark, size: "sm" }) }), entries.length > 0 ? (_jsx("nav", { "aria-label": navLabel, className: "hidden items-center lg:flex", children: entries.map((entry) =>
108
+ /*
109
+ * A group is the one control in the row, because it is the one
110
+ * entry that is a place rather than a destination, and its menu is
111
+ * a client island for the same reason the controls cluster is:
112
+ * whether it is open is a reader's state. The links around it stay
113
+ * server-rendered anchors, so a bar whose row is all links still
114
+ * ships a navigation with no JavaScript in it.
115
+ */
116
+ 'href' in entry ? (_jsx("a", { href: entry.href, "aria-current": entryCurrent(entry) ? 'page' : undefined, rel: entry.newTab ? 'noopener noreferrer' : undefined, target: entry.newTab ? '_blank' : undefined, className: entryCurrent(entry)
87
117
  ? 'bg-accent text-accent-foreground focus-visible:border-ring focus-visible:ring-ring rounded-md px-2 py-1.5 text-sm font-medium transition-colors focus-visible:ring-[3px] focus-visible:outline-none'
88
- : 'text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:border-ring focus-visible:ring-ring rounded-md px-2 py-1.5 text-sm font-medium transition-colors focus-visible:ring-[3px] focus-visible:outline-none', children: link.label }, link.href))) })) : null, _jsx(NavbarControls, { product: product, nav: links.map((link) => ({ ...link, current: isCurrent(link) })), navLabel: navLabel, sites: sites, currentSiteId: currentSiteId, sitesLabel: sitesLabel, search: search, theme: theme, mode: mode, mobileLabels: mobileLabels, defaultPack: defaultPack, defaultMode: defaultMode, actions: actions })] }) }));
118
+ : 'text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:border-ring focus-visible:ring-ring rounded-md px-2 py-1.5 text-sm font-medium transition-colors focus-visible:ring-[3px] focus-visible:outline-none', children: entry.label }, entry.href)) : (_jsx(NavMenu, { label: entry.label, current: entryCurrent(entry), links: entry.children.map((child) => ({ ...child, current: leafCurrent(child) })) }, entry.label))) })) : null, _jsx(NavbarControls, { product: product, nav: entries.map((entry) => 'href' in entry
119
+ ? { ...entry, current: entryCurrent(entry) }
120
+ : {
121
+ label: entry.label,
122
+ children: entry.children.map((child) => ({ ...child, current: leafCurrent(child) })),
123
+ }), navLabel: navLabel, sites: sites, currentSiteId: currentSiteId, sitesLabel: sitesLabel, search: search, theme: theme, mode: mode, mobileLabels: mobileLabels, defaultPack: defaultPack, defaultMode: defaultMode, actions: actions })] }) }));
89
124
  }
90
125
  export default SiteNavbar;
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,aAAa,iDAAkD,CAAA;AAC5E,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD,eAAO,MAAM,oBAAoB,sHAQvB,CAAA;AACV,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAA;AAErE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,WAAW,CAAA;IACjB,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAClC,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,EAAE,QAAQ,GAAG,YAAY,CAAA;CAChC;AAED,eAAO,MAAM,OAAO,EAAE,SAAS,WAAW,EAugGzC,CAAA;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,KAAK,GAAE,SAAS,WAAW,EAAY,GAAG,WAAW,EAAE,CAEnF;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAE1D,CAAA"}
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,aAAa,iDAAkD,CAAA;AAC5E,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD,eAAO,MAAM,oBAAoB,sHAQvB,CAAA;AACV,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAA;AAErE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,WAAW,CAAA;IACjB,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAClC,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,EAAE,QAAQ,GAAG,YAAY,CAAA;CAChC;AAED,eAAO,MAAM,OAAO,EAAE,SAAS,WAAW,EAkhGzC,CAAA;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,KAAK,GAAE,SAAS,WAAW,EAAY,GAAG,WAAW,EAAE,CAEnF;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAE1D,CAAA"}
package/dist/catalog.js CHANGED
@@ -1796,6 +1796,16 @@ export const catalog = [
1796
1796
  exports: ['Gallery01'],
1797
1797
  status: 'stable',
1798
1798
  },
1799
+ {
1800
+ name: 'MediaBand01',
1801
+ slug: 'media-band-01',
1802
+ kind: 'block',
1803
+ category: null,
1804
+ description: 'An editorial band: one media slot held at a caller ratio beside one title, description and optional destination, with the copy always on a solid surface.',
1805
+ source: 'src/blocks/media-band-01/index.tsx',
1806
+ exports: ['MediaBand01'],
1807
+ status: 'stable',
1808
+ },
1799
1809
  {
1800
1810
  name: 'Download01',
1801
1811
  slug: 'download-01',
@@ -1 +1 @@
1
- {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../src/components/ui/calendar.tsx"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS;IACtC,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;CACP,CAAA;AAED,sCAAsC;AACtC,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,gEAAgE;IAChE,aAAa,EAAE,gBAAgB,CAAA;IAC/B;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACxC,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAA;IACrB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;;OAOG;IACH,KAAK,EAAE,IAAI,CAAA;IACX,mFAAmF;IACnF,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,IAAI,CAAA;IACpC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;IACnB,kFAAkF;IAClF,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACpC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAA;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAA;IACjC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,mBAAmB;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAwED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,iBAAS,QAAQ,CAAC,EAChB,KAAK,EACL,UAAU,EACV,aAAa,EACb,YAAgB,EAChB,aAAa,EACb,SAAS,EACT,KAAK,EACL,aAAa,EACb,KAAY,EACZ,aAAa,EACb,cAAc,EACd,QAAQ,EACR,KAAK,EACL,QAAgB,EAChB,IAAI,EACJ,IAAI,EACJ,SAAS,GACV,EAAE,aAAa,+BAyPf;AAED,OAAO,EAAE,QAAQ,EAAE,CAAA"}
1
+ {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../src/components/ui/calendar.tsx"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS;IACtC,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;CACP,CAAA;AAED,sCAAsC;AACtC,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,gEAAgE;IAChE,aAAa,EAAE,gBAAgB,CAAA;IAC/B;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACxC,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAA;IACrB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;;OAOG;IACH,KAAK,EAAE,IAAI,CAAA;IACX,mFAAmF;IACnF,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,IAAI,CAAA;IACpC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;IACnB,kFAAkF;IAClF,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACpC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAA;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAA;IACjC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,mBAAmB;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA6ED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,iBAAS,QAAQ,CAAC,EAChB,KAAK,EACL,UAAU,EACV,aAAa,EACb,YAAgB,EAChB,aAAa,EACb,SAAS,EACT,KAAK,EACL,aAAa,EACb,KAAY,EACZ,aAAa,EACb,cAAc,EACd,QAAQ,EACR,KAAK,EACL,QAAgB,EAChB,IAAI,EACJ,IAAI,EACJ,SAAS,GACV,EAAE,aAAa,+BAyPf;AAED,OAAO,EAAE,QAAQ,EAAE,CAAA"}
@@ -35,7 +35,12 @@ const DAY_CELL =
35
35
  // was added to it, and the fuller grid kept the 36px cell that is the same cell at
36
36
  // a different size. A month grid is the densest control in the package and the one
37
37
  // most worked with a thumb, so it is the last place the floor can be argued about.
38
- 'text-foreground hover:bg-accent hover:text-accent-foreground flex h-9 w-full cursor-pointer items-center justify-center rounded-md text-sm tabular-nums outline-none transition-colors duration-fast ease-out pointer-coarse:h-11 focus-visible:ring-ring focus-visible:ring-[3px]';
38
+ //
39
+ // The cell is centred by the table, not by a `flex` utility: a `display: flex` on
40
+ // a `<th>` or a `<td>` takes the cell out of the table's column layout, and the
41
+ // month drew as one stacked column at four times its height before the utility
42
+ // came off.
43
+ 'text-foreground hover:bg-accent hover:text-accent-foreground h-9 cursor-pointer rounded-md p-0 align-middle text-center text-sm tabular-nums outline-none transition-colors duration-fast ease-out pointer-coarse:h-11 focus-visible:ring-ring focus-visible:ring-[3px]';
39
44
  /**
40
45
  * The two paging controls.
41
46
  *
@@ -226,7 +231,7 @@ function Calendar({ label, monthLabel, weekdayLabels, firstWeekday = 0, previous
226
231
  const focusedIso = iso(focused);
227
232
  const chosenIso = value === null ? null : iso(value);
228
233
  const todayIso = today === undefined ? null : iso(today);
229
- return (_jsxs("div", { "data-slot": "calendar", className: cn('bg-popover text-popover-foreground w-64 rounded-md border p-3 shadow-md', className), children: [_jsxs("div", { className: "mb-2 flex items-center justify-between gap-1", children: [_jsx("button", { type: "button", "data-slot": "calendar-previous", "aria-label": previousLabel, disabled: disabled, onClick: () => onMonthChange(shiftMonth(month, -1)), className: cn(NAV_CONTROL, 'disabled:pointer-events-none disabled:opacity-50'), children: _jsx(ChevronLeftIcon, { className: "size-4" }) }), _jsx("div", { "data-slot": "calendar-caption", "aria-live": "polite", className: "text-foreground text-sm font-medium tabular-nums", children: monthLabel }), _jsx("button", { type: "button", "data-slot": "calendar-next", "aria-label": nextLabel, disabled: disabled, onClick: () => onMonthChange(shiftMonth(month, 1)), className: cn(NAV_CONTROL, 'disabled:pointer-events-none disabled:opacity-50'), children: _jsx(ChevronRightIcon, { className: "size-4" }) })] }), _jsx("div", { ref: gridRef, children: _jsxs("table", { "data-slot": "calendar-grid", role: "grid", "aria-label": label, className: "w-full border-collapse tabular-nums", children: [_jsx("thead", { children: _jsx("tr", { children: weekdayLabels.map((weekday, column) => (_jsx("th", { scope: "col", "data-slot": "calendar-weekday", className: "text-muted-foreground flex h-8 items-center justify-center text-xs font-medium", children: weekday }, `${weekday}-${column}`))) }) }), _jsx("tbody", { children: Array.from({ length: 6 }, (_, row) => (_jsx("tr", { children: cells.slice(row * 7, row * 7 + 7).map((date) => {
234
+ return (_jsxs("div", { "data-slot": "calendar", className: cn('bg-popover text-popover-foreground w-64 rounded-md border p-3 shadow-md', className), children: [_jsxs("div", { className: "mb-2 flex items-center justify-between gap-1", children: [_jsx("button", { type: "button", "data-slot": "calendar-previous", "aria-label": previousLabel, disabled: disabled, onClick: () => onMonthChange(shiftMonth(month, -1)), className: cn(NAV_CONTROL, 'disabled:pointer-events-none disabled:opacity-50'), children: _jsx(ChevronLeftIcon, { className: "size-4" }) }), _jsx("div", { "data-slot": "calendar-caption", "aria-live": "polite", className: "text-foreground text-sm font-medium tabular-nums", children: monthLabel }), _jsx("button", { type: "button", "data-slot": "calendar-next", "aria-label": nextLabel, disabled: disabled, onClick: () => onMonthChange(shiftMonth(month, 1)), className: cn(NAV_CONTROL, 'disabled:pointer-events-none disabled:opacity-50'), children: _jsx(ChevronRightIcon, { className: "size-4" }) })] }), _jsx("div", { ref: gridRef, children: _jsxs("table", { "data-slot": "calendar-grid", role: "grid", "aria-label": label, className: "w-full border-collapse tabular-nums", children: [_jsx("thead", { children: _jsx("tr", { children: weekdayLabels.map((weekday, column) => (_jsx("th", { scope: "col", "data-slot": "calendar-weekday", className: "text-muted-foreground h-8 p-0 align-middle text-center text-xs font-medium", children: weekday }, `${weekday}-${column}`))) }) }), _jsx("tbody", { children: Array.from({ length: 6 }, (_, row) => (_jsx("tr", { children: cells.slice(row * 7, row * 7 + 7).map((date) => {
230
235
  const key = iso(date);
231
236
  if (!sameMonth(date, month)) {
232
237
  // The cells that pad the month out. They are hidden from the
@@ -243,7 +243,7 @@ function MiniCalendar({ selected, onSelect, month, defaultMonth, onMonthChange,
243
243
  default:
244
244
  }
245
245
  };
246
- return (_jsx("div", { ref: gridRef, "data-slot": "mini-calendar", className: cn('bg-card text-card-foreground w-64 rounded-md border p-3 shadow-sm', className), children: _jsxs("table", { "data-slot": "mini-calendar-grid", role: "grid", "aria-label": label, className: "w-full border-collapse tabular-nums", children: [_jsx("caption", { "data-slot": "mini-calendar-caption", className: "mb-2", children: _jsxs("span", { className: "flex items-center justify-between gap-1", children: [_jsx("button", { type: "button", "data-slot": "mini-calendar-previous", "aria-label": previousLabel, onClick: () => move(shiftMonth(shown, -1)), className: NAV_CONTROL, children: _jsx(ChevronLeftIcon, { className: "size-4", "aria-hidden": "true" }) }), _jsx("span", { "data-slot": "mini-calendar-month", "aria-live": "polite", className: "text-foreground text-sm font-medium", children: monthText }), _jsx("button", { type: "button", "data-slot": "mini-calendar-next", "aria-label": nextLabel, onClick: () => move(shiftMonth(shown, 1)), className: NAV_CONTROL, children: _jsx(ChevronRightIcon, { className: "size-4", "aria-hidden": "true" }) })] }) }), _jsx("thead", { children: _jsx("tr", { children: headings.map((heading) => (_jsx("th", { scope: "col", abbr: heading.long, "data-slot": "mini-calendar-weekday", className: "text-muted-foreground flex h-8 items-center justify-center text-xs font-medium", children: heading.short }, heading.long))) }) }), _jsx("tbody", { children: Array.from({ length: 6 }, (_, row) => (_jsx("tr", { children: cells.slice(row * 7, row * 7 + 7).map((date) => {
246
+ return (_jsx("div", { ref: gridRef, "data-slot": "mini-calendar", className: cn('bg-card text-card-foreground w-64 rounded-md border p-3 shadow-sm', className), children: _jsxs("table", { "data-slot": "mini-calendar-grid", role: "grid", "aria-label": label, className: "w-full border-collapse tabular-nums", children: [_jsx("caption", { "data-slot": "mini-calendar-caption", className: "mb-2", children: _jsxs("span", { className: "flex items-center justify-between gap-1", children: [_jsx("button", { type: "button", "data-slot": "mini-calendar-previous", "aria-label": previousLabel, onClick: () => move(shiftMonth(shown, -1)), className: NAV_CONTROL, children: _jsx(ChevronLeftIcon, { className: "size-4", "aria-hidden": "true" }) }), _jsx("span", { "data-slot": "mini-calendar-month", "aria-live": "polite", className: "text-foreground text-sm font-medium", children: monthText }), _jsx("button", { type: "button", "data-slot": "mini-calendar-next", "aria-label": nextLabel, onClick: () => move(shiftMonth(shown, 1)), className: NAV_CONTROL, children: _jsx(ChevronRightIcon, { className: "size-4", "aria-hidden": "true" }) })] }) }), _jsx("thead", { children: _jsx("tr", { children: headings.map((heading) => (_jsx("th", { scope: "col", abbr: heading.long, "data-slot": "mini-calendar-weekday", className: "text-muted-foreground h-8 p-0 align-middle text-center text-xs font-medium", children: heading.short }, heading.long))) }) }), _jsx("tbody", { children: Array.from({ length: 6 }, (_, row) => (_jsx("tr", { children: cells.slice(row * 7, row * 7 + 7).map((date) => {
247
247
  const key = iso(date);
248
248
  if (!sameMonth(date, shown)) {
249
249
  // The cells that pad the month out. Hidden from the accessibility
@@ -1,3 +1,4 @@
1
+ import type { ReactNode } from 'react';
1
2
  import type { PackId } from '../../theming/index.js';
2
3
  /**
3
4
  * The three sizes a product mark is drawn at: the mark's diameter and the name
@@ -32,10 +33,11 @@ export type ProductMarkSize = keyof typeof SIZES;
32
33
  * The props a ProductMark takes.
33
34
  *
34
35
  * There is no product directory in this package. A product's identity is the
35
- * consumer's data, so the Component takes the three facts the mark is drawn
36
- * from and owns none of them: `id` for the markup, `name` for the second part,
37
- * and `pack` for the hue. Where the set of products is published is the
38
- * consumer's decision, and Prism ships the mark rather than a list of products.
36
+ * consumer's data, so the Component takes the facts the mark is drawn from and
37
+ * owns none of them: `id` for the markup, `name` for the second part, `pack`
38
+ * for the hue, and `mark` for the drawing itself when the identity has one.
39
+ * Where the set of products is published is the consumer's decision, and Prism
40
+ * ships the mark rather than a list of products.
39
41
  */
40
42
  export type ProductMarkProps = {
41
43
  /**
@@ -63,6 +65,21 @@ export type ProductMarkProps = {
63
65
  * core resolves the base pack's `primary` from the page.
64
66
  */
65
67
  pack?: PackId | null;
68
+ /**
69
+ * The product's drawn mark, when its identity includes one: the logo in
70
+ * place of the disc. The node is the consumer's drawing and this Component
71
+ * owns none of it, exactly as it owns none of the name; it is drawn inside
72
+ * the sized slot the disc occupies, so a `viewBox` drawing that fills its
73
+ * box arrives at every drawn size from one node. Ink strokes drawn in
74
+ * `currentColor` and accents drawn through a token class keep the drawing
75
+ * reachable by the cascade and by a pack boundary the same way the disc is.
76
+ *
77
+ * The drawing is hidden from assistive technology, as the disc is: the name
78
+ * is the mark's announced half either way. Omit it for the two-colour disc,
79
+ * which remains the mark of a product whose identity is a hue rather than a
80
+ * drawing.
81
+ */
82
+ mark?: ReactNode;
66
83
  /**
67
84
  * Which of the three drawn sizes to use. @defaultValue 'md'
68
85
  */
@@ -75,8 +92,10 @@ export type ProductMarkProps = {
75
92
  className?: string;
76
93
  };
77
94
  /**
78
- * A product's two-part colour mark: a hairline ring in the brand ink around a
79
- * core in the pack's own fill, beside the product's name in the brand ink.
95
+ * A product's two-part mark: the shape, beside the product's name in the brand
96
+ * ink. The shape is a disc by default, a hairline ring in the brand ink around
97
+ * a core in the pack's own fill, and it is the product's own drawing when
98
+ * `mark` carries one.
80
99
  *
81
100
  * Two parts, and both colours come from tokens, which is the whole reason this
82
101
  * is promoted out of the two hand-written copies it replaces. The ring is
@@ -110,6 +129,6 @@ export type ProductMarkProps = {
110
129
  * The disc is `aria-hidden` and the name is not, so the mark adds a shape beside
111
130
  * the word rather than a second reading of it.
112
131
  */
113
- declare function ProductMark({ id, name, pack, size, className }: ProductMarkProps): import("react").JSX.Element;
132
+ declare function ProductMark({ id, name, pack, mark, size, className }: ProductMarkProps): import("react").JSX.Element;
114
133
  export { ProductMark };
115
134
  //# sourceMappingURL=product-mark.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"product-mark.d.ts","sourceRoot":"","sources":["../../../src/components/ui/product-mark.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AA2B3C;;;;;;;;GAQG;AACH,QAAA,MAAM,KAAK;;;;;;;;;;;;;CAID,CAAA;AAEV;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,KAAK,CAAA;AAEhD;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAA;IACV;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;OAEG;IACH,IAAI,CAAC,EAAE,eAAe,CAAA;IACtB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,iBAAS,WAAW,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAW,EAAE,SAAS,EAAE,EAAE,gBAAgB,+BA2BhF;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
1
+ {"version":3,"file":"product-mark.d.ts","sourceRoot":"","sources":["../../../src/components/ui/product-mark.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AA2B3C;;;;;;;;GAQG;AACH,QAAA,MAAM,KAAK;;;;;;;;;;;;;CAID,CAAA;AAEV;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,KAAK,CAAA;AAEhD;;;;;;;;;GASG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAA;IACV;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;;;;;;;;;;OAaG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;OAEG;IACH,IAAI,CAAC,EAAE,eAAe,CAAA;IACtB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,iBAAS,WAAW,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAW,EAAE,SAAS,EAAE,EAAE,gBAAgB,+BAsCtF;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
@@ -36,8 +36,10 @@ const SIZES = {
36
36
  lg: { mark: 'size-6', name: 'text-base' },
37
37
  };
38
38
  /**
39
- * A product's two-part colour mark: a hairline ring in the brand ink around a
40
- * core in the pack's own fill, beside the product's name in the brand ink.
39
+ * A product's two-part mark: the shape, beside the product's name in the brand
40
+ * ink. The shape is a disc by default, a hairline ring in the brand ink around
41
+ * a core in the pack's own fill, and it is the product's own drawing when
42
+ * `mark` carries one.
41
43
  *
42
44
  * Two parts, and both colours come from tokens, which is the whole reason this
43
45
  * is promoted out of the two hand-written copies it replaces. The ring is
@@ -71,12 +73,12 @@ const SIZES = {
71
73
  * The disc is `aria-hidden` and the name is not, so the mark adds a shape beside
72
74
  * the word rather than a second reading of it.
73
75
  */
74
- function ProductMark({ id, name, pack, size = 'md', className }) {
76
+ function ProductMark({ id, name, pack, mark, size = 'md', className }) {
75
77
  const drawn = SIZES[size];
76
78
  // `default` is the absence of a pack id, spelled the way the token build
77
79
  // spells it, so a base-pack product resolves `primary` from the page rather
78
80
  // than from an attribute that matches no emitted rule.
79
81
  const boundary = pack && pack !== 'default' ? pack : undefined;
80
- return (_jsxs("span", { "data-slot": "product-mark", "data-pack": boundary, className: cn('inline-flex items-center gap-2', className), children: [_jsx("span", { "data-slot": "product-mark-disc", "data-product": id, "aria-hidden": true, className: cn('inline-block shrink-0 rounded-full border border-brand-ink', drawn.mark, pack ? 'bg-primary' : SPECTRUM) }), _jsx("span", { "data-slot": "product-mark-name", className: cn('font-medium text-brand-ink', drawn.name), children: name })] }));
82
+ return (_jsxs("span", { "data-slot": "product-mark", "data-pack": boundary, className: cn('inline-flex items-center gap-2', className), children: [mark ? (_jsx("span", { "data-slot": "product-mark-drawn", "data-product": id, "aria-hidden": true, className: cn('inline-block shrink-0', drawn.mark), children: mark })) : (_jsx("span", { "data-slot": "product-mark-disc", "data-product": id, "aria-hidden": true, className: cn('inline-block shrink-0 rounded-full border border-brand-ink', drawn.mark, pack ? 'bg-primary' : SPECTRUM) })), _jsx("span", { "data-slot": "product-mark-name", className: cn('font-medium text-brand-ink', drawn.name), children: name })] }));
81
83
  }
82
84
  export { ProductMark };
@@ -1 +1 @@
1
- {"version":3,"file":"pulse-series.d.ts","sourceRoot":"","sources":["../../../src/components/ui/pulse-series.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,oFAAoF;IACpF,EAAE,EAAE,MAAM,CAAA;IACV,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED;;;;;;;GAOG;AACH,KAAK,iBAAiB,GAClB;IACE;;;;OAIG;IACH,UAAU,EAAE,IAAI,CAAA;IAChB,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,GACD;IACE,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,KAAK,CAAA;CACnB,CAAA;AAEL,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,GAAG;IACjD;;;;OAIG;IACD,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAA;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAoCH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,iBAAS,WAAW,CAAC,EACnB,IAAI,EACJ,QAAQ,EACR,IAAY,EACZ,KAAK,EACL,UAAkB,EAClB,SAAS,GACV,EAAE,gBAAgB,+BAsIlB;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
1
+ {"version":3,"file":"pulse-series.d.ts","sourceRoot":"","sources":["../../../src/components/ui/pulse-series.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,oFAAoF;IACpF,EAAE,EAAE,MAAM,CAAA;IACV,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED;;;;;;;GAOG;AACH,KAAK,iBAAiB,GAClB;IACE;;;;OAIG;IACH,UAAU,EAAE,IAAI,CAAA;IAChB,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,GACD;IACE,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,KAAK,CAAA;CACnB,CAAA;AAEL,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,GAAG;IACjD;;;;OAIG;IACD,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAA;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAoCH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,iBAAS,WAAW,CAAC,EACnB,IAAI,EACJ,QAAQ,EACR,IAAY,EACZ,KAAK,EACL,UAAkB,EAClB,SAAS,GACV,EAAE,gBAAgB,+BAiJlB;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
@@ -81,7 +81,7 @@ function PulseSeries({ bars, baseline, scan = false, label, decorative = false,
81
81
  const tallest = Math.max(...bars.map((bar) => bar.value), 0);
82
82
  const slot = bars.length ? roomX / bars.length : roomX;
83
83
  const barWidth = Math.max(2, Math.min(MAX_BAR_WIDTH, slot * (1 / (1 + BAR_GAP))));
84
- return (_jsxs("svg", { "data-slot": "pulse-series", "data-columns": bars.length, "data-scan": scan || undefined, viewBox: `0 0 ${CANVAS_WIDTH} ${CANVAS_HEIGHT}`, xmlns: "http://www.w3.org/2000/svg", role: decorative ? undefined : 'img', "aria-label": decorative ? undefined : label, "aria-hidden": decorative || undefined, className: cn('h-auto w-full', className), children: [scan ? (_jsx("g", { "data-slot": "pulse-series-scan", "aria-hidden": true, children: _jsxs("g", { className: "prism-ambient-scan", style: { '--ambient-scan-distance': `${roomX + slot}px` }, children: [_jsx("rect", { "data-slot": "pulse-series-scan-wash", x: PADDING - slot, y: PADDING_TOP - 12, width: slot, height: roomY + 12, className: "fill-brand-ink/10" }), _jsx("line", { "data-slot": "pulse-series-scan-line", x1: 0, y1: PADDING_TOP - 12, x2: 0, y2: baselineY, strokeWidth: 1, className: "stroke-brand-ink/60" })] }) })) : null, bars.map((bar, index) => {
84
+ return (_jsxs("svg", { "data-slot": "pulse-series", "data-columns": bars.length, "data-scan": scan || undefined, viewBox: `0 0 ${CANVAS_WIDTH} ${CANVAS_HEIGHT}`, xmlns: "http://www.w3.org/2000/svg", role: decorative ? undefined : 'img', "aria-label": decorative ? undefined : label, "aria-hidden": decorative || undefined, className: cn('h-auto w-full', className), children: [scan ? (_jsx("g", { "data-slot": "pulse-series-scan", "aria-hidden": true, transform: `translate(${PADDING} 0)`, children: _jsxs("g", { className: "prism-ambient-scan", style: { '--ambient-scan-distance': `${roomX}px` }, children: [_jsx("rect", { "data-slot": "pulse-series-scan-wash", x: -slot / 2, y: PADDING_TOP - 12, width: slot, height: baselineY - PADDING_TOP + 12, className: "fill-brand-ink/10" }), _jsx("line", { "data-slot": "pulse-series-scan-line", x1: 0, y1: PADDING_TOP - 12, x2: 0, y2: baselineY, strokeWidth: 1, className: "stroke-brand-ink/60" })] }) })) : null, bars.map((bar, index) => {
85
85
  const centre = PADDING + slot * (index + 0.5);
86
86
  // A column of value zero is drawn as a hairline rather than as nothing,
87
87
  // because a gap in a series is a missing reading and a missing reading
@@ -1,4 +1,20 @@
1
1
  import type { ReactNode } from 'react';
2
+ /**
3
+ * The three vertical rhythms a Section can be composed at.
4
+ *
5
+ * `default` is the rhythm every existing block already composes, so a Block that
6
+ * does not pass a size renders exactly what it rendered before this prop existed.
7
+ * `compact` is for a transition band: a strip of facts or a line of status whose
8
+ * job is to separate two larger bands rather than to be one. `tall` is for a band
9
+ * that is a statement: a hero, a manifesto, or an editorial moment that needs the
10
+ * page to stop for it.
11
+ *
12
+ * Three and not a number, for the reason every closed union in this package is
13
+ * closed: a `padding: number` prop hands a consumer a spacing scale to reproduce,
14
+ * and the first consumer to pass 73 has a rhythm nobody authored. The names say
15
+ * what the band is for, which is the decision a caller actually makes.
16
+ */
17
+ export type SectionSize = 'compact' | 'default' | 'tall';
2
18
  /**
3
19
  * Container primitive. Every block composes its own section from this so vertical
4
20
  * rhythm stays consistent across the catalog instead of being re-invented per block.
@@ -17,8 +33,22 @@ import type { ReactNode } from 'react';
17
33
  * Tailwind's extractor reads this file, comments included, so a utility quoted in
18
34
  * a JSDoc block is a utility the shipped stylesheet emits. A record of a retired
19
35
  * class written as a class is a second copy of the tree nobody edits.
36
+ *
37
+ * **`size` is the band's job, not a spacing slider.** See `SectionSize`: a
38
+ * transition band, an ordinary band and a statement are three different amounts
39
+ * of room, and a page that gives all three the same amount reads as one band
40
+ * repeated. The prop exists so the choice is made once in the design system
41
+ * rather than five times in five site stylesheets.
20
42
  */
21
- export declare function Section({ className, children, ...props }: React.ComponentProps<'section'>): import("react").JSX.Element;
43
+ export declare function Section({ size, className, children, ...props }: React.ComponentProps<'section'> & {
44
+ /**
45
+ * The band's vertical rhythm. Defaults to `default`, which is the rhythm every
46
+ * existing Block already composes. See `SectionSize`.
47
+ *
48
+ * @defaultValue 'default'
49
+ */
50
+ size?: SectionSize;
51
+ }): import("react").JSX.Element;
22
52
  /**
23
53
  * The six heading levels a block can be composed at.
24
54
  *
@@ -1 +1 @@
1
- {"version":3,"file":"section.d.ts","sourceRoot":"","sources":["../../../src/components/ui/section.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,OAAO,CAAC,EACtB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,+BAWjC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAKlE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY,CAG5D;AAoBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAE5D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,OAAO,EACP,KAAK,EACL,EAAE,EAAE,OAAc,EAClB,WAAW,EACX,KAAgB,EAChB,EAAE,EACF,SAAS,GACV,EAAE;IACD;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,KAAK,EAAE,SAAS,CAAA;IAChB,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;IACzB;;;;;;;;;;;;OAYG;IACH,EAAE,CAAC,EAAE,YAAY,CAAA;IACjB;;;;;;;;;;;OAWG;IACH,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,+BAgCA"}
1
+ {"version":3,"file":"section.d.ts","sourceRoot":"","sources":["../../../src/components/ui/section.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAA;AAiBxD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,OAAO,CAAC,EACtB,IAAgB,EAChB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,GAAG;IACnC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,WAAW,CAAA;CACnB,+BAMA;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAKlE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY,CAG5D;AAoBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAE5D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,OAAO,EACP,KAAK,EACL,EAAE,EAAE,OAAc,EAClB,WAAW,EACX,KAAgB,EAChB,EAAE,EACF,SAAS,GACV,EAAE;IACD;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,KAAK,EAAE,SAAS,CAAA;IAChB,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;IACzB;;;;;;;;;;;;OAYG;IACH,EAAE,CAAC,EAAE,YAAY,CAAA;IACjB;;;;;;;;;;;OAWG;IACH,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,+BAgCA"}
@@ -1,5 +1,19 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cn } from '../../lib/utils.js';
3
+ /**
4
+ * The padding each rhythm sets.
5
+ *
6
+ * `default` is byte-identical to the string this Component rendered before the
7
+ * prop existed, so adding the prop cannot move a band that did not ask to move.
8
+ * The other two are on the same quarter-rem grid: `compact` is 3rem to 4rem and
9
+ * `tall` is 5rem to 8rem, which are the same two statements the four consumer
10
+ * sites had been writing by hand in their own stylesheets.
11
+ */
12
+ const SECTION_SIZE = {
13
+ compact: 'py-12 sm:py-16',
14
+ default: 'py-16 sm:py-24',
15
+ tall: 'py-20 sm:py-32',
16
+ };
3
17
  /**
4
18
  * Container primitive. Every block composes its own section from this so vertical
5
19
  * rhythm stays consistent across the catalog instead of being re-invented per block.
@@ -18,9 +32,15 @@ import { cn } from '../../lib/utils.js';
18
32
  * Tailwind's extractor reads this file, comments included, so a utility quoted in
19
33
  * a JSDoc block is a utility the shipped stylesheet emits. A record of a retired
20
34
  * class written as a class is a second copy of the tree nobody edits.
35
+ *
36
+ * **`size` is the band's job, not a spacing slider.** See `SectionSize`: a
37
+ * transition band, an ordinary band and a statement are three different amounts
38
+ * of room, and a page that gives all three the same amount reads as one band
39
+ * repeated. The prop exists so the choice is made once in the design system
40
+ * rather than five times in five site stylesheets.
21
41
  */
22
- export function Section({ className, children, ...props }) {
23
- return (_jsx("section", { className: cn('py-16 sm:py-24', className), ...props, children: _jsx("div", { className: "mx-auto w-full max-w-page px-6 lg:px-8", children: children }) }));
42
+ export function Section({ size = 'default', className, children, ...props }) {
43
+ return (_jsx("section", { className: cn(SECTION_SIZE[size], className), ...props, children: _jsx("div", { className: "mx-auto w-full max-w-page px-6 lg:px-8", children: children }) }));
24
44
  }
25
45
  /** The six levels in outline order, so a level can be stepped rather than compared. */
26
46
  const LEVELS = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
@@ -1 +1 @@
1
- {"version":3,"file":"sparkline.d.ts","sourceRoot":"","sources":["../../../src/components/ui/sparkline.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAE3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AA2C/C,uCAAuC;AACvC,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC;IAChF;;;;;;;;OAQG;IACH,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IACzB;;;;;;;;;OASG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,iBAAS,SAAS,CAAC,EACjB,SAAS,EACT,MAAM,EACN,KAAK,EACL,IAAgB,EAChB,KAAU,EACV,MAAW,EACX,aAAqB,EACrB,GAAG,KAAK,EACT,EAAE,cAAc,+BA6FhB;AAED,OAAO,EAAE,SAAS,EAAE,CAAA"}
1
+ {"version":3,"file":"sparkline.d.ts","sourceRoot":"","sources":["../../../src/components/ui/sparkline.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAE3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AA0C/C,uCAAuC;AACvC,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC;IAChF;;;;;;;;OAQG;IACH,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IACzB;;;;;;;;;OASG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,iBAAS,SAAS,CAAC,EACjB,SAAS,EACT,MAAM,EACN,KAAK,EACL,IAAgB,EAChB,KAAU,EACV,MAAW,EACX,aAAqB,EACrB,GAAG,KAAK,EACT,EAAE,cAAc,+BAoGhB;AAED,OAAO,EAAE,SAAS,EAAE,CAAA"}