@nanisoft/prism-ui 0.15.0 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/blocks/data-table-01/data-table.d.ts +10 -0
  3. package/dist/blocks/data-table-01/data-table.d.ts.map +1 -1
  4. package/dist/blocks/data-table-01/data-table.js +2 -1
  5. package/dist/blocks/page-header-01/page-header.d.ts +7 -0
  6. package/dist/blocks/page-header-01/page-header.d.ts.map +1 -1
  7. package/dist/blocks/page-header-01/page-header.js +9 -1
  8. package/dist/blocks/run-console-01/run-console.d.ts.map +1 -1
  9. package/dist/blocks/run-console-01/run-console.js +2 -1
  10. package/dist/blocks/site-footer/site-footer.d.ts +16 -0
  11. package/dist/blocks/site-footer/site-footer.d.ts.map +1 -1
  12. package/dist/blocks/site-footer/site-footer.js +17 -1
  13. package/dist/blocks/stack-grid-01/stack-grid.d.ts +13 -0
  14. package/dist/blocks/stack-grid-01/stack-grid.d.ts.map +1 -1
  15. package/dist/blocks/stack-grid-01/stack-grid.js +14 -1
  16. package/dist/components/ui/diagram.d.ts +38 -7
  17. package/dist/components/ui/diagram.d.ts.map +1 -1
  18. package/dist/components/ui/diagram.js +134 -52
  19. package/dist/components/ui/fact-list.d.ts +17 -0
  20. package/dist/components/ui/fact-list.d.ts.map +1 -1
  21. package/dist/components/ui/fact-list.js +18 -1
  22. package/dist/components/ui/prose.d.ts +11 -0
  23. package/dist/components/ui/prose.d.ts.map +1 -1
  24. package/dist/components/ui/prose.js +12 -1
  25. package/dist/components/ui/pulse-graph.d.ts +36 -4
  26. package/dist/components/ui/pulse-graph.d.ts.map +1 -1
  27. package/dist/components/ui/pulse-graph.js +174 -60
  28. package/dist/components/ui/section.d.ts +33 -31
  29. package/dist/components/ui/section.d.ts.map +1 -1
  30. package/dist/components/ui/section.js +33 -31
  31. package/dist/lib/figure.d.ts +149 -0
  32. package/dist/lib/figure.d.ts.map +1 -0
  33. package/dist/lib/figure.js +242 -0
  34. package/dist/live/run-stream-01/run-stream.d.ts.map +1 -1
  35. package/dist/live/run-stream-01/run-stream.js +2 -1
  36. package/dist/pages/blog-post-page/index.d.ts +36 -15
  37. package/dist/pages/blog-post-page/index.d.ts.map +1 -1
  38. package/dist/pages/blog-post-page/index.js +35 -10
  39. package/dist/pages/docs-shell/docs-shell.d.ts +131 -17
  40. package/dist/pages/docs-shell/docs-shell.d.ts.map +1 -1
  41. package/dist/pages/docs-shell/docs-shell.js +230 -24
  42. package/dist/pages/error-page/index.d.ts.map +1 -1
  43. package/dist/pages/error-page/index.js +7 -3
  44. package/dist/pages/not-found-page/index.d.ts.map +1 -1
  45. package/dist/pages/not-found-page/index.js +4 -3
  46. package/dist/styles.css +131 -12
  47. package/package.json +2 -2
@@ -15,15 +15,36 @@ import type { ReactNode } from 'react';
15
15
  * block used to make was true only of the group arm; it is now true of what the
16
16
  * Page renders, and it says which of the two is doing the work.
17
17
  *
18
- * `title` may be the empty string, and an entry with no words is not a page: it
19
- * renders as a label on the same rule that makes a group with no index a label,
20
- * and it is not a page the pager can reach either. A link with no accessible name
21
- * is announced as "link" and nothing else, which is the one thing a link in a
22
- * rail must never be.
18
+ * `title` may be the empty string, and an entry the Page cannot name is not
19
+ * rendered at all. It used to render as a label on the same rule that makes a
20
+ * group with no index a label, and that produced `<li><span></span></li>`: a
21
+ * row of nothing inside a `<nav>`, which one consumer measured at 102 across 25
22
+ * of its 31 documentation pages. A blank row is not a destination and it is not
23
+ * a label either, because a label is words. So the Page drops the entry and
24
+ * counts the drop on the rail as `data-unnamed-entries`, which is the same
25
+ * answer `Diagram` gives a relation it cannot resolve.
26
+ *
27
+ * **The page is not lost, and one thing is.** The address still resolves, the
28
+ * document still renders at it, and every link into it still works; what the rail
29
+ * loses is one row. The pager does lose that page, because the pager walks the
30
+ * same list and the row is no longer in it, so a reader who arrives at the address
31
+ * directly finds a document and no pager. That was already the answer before the
32
+ * row was dropped, and it is the cost of dropping rather than of this change:
33
+ * keeping it would mean publishing the nameless link a second time.
34
+ *
35
+ * **A blank `title` and a blank `href` are answered differently, and the reason
36
+ * is what each one has left.** A page with an address and no words is a real page
37
+ * with one unrenderable field, so dropping the row costs a reader nothing and
38
+ * taking the whole screen down over it would cost them everything. A page with
39
+ * words and no address is a row whose entire content is a route that does not
40
+ * exist, and there is nothing to render in its place, so `readTree` refuses it.
23
41
  */
