shelving 1.236.2 → 1.237.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 (219) hide show
  1. package/extract/IndexExtractor.js +9 -2
  2. package/extract/MergingExtractor.d.ts +3 -1
  3. package/extract/MergingExtractor.js +41 -3
  4. package/extract/TypescriptExtractor.d.ts +1 -0
  5. package/extract/TypescriptExtractor.js +12 -2
  6. package/package.json +1 -1
  7. package/ui/README.md +19 -274
  8. package/ui/app/App.d.ts +1 -0
  9. package/ui/app/App.js +1 -0
  10. package/ui/app/App.md +58 -0
  11. package/ui/app/App.tsx +1 -0
  12. package/ui/block/Card.d.ts +1 -0
  13. package/ui/block/Card.js +1 -0
  14. package/ui/block/Card.md +85 -0
  15. package/ui/block/Card.tsx +1 -0
  16. package/ui/block/Heading.d.ts +1 -0
  17. package/ui/block/Heading.js +1 -0
  18. package/ui/block/Heading.md +70 -0
  19. package/ui/block/Heading.tsx +1 -0
  20. package/ui/block/List.d.ts +1 -0
  21. package/ui/block/List.js +1 -0
  22. package/ui/block/List.md +51 -0
  23. package/ui/block/List.tsx +1 -0
  24. package/ui/block/Panel.d.ts +1 -0
  25. package/ui/block/Panel.js +1 -0
  26. package/ui/block/Panel.md +50 -0
  27. package/ui/block/Panel.tsx +1 -0
  28. package/ui/block/Paragraph.d.ts +1 -0
  29. package/ui/block/Paragraph.js +1 -0
  30. package/ui/block/Paragraph.md +48 -0
  31. package/ui/block/Paragraph.tsx +1 -0
  32. package/ui/block/Prose.d.ts +1 -0
  33. package/ui/block/Prose.js +1 -0
  34. package/ui/block/Prose.md +49 -0
  35. package/ui/block/Prose.tsx +1 -0
  36. package/ui/block/Section.d.ts +6 -0
  37. package/ui/block/Section.js +6 -0
  38. package/ui/block/Section.md +56 -0
  39. package/ui/block/Section.tsx +6 -0
  40. package/ui/block/Subheading.d.ts +1 -0
  41. package/ui/block/Subheading.js +1 -0
  42. package/ui/block/Subheading.md +58 -0
  43. package/ui/block/Subheading.tsx +1 -0
  44. package/ui/block/Table.d.ts +1 -0
  45. package/ui/block/Table.js +1 -0
  46. package/ui/block/Table.md +54 -0
  47. package/ui/block/Table.tsx +1 -0
  48. package/ui/block/Title.d.ts +1 -0
  49. package/ui/block/Title.js +1 -0
  50. package/ui/block/Title.md +57 -0
  51. package/ui/block/Title.tsx +1 -0
  52. package/ui/dialog/Dialog.d.ts +1 -0
  53. package/ui/dialog/Dialog.js +1 -0
  54. package/ui/dialog/Dialog.md +73 -0
  55. package/ui/dialog/Dialog.tsx +1 -0
  56. package/ui/dialog/Modal.d.ts +1 -0
  57. package/ui/dialog/Modal.js +1 -0
  58. package/ui/dialog/Modal.md +40 -0
  59. package/ui/dialog/Modal.tsx +1 -0
  60. package/ui/docs/DocumentationButtons.d.ts +2 -0
  61. package/ui/docs/DocumentationButtons.js +2 -0
  62. package/ui/docs/DocumentationButtons.md +38 -0
  63. package/ui/docs/DocumentationButtons.tsx +2 -0
  64. package/ui/docs/DocumentationCard.d.ts +1 -0
  65. package/ui/docs/DocumentationCard.js +1 -0
  66. package/ui/docs/DocumentationCard.md +35 -0
  67. package/ui/docs/DocumentationCard.tsx +1 -0
  68. package/ui/docs/DocumentationKind.d.ts +1 -1
  69. package/ui/docs/DocumentationKind.js +9 -4
  70. package/ui/docs/DocumentationKind.tsx +10 -5
  71. package/ui/docs/DocumentationPage.d.ts +1 -0
  72. package/ui/docs/DocumentationPage.js +2 -0
  73. package/ui/docs/DocumentationPage.md +46 -0
  74. package/ui/docs/DocumentationPage.tsx +2 -0
  75. package/ui/form/Button.d.ts +1 -0
  76. package/ui/form/Button.js +1 -0
  77. package/ui/form/Button.md +88 -0
  78. package/ui/form/Button.tsx +1 -0
  79. package/ui/form/Field.d.ts +6 -1
  80. package/ui/form/Field.js +6 -1
  81. package/ui/form/Field.md +59 -0
  82. package/ui/form/Field.tsx +6 -1
  83. package/ui/form/Form.d.ts +1 -0
  84. package/ui/form/Form.md +118 -0
  85. package/ui/form/Form.tsx +1 -0
  86. package/ui/form/FormStore.md +47 -0
  87. package/ui/form/SchemaInput.d.ts +1 -0
  88. package/ui/form/SchemaInput.md +64 -0
  89. package/ui/form/SchemaInput.tsx +1 -0
  90. package/ui/inline/Code.d.ts +1 -0
  91. package/ui/inline/Code.js +1 -0
  92. package/ui/inline/Code.md +58 -0
  93. package/ui/inline/Code.tsx +1 -0
  94. package/ui/inline/Link.d.ts +1 -0
  95. package/ui/inline/Link.js +1 -0
  96. package/ui/inline/Link.md +47 -0
  97. package/ui/inline/Link.tsx +1 -0
  98. package/ui/inline/Mark.d.ts +1 -0
  99. package/ui/inline/Mark.js +1 -0
  100. package/ui/inline/Mark.md +40 -0
  101. package/ui/inline/Mark.tsx +1 -0
  102. package/ui/inline/Strong.d.ts +1 -0
  103. package/ui/inline/Strong.js +1 -0
  104. package/ui/inline/Strong.md +34 -0
  105. package/ui/inline/Strong.tsx +1 -0
  106. package/ui/layout/CenteredLayout.d.ts +1 -0
  107. package/ui/layout/CenteredLayout.js +1 -0
  108. package/ui/layout/CenteredLayout.md +38 -0
  109. package/ui/layout/CenteredLayout.tsx +1 -0
  110. package/ui/layout/SidebarLayout.d.ts +1 -0
  111. package/ui/layout/SidebarLayout.js +1 -0
  112. package/ui/layout/SidebarLayout.md +65 -0
  113. package/ui/layout/SidebarLayout.tsx +1 -0
  114. package/ui/menu/Menu.d.ts +2 -0
  115. package/ui/menu/Menu.js +2 -0
  116. package/ui/menu/Menu.md +51 -0
  117. package/ui/menu/Menu.tsx +2 -0
  118. package/ui/menu/MenuItem.md +54 -0
  119. package/ui/misc/Catcher.d.ts +1 -0
  120. package/ui/misc/Catcher.js +1 -0
  121. package/ui/misc/Catcher.md +41 -0
  122. package/ui/misc/Catcher.tsx +1 -0
  123. package/ui/misc/Loading.d.ts +1 -0
  124. package/ui/misc/Loading.js +1 -0
  125. package/ui/misc/Loading.md +28 -0
  126. package/ui/misc/Loading.tsx +1 -0
  127. package/ui/misc/Mapper.md +40 -0
  128. package/ui/misc/Markup.d.ts +1 -0
  129. package/ui/misc/Markup.js +1 -0
  130. package/ui/misc/Markup.md +34 -0
  131. package/ui/misc/Markup.tsx +1 -0
  132. package/ui/misc/StatusIcon.d.ts +1 -0
  133. package/ui/misc/StatusIcon.js +1 -0
  134. package/ui/misc/StatusIcon.md +25 -0
  135. package/ui/misc/StatusIcon.tsx +1 -0
  136. package/ui/misc/Tag.d.ts +1 -0
  137. package/ui/misc/Tag.js +1 -0
  138. package/ui/misc/Tag.md +47 -0
  139. package/ui/misc/Tag.tsx +1 -0
  140. package/ui/notice/Notice.d.ts +1 -0
  141. package/ui/notice/Notice.js +1 -0
  142. package/ui/notice/Notice.md +53 -0
  143. package/ui/notice/Notice.tsx +1 -0
  144. package/ui/notice/Notices.d.ts +1 -0
  145. package/ui/notice/Notices.js +1 -0
  146. package/ui/notice/Notices.md +59 -0
  147. package/ui/notice/Notices.tsx +1 -0
  148. package/ui/page/HTML.d.ts +1 -0
  149. package/ui/page/HTML.js +1 -0
  150. package/ui/page/HTML.md +36 -0
  151. package/ui/page/HTML.tsx +1 -0
  152. package/ui/page/Head.d.ts +1 -0
  153. package/ui/page/Head.js +1 -0
  154. package/ui/page/Head.md +26 -0
  155. package/ui/page/Head.tsx +1 -0
  156. package/ui/page/Page.d.ts +1 -0
  157. package/ui/page/Page.js +1 -0
  158. package/ui/page/Page.md +42 -0
  159. package/ui/page/Page.tsx +1 -0
  160. package/ui/router/Navigation.d.ts +1 -0
  161. package/ui/router/Navigation.js +1 -0
  162. package/ui/router/Navigation.md +41 -0
  163. package/ui/router/Navigation.tsx +1 -0
  164. package/ui/router/NavigationStore.md +34 -0
  165. package/ui/router/Router.d.ts +1 -0
  166. package/ui/router/Router.js +1 -0
  167. package/ui/router/Router.md +143 -0
  168. package/ui/router/Router.tsx +1 -0
  169. package/ui/style/TINT_CLASS.md +55 -0
  170. package/ui/transition/CollapseTransition.d.ts +1 -0
  171. package/ui/transition/CollapseTransition.js +1 -0
  172. package/ui/transition/CollapseTransition.md +34 -0
  173. package/ui/transition/CollapseTransition.tsx +1 -0
  174. package/ui/transition/FadeTransition.d.ts +1 -0
  175. package/ui/transition/FadeTransition.js +1 -0
  176. package/ui/transition/FadeTransition.md +36 -0
  177. package/ui/transition/FadeTransition.tsx +1 -0
  178. package/ui/transition/HorizontalTransition.d.ts +1 -0
  179. package/ui/transition/HorizontalTransition.js +1 -0
  180. package/ui/transition/HorizontalTransition.md +44 -0
  181. package/ui/transition/HorizontalTransition.tsx +1 -0
  182. package/ui/transition/Transition.d.ts +1 -0
  183. package/ui/transition/Transition.js +1 -0
  184. package/ui/transition/Transition.md +70 -0
  185. package/ui/transition/Transition.tsx +1 -0
  186. package/ui/transition/VerticalTransition.d.ts +1 -0
  187. package/ui/transition/VerticalTransition.js +1 -0
  188. package/ui/transition/VerticalTransition.md +34 -0
  189. package/ui/transition/VerticalTransition.tsx +1 -0
  190. package/ui/tree/TreeApp.d.ts +1 -0
  191. package/ui/tree/TreeApp.js +1 -0
  192. package/ui/tree/TreeApp.md +59 -0
  193. package/ui/tree/TreeApp.tsx +1 -0
  194. package/ui/tree/TreeMenu.d.ts +1 -0
  195. package/ui/tree/TreeMenu.js +1 -0
  196. package/ui/tree/TreeMenu.md +35 -0
  197. package/ui/tree/TreeMenu.tsx +1 -0
  198. package/ui/tree/TreeSidebar.d.ts +1 -0
  199. package/ui/tree/TreeSidebar.js +1 -0
  200. package/ui/tree/TreeSidebar.md +24 -0
  201. package/ui/tree/TreeSidebar.tsx +1 -0
  202. package/ui/util/getClass.md +55 -0
  203. package/ui/util/notify.md +50 -0
  204. package/ui/util/requireContext.md +24 -0
  205. package/ui/app/README.md +0 -32
  206. package/ui/block/README.md +0 -144
  207. package/ui/dialog/README.md +0 -80
  208. package/ui/docs/README.md +0 -71
  209. package/ui/form/README.md +0 -165
  210. package/ui/inline/README.md +0 -86
  211. package/ui/layout/README.md +0 -71
  212. package/ui/menu/README.md +0 -33
  213. package/ui/misc/README.md +0 -121
  214. package/ui/notice/README.md +0 -94
  215. package/ui/page/README.md +0 -56
  216. package/ui/router/README.md +0 -186
  217. package/ui/transition/README.md +0 -80
  218. package/ui/tree/README.md +0 -78
  219. package/ui/util/README.md +0 -153
