@nanisoft/prism-ui 0.15.0 → 0.16.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 (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 +120 -18
  40. package/dist/pages/docs-shell/docs-shell.d.ts.map +1 -1
  41. package/dist/pages/docs-shell/docs-shell.js +208 -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 +107 -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
@@ -159,11 +188,17 @@ export type DocsShellProps = {
159
188
  * The accessible name of the rail. Required whenever `nav` is passed, because
160
189
  * it is a word a reader hears and a Page that ships no copy ships no
161
190
  * reader-facing copy either.
191
+ *
192
+ * It is also the visible word on the disclosure that stands in for the rail
193
+ * below `lg`, because it is already this navigation's name and a Page cannot
194
+ * invent a second one. A consumer that files its navigation in another language
195
+ * gets the control in that language by having done this once.
162
196
  */
163
197
  navLabel: string;
164
198
  /**
165
199
  * The accessible name of the contents rail. Required whenever `toc` is passed,
166
- * for the same reason `navLabel` is.
200
+ * for the same reason `navLabel` is, and the contents disclosure below `lg`
201
+ * carries it for the same reason too.
167
202
  */
168
203
  tocLabel: string;
169
204
  /**
@@ -259,21 +294,85 @@ export type DocsShellProps = {
259
294
  * author filed it. A Page that sorted its rail would flatten the one thing the
260
295
  * three sites disagree about and cannot both be right about.
261
296
  *
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.
297
+ * **The rail is bounded in height, and the bound is what the reader has to be
298
+ * able to see.** Atlas files nineteen pages and AlphaLens files twenty-seven, so
299
+ * the taller tree is half again as long, and an unbounded rail on a tall tree is
300
+ * a rail that pushes the document below the fold before the reader has read a
301
+ * line of it. The rail and the contents rail are each capped against the viewport
302
+ * and scroll inside it, so a nineteen-page tree and a twenty-seven-page tree are
303
+ * both a full screen and neither is a page-long sidebar.
304
+ *
305
+ * **A cap the reader cannot see is a defect, and this one was invisible for 344
306
+ * and 750 pixels.** Measured in two consumers' built exports at 1280 by 680, the
307
+ * rail's content stood at 912 and 1318 pixels against a 568 pixel box, so more
308
+ * than half of one site's documentation navigation sat below the fold with
309
+ * `mask-image: none`, no scrollbar Chrome would draw until the reader scrolled,
310
+ * no fade and no count. Chrome's overlay scrollbars are the reason this is not
311
+ * caught by looking: they appear on scroll, so the first render of a rail that
312
+ * hides half its content is indistinguishable from a rail that has all of it, and
313
+ * the last visible row was cut mid-word above dead space, which reads as a
314
+ * rendering fault rather than as a scroll region.
315
+ *
316
+ * **The affordance is a fade across the foot of the scroll region, with the space
317
+ * it covers reserved rather than borrowed.** Three answers were available and the
318
+ * other two were rejected for stated reasons. A persistent themed scrollbar is
319
+ * truthful at both ends and costs nothing to legibility, and it is still here in
320
+ * the sense that the region is a real scroll region rather than a clipped box,
321
+ * but it is a single hairline whose length says "there is more" without saying
322
+ * which way, it needs `scrollbar-gutter` reserved in a 15rem rail to stop the
323
+ * content shifting when a short tree becomes a tall one, and in Chrome it is the
324
+ * least reliable of the three to reason about because the property that makes it
325
+ * persistent is the same one whose default is an overlay. A collapse control needs
326
+ * a disclosure whose state a server Component cannot hold. An "N more" control is
327
+ * copy this Page does not ship and it needs the count the Page cannot compute
328
+ * without measuring, which is the one thing a server Component cannot do.
329
+ *
330
+ * So the fade, and it is painted on a wrapper that does not scroll rather than on
331
+ * the scroll region itself: a mask or a gradient inside the scrollport moves with
332
+ * the content and marks nothing. The wrapper carries the sticky offset, the
333
+ * scroll region keeps the cap and the scrolling, and the gradient is pinned to
334
+ * the wrapper's foot. The region carries `padding-bottom` of the same height, and
335
+ * that is the half that makes it honest: at rest the gradient falls on reserved
336
+ * space and the last row is fully legible, and only while there is more below
337
+ * does it fall on content. Without the padding the same gradient washes the last
338
+ * row permanently, which is the usual cost of this answer and the reason the
339
+ * padding is not optional.
340
+ *
341
+ * **The fade is paint, and it is on the accessibility tree's terms.** It is an
342
+ * empty element with `aria-hidden` and `pointer-events-none`, inside the `<nav>`
343
+ * and outside the `<ul>`, so it is not a list item, it is not announced, it never
344
+ * takes a press and it never reaches a Tab stop. A keyboard reader scrolls the
345
+ * region with the arrow keys exactly as before and reaches every entry, and a
346
+ * screen-reader user is told nothing at all, which is the right answer: the cut is
347
+ * a fact about ink, and announcing it would put a sentence in the middle of a
348
+ * list of a consumer's own pages.
349
+ *
350
+ * **Below `lg` there was no navigation at all, and the fix is a native
351
+ * disclosure.** At 768 and at 1024 both rails were `hidden` and the only
352
+ * navigation left inside `<main>` was the two-item pager at the foot of the
353
+ * article, so a reader on a thirty-one document reference at tablet width had no
354
+ * contents, no on-this-page and no way to a sibling page except Previous and
355
+ * Next. The disclosure is `<details>` and `<summary>`, which is the one
356
+ * disclosure widget that needs no runtime at all: it is expanded and collapsed by
357
+ * the platform, it is a real control in the Tab order with a real expanded state,
358
+ * and adding a client boundary to a server Component to reinvent it would put
359
+ * bytes on every documentation page in the family to save a reader one click.
360
+ *
361
+ * The tree is rendered twice, once in each arrangement, and only one of them is
362
+ * in the document at any width: the rail is `hidden lg:block` and the disclosure
363
+ * is `lg:hidden`, so the other one is `display: none` and is out of the
364
+ * accessibility tree and out of the tab order rather than a second copy a reader
365
+ * meets. The summary carries `navLabel` or `tocLabel`, which is the caller's own
366
+ * word for that navigation and therefore the one word on the control that cannot
367
+ * be wrong in a language the consumer did not write in.
269
368
  *
270
369
  * **The frame is three columns from `lg`, and the third is the consumer's to
271
370
  * fill.** The rail is 15rem, the document takes the rest, and the contents rail
272
371
  * is 13rem. The third track exists only when `toc` is passed, because the
273
372
  * document takes the second track by auto-placement and a template that named a
274
373
  * 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.
374
+ * the frame is a single column: the document fills it and the two navigations sit
375
+ * above it behind their disclosures.
277
376
  *
278
377
  * It is a Page rather than a Block because it is a whole screen and it is the
279
378
  * screen a content pipeline targets: a route that reads one document and hands
@@ -284,7 +383,10 @@ export type DocsShellProps = {
284
383
  *
285
384
  * It is a server Component. It fetches nothing, it holds no state and it imports
286
385
  * no router, so a consumer renders it from whichever route their framework names
287
- * and hands it the document their pipeline produced.
386
+ * and hands it the document their pipeline produced. The sub-`lg` disclosure is
387
+ * the reason that stays true under pressure: it is the one feature on this Page
388
+ * that a hand would be tempted to write as a client Component, and it is written
389
+ * as markup instead.
288
390
  */
289
391
  export declare function DocsShell({ title, description, nav, toc, currentHref, navLabel, tocLabel, pagerLabel, pagerLabels, header, footer, className, children, }: DocsShellProps): import("react").JSX.Element;
290
392
  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;;;;;;;;;OASG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;OAIG;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsJG;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,+BA6GhB;AAsgBD,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,85 @@ 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
+ * **Below `lg` there was no navigation at all, and the fix is a native
117
+ * disclosure.** At 768 and at 1024 both rails were `hidden` and the only
118
+ * navigation left inside `<main>` was the two-item pager at the foot of the
119
+ * article, so a reader on a thirty-one document reference at tablet width had no
120
+ * contents, no on-this-page and no way to a sibling page except Previous and
121
+ * Next. The disclosure is `<details>` and `<summary>`, which is the one
122
+ * disclosure widget that needs no runtime at all: it is expanded and collapsed by
123
+ * the platform, it is a real control in the Tab order with a real expanded state,
124
+ * and adding a client boundary to a server Component to reinvent it would put
125
+ * bytes on every documentation page in the family to save a reader one click.
126
+ *
127
+ * The tree is rendered twice, once in each arrangement, and only one of them is
128
+ * in the document at any width: the rail is `hidden lg:block` and the disclosure
129
+ * is `lg:hidden`, so the other one is `display: none` and is out of the
130
+ * accessibility tree and out of the tab order rather than a second copy a reader
131
+ * meets. The summary carries `navLabel` or `tocLabel`, which is the caller's own
132
+ * word for that navigation and therefore the one word on the control that cannot
133
+ * be wrong in a language the consumer did not write in.
69
134
  *
70
135
  * **The frame is three columns from `lg`, and the third is the consumer's to
71
136
  * fill.** The rail is 15rem, the document takes the rest, and the contents rail
72
137
  * is 13rem. The third track exists only when `toc` is passed, because the
73
138
  * document takes the second track by auto-placement and a template that named a
74
139
  * 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.
140
+ * the frame is a single column: the document fills it and the two navigations sit
141
+ * above it behind their disclosures.
77
142
  *
78
143
  * It is a Page rather than a Block because it is a whole screen and it is the
79
144
  * screen a content pipeline targets: a route that reads one document and hands
@@ -84,7 +149,10 @@ import { cn } from '../../lib/utils.js';
84
149
  *
85
150
  * It is a server Component. It fetches nothing, it holds no state and it imports
86
151
  * no router, so a consumer renders it from whichever route their framework names
87
- * and hands it the document their pipeline produced.
152
+ * and hands it the document their pipeline produced. The sub-`lg` disclosure is
153
+ * the reason that stays true under pressure: it is the one feature on this Page
154
+ * that a hand would be tempted to write as a client Component, and it is written
155
+ * as markup instead.
88
156
  */
89
157
  export function DocsShell({ title, description, nav, toc, currentHref, navLabel, tocLabel, pagerLabel, pagerLabels, header, footer, className, children, }) {
90
158
  readTree(nav, 'nav');
@@ -92,6 +160,8 @@ export function DocsShell({ title, description, nav, toc, currentHref, navLabel,
92
160
  const rail = nav && nav.length > 0 ? nav : undefined;
93
161
  const contents = toc && toc.length > 0 ? toc : undefined;
94
162
  const neighbours = currentHref === undefined ? undefined : deriveNeighbours(nav ?? [], currentHref);
163
+ const unnamedNav = unnamedIn(nav);
164
+ const unnamedToc = unnamedIn(toc);
95
165
  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
166
  // One track for the rail, one for the document, and a third for the
97
167
  // contents rail when there is one. The template names exactly the
@@ -107,7 +177,69 @@ export function DocsShell({ title, description, nav, toc, currentHref, navLabel,
107
177
  // contents rail landed in an implicit `auto` track rather than the
108
178
  // 13rem one. `check-breakpoint-variants.mjs` is the gate that says so
109
179
  // 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] }));
180
+ contents ? 'lg:grid lg:grid-cols-[15rem_minmax(0,1fr)_13rem]' : 'lg:grid lg:grid-cols-[15rem_minmax(0,1fr)]'), children: [rail || contents ? (_jsxs("div", { "data-slot": "docs-nav-compact", className: "flex flex-col gap-4 lg:hidden", children: [rail ? (_jsx(Disclosure, { label: navLabel, unnamed: unnamedNav, children: _jsx(NavList, { entries: rail, currentHref: currentHref, depth: 0 }) })) : null, contents ? (_jsx(Disclosure, { label: tocLabel, unnamed: unnamedToc, children: _jsx(NavList, { entries: contents, currentHref: currentHref, depth: 0 }) })) : null] })) : null, rail ? (_jsx("aside", { "data-slot": "docs-rail", className: "hidden lg:col-start-1 lg:row-start-1 lg:block", 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: "hidden lg:col-start-3 lg:row-start-1 lg:block", children: _jsx(Rail, { label: tocLabel, entries: contents, currentHref: currentHref, unnamed: unnamedToc }) })) : null] }), footer] }));
181
+ }
182
+ /* ------------------------------------------------------------------ *
183
+ * The two rails at lg
184
+ * ------------------------------------------------------------------ */
185
+ /**
186
+ * One rail: a region that scrolls inside a bound against the viewport, with the
187
+ * cut at its foot marked so the rows below it are discoverable.
188
+ *
189
+ * **The sticky offset is on this wrapper and not on the scroll region, and the
190
+ * reason is the fade.** The fade has to be pinned to the foot of the region while
191
+ * the region scrolls, and nothing inside a scroll container stays put while that
192
+ * container scrolls: a gradient or a mask written into the scrollport travels with
193
+ * the content and so marks nothing once the reader has scrolled. The wrapper is
194
+ * the element that travels, so the fade is absolutely positioned against it and
195
+ * the `<nav>` inside is free to be nothing but the scroll region.
196
+ *
197
+ * The cap is `100svh` less the sticky offset plus the gap under it, and `svh` is
198
+ * the smallest viewport height rather than the largest, which is the direction that
199
+ * matters here. `dvh` would grow to the full height the moment a mobile browser's
200
+ * URL bar retracted, and a sticky region measured against a viewport taller than
201
+ * the one on screen is a region whose foot is below the bottom of the screen
202
+ * while it is pinned, which is the one state the bound exists to prevent. `svh`
203
+ * is never taller than what is visible, so `top` plus `max-h` is always inside the
204
+ * viewport and the rail's foot always clears it. `top-20` is 5rem and the cap is
205
+ * the viewport less 7rem, so the foot sits 2rem above the bottom edge at every
206
+ * window height; a short window makes the rail shorter and does not make it
207
+ * taller than the space it is pinned in.
208
+ *
209
+ * `padding-bottom` is the same height as the fade, and it is what makes the fade
210
+ * honest. The gradient falls on the last 2rem of whatever the scrollport is
211
+ * showing, so with the padding reserved it falls on empty space once the reader
212
+ * has reached the end and on content only while there is more below. Without it
213
+ * the same gradient permanently washes the bottom of the last row, which is the
214
+ * usual cost of a fade and the reason the padding is part of the answer rather
215
+ * than a detail.
216
+ *
217
+ * `unnamed` is carried on the `<nav>` rather than logged, for the reason
218
+ * `unnamedIn` gives.
219
+ */
220
+ function Rail({ label, entries, currentHref, unnamed, }) {
221
+ 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: "from-background pointer-events-none absolute inset-x-0 bottom-0 h-8 bg-gradient-to-t to-transparent" })] }));
222
+ }
223
+ /**
224
+ * One tree behind a native disclosure, for the widths where there is no rail.
225
+ *
226
+ * `<details>` rather than a Component, and the whole argument is that it needs no
227
+ * runtime: the platform holds the expanded state, the control is in the Tab order
228
+ * with a real expanded state, and the Page stays a server Component. A Page that
229
+ * shipped a client disclosure for this would put JavaScript on every documentation
230
+ * page in the family to replace a browser feature that has been in the platform
231
+ * since 2018.
232
+ *
233
+ * **The summary's word is the caller's.** `navLabel` and `tocLabel` are already
234
+ * required props and already name these two navigations for a screen reader, so
235
+ * the control that opens one says the same thing the region inside it is called
236
+ * and the Page ships no word of its own. Nothing suppresses the browser's own
237
+ * disclosure marker or its own focus ring: the marker is the cheapest honest
238
+ * signal that the row is a disclosure, and `py-3` puts the target at 44 pixels for
239
+ * a coarse pointer without a variant.
240
+ */
241
+ function Disclosure({ label, unnamed, children, }) {
242
+ return (_jsxs("details", { "data-slot": "docs-nav-disclosure", children: [_jsx("summary", { className: "text-foreground cursor-pointer py-3 text-sm font-medium", children: label }), _jsx("nav", { "aria-label": label, "data-unnamed-entries": unnamed, className: "pb-4", children: children })] }));
111
243
  }