24
42
  export type DocsNavPage = {
25
43
  type: 'page';
26
- /** The words the reader meets. An empty string makes this a label, not a link. */
44
+ /**
45
+ * The words the reader meets. An empty or blank string names nothing, and the
46
+ * Page renders no row for the entry and counts the drop.
47
+ */
27
48
  title: string;
28
49
  /** The address the page is read at. An empty string is refused, by name. */
29
50
  href: string;
@@ -47,7 +68,15 @@ export type DocsNavPage = {
47
68
  */
48
69
  export type DocsNavGroup = {
49
70
  type: 'group';
50
- /** The words on the label. A status inside them stays inside them. */
71
+ /**
72
+ * The words on the label. A status inside them stays inside them.
73
+ *
74
+ * An empty or blank string renders no label, and the pages under the group are
75
+ * still rendered: a group whose title cannot be rendered has lost a heading and
76
+ * not a section, so dropping the whole entry would delete pages the reader can
77
+ * otherwise reach. A group with no words and no children has nothing left to
78
+ * draw and is dropped whole.
79
+ */
51
80
  title: string;
52
81
  /**
53
82
  * The group's own index page, where it has one. Omit it for a group that is
@@ -259,21 +288,104 @@ export type DocsShellProps = {
259
288
  * author filed it. A Page that sorted its rail would flatten the one thing the
260
289
  * three sites disagree about and cannot both be right about.
261
290
  *
262
- * **The rail is bounded in height.** Atlas files nineteen pages and AlphaLens
263
- * files twenty-seven, so the taller tree is half again as long, and an unbounded
264
- * rail on a tall tree is a rail that pushes the document below the fold before
265
- * the reader has read a line of it. The rail and the contents rail are each
266
- * capped against the viewport and scroll inside it, so a nineteen-page tree and a
267
- * twenty-seven-page tree are both a full screen and neither is a page-long
268
- * sidebar.
291
+ * **The rail is bounded in height, and the bound is what the reader has to be
292
+ * able to see.** Atlas files nineteen pages and AlphaLens files twenty-seven, so
293
+ * the taller tree is half again as long, and an unbounded rail on a tall tree is
294
+ * a rail that pushes the document below the fold before the reader has read a
295
+ * line of it. The rail and the contents rail are each capped against the viewport
296
+ * and scroll inside it, so a nineteen-page tree and a twenty-seven-page tree are
297
+ * both a full screen and neither is a page-long sidebar.
298
+ *
299
+ * **A cap the reader cannot see is a defect, and this one was invisible for 344
300
+ * and 750 pixels.** Measured in two consumers' built exports at 1280 by 680, the
301
+ * rail's content stood at 912 and 1318 pixels against a 568 pixel box, so more
302
+ * than half of one site's documentation navigation sat below the fold with
303
+ * `mask-image: none`, no scrollbar Chrome would draw until the reader scrolled,
304
+ * no fade and no count. Chrome's overlay scrollbars are the reason this is not
305
+ * caught by looking: they appear on scroll, so the first render of a rail that
306
+ * hides half its content is indistinguishable from a rail that has all of it, and
307
+ * the last visible row was cut mid-word above dead space, which reads as a
308
+ * rendering fault rather than as a scroll region.
309
+ *
310
+ * **The affordance is a fade across the foot of the scroll region, with the space
311
+ * it covers reserved rather than borrowed.** Three answers were available and the
312
+ * other two were rejected for stated reasons. A persistent themed scrollbar is
313
+ * truthful at both ends and costs nothing to legibility, and it is still here in
314
+ * the sense that the region is a real scroll region rather than a clipped box,
315
+ * but it is a single hairline whose length says "there is more" without saying
316
+ * which way, it needs `scrollbar-gutter` reserved in a 15rem rail to stop the
317
+ * content shifting when a short tree becomes a tall one, and in Chrome it is the
318
+ * least reliable of the three to reason about because the property that makes it
319
+ * persistent is the same one whose default is an overlay. A collapse control needs
320
+ * a disclosure whose state a server Component cannot hold. An "N more" control is
321
+ * copy this Page does not ship and it needs the count the Page cannot compute
322
+ * without measuring, which is the one thing a server Component cannot do.
323
+ *
324
+ * So the fade, and it is painted on a wrapper that does not scroll rather than on
325
+ * the scroll region itself: a mask or a gradient inside the scrollport moves with
326
+ * the content and marks nothing. The wrapper carries the sticky offset, the
327
+ * scroll region keeps the cap and the scrolling, and the gradient is pinned to
328
+ * the wrapper's foot. The region carries `padding-bottom` of the same height, and
329
+ * that is the half that makes it honest: at rest the gradient falls on reserved
330
+ * space and the last row is fully legible, and only while there is more below
331
+ * does it fall on content. Without the padding the same gradient washes the last
332
+ * row permanently, which is the usual cost of this answer and the reason the
333
+ * padding is not optional.
334
+ *
335
+ * **The fade is paint, and it is on the accessibility tree's terms.** It is an
336
+ * empty element with `aria-hidden` and `pointer-events-none`, inside the `<nav>`
337
+ * and outside the `<ul>`, so it is not a list item, it is not announced, it never
338
+ * takes a press and it never reaches a Tab stop. A keyboard reader scrolls the
339
+ * region with the arrow keys exactly as before and reaches every entry, and a
340
+ * screen-reader user is told nothing at all, which is the right answer: the cut is
341
+ * a fact about ink, and announcing it would put a sentence in the middle of a
342
+ * list of a consumer's own pages.
343
+ *
344
+ * **The tree is in the document once, at every width, and the price of that is a
345
+ * phone reader crossing the whole navigation to reach the article.** At 768 and
346
+ * at 1024 both rails used to be `hidden`, so the only navigation left inside
347
+ * `<main>` was the two-item pager at the foot of the article, and a reader on a
348
+ * twenty-seven document reference at tablet width had no contents, no
349
+ * on-this-page and no way to a sibling page except Previous and Next. 0.16.0
350
+ * closed that gap by drawing the tree a second time behind a `<details>`, one
351
+ * copy for the rail and one for the narrow frame, and it shipped a doubled
352
+ * document to every documentation page in the family with it: two
353
+ * `aria-current` markers for one page, two lists of a consumer's own pages, two
354
+ * `docs-nav-page` rows for every document, and a second `<nav>` carrying the
355
+ * rail's own accessible name, which is the thing a consumer's suite reaches the
356
+ * rail by. Three consumer test files failed against the published package: two on
357
+ * a count that came back doubled, and two on a `getByRole('navigation', { name })`
358
+ * that had two answers where it expects one. So there is one copy now, and it
359
+ * shows at every width.
360
+ *
361
+ * **What that costs, with the number.** A consumer's rail measured 1,318 pixels
362
+ * of content in a built export at 1280 by 680, and those rows wrap on a phone, so
363
+ * a reader arriving at the top of a documentation page scrolls past a thousand
364
+ * pixels of navigation before the article begins. That is a real regression for
365
+ * that reader and it is the reason a disclosure was reached for in the first
366
+ * place. It is paid rather than hidden, because every arrangement that avoids it
367
+ * is worse: a rail at `lg` and nothing below it reopens the gap this change
368
+ * closes, and drawing the tree twice to keep the scroll short is what broke three
369
+ * consumer test files in the first place.
370
+ *
371
+ * **What would actually remove the scroll.** A collapsed rail below `lg` needs an
372
+ * open state. A server Component cannot hold one, and `<details>` is the only
373
+ * disclosure the platform holds on the Page's behalf, so the control that opens
374
+ * it has to be a client island inside the Page. That is a separate piece of work
375
+ * with its own budget, and it is not built here: a Page that ships a client
376
+ * boundary to save one scroll is making a different trade than the one stated
377
+ * above, and it should be made deliberately rather than smuggled in as the side
378
+ * effect of fixing a duplicate.
269
379
  *
270
380
  * **The frame is three columns from `lg`, and the third is the consumer's to
271
381
  * fill.** The rail is 15rem, the document takes the rest, and the contents rail
272
382
  * is 13rem. The third track exists only when `toc` is passed, because the
273
383
  * document takes the second track by auto-placement and a template that named a
274
384
  * track no child occupies would leave the document in the 15rem one. Below `lg`
275
- * the frame is a single column with both rails hidden, which is the arrangement
276
- * a phone gets.
385
+ * the frame is a single column and the children flow in the order they are
386
+ * written: the rail, then the document, then the contents rail. The rail is the
387
+ * first child for that reason, so a reader below `lg` meets the navigation before
388
+ * the text rather than after it, and one arrangement serves both widths.
277
389
  *
278
390
  * It is a Page rather than a Block because it is a whole screen and it is the
279
391
  * screen a content pipeline targets: a route that reads one document and hands
@@ -284,7 +396,9 @@ export type DocsShellProps = {
284
396
  *
285
397
  * It is a server Component. It fetches nothing, it holds no state and it imports
286
398
  * no router, so a consumer renders it from whichever route their framework names
287
- * and hands it the document their pipeline produced.
399
+ * and hands it the document their pipeline produced. Nothing here needs a client
400
+ * boundary, and the one feature that would want one is named above as work not
401
+ * done rather than quietly added.
288
402
  */
289
403
  export declare function DocsShell({ title, description, nav, toc, currentHref, navLabel, tocLabel, pagerLabel, pagerLabels, header, footer, className, children, }: DocsShellProps): import("react").JSX.Element;
290
404
  export default DocsShell;
@@ -1 +1 @@
1
- {"version":3,"file":"docs-shell.d.ts","sourceRoot":"","sources":["../../../src/pages/docs-shell/docs-shell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAMtC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,kFAAkF;IAClF,KAAK,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;CAC/B,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,SAAS,CAAA;IACf,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,cAAc,CAAA;AAEtE;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAChB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;OAIG;IACH,GAAG,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;IAC7B;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;IAC7B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,WAAW,EAAE,eAAe,CAAA;IAC5B;;;;;OAKG;IACH,QAAQ,EAAE,SAAS,CAAA;IACnB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmFG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,WAAW,EACX,GAAG,EACH,GAAG,EACH,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,WAAW,EACX,MAAM,EACN,MAAM,EACN,SAAS,EACT,QAAQ,GACT,EAAE,cAAc,+BA+FhB;AAwWD,eAAe,SAAS,CAAA"}
1
+ {"version":3,"file":"docs-shell.d.ts","sourceRoot":"","sources":["../../../src/pages/docs-shell/docs-shell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAOtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,CAAA;IACb;;;;;;;;OAQG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;CAC/B,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,SAAS,CAAA;IACf,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,cAAc,CAAA;AAEtE;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAChB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;OAIG;IACH,GAAG,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;IAC7B;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;IAC7B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,WAAW,EAAE,eAAe,CAAA;IAC5B;;;;;OAKG;IACH,QAAQ,EAAE,SAAS,CAAA;IACnB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwKG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,WAAW,EACX,GAAG,EACH,GAAG,EACH,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,WAAW,EACX,MAAM,EACN,MAAM,EACN,SAAS,EACT,QAAQ,GACT,EAAE,cAAc,+BAiFhB;AA+fD,eAAe,SAAS,CAAA"}
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Prose } from '../../components/ui/prose.js';
3
+ import { headingSizeClass } from '../../components/ui/section.js';
3
4
  import { Heading, Text } from '../../components/ui/typography.js';
4
5
  import { cn } from '../../lib/utils.js';
5
6
  /**
@@ -59,21 +60,104 @@ import { cn } from '../../lib/utils.js';
59
60
  * author filed it. A Page that sorted its rail would flatten the one thing the
60
61
  * three sites disagree about and cannot both be right about.
61
62
  *
62
- * **The rail is bounded in height.** Atlas files nineteen pages and AlphaLens
63
- * files twenty-seven, so the taller tree is half again as long, and an unbounded
64
- * rail on a tall tree is a rail that pushes the document below the fold before
65
- * the reader has read a line of it. The rail and the contents rail are each
66
- * capped against the viewport and scroll inside it, so a nineteen-page tree and a
67
- * twenty-seven-page tree are both a full screen and neither is a page-long
68
- * sidebar.
63
+ * **The rail is bounded in height, and the bound is what the reader has to be
64
+ * able to see.** Atlas files nineteen pages and AlphaLens files twenty-seven, so
65
+ * the taller tree is half again as long, and an unbounded rail on a tall tree is
66
+ * a rail that pushes the document below the fold before the reader has read a
67
+ * line of it. The rail and the contents rail are each capped against the viewport
68
+ * and scroll inside it, so a nineteen-page tree and a twenty-seven-page tree are
69
+ * both a full screen and neither is a page-long sidebar.
70
+ *
71
+ * **A cap the reader cannot see is a defect, and this one was invisible for 344
72
+ * and 750 pixels.** Measured in two consumers' built exports at 1280 by 680, the
73
+ * rail's content stood at 912 and 1318 pixels against a 568 pixel box, so more
74
+ * than half of one site's documentation navigation sat below the fold with
75
+ * `mask-image: none`, no scrollbar Chrome would draw until the reader scrolled,
76
+ * no fade and no count. Chrome's overlay scrollbars are the reason this is not
77
+ * caught by looking: they appear on scroll, so the first render of a rail that
78
+ * hides half its content is indistinguishable from a rail that has all of it, and
79
+ * the last visible row was cut mid-word above dead space, which reads as a
80
+ * rendering fault rather than as a scroll region.
81
+ *
82
+ * **The affordance is a fade across the foot of the scroll region, with the space
83
+ * it covers reserved rather than borrowed.** Three answers were available and the
84
+ * other two were rejected for stated reasons. A persistent themed scrollbar is
85
+ * truthful at both ends and costs nothing to legibility, and it is still here in
86
+ * the sense that the region is a real scroll region rather than a clipped box,
87
+ * but it is a single hairline whose length says "there is more" without saying
88
+ * which way, it needs `scrollbar-gutter` reserved in a 15rem rail to stop the
89
+ * content shifting when a short tree becomes a tall one, and in Chrome it is the
90
+ * least reliable of the three to reason about because the property that makes it
91
+ * persistent is the same one whose default is an overlay. A collapse control needs
92
+ * a disclosure whose state a server Component cannot hold. An "N more" control is
93
+ * copy this Page does not ship and it needs the count the Page cannot compute
94
+ * without measuring, which is the one thing a server Component cannot do.
95
+ *
96
+ * So the fade, and it is painted on a wrapper that does not scroll rather than on
97
+ * the scroll region itself: a mask or a gradient inside the scrollport moves with
98
+ * the content and marks nothing. The wrapper carries the sticky offset, the
99
+ * scroll region keeps the cap and the scrolling, and the gradient is pinned to
100
+ * the wrapper's foot. The region carries `padding-bottom` of the same height, and
101
+ * that is the half that makes it honest: at rest the gradient falls on reserved
102
+ * space and the last row is fully legible, and only while there is more below
103
+ * does it fall on content. Without the padding the same gradient washes the last
104
+ * row permanently, which is the usual cost of this answer and the reason the
105
+ * padding is not optional.
106
+ *
107
+ * **The fade is paint, and it is on the accessibility tree's terms.** It is an
108
+ * empty element with `aria-hidden` and `pointer-events-none`, inside the `<nav>`
109
+ * and outside the `<ul>`, so it is not a list item, it is not announced, it never
110
+ * takes a press and it never reaches a Tab stop. A keyboard reader scrolls the
111
+ * region with the arrow keys exactly as before and reaches every entry, and a
112
+ * screen-reader user is told nothing at all, which is the right answer: the cut is
113
+ * a fact about ink, and announcing it would put a sentence in the middle of a
114
+ * list of a consumer's own pages.
115
+ *
116
+ * **The tree is in the document once, at every width, and the price of that is a
117
+ * phone reader crossing the whole navigation to reach the article.** At 768 and
118
+ * at 1024 both rails used to be `hidden`, so the only navigation left inside
119
+ * `<main>` was the two-item pager at the foot of the article, and a reader on a
120
+ * twenty-seven document reference at tablet width had no contents, no
121
+ * on-this-page and no way to a sibling page except Previous and Next. 0.16.0
122
+ * closed that gap by drawing the tree a second time behind a `<details>`, one
123
+ * copy for the rail and one for the narrow frame, and it shipped a doubled
124
+ * document to every documentation page in the family with it: two
125
+ * `aria-current` markers for one page, two lists of a consumer's own pages, two
126
+ * `docs-nav-page` rows for every document, and a second `<nav>` carrying the
127
+ * rail's own accessible name, which is the thing a consumer's suite reaches the
128
+ * rail by. Three consumer test files failed against the published package: two on
129
+ * a count that came back doubled, and two on a `getByRole('navigation', { name })`
130
+ * that had two answers where it expects one. So there is one copy now, and it
131
+ * shows at every width.
132
+ *
133
+ * **What that costs, with the number.** A consumer's rail measured 1,318 pixels
134
+ * of content in a built export at 1280 by 680, and those rows wrap on a phone, so
135
+ * a reader arriving at the top of a documentation page scrolls past a thousand
136
+ * pixels of navigation before the article begins. That is a real regression for
137
+ * that reader and it is the reason a disclosure was reached for in the first
138
+ * place. It is paid rather than hidden, because every arrangement that avoids it
139
+ * is worse: a rail at `lg` and nothing below it reopens the gap this change
140
+ * closes, and drawing the tree twice to keep the scroll short is what broke three
141
+ * consumer test files in the first place.
142
+ *
143
+ * **What would actually remove the scroll.** A collapsed rail below `lg` needs an
144
+ * open state. A server Component cannot hold one, and `<details>` is the only
145
+ * disclosure the platform holds on the Page's behalf, so the control that opens
146
+ * it has to be a client island inside the Page. That is a separate piece of work
147
+ * with its own budget, and it is not built here: a Page that ships a client
148
+ * boundary to save one scroll is making a different trade than the one stated
149
+ * above, and it should be made deliberately rather than smuggled in as the side
150
+ * effect of fixing a duplicate.
69
151
  *
70
152
  * **The frame is three columns from `lg`, and the third is the consumer's to
71
153
  * fill.** The rail is 15rem, the document takes the rest, and the contents rail
72
154
  * is 13rem. The third track exists only when `toc` is passed, because the
73
155
  * document takes the second track by auto-placement and a template that named a
74
156
  * track no child occupies would leave the document in the 15rem one. Below `lg`
75
- * the frame is a single column with both rails hidden, which is the arrangement
76
- * a phone gets.
157
+ * the frame is a single column and the children flow in the order they are
158
+ * written: the rail, then the document, then the contents rail. The rail is the
159
+ * first child for that reason, so a reader below `lg` meets the navigation before
160
+ * the text rather than after it, and one arrangement serves both widths.
77
161
  *
78
162
  * It is a Page rather than a Block because it is a whole screen and it is the
79
163
  * screen a content pipeline targets: a route that reads one document and hands
@@ -84,7 +168,9 @@ import { cn } from '../../lib/utils.js';
84
168
  *
85
169
  * It is a server Component. It fetches nothing, it holds no state and it imports
86
170
  * no router, so a consumer renders it from whichever route their framework names
87
- * and hands it the document their pipeline produced.
171
+ * and hands it the document their pipeline produced. Nothing here needs a client
172
+ * boundary, and the one feature that would want one is named above as work not
173
+ * done rather than quietly added.
88
174
  */
89
175
  export function DocsShell({ title, description, nav, toc, currentHref, navLabel, tocLabel, pagerLabel, pagerLabels, header, footer, className, children, }) {
90
176
  readTree(nav, 'nav');
@@ -92,6 +178,8 @@ export function DocsShell({ title, description, nav, toc, currentHref, navLabel,
92
178
  const rail = nav && nav.length > 0 ? nav : undefined;
93
179
  const contents = toc && toc.length > 0 ? toc : undefined;
94
180
  const neighbours = currentHref === undefined ? undefined : deriveNeighbours(nav ?? [], currentHref);
181
+ const unnamedNav = unnamedIn(nav);
182
+ const unnamedToc = unnamedIn(toc);
95
183
  return (_jsxs("div", { "data-slot": "docs-shell", className: cn('mx-auto w-full max-w-page px-6 py-10 lg:px-8', className), children: [header, _jsxs("div", { "data-slot": "docs-shell-frame", className: cn('flex flex-col gap-8 lg:flex-row lg:gap-10',
96
184
  // One track for the rail, one for the document, and a third for the
97
185
  // contents rail when there is one. The template names exactly the
@@ -107,7 +195,73 @@ export function DocsShell({ title, description, nav, toc, currentHref, navLabel,
107
195
  // contents rail landed in an implicit `auto` track rather than the
108
196
  // 13rem one. `check-breakpoint-variants.mjs` is the gate that says so
109
197
  // to the next class written against a screen the theme does not emit.
110
- contents ? 'lg:grid lg:grid-cols-[15rem_minmax(0,1fr)_13rem]' : 'lg:grid lg:grid-cols-[15rem_minmax(0,1fr)]'), children: [rail ? (_jsx("aside", { "data-slot": "docs-rail", className: "hidden lg:col-start-1 lg:row-start-1 lg:block", children: _jsx("nav", { "aria-label": navLabel, className: "lg:sticky lg:top-20 lg:max-h-[calc(100svh-7rem)] lg:overflow-y-auto lg:pe-4", children: _jsx(NavList, { entries: rail, currentHref: currentHref, depth: 0 }) }) })) : null, _jsxs("article", { "data-slot": "docs-article", className: "flex min-w-0 flex-col gap-8", children: [title ? (_jsxs("header", { className: "flex flex-col gap-3", children: [_jsx(Heading, { as: "h1", size: "3xl", children: title }), description ? (_jsx(Text, { size: "lg", tone: "muted", className: "max-w-measure", children: description })) : null] })) : description ? (_jsx(Text, { size: "lg", tone: "muted", className: "max-w-measure", children: description })) : null, _jsx(Prose, { fullWidth: true, children: children }), neighbours ? (_jsx(Pager, { previous: neighbours.previous, next: neighbours.next, label: pagerLabel, labels: pagerLabels })) : null] }), contents ? (_jsx("aside", { "data-slot": "docs-contents", className: "hidden lg:col-start-3 lg:row-start-1 lg:block", children: _jsx("nav", { "aria-label": tocLabel, className: "lg:sticky lg:top-20 lg:max-h-[calc(100svh-7rem)] lg:overflow-y-auto", children: _jsx(NavList, { entries: contents, currentHref: currentHref, depth: 0 }) }) })) : null] }), footer] }));
198
+ contents ? 'lg:grid lg:grid-cols-[15rem_minmax(0,1fr)_13rem]' : 'lg:grid lg:grid-cols-[15rem_minmax(0,1fr)]'), children: [rail ? (_jsx("aside", { "data-slot": "docs-rail", className: "lg:col-start-1 lg:row-start-1", children: _jsx(Rail, { label: navLabel, entries: rail, currentHref: currentHref, unnamed: unnamedNav }) })) : null, _jsxs("article", { "data-slot": "docs-article", className: "flex min-w-0 flex-col gap-8", children: [title ? (_jsxs("header", { className: "flex flex-col gap-3", children: [_jsx(Heading, { as: "h1", className: headingSizeClass('h1'), children: title }), description ? (_jsx(Text, { size: "lg", tone: "muted", className: "max-w-measure", children: description })) : null] })) : description ? (_jsx(Text, { size: "lg", tone: "muted", className: "max-w-measure", children: description })) : null, _jsx(Prose, { fullWidth: true, children: children }), neighbours ? (_jsx(Pager, { previous: neighbours.previous, next: neighbours.next, label: pagerLabel, labels: pagerLabels })) : null] }), contents ? (_jsx("aside", { "data-slot": "docs-contents", className: "lg:col-start-3 lg:row-start-1", children: _jsx(Rail, { label: tocLabel, entries: contents, currentHref: currentHref, unnamed: unnamedToc }) })) : null] }), footer] }));
199
+ }
200
+ /* ------------------------------------------------------------------ *
201
+ * The two rails
202
+ * ------------------------------------------------------------------ */
203
+ /**
204
+ * One rail: a region that scrolls inside a bound against the viewport, with the
205
+ * cut at its foot marked so the rows below it are discoverable.
206
+ *
207
+ * **The sticky offset is on this wrapper and not on the scroll region, and the
208
+ * reason is the fade.** The fade has to be pinned to the foot of the region while
209
+ * the region scrolls, and nothing inside a scroll container stays put while that
210
+ * container scrolls: a gradient or a mask written into the scrollport travels with
211
+ * the content and so marks nothing once the reader has scrolled. The wrapper is
212
+ * the element that travels, so the fade is absolutely positioned against it and
213
+ * the `<nav>` inside is free to be nothing but the scroll region.
214
+ *
215
+ * The cap is `100svh` less the sticky offset plus the gap under it, and `svh` is
216
+ * the smallest viewport height rather than the largest, which is the direction that
217
+ * matters here. `dvh` would grow to the full height the moment a mobile browser's
218
+ * URL bar retracted, and a sticky region measured against a viewport taller than
219
+ * the one on screen is a region whose foot is below the bottom of the screen
220
+ * while it is pinned, which is the one state the bound exists to prevent. `svh`
221
+ * is never taller than what is visible, so `top` plus `max-h` is always inside the
222
+ * viewport and the rail's foot always clears it. `top-20` is 5rem and the cap is
223
+ * the viewport less 7rem, so the foot sits 2rem above the bottom edge at every
224
+ * window height; a short window makes the rail shorter and does not make it
225
+ * taller than the space it is pinned in.
226
+ *
227
+ * `padding-bottom` is the same height as the fade, and it is what makes the fade
228
+ * honest. The gradient falls on the last 2rem of whatever the scrollport is
229
+ * showing, so with the padding reserved it falls on empty space once the reader
230
+ * has reached the end and on content only while there is more below. Without it
231
+ * the same gradient permanently washes the bottom of the last row, which is the
232
+ * usual cost of a fade and the reason the padding is part of the answer rather
233
+ * than a detail.
234
+ *
235
+ * **The bound, the sticky offset, the band and the fade are all written against
236
+ * the same `lg:`, so below `lg` this rail is an ordinary block in the flow: no
237
+ * scroll region, no cut, no reservation, no fade.** That is the right shape for a
238
+ * rail that is the first thing in a single column, because a sticky region a full
239
+ * viewport tall above the article would be worse than a long one above it.
240
+ *
241
+ * **The fade and the band are bound to the cut rather than to the rail, and the
242
+ * reason is what a fade is for.** A fade marks a cut, so it may only exist where
243
+ * there is one, and the cut is the `overflow-y` at `lg`: the scroll region, the
244
+ * cap, the sticky offset, the reserved band and the gradient are one arrangement
245
+ * or none of them. The fade shipped unprefixed, which put the arrangement out of
246
+ * step with itself at the one width that has no cut to mark. Below `lg` the region
247
+ * is an ordinary block, so the `pb-8` reserving the band is inert there and the
248
+ * gradient fell on the last row or two of a rail that was simply running on. The
249
+ * band and the fade still measured equal and the reader still lost nothing
250
+ * legible, which is why looking did not catch it: a gradient painted over content
251
+ * is a defect, and it is not a legibility one.
252
+ *
253
+ * **The limit bought here is stated rather than left to be found.** Below `lg` the
254
+ * rail is bounded by its content rather than by the viewport, so a long tree runs
255
+ * past the fold of a phone screen with nothing marking where it ends: no cut to
256
+ * mark, no band to reserve and no scroll region to scroll inside. That is the
257
+ * honest price of one copy of the tree at every width, and the measurement behind
258
+ * it is stated once on the Page above rather than repeated here.
259
+ *
260
+ * `unnamed` is carried on the `<nav>` rather than logged, for the reason
261
+ * `unnamedIn` gives.
262
+ */
263
+ function Rail({ label, entries, currentHref, unnamed, }) {
264
+ return (_jsxs("div", { className: "relative lg:sticky lg:top-20", children: [_jsx("nav", { "aria-label": label, "data-unnamed-entries": unnamed, className: "lg:max-h-[calc(100svh-7rem)] lg:overflow-y-auto lg:pe-4 lg:pb-8", children: _jsx(NavList, { entries: entries, currentHref: currentHref, depth: 0 }) }), _jsx("div", { "aria-hidden": "true", "data-slot": "docs-rail-fade", className: "pointer-events-none lg:absolute lg:inset-x-0 lg:bottom-0 lg:h-8 lg:bg-gradient-to-t lg:from-background lg:to-transparent" })] }));
111
265
  }
112
266
  /* ------------------------------------------------------------------ *
113
267
  * The tree
@@ -124,6 +278,21 @@ export function DocsShell({ title, description, nav, toc, currentHref, navLabel,
124
278
  function addressOf(href) {
125
279
  return href !== undefined && href.trim() !== '' ? href : undefined;
126
280
  }
281
+ /**
282
+ * Whether an entry has words to be called by.
283
+ *
284
+ * Blank counts as none, for the same reason `addressOf` counts it: all three
285
+ * consumer adapters build a title out of whatever their content pipeline produced,
286
+ * and one of them maps a heading whose title arrives as a React element to the
287
+ * empty string. A row is rendered from a string, and a string of spaces is not a
288
+ * name. It is read here rather than at each use because `destinationOf` decides
289
+ * between a link and a label, the renderer decides between a row and nothing, and
290
+ * the counter decides how many rows went missing, and three answers to one
291
+ * question is how a fourth grows.
292
+ */
293
+ function named(entry) {
294
+ return entry.title.trim() !== '';
295
+ }
127
296
  /**
128
297
  * The address an entry is reachable at, or `undefined` when it is not a
129
298
  * destination at all.
@@ -136,7 +305,35 @@ function addressOf(href) {
136
305
  * `Next` link under it.
137
306
  */
138
307
  function destinationOf(entry) {
139
- return entry.title === '' ? undefined : addressOf(entry.href);
308
+ return named(entry) ? addressOf(entry.href) : undefined;
309
+ }
310
+ /**
311
+ * How many entries in a tree the Page cannot name, and therefore did not draw.
312
+ *
313
+ * **It is counted rather than swallowed, and the place it is counted on is the
314
+ * rail itself.** `Diagram` answers a dropped relation the same way, with
315
+ * `data-unresolved-relations` on the drawing, and the argument is the same one: a
316
+ * row that did not draw is a fact about the caller's data, and a fact that leaves
317
+ * no trace is a defect the next reader has to rediscover from a screenshot. The
318
+ * number is on the element rather than in a console because a caller reads the
319
+ * element and does not read the console.
320
+ *
321
+ * Three things are counted, because three things go missing rather than two:
322
+ * a page with no words, which is dropped whole; a group with no words, whose
323
+ * label is not rendered while its pages still are; and a group with neither words
324
+ * nor children, which has nothing left to draw and is dropped whole.
325
+ */
326
+ function unnamedIn(entries) {
327
+ let count = 0;
328
+ for (const entry of entries ?? []) {
329
+ if (entry.type === 'divider')
330
+ continue;
331
+ if (!named(entry))
332
+ count += 1;
333
+ if (entry.type === 'group')
334
+ count += unnamedIn(entry.items);
335
+ }
336
+ return count;
140
337
  }
141
338
  /**
142
339
  * Reads a tree before any of it renders, and refuses the one shape it cannot
@@ -205,15 +402,23 @@ function NavNode({ entry, currentHref, depth, }) {
205
402
  }
206
403
  const href = destinationOf(entry);
207
404
  if (entry.type === 'group') {
208
- return (_jsx("li", { "data-slot": "docs-nav-group", children: _jsxs("div", { className: "flex flex-col gap-1", children: [href === undefined ? (_jsx("span", { "data-slot": "docs-nav-label", className: cn('rounded-sm text-sm font-semibold tracking-tight', containsHref(entry, currentHref) ? 'text-foreground' : 'text-muted-foreground'), children: entry.title })) : (_jsx("a", { "data-slot": "docs-nav-heading", href: href, "aria-current": currentHref === href ? 'page' : undefined, className: cn('hover:text-foreground rounded-sm text-sm font-semibold tracking-tight transition-colors duration-fast ease-out', under(href, currentHref) ? 'text-foreground' : 'text-muted-foreground'), children: entry.title })), entry.items.length > 0 ? (_jsx(NavList, { entries: entry.items, currentHref: currentHref, depth: depth + 1 })) : null] }) }));
405
+ // A group with no words and no pages has nothing left to draw, and a `<li>`
406
+ // holding an empty `<div>` is the same empty row the page arm used to render.
407
+ // Dropping it costs nothing, because there was nothing in it.
408
+ if (!named(entry) && entry.items.length === 0)
409
+ return null;
410
+ return (_jsx("li", { "data-slot": "docs-nav-group", children: _jsxs("div", { className: "flex flex-col gap-1", children: [!named(entry) ? null : href === undefined ? (_jsx("span", { "data-slot": "docs-nav-label", className: cn('rounded-sm text-sm font-semibold tracking-tight', containsHref(entry, currentHref) ? 'text-foreground' : 'text-muted-foreground'), children: entry.title })) : (_jsx("a", { "data-slot": "docs-nav-heading", href: href, "aria-current": currentHref === href ? 'page' : undefined, className: cn('hover:text-foreground rounded-sm text-sm font-semibold tracking-tight transition-colors duration-fast ease-out', under(href, currentHref) ? 'text-foreground' : 'text-muted-foreground'), children: entry.title })), entry.items.length > 0 ? (_jsx(NavList, { entries: entry.items, currentHref: currentHref, depth: depth + 1 })) : null] }) }));
209
411
  }
210
- // The page arm. `href` is never empty here: `readTree` refused that above and
211
- // named the entry, so what is left to decide is the words. A nameless page is
212
- // the same rule as a group with no index, rendered as a label, because a link
213
- // with no accessible name is announced as "link" and nothing else.
214
- return (_jsx("li", { "data-slot": "docs-nav-page", children: href === undefined ? (_jsx("span", { "data-slot": "docs-nav-label", className: "-ms-px block border-l-2 border-transparent py-1 ps-3 text-sm text-muted-foreground", children: entry.title })) : (_jsx("a", { "data-slot": "docs-nav-link", href: href, "aria-current": currentHref === href ? 'page' : undefined, className: cn('-ms-px block border-l-2 py-1 ps-3 text-sm transition-colors duration-fast ease-out', currentHref === href
412
+ // The page arm, and there is one shape of it left. `readTree` refused a page
413
+ // with no address above and named the entry, so every page that reaches here has
414
+ // one, and a page with no words is not rendered at all rather than rendered as
415
+ // a label: a link with no accessible name is announced as "link" and nothing
416
+ // else, and a blank row is not a label either because a label is words.
417
+ if (!named(entry))
418
+ return null;
419
+ return (_jsx("li", { "data-slot": "docs-nav-page", children: _jsx("a", { "data-slot": "docs-nav-link", href: href, "aria-current": currentHref === href ? 'page' : undefined, className: cn('-ms-px block border-l-2 py-1 ps-3 text-sm transition-colors duration-fast ease-out', currentHref === href
215
420
  ? 'text-foreground border-primary font-medium'
216
- : 'text-muted-foreground hover:text-foreground border-transparent'), children: entry.title })) }));
421
+ : 'text-muted-foreground hover:text-foreground border-transparent'), children: entry.title }) }));
217
422
  }
218
423
  /**
219
424
  * Every page the tree holds, in the order a reader meets them.
@@ -225,11 +430,12 @@ function NavNode({ entry, currentHref, depth, }) {
225
430
  * the pager to reach either. A divider contributes nothing at all.
226
431
  *
227
432
  * The row a reader can click and the row the pager can reach are decided by the
228
- * SAME function, `destinationOf`. A nameless entry is a label on the rail and is
229
- * dropped here, so one malformed row cannot be published twice: a nameless anchor
230
- * in the rail, and a `Next` link under it carrying the text of whichever entry
231
- * happened to follow, is what deriving without asking produced. The two surfaces
232
- * do not each get their own opinion about a row that is not a page.
433
+ * SAME function, `destinationOf`, and a row with no words is now dropped from the
434
+ * rail as well, so the two surfaces agree twice over rather than once by accident.
435
+ * The defect this replaced was a nameless anchor in the rail and a `Next` link
436
+ * under it carrying the text of whichever entry happened to follow, published
437
+ * from one malformed row. Deriving without asking produced the first and a
438
+ * separate opinion produced the second; asking once produces neither.
233
439
  */
234
440
  function flatten(entries, into = []) {
235
441
  for (const entry of entries) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pages/error-page/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAIpF;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;;;;;GAQG;AACH,KAAK,eAAe,GAAG;IACrB;;;;;;;OAOG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB;;;OAGG;IACH,KAAK,EAAE,SAAS,aAAa,EAAE,CAAA;IAC/B;;;;;;;OAOG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,wFAAwF;IACxF,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,6GAA6G;IAC7G,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,eAAe,GAC1C,CACI;IACE;;;;;;;OAOG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;OAMG;IACH,cAAc,EAAE,MAAM,CAAA;CACvB,GACD;IACE,mCAAmC;IACnC,SAAS,CAAC,EAAE,KAAK,CAAA;IACjB,4DAA4D;IAC5D,cAAc,CAAC,EAAE,KAAK,CAAA;CACvB,CACJ,CAAA;AAqBH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,KAAK,EACL,WAAW,EACX,KAAK,EACL,KAAK,EACL,UAAU,EACV,SAAS,EACT,cAAc,EACd,QAAQ,EACR,YAAmB,EACnB,SAAS,GACV,EAAE,cAAc,+BAkEhB;AAED,eAAe,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pages/error-page/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,OAAO,EAAyC,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAItG;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;;;;;GAQG;AACH,KAAK,eAAe,GAAG;IACrB;;;;;;;OAOG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB;;;OAGG;IACH,KAAK,EAAE,SAAS,aAAa,EAAE,CAAA;IAC/B;;;;;;;OAOG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,wFAAwF;IACxF,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,6GAA6G;IAC7G,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,eAAe,GAC1C,CACI;IACE;;;;;;;OAOG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;OAMG;IACH,cAAc,EAAE,MAAM,CAAA;CACvB,GACD;IACE,mCAAmC;IACnC,SAAS,CAAC,EAAE,KAAK,CAAA;IACjB,4DAA4D;IAC5D,cAAc,CAAC,EAAE,KAAK,CAAA;CACvB,CACJ,CAAA;AAqBH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,KAAK,EACL,WAAW,EACX,KAAK,EACL,KAAK,EACL,UAAU,EACV,SAAS,EACT,cAAc,EACd,QAAQ,EACR,YAAmB,EACnB,SAAS,GACV,EAAE,cAAc,+BAqEhB;AAED,eAAe,SAAS,CAAA"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { CtaLink } from '../../components/ui/cta-link.js';
3
3
  import { Prose } from '../../components/ui/prose.js';
4
- import { Section, childLevel } from '../../components/ui/section.js';
4
+ import { Section, childLevel, headingSizeClass } from '../../components/ui/section.js';
5
5
  import { Heading, Text } from '../../components/ui/typography.js';
6
6
  import { cn } from '../../lib/utils.js';
7
7
  /**
@@ -90,9 +90,13 @@ export function ErrorPage({ code, title, description, retry, links, linksLabel,
90
90
  assertReference(reference, referenceLabel);
91
91
  /*
92
92
  The headline sits one step below the code, which is the same derivation every
93
- Block uses and the reason a hardcoded `h2` would be right exactly once.
93
+ Block uses and the reason a hardcoded `h2` would be right exactly once. The
94
+ size is asked of the same table rather than passed to `Heading`'s `size`, which
95
+ is a step of the scale and not a rung of the ladder, so the code and the
96
+ headline were drawn at fixed sizes that the ladder's ceiling has since left
97
+ behind.
94
98
  */
95
99
  const TitleHeading = childLevel(headingLevel);
96
- return (_jsx(Section, { "data-slot": "error-page", className: cn(className), children: _jsxs("div", { "data-slot": "error-page-body", className: "flex flex-col items-start gap-6", children: [_jsx(Heading, { as: headingLevel, size: "4xl", className: "text-muted-foreground font-mono", children: code }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(Heading, { as: TitleHeading, size: "2xl", children: title }), description ? _jsx(Text, { size: "lg", children: description }) : null] }), retry === undefined ? null : (_jsx("div", { "data-slot": "error-page-retry", children: retry })), links.length > 0 ? (_jsx("nav", { "aria-label": linksLabel, className: "flex flex-wrap items-center gap-3", children: links.map((link, index) => (_jsx(CtaLink, { href: link.href, newTab: link.newTab, variant: index === 0 ? 'default' : 'outline', children: link.label }, link.href))) })) : null, reference === undefined ? null : (_jsxs("dl", { "data-slot": "error-page-reference", className: "flex flex-col items-start gap-1", children: [_jsx("dt", { className: "text-muted-foreground text-xs", children: referenceLabel }), _jsx("dd", { className: "font-mono text-sm", children: reference })] })), children ? _jsx(Prose, { className: "mt-2", children: children }) : null] }) }));
100
+ return (_jsx(Section, { "data-slot": "error-page", className: cn(className), children: _jsxs("div", { "data-slot": "error-page-body", className: "flex flex-col items-start gap-6", children: [_jsx(Heading, { as: headingLevel, className: cn('text-muted-foreground font-mono', headingSizeClass(headingLevel)), children: code }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(Heading, { as: TitleHeading, className: headingSizeClass(TitleHeading), children: title }), description ? _jsx(Text, { size: "lg", children: description }) : null] }), retry === undefined ? null : (_jsx("div", { "data-slot": "error-page-retry", children: retry })), links.length > 0 ? (_jsx("nav", { "aria-label": linksLabel, className: "flex flex-wrap items-center gap-3", children: links.map((link, index) => (_jsx(CtaLink, { href: link.href, newTab: link.newTab, variant: index === 0 ? 'default' : 'outline', children: link.label }, link.href))) })) : null, reference === undefined ? null : (_jsxs("dl", { "data-slot": "error-page-reference", className: "flex flex-col items-start gap-1", children: [_jsx("dt", { className: "text-muted-foreground text-xs", children: referenceLabel }), _jsx("dd", { className: "font-mono text-sm", children: reference })] })), children ? _jsx(Prose, { className: "mt-2", children: children }) : null] }) }));
97
101
  }