package/ui/page/HTML.tsx CHANGED
@@ -14,6 +14,7 @@ export interface HTMLProps extends PossibleMeta, ChildProps {}
14
14
  * Render the full `<html>` document shell wrapping `<head>` and `<body>`.
15
15
  * - Emits the literal `<head>` with `<base>` and other shell-level metadata; per-page hoistable elements (title, meta, links, stylesheets, scripts) come from `<Head>` inside `<Page>` and are hoisted into this `<head>` by React 19.
16
16
  *
17
+ * @kind component
17
18
  * @param children The document body content.
18
19
  * @param meta Initial meta (language/root/app) merged with the surrounding `<Meta>` context.
19
20
  * @returns The `<html>` document element.
package/ui/page/Head.d.ts CHANGED
@@ -5,6 +5,7 @@ import { type ReactElement } from "react";
5
5
  * - Does not render `<base>` (not hoistable — that lives in `<Head>` in the `<HTML>` shell component).
6
6
  * - Updates `window.history` to match the page URL.
7
7
  *
8
+ * @kind component
8
9
  * @returns The hoistable head elements derived from the current `<Meta>` context.
9
10
  * @example <Page title="Settings"><Head />…</Page>
10
11
  * @see https://dhoulb.github.io/shelving/ui/page/Head/Head