112
244
  /* ------------------------------------------------------------------ *
113
245
  * The tree
@@ -124,6 +256,21 @@ export function DocsShell({ title, description, nav, toc, currentHref, navLabel,
124
256
  function addressOf(href) {
125
257
  return href !== undefined && href.trim() !== '' ? href : undefined;
126
258
  }
259
+ /**
260
+ * Whether an entry has words to be called by.
261
+ *
262
+ * Blank counts as none, for the same reason `addressOf` counts it: all three
263
+ * consumer adapters build a title out of whatever their content pipeline produced,
264
+ * and one of them maps a heading whose title arrives as a React element to the
265
+ * empty string. A row is rendered from a string, and a string of spaces is not a
266
+ * name. It is read here rather than at each use because `destinationOf` decides
267
+ * between a link and a label, the renderer decides between a row and nothing, and
268
+ * the counter decides how many rows went missing, and three answers to one
269
+ * question is how a fourth grows.
270
+ */
271
+ function named(entry) {
272
+ return entry.title.trim() !== '';
273
+ }
127
274
  /**
128
275
  * The address an entry is reachable at, or `undefined` when it is not a
129
276
  * destination at all.
@@ -136,7 +283,35 @@ function addressOf(href) {
136
283
  * `Next` link under it.
137
284
  */