98
102
  export default ErrorPage;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pages/not-found-page/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAOtC;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IAC9B;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,KAAK,EACL,WAAW,EACX,KAAK,EACL,UAAU,EACV,QAAQ,EACR,SAAS,GACV,EAAE,iBAAiB,+BA+BnB;AAED,eAAe,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pages/not-found-page/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAQtC;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IAC9B;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,KAAK,EACL,WAAW,EACX,KAAK,EACL,UAAU,EACV,QAAQ,EACR,SAAS,GACV,EAAE,iBAAiB,+BA+BnB;AAED,eAAe,YAAY,CAAA"}
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { CtaLink } from '../../components/ui/cta-link.js';
3
- import { Heading, Text } from '../../components/ui/typography.js';
4
3
  import { Prose } from '../../components/ui/prose.js';
5
- import { Section } from '../../components/ui/section.js';
4
+ import { Section, headingSizeClass } from '../../components/ui/section.js';
5
+ import { Heading, Text } from '../../components/ui/typography.js';
6
+ import { cn } from '../../lib/utils.js';
6
7
  /**
7
8
  * A complete not-found screen: the code, what happened, and the ways out.
8
9
  *
@@ -41,6 +42,6 @@ import { Section } from '../../components/ui/section.js';
41
42
  * route their framework names.
42
43
  */