package/ui/page/Head.js CHANGED
@@ -11,6 +11,7 @@ const R_HTTP_EQUIV = /^[A-Z][a-zA-Z0-9]*(-[A-Z][a-zA-Z0-9]*)*$/;
11
11
  * - Does not render `<base>` (not hoistable — that lives in `<Head>` in the `<HTML>` shell component).
12
12
  * - Updates `window.history` to match the page URL.
13
13
  *
14
+ * @kind component
14
15
  * @returns The hoistable head elements derived from the current `<Meta>` context.
15
16
  * @example <Page title="Settings"><Head />…</Page>
16
17
  * @see https://dhoulb.github.io/shelving/ui/page/Head/Head
@@ -0,0 +1,26 @@
1
+ # Head
2
+
3
+ Low-level emitter of hoistable head metadata from the current `Meta` context. It outputs `<title>`, `<meta>`, `<link>`, stylesheet, module, and script elements inline, and React 19 hoists each one into the document `<head>`. It also syncs `window.history` to the page URL.
4
+
5
+ **Things to know:**
6
+
7
+ - [`Page`](/ui/Page) renders `<Head>` automatically — you rarely need it directly.
8
+ - It does not render `<base>`, which is not hoistable; that lives in the [`HTML`](/ui/HTML) shell.
9
+ - The composed title combines the page `title` with the app name from context.
10
+
11
+ ## Usage
12
+
13
+ ```tsx
14
+ import { Page, Head } from "shelving/ui";
15
+
16
+ // <Page> already renders <Head> for you; render it directly only for custom shells.
17
+ <Page title="Settings">
18
+ <Head/>
19
+
20
+ </Page>
21
+ ```
22
+
23
+ ## See also
24
+
25
+ - [`Page`](/ui/Page) — renders `Head` and supplies the per-page meta it reads
26
+ - [`HTML`](/ui/HTML) — owns the literal `<head>` and the non-hoistable `<base>`
package/ui/page/Head.tsx CHANGED
@@ -13,6 +13,7 @@ const R_HTTP_EQUIV = /^[A-Z][a-zA-Z0-9]*(-[A-Z][a-zA-Z0-9]*)*$/;
13
13
  * - Does not render `<base>` (not hoistable — that lives in `<Head>` in the `<HTML>` shell component).
14
14
  * - Updates `window.history` to match the page URL.
15
15
  *
16
+ * @kind component
16
17
  * @returns The hoistable head elements derived from the current `<Meta>` context.
17
18
  * @example <Page title="Settings"><Head />…</Page>
18
19
  * @see https://dhoulb.github.io/shelving/ui/page/Head/Head