138
285
  function destinationOf(entry) {
139
- return entry.title === '' ? undefined : addressOf(entry.href);
286
+ return named(entry) ? addressOf(entry.href) : undefined;
287
+ }
288
+ /**
289
+ * How many entries in a tree the Page cannot name, and therefore did not draw.
290
+ *
291
+ * **It is counted rather than swallowed, and the place it is counted on is the
292
+ * rail itself.** `Diagram` answers a dropped relation the same way, with
293
+ * `data-unresolved-relations` on the drawing, and the argument is the same one: a
294
+ * row that did not draw is a fact about the caller's data, and a fact that leaves
295
+ * no trace is a defect the next reader has to rediscover from a screenshot. The
296
+ * number is on the element rather than in a console because a caller reads the
297
+ * element and does not read the console.
298
+ *
299
+ * Three things are counted, because three things go missing rather than two:
300
+ * a page with no words, which is dropped whole; a group with no words, whose
301
+ * label is not rendered while its pages still are; and a group with neither words
302
+ * nor children, which has nothing left to draw and is dropped whole.
303
+ */
304
+ function unnamedIn(entries) {
305
+ let count = 0;
306
+ for (const entry of entries ?? []) {
307
+ if (entry.type === 'divider')
308
+ continue;
309
+ if (!named(entry))
310
+ count += 1;
311
+ if (entry.type === 'group')
312
+ count += unnamedIn(entry.items);
313
+ }
314
+ return count;
140
315
  }