43
44
  export function NotFoundPage({ code, title, description, links, linksLabel, children, className, }) {
44
- return (_jsx(Section, { className: className, children: _jsxs("div", { className: "flex flex-col items-start gap-6", children: [_jsx(Heading, { as: "h1", size: "4xl", className: "text-muted-foreground font-mono", children: code }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(Heading, { as: "h2", size: "2xl", children: title }), description ? _jsx(Text, { size: "lg", children: description }) : null] }), links.length > 0 ? (_jsx("nav", { "aria-label": linksLabel, className: "flex flex-wrap items-center gap-3", children: links.map((link, index) => (_jsx(CtaLink, { href: link.href, newTab: link.newTab, variant: index === 0 ? 'default' : 'outline', children: link.label }, link.href))) })) : null, children ? _jsx(Prose, { className: "mt-2", children: children }) : null] }) }));
45
+ return (_jsx(Section, { className: className, children: _jsxs("div", { className: "flex flex-col items-start gap-6", children: [_jsx(Heading, { as: "h1", className: cn('text-muted-foreground font-mono', headingSizeClass('h1')), children: code }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(Heading, { as: "h2", className: headingSizeClass('h2'), children: title }), description ? _jsx(Text, { size: "lg", children: description }) : null] }), links.length > 0 ? (_jsx("nav", { "aria-label": linksLabel, className: "flex flex-wrap items-center gap-3", children: links.map((link, index) => (_jsx(CtaLink, { href: link.href, newTab: link.newTab, variant: index === 0 ? 'default' : 'outline', children: link.label }, link.href))) })) : null, children ? _jsx(Prose, { className: "mt-2", children: children }) : null] }) }));
45
46
  }
46
47
  export default NotFoundPage;