@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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/blocks/data-table-01/data-table.d.ts +10 -0
- package/dist/blocks/data-table-01/data-table.d.ts.map +1 -1
- package/dist/blocks/data-table-01/data-table.js +2 -1
- package/dist/blocks/page-header-01/page-header.d.ts +7 -0
- package/dist/blocks/page-header-01/page-header.d.ts.map +1 -1
- package/dist/blocks/page-header-01/page-header.js +9 -1
- package/dist/blocks/run-console-01/run-console.d.ts.map +1 -1
- package/dist/blocks/run-console-01/run-console.js +2 -1
- package/dist/blocks/site-footer/site-footer.d.ts +16 -0
- package/dist/blocks/site-footer/site-footer.d.ts.map +1 -1
- package/dist/blocks/site-footer/site-footer.js +17 -1
- package/dist/blocks/stack-grid-01/stack-grid.d.ts +13 -0
- package/dist/blocks/stack-grid-01/stack-grid.d.ts.map +1 -1
- package/dist/blocks/stack-grid-01/stack-grid.js +14 -1
- package/dist/components/ui/diagram.d.ts +38 -7
- package/dist/components/ui/diagram.d.ts.map +1 -1
- package/dist/components/ui/diagram.js +134 -52
- package/dist/components/ui/fact-list.d.ts +17 -0
- package/dist/components/ui/fact-list.d.ts.map +1 -1
- package/dist/components/ui/fact-list.js +18 -1
- package/dist/components/ui/prose.d.ts +11 -0
- package/dist/components/ui/prose.d.ts.map +1 -1
- package/dist/components/ui/prose.js +12 -1
- package/dist/components/ui/pulse-graph.d.ts +36 -4
- package/dist/components/ui/pulse-graph.d.ts.map +1 -1
- package/dist/components/ui/pulse-graph.js +174 -60
- package/dist/components/ui/section.d.ts +33 -31
- package/dist/components/ui/section.d.ts.map +1 -1
- package/dist/components/ui/section.js +33 -31
- package/dist/lib/figure.d.ts +149 -0
- package/dist/lib/figure.d.ts.map +1 -0
- package/dist/lib/figure.js +242 -0
- package/dist/live/run-stream-01/run-stream.d.ts.map +1 -1
- package/dist/live/run-stream-01/run-stream.js +2 -1
- package/dist/pages/blog-post-page/index.d.ts +36 -15
- package/dist/pages/blog-post-page/index.d.ts.map +1 -1
- package/dist/pages/blog-post-page/index.js +35 -10
- package/dist/pages/docs-shell/docs-shell.d.ts +120 -18
- package/dist/pages/docs-shell/docs-shell.d.ts.map +1 -1
- package/dist/pages/docs-shell/docs-shell.js +208 -24
- package/dist/pages/error-page/index.d.ts.map +1 -1
- package/dist/pages/error-page/index.js +7 -3
- package/dist/pages/not-found-page/index.d.ts.map +1 -1
- package/dist/pages/not-found-page/index.js +4 -3
- package/dist/styles.css +107 -12
- 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
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
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
|
|
276
|
-
*
|
|
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;
|
|
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
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
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
|
|
76
|
-
*
|
|
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 ? (
|
|
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
|
|
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
|
-
|
|
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
|
|
211
|
-
// named the entry, so
|
|
212
|
-
//
|
|
213
|
-
// with no accessible name is announced as "link" and nothing
|
|
214
|
-
|
|
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
|
|
229
|
-
*
|
|
230
|
-
* in the rail
|
|
231
|
-
*
|
|
232
|
-
*
|
|
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,
|
|
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,
|
|
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;
|
|
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",
|
|
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;
|