package/ui/page/Page.d.ts CHANGED
@@ -13,6 +13,7 @@ export interface PageProps extends PossibleMeta, ChildProps {
13
13
  * - Sets the document title and other head metadata via `<Head>`, which emits hoistable tags inline; React 19 hoists each one into the document `<head>`. `<base>` is not emitted here — it lives in the `<HTML>` shell's `<Head>`.
14
14
  * - Also updates `window.history` to match the page URL.
15
15
  *
16
+ * @kind component
16
17
  * @param children The page content.
17
18
  * @param meta Per-page meta (title, description, etc.) merged with the surrounding `<Meta>` context.
18
19
  * @returns The page element with its meta applied.
package/ui/page/Page.js CHANGED
@@ -6,6 +6,7 @@ import { Head } from "./Head.js";
6
6
  * - Sets the document title and other head metadata via `<Head>`, which emits hoistable tags inline; React 19 hoists each one into the document `<head>`. `<base>` is not emitted here — it lives in the `<HTML>` shell's `<Head>`.
7
7
  * - Also updates `window.history` to match the page URL.
8
8
  *
9
+ * @kind component
9
10
  * @param children The page content.
10
11
  * @param meta Per-page meta (title, description, etc.) merged with the surrounding `<Meta>` context.
11
12
  * @returns The page element with its meta applied.
@@ -0,0 +1,42 @@
1
+ # Page
2
+
3
+ Wraps one page (or screen) inside an app, applying its per-page metadata. It merges `PossibleMeta` props into context and emits hoistable head tags (title, description, meta, links, stylesheets, scripts) that React 19 lifts into the document `<head>`. It also updates `window.history` to match the page URL.
4
+
5
+ **Things to know:**
6
+
7
+ - Accepts `PossibleMeta` props (`app`, `root`, `url`, `title`, `description`, `language`, `tags`, `links`, `stylesheets`, `modules`, `scripts`) and merges them with the surrounding `Meta` context.
8
+ - The page title is composed with the app name from context — `title="User profile"` under `app="My App"` renders `"User profile - My App"`.
9
+ - It renders [`Head`](/ui/Head) inline; React 19 hoists each `<title>`, `<meta>`, `<link>`, and `<script>` into `<head>`, so no portal is needed. `<base>` is the exception — that lives in [`HTML`](/ui/HTML).
10
+
11
+ ## Usage
12
+
13
+ ```tsx
14
+ import { Page, Section } from "shelving/ui";
15
+
16
+ function UserPage({ id }: { id: string }) {
17
+ return (
18
+ <Page title="User profile" url={`/users/${id}`} description="View user details.">
19
+ <Section>…</Section>
20
+ </Page>
21
+ );
22
+ }
23
+ ```
24
+
25
+ Layouts go inside `<Page>`:
26
+
27
+ ```tsx
28
+ import { Page, CenteredLayout } from "shelving/ui";
29
+
30
+ <Page title="Sign in">
31
+ <CenteredLayout>
32
+ <LoginForm/>
33
+ </CenteredLayout>
34
+ </Page>
35
+ ```
36
+
37
+ ## See also
38
+
39
+ - [`HTML`](/ui/HTML) — the document shell that sits above pages
40
+ - [`Head`](/ui/Head) — the hoistable-tag emitter that `Page` renders
41
+ - [`SidebarLayout`](/ui/SidebarLayout) / [`CenteredLayout`](/ui/CenteredLayout) — layouts that live inside a page
42
+ - [`Router`](/ui/Router) — matches URLs to the pages it renders
package/ui/page/Page.tsx CHANGED
@@ -16,6 +16,7 @@ export interface PageProps extends PossibleMeta, ChildProps {}
16
16
  * - Sets the document title and other head metadata via `<Head>`, which emits hoistable tags inline; React 19 hoists each one into the document `<head>`. `<base>` is not emitted here — it lives in the `<HTML>` shell's `<Head>`.
17
17
  * - Also updates `window.history` to match the page URL.
18
18
  *
19
+ * @kind component
19
20
  * @param children The page content.
20
21
  * @param meta Per-page meta (title, description, etc.) merged with the surrounding `<Meta>` context.
21
22
  * @returns The page element with its meta applied.
@@ -19,6 +19,7 @@ export interface NavigationProps extends PossibleMeta, OptionalChildProps {
19
19
  *
20
20
  * TODO: switch click/popstate handling to the browser Navigation API when broadly supported.
21
21
  *
22
+ * @kind component
22
23
  * @param children The app subtree to provide navigation to.
23
24
  * @param meta Initial meta (url/base) merged with the surrounding `<Meta>` context.
24
25
  * @returns The navigation provider wrapping `children`.
@@ -16,6 +16,7 @@ import { NavigationStore } from "./NavigationStore.js";
16
16
  *
17
17
  * TODO: switch click/popstate handling to the browser Navigation API when broadly supported.
18
18
  *
19
+ * @kind component
19
20
  * @param children The app subtree to provide navigation to.
20
21
  * @param meta Initial meta (url/base) merged with the surrounding `<Meta>` context.
21
22
  * @returns The navigation provider wrapping `children`.
@@ -0,0 +1,41 @@
1
+ # Navigation
2
+
3
+ The top-level navigation provider for a client-side app. It owns a single [`NavigationStore`](/ui/NavigationStore), publishes the live URL into the `<Meta>` context so descendant [`Router`](/ui/Router)s re-render on navigation, and wires up browser history. Use exactly one `<Navigation>` per app — nested routers all share its single store.
4
+
5
+ **Things to know:**
6
+
7
+ - Same-origin anchor clicks are intercepted automatically and turned into `forward()` calls. Add a `download` attribute to an anchor to opt out.
8
+ - It listens for `popstate` so the store stays in sync with browser back/forward.
9
+ - It initialises the store from the surrounding `<Meta>` url/base, so set those on an ancestor [`HTML`](/ui/HTML) / [`Page`](/ui/Page).
10
+ - [`Router`](/ui/Router) works with no `<Navigation>` at all (SSR, static rendering, tests) — `<Navigation>` is only what makes the URL *live* on the client.
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { HTML, Navigation, Router } from "shelving/ui";
16
+
17
+ <HTML url={initialUrl} root="https://example.com/">
18
+ <Navigation>
19
+ <Router routes={ROUTES}/>
20
+ </Navigation>
21
+ </HTML>
22
+ ```
23
+
24
+ ### Imperative navigation
25
+
26
+ Read the navigation store from anywhere in the tree with `requireNavigation()` for imperative URL changes:
27
+
28
+ ```tsx
29
+ import { requireNavigation } from "shelving/ui";
30
+
31
+ const nav = requireNavigation();
32
+ nav.forward("/users/123"); // push a new history entry
33
+ nav.redirect("/login"); // replace the current history entry
34
+ ```
35
+
36
+ ## See also
37
+
38
+ - [`Router`](/ui/Router) — matches the URL `<Navigation>` publishes and renders the page
39
+ - [`NavigationStore`](/ui/NavigationStore) — the store `<Navigation>` owns and `requireNavigation()` returns
40
+ - [`HTML`](/ui/HTML) / [`Page`](/ui/Page) — supply the initial url/base meta
41
+ - [`HorizontalTransition`](/ui/HorizontalTransition) — animate the route changes `<Navigation>` triggers
@@ -25,6 +25,7 @@ export interface NavigationProps extends PossibleMeta, OptionalChildProps {}
25
25
  *
26
26
  * TODO: switch click/popstate handling to the browser Navigation API when broadly supported.
27
27
  *
28
+ * @kind component
28
29
  * @param children The app subtree to provide navigation to.
29
30
  * @param meta Initial meta (url/base) merged with the surrounding `<Meta>` context.
30
31
  * @returns The navigation provider wrapping `children`.
@@ -0,0 +1,34 @@
1
+ # NavigationStore
2
+
3
+ The store holding the current navigation URL and driving browser history. It extends `URLStore` — the current location is its `value` — and is owned by [`Navigation`](/ui/Navigation). Reach for it via [`requireNavigation()`](/ui/Navigation) rather than constructing your own.
4
+
5
+ **Things to know:**
6
+
7
+ - `forward()` pushes a new browser history entry; `redirect()` replaces the current one. Both resolve the destination against the store's `base`.
8
+ - It is a [`store`](/store) `URLStore`, so components can subscribe to it for re-renders.
9
+
10
+ ## Usage
11
+
12
+ ```tsx
13
+ import { requireNavigation } from "shelving/ui";
14
+
15
+ function LogoutButton() {
16
+ const nav = requireNavigation();
17
+ return <Button onClick={() => nav.redirect("/login")}>Log out</Button>;
18
+ }
19
+ ```
20
+
21
+ Constructed directly only when wiring navigation by hand (e.g. tests):
22
+
23
+ ```tsx
24
+ import { NavigationStore } from "shelving/ui";
25
+
26
+ const nav = new NavigationStore("/");
27
+ nav.forward("/home");
28
+ ```
29
+
30
+ ## See also
31
+
32
+ - [`Navigation`](/ui/Navigation) — owns the store and exposes it via `requireNavigation()`
33
+ - [`Router`](/ui/Router) — renders the page for whatever URL the store holds
34
+ - [`store`](/store) — the `URLStore` base and subscription model
@@ -23,6 +23,7 @@ export interface RouterProps extends PossibleMeta {
23
23
  * - Route `{placeholders}` are passed as props to function/component route values along with merged URL `?query` params. They are not published into context — descendants of a `ReactElement`-valued route can't see them automatically.
24
24
  * - Returns `null` when there's no URL in context or the URL is outside the base.
25
25
  *
26
+ * @kind component
26
27
  * @param routes The route table to match the current URL against.
27
28
  * @param fallback Element to render when nothing matches; explicit `null` renders nothing instead of throwing.
28
29
  * @param meta Optional meta (url/base) overrides for this router scope.
@@ -12,6 +12,7 @@ import { MetaContext, requireMetaURL } from "../misc/MetaContext.js";
12
12
  * - Route `{placeholders}` are passed as props to function/component route values along with merged URL `?query` params. They are not published into context — descendants of a `ReactElement`-valued route can't see them automatically.
13
13
  * - Returns `null` when there's no URL in context or the URL is outside the base.
14
14
  *
15
+ * @kind component
15
16
  * @param routes The route table to match the current URL against.
16
17
  * @param fallback Element to render when nothing matches; explicit `null` renders nothing instead of throwing.
17
18
  * @param meta Optional meta (url/base) overrides for this router scope.
@@ -0,0 +1,143 @@
1
+ # Router
2
+
3
+ A pure URL matcher: it reads the current URL from the surrounding `<Meta>` context, matches it against a `routes` table, and renders the matched element. It has no client requirements, so it works for SSR, static rendering, and tests with no [`Navigation`](/ui/Navigation) at all — wrap it in [`Navigation`](/ui/Navigation) on the client to get live URL updates.
4
+
5
+ **Things to know:**
6
+
7
+ - Route keys are `AbsolutePath` strings starting with `/`. Placeholders (`{id}`, `:id`, `[id]`, `${id}`, `{{id}}`) are passed to function/component route values as props, merged with URL `?query` params (placeholders win on conflict).
8
+ - `<Router>` accepts `PossibleMeta` props (`url`, `base`, etc.) to override the surrounding context — this is how nested routers scope themselves.
9
+ - With a `base` set, the path used for matching is the URL after `matchURLPrefix` strips the base prefix; URLs outside the base render as `null`.
10
+ - Pass `fallback` to control no-match behaviour. An explicit `null` renders nothing; leaving it `undefined` throws a `NotFoundError`.
11
+
12
+ ## Usage
13
+
14
+ ### Basic setup
15
+
16
+ ```tsx
17
+ import { HTML, Navigation, Router } from "shelving/ui";
18
+
19
+ <HTML url={initialUrl} root="https://example.com/">
20
+ <Navigation>
21
+ <Router routes={{
22
+ "/": HomePage,
23
+ "/users/{id}": UserPage,
24
+ "/about": AboutPage,
25
+ }}/>
26
+ </Navigation>
27
+ </HTML>
28
+ ```
29
+
30
+ ### Route value types
31
+
32
+ | Value | Behaviour |
33
+ |---|---|
34
+ | Component | Rendered as `<Component {...params}/>` with merged placeholder + query props. |
35
+ | `AbsolutePath` string | Redirects to that path (placeholders resolved against the source). |
36
+ | `ReactElement` | Rendered as-is — use for layout wrapping or composing inner routers. |
37
+ | `null` / `false` | Skipped, as if the route were absent — lets a route be conditionally disabled. |
38
+
39
+ ### Placeholder syntax
40
+
41
+ All forms produce the same matched value — pick whichever reads best:
42
+
43
+ | Form | Single segment | Catchall (one+ segments, also matches empty) |
44
+ |---|---|---|
45
+ | Anonymous | `*` (named `"0"`) | `**` / `***` (named `"0"`) |
46
+ | Colon | `:name` | `:name*` / `:name**` |
47
+ | Single brace | `{name}` | `{...name}` / `{name*}` |
48
+ | Square bracket | `[name]` | `[...name]` / `[name*]` |
49
+ | Dollar brace | `${name}` | `${...name}` / `${name*}` |
50
+ | Double brace | `{{name}}` | `{{...name}}` / `{{name*}}` |
51
+
52
+ Modifier chars are tolerant: one-or-more stars and three-or-more dots are all equivalent, so `{path*}`, `{path**}`, `{...path}`, and `{....path}` behave the same. Catchall placeholders allow empty values, so a trailing catchall matches the trailing-slash-absent variant — `/files/{...path}` matches `/files`, `/files/`, and `/files/a/b/c`.
53
+
54
+ ### Layout wrapping
55
+
56
+ Put layout JSX as the route value with another `<Router>` inside. Since `<Router>` reads its URL from `<Meta>`, the inner router sees the same URL.
57
+
58
+ ```tsx
59
+ const SIDEBARRED_ROUTES = {
60
+ "/users": <UsersPage/>,
61
+ "/users/{id}": <UserPage/>,
62
+ "/settings": <SettingsPage/>,
63
+ };
64
+
65
+ <Router routes={{
66
+ "/": <HomePage/>,
67
+ "/{...path}": (
68
+ <SidebarLayout sidebar={<Nav/>}>
69
+ <Router routes={SIDEBARRED_ROUTES}/>
70
+ </SidebarLayout>
71
+ ),
72
+ }}/>
73
+ ```
74
+
75
+ ### Section / microsite pattern
76
+
77
+ A self-contained "section" — its own URL prefix and routes — composes via a catchall plus a function route value that hands the captured sub-path to a nested router as its `url`.
78
+
79
+ ```tsx
80
+ const USER_ROUTES = {
81
+ "/": UsersPage,
82
+ "/{id}": UserPage,
83
+ "/{id}/edit": UserEditPage,
84
+ };
85
+
86
+ export function UserRouter({ path = "/" }: { path?: AbsolutePath }) {
87
+ return <Router routes={USER_ROUTES} url={path}/>;
88
+ }
89
+
90
+ // At the call site — the top-level router stays a flat list of section prefixes:
91
+ <Router routes={{
92
+ "/": <HomePage/>,
93
+ "/users/{...path}": ({ path }) => <UserRouter path={path}/>,
94
+ "/blog/{...path}": ({ path }) => <BlogRouter path={path}/>,
95
+ }}/>
96
+ ```
97
+
98
+ The outer router captures everything under `/users` into `path`; the inner router treats it as its starting URL, so its `"/"` matches the bare `/users` and `/{id}` matches `/users/123`.
99
+
100
+ ### Stacking layouts and sections
101
+
102
+ The two patterns compose — wrap a group of routes in a layout, then route further inside, handing off to section routers via the catchall:
103
+
104
+ ```tsx
105
+ const SIDEBARRED_ROUTES = {
106
+ "/": <Dashboard/>,
107
+ "/users/{...path}": ({ path }) => <UserRouter path={path}/>,
108
+ "/blog/{...path}": ({ path }) => <BlogRouter path={path}/>,
109
+ "/settings": <SettingsPage/>,
110
+ };
111
+
112
+ <Router routes={{
113
+ "/login": <LoginPage/>, // no sidebar
114
+ "/{...path}": ( // everything else wrapped
115
+ <SidebarLayout sidebar={<Nav/>}>
116
+ <Router routes={SIDEBARRED_ROUTES}/>
117
+ </SidebarLayout>
118
+ ),
119
+ }}/>
120
+ ```
121
+
122
+ ### SSR / static rendering
123
+
124
+ `<Router>` re-renders when context changes and needs no client. For static rendering set `url` and `root` on the outer wrapper and skip `<Navigation>`:
125
+
126
+ ```tsx
127
+ renderToString(
128
+ <HTML url={path} root="https://example.com/">
129
+ <Router routes={ROUTES}/>
130
+ </HTML>
131
+ );
132
+ ```
133
+
134
+ ### Base paths
135
+
136
+ `root="https://example.com/app/"` is supported — the base path prefix is stripped from the URL before matching, and URLs that fall outside the base render as `null`.
137
+
138
+ ## See also
139
+
140
+ - [`Navigation`](/ui/Navigation) — publishes a live URL into `<Meta>` for client-side routing
141
+ - [`HTML`](/ui/HTML) / [`Page`](/ui/Page) — supply the `<Meta>` URL the router reads
142
+ - [`SidebarLayout`](/ui/SidebarLayout) / [`CenteredLayout`](/ui/CenteredLayout) — wrap route groups in a shared layout
143
+ - [`HorizontalTransition`](/ui/HorizontalTransition) — animate route changes
@@ -31,6 +31,7 @@ export interface RouterProps extends PossibleMeta {
31
31
  * - Route `{placeholders}` are passed as props to function/component route values along with merged URL `?query` params. They are not published into context — descendants of a `ReactElement`-valued route can't see them automatically.
32
32
  * - Returns `null` when there's no URL in context or the URL is outside the base.
33
33
  *
34
+ * @kind component
34
35
  * @param routes The route table to match the current URL against.
35
36
  * @param fallback Element to render when nothing matches; explicit `null` renders nothing instead of throwing.
36
37
  * @param meta Optional meta (url/base) overrides for this router scope.
@@ -0,0 +1,55 @@
1
+ # TINT_CLASS
2
+
3
+ All colour in the library flows from a single anchor variable, **`--tint-50`**, defined in `Tint.module.css`. From that one hue a 21-step ladder — `--tint-00`, `--tint-05`, … `--tint-95`, `--tint-100` — is computed with `color-mix()` in OKLCH: `--tint-00` is black, `--tint-50` is the anchor hue itself, `--tint-100` is white, and every step in between mixes the anchor toward one extreme or the other.
4
+
5
+ The anchor defaults to `--color-gray`, so the default ladder is a neutral grey ramp — grey is just the colour you get when nothing moves the anchor. The page baseline paints from the extremes: `body { color: var(--tint-00); background: var(--tint-100); }`.
6
+
7
+ ## The recompute trick
8
+
9
+ The ladder is computed at `:root` and *recomputed* under `TINT_CLASS` (the `.tint` class). That recomputation is the whole trick: move the anchor at any scope, apply `TINT_CLASS`, and all 21 shades rebuild from the new hue at that scope. Colour and status classes are exactly that — they only move the anchor:
10
+
11
+ ```css
12
+ /* Color.module.css — a colour variant just moves the anchor. */
13
+ .red {
14
+ --tint-50: var(--color-red);
15
+ }
16
+
17
+ /* Status.module.css — a status maps a semantic name onto a palette colour. */
18
+ .success {
19
+ --tint-50: var(--color-success);
20
+ }
21
+ ```
22
+
23
+ [`getColorClass`](/ui/getColorClass) and [`getStatusClass`](/ui/getStatusClass) compose `TINT_CLASS` automatically, so `<Card color="red">` is: move the anchor to red, rebuild the ladder, and let the card paint from the same steps it always paints from. Descendants inherit the rebuilt ladder, which is why a [`Tag`](/ui/Tag) or [`Preformatted`](/ui/Preformatted) nested in a red card tints to match it.
24
+
25
+ ## How components paint from the ladder
26
+
27
+ Components paint from the ladder by convention:
28
+
29
+ | Step | Used for |
30
+ |---|---|
31
+ | `--tint-00` | Body text, headings — maximum contrast |
32
+ | `--tint-50` | The hue itself — accents, labels, `Tag` backgrounds, `strong` button backgrounds |
33
+ | `--tint-80` | Borders |
34
+ | `--tint-90` | Surfaces — [`Card`](/ui/Card), `Preformatted`, [`Button`](/ui/Button) backgrounds |
35
+ | `--tint-95` | Hover state of those surfaces |
36
+ | `--tint-100` | The page background; text on `--tint-50` backgrounds |
37
+
38
+ Pairings follow contrast: long text reads at `00`-on-`90` or `00`-on-`100`; short labels read at `100`-on-`50`.
39
+
40
+ ## Theming
41
+
42
+ A theme is a CSS file of custom-property overrides at `:root`, imported after the base styles. Work from broadest to narrowest:
43
+
44
+ 1. **Move a palette colour.** Overriding `--color-gray` moves the default anchor, retinting every neutral ladder in the app — the broadest possible change. Overriding `--color-red`, `--color-primary`, etc. re-aims every variant and status that maps to it.
45
+ 2. **Retint one component family.** Set its tint hook: `--card-tint: var(--color-purple)` makes all cards (and their nested content) purple-tinted, with text, border, surface, and hover shades all derived for free.
46
+ 3. **Override one property.** Per-property hooks are the scalpel: `--button-radius: 999px`, `--card-border: none`, `--tag-case: none`.
47
+
48
+ **Don't override individual ladder steps (`--tint-90`, etc.) at `:root`.** The ladder is *recomputed* from the anchor inside every `TINT_CLASS` scope — which includes every component that accepts `color=` or `status=` — so a step override at `:root` only reaches untinted regions and produces inconsistent surfaces. Move the anchor (option 1 or 2) instead, and the steps follow.
49
+
50
+ ## See also
51
+
52
+ - [`getColorClass`](/ui/getColorClass) — moves the anchor to a named palette colour and composes `TINT_CLASS`.
53
+ - [`getStatusClass`](/ui/getStatusClass) — moves the anchor via a semantic status name.
54
+ - [`Card`](/ui/Card) — a painting component whose Styling section shows the per-component tint hook in practice.
55
+ - [`ui`](/ui) — the styling system overview: design tokens, cascade layers, and styling props.
@@ -11,6 +11,7 @@ export interface CollapseTransitionProps extends TransitionProps {
11
11
  /**
12
12
  * Transition that collapses its children in and out by animating their size.
13
13
  *
14
+ * @kind component
14
15
  * @param props Shared transition variant props plus `children`.
15
16
  * @returns A `<Transition>` element configured with the `collapse` class.
16
17
  * @example <CollapseTransition>{visible && <Panel />}</CollapseTransition>
@@ -4,6 +4,7 @@ import { Transition } from "./Transition.js";
4
4
  /**
5
5
  * Transition that collapses its children in and out by animating their size.
6
6
  *
7
+ * @kind component
7
8
  * @param props Shared transition variant props plus `children`.
8
9
  * @returns A `<Transition>` element configured with the `collapse` class.
9
10
  * @example <CollapseTransition>{visible && <Panel />}</CollapseTransition>
@@ -0,0 +1,34 @@
1
+ # CollapseTransition
2
+
3
+ A [`Transition`](/ui/Transition) preset that collapses its children in and out by animating their size, clipping the overflow during the animation.
4
+
5
+ **Things to know:**
6
+
7
+ - Uses the `collapse` transition class — there is no direction-aware variant, so forward and back animate identically.
8
+ - Pass `overlay` to raise the transition group above surrounding content during the animation (`z-index: 100`).
9
+
10
+ ## Usage
11
+
12
+ ```tsx
13
+ import { CollapseTransition } from "shelving/ui";
14
+
15
+ function Details({ open }: { open: boolean }) {
16
+ return open ? (
17
+ <CollapseTransition>
18
+ <Panel>…</Panel>
19
+ </CollapseTransition>
20
+ ) : null;
21
+ }
22
+ ```
23
+
24
+ ## Styling
25
+
26
+ This preset exposes no own `--collapse-transition-*` hooks. Its `CollapseTransition.css` only sets `overflow: hidden` on `::view-transition-image-pair(.collapse)` so the collapsing content is clipped.
27
+
28
+ **Global tokens it reads** — none.
29
+
30
+ ## See also
31
+
32
+ - [`Transition`](/ui/Transition) — the base wrapper and `overlay` variant
33
+ - [`FadeTransition`](/ui/FadeTransition) — opacity-based alternative
34
+ - [`VerticalTransition`](/ui/VerticalTransition) / [`HorizontalTransition`](/ui/HorizontalTransition) — direction-aware slides
@@ -12,6 +12,7 @@ export interface CollapseTransitionProps extends TransitionProps {}
12
12
  /**
13
13
  * Transition that collapses its children in and out by animating their size.
14
14
  *
15
+ * @kind component
15
16
  * @param props Shared transition variant props plus `children`.
16
17
  * @returns A `<Transition>` element configured with the `collapse` class.
17
18
  * @example <CollapseTransition>{visible && <Panel />}</CollapseTransition>
@@ -10,6 +10,7 @@ export interface FadeTransitionProps extends TransitionProps {
10
10
  /**
11
11
  * Transition that fades its children in and out by animating their opacity.
12
12
  *
13
+ * @kind component
13
14
  * @param props Shared transition variant props plus `children`.
14
15
  * @returns A `<Transition>` element configured with the `fade` class.
15
16
  * @example <FadeTransition>{visible && <Toast />}</FadeTransition>
@@ -3,6 +3,7 @@ import { Transition } from "./Transition.js";
3
3
  /**
4
4
  * Transition that fades its children in and out by animating their opacity.
5
5
  *
6
+ * @kind component
6
7
  * @param props Shared transition variant props plus `children`.
7
8
  * @returns A `<Transition>` element configured with the `fade` class.
8
9
  * @example <FadeTransition>{visible && <Toast />}</FadeTransition>
@@ -0,0 +1,36 @@
1
+ # FadeTransition
2
+
3
+ A [`Transition`](/ui/Transition) preset that fades its children in and out by animating opacity. Wrap any content that should animate when it mounts or unmounts.
4
+
5
+ **Things to know:**
6
+
7
+ - Uses the `fade` transition class — there is no direction-aware variant, so forward and back animate identically.
8
+ - Pass `overlay` to raise the transition group above surrounding content during the animation (`z-index: 100`).
9
+
10
+ ## Usage
11
+
12
+ ```tsx
13
+ import { FadeTransition } from "shelving/ui";
14
+
15
+ function Panel({ visible }: { visible: boolean }) {
16
+ return visible ? (
17
+ <FadeTransition>
18
+ <div className="panel">…</div>
19
+ </FadeTransition>
20
+ ) : null;
21
+ }
22
+ ```
23
+
24
+ ## Styling
25
+
26
+ | Variable | Styles | Default |
27
+ |---|---|---|
28
+ | `--fade-transition-duration` | Duration of the fade-in and fade-out keyframes | `var(--duration-fast)` |
29
+
30
+ **Global tokens it reads** — `--duration-fast`.
31
+
32
+ ## See also
33
+
34
+ - [`Transition`](/ui/Transition) — the base wrapper and `overlay` variant
35
+ - [`CollapseTransition`](/ui/CollapseTransition) — size-based alternative
36
+ - [`VerticalTransition`](/ui/VerticalTransition) / [`HorizontalTransition`](/ui/HorizontalTransition) — direction-aware slides
@@ -11,6 +11,7 @@ export interface FadeTransitionProps extends TransitionProps {}
11
11
  /**
12
12
  * Transition that fades its children in and out by animating their opacity.
13
13
  *
14
+ * @kind component
14
15
  * @param props Shared transition variant props plus `children`.
15
16
  * @returns A `<Transition>` element configured with the `fade` class.
16
17
  * @example <FadeTransition>{visible && <Toast />}</FadeTransition>
@@ -11,6 +11,7 @@ export interface HorizontalTransitionProps extends TransitionProps {
11
11
  /**
12
12
  * Transition that slides its children horizontally — right when moving forward, left when moving back.
13
13
  *
14
+ * @kind component
14
15
  * @param props Shared transition variant props plus `children`.
15
16
  * @returns A `<Transition>` element configured with the horizontal slide classes.
16
17
  * @example <HorizontalTransition>{currentStep}</HorizontalTransition>
@@ -4,6 +4,7 @@ import { Transition } from "./Transition.js";
4
4
  /**
5
5
  * Transition that slides its children horizontally — right when moving forward, left when moving back.
6
6
  *
7
+ * @kind component
7
8
  * @param props Shared transition variant props plus `children`.
8
9
  * @returns A `<Transition>` element configured with the horizontal slide classes.
9
10
  * @example <HorizontalTransition>{currentStep}</HorizontalTransition>