141
316
  /**
142
317
  * Reads a tree before any of it renders, and refuses the one shape it cannot
@@ -205,15 +380,23 @@ function NavNode({ entry, currentHref, depth, }) {
205
380
  }
206
381
  const href = destinationOf(entry);
207
382
  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] }) }));
383
+ // A group with no words and no pages has nothing left to draw, and a `<li>`
384
+ // holding an empty `<div>` is the same empty row the page arm used to render.
385
+ // Dropping it costs nothing, because there was nothing in it.
386
+ if (!named(entry) && entry.items.length === 0)
387
+ return null;
388
+ 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
389
  }
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
390
+ // The page arm, and there is one shape of it left. `readTree` refused a page
391
+ // with no address above and named the entry, so every page that reaches here has
392
+ // one, and a page with no words is not rendered at all rather than rendered as
393
+ // a label: a link with no accessible name is announced as "link" and nothing
394
+ // else, and a blank row is not a label either because a label is words.
395
+ if (!named(entry))
396
+ return null;
397
+ 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
398
  ? 'text-foreground border-primary font-medium'
216
- : 'text-muted-foreground hover:text-foreground border-transparent'), children: entry.title })) }));
399
+ : 'text-muted-foreground hover:text-foreground border-transparent'), children: entry.title }) }));
217
400
  }
218
401
  /**
219
402
  * Every page the tree holds, in the order a reader meets them.
@@ -225,11 +408,12 @@ function NavNode({ entry, currentHref, depth, }) {
225
408
  * the pager to reach either. A divider contributes nothing at all.
226
409
  *
227
410
  * 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.
411
+ * SAME function, `destinationOf`, and a row with no words is now dropped from the
412
+ * rail as well, so the two surfaces agree twice over rather than once by accident.
413
+ * The defect this replaced was a nameless anchor in the rail and a `Next` link
414
+ * under it carrying the text of whichever entry happened to follow, published
415
+ * from one malformed row. Deriving without asking produced the first and a
416
+ * separate opinion produced the second; asking once produces neither.
233
417
  */
234
418
  function flatten(entries, into = []) {
235
419
  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;