shelving 1.236.2 → 1.238.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/extract/IndexExtractor.js +9 -2
- package/extract/MergingExtractor.d.ts +3 -1
- package/extract/MergingExtractor.js +41 -3
- package/extract/TypescriptExtractor.d.ts +1 -0
- package/extract/TypescriptExtractor.js +12 -2
- package/package.json +1 -1
- package/ui/README.md +35 -267
- package/ui/app/App.d.ts +14 -2
- package/ui/app/App.js +16 -2
- package/ui/app/App.md +58 -0
- package/ui/app/App.tsx +16 -2
- package/ui/block/Address.module.css +2 -1
- package/ui/block/Block.module.css +1 -1
- package/ui/block/Blockquote.module.css +5 -1
- package/ui/block/Caption.module.css +4 -1
- package/ui/block/Card.d.ts +1 -0
- package/ui/block/Card.js +1 -0
- package/ui/block/Card.md +85 -0
- package/ui/block/Card.module.css +7 -1
- package/ui/block/Card.tsx +1 -0
- package/ui/block/Definitions.module.css +6 -1
- package/ui/block/Divider.module.css +4 -1
- package/ui/block/Heading.d.ts +1 -0
- package/ui/block/Heading.js +1 -0
- package/ui/block/Heading.md +70 -0
- package/ui/block/Heading.module.css +5 -1
- package/ui/block/Heading.tsx +1 -0
- package/ui/block/Image.module.css +2 -1
- package/ui/block/Label.module.css +6 -1
- package/ui/block/List.d.ts +1 -0
- package/ui/block/List.js +1 -0
- package/ui/block/List.md +51 -0
- package/ui/block/List.module.css +2 -1
- package/ui/block/List.tsx +1 -0
- package/ui/block/Panel.d.ts +1 -0
- package/ui/block/Panel.js +1 -0
- package/ui/block/Panel.md +50 -0
- package/ui/block/Panel.module.css +3 -1
- package/ui/block/Panel.tsx +1 -0
- package/ui/block/Paragraph.d.ts +1 -0
- package/ui/block/Paragraph.js +1 -0
- package/ui/block/Paragraph.md +48 -0
- package/ui/block/Paragraph.module.css +2 -1
- package/ui/block/Paragraph.tsx +1 -0
- package/ui/block/Preformatted.module.css +10 -1
- package/ui/block/Prose.d.ts +1 -0
- package/ui/block/Prose.js +1 -0
- package/ui/block/Prose.md +49 -0
- package/ui/block/Prose.module.css +2 -1
- package/ui/block/Prose.tsx +1 -0
- package/ui/block/Section.d.ts +6 -0
- package/ui/block/Section.js +6 -0
- package/ui/block/Section.md +56 -0
- package/ui/block/Section.module.css +2 -1
- package/ui/block/Section.tsx +6 -0
- package/ui/block/Subheading.d.ts +1 -0
- package/ui/block/Subheading.js +1 -0
- package/ui/block/Subheading.md +58 -0
- package/ui/block/Subheading.module.css +5 -1
- package/ui/block/Subheading.tsx +1 -0
- package/ui/block/Table.d.ts +1 -0
- package/ui/block/Table.js +1 -0
- package/ui/block/Table.md +54 -0
- package/ui/block/Table.module.css +7 -1
- package/ui/block/Table.tsx +1 -0
- package/ui/block/Title.d.ts +1 -0
- package/ui/block/Title.js +1 -0
- package/ui/block/Title.md +57 -0
- package/ui/block/Title.module.css +5 -1
- package/ui/block/Title.tsx +1 -0
- package/ui/block/Video.module.css +7 -1
- package/ui/dialog/Dialog.d.ts +1 -0
- package/ui/dialog/Dialog.js +1 -0
- package/ui/dialog/Dialog.md +73 -0
- package/ui/dialog/Dialog.module.css +4 -1
- package/ui/dialog/Dialog.tsx +1 -0
- package/ui/dialog/Modal.d.ts +1 -0
- package/ui/dialog/Modal.js +1 -0
- package/ui/dialog/Modal.md +40 -0
- package/ui/dialog/Modal.module.css +8 -1
- package/ui/dialog/Modal.tsx +1 -0
- package/ui/docs/DocumentationButtons.d.ts +2 -0
- package/ui/docs/DocumentationButtons.js +2 -0
- package/ui/docs/DocumentationButtons.md +38 -0
- package/ui/docs/DocumentationButtons.tsx +2 -0
- package/ui/docs/DocumentationCard.d.ts +1 -0
- package/ui/docs/DocumentationCard.js +1 -0
- package/ui/docs/DocumentationCard.md +35 -0
- package/ui/docs/DocumentationCard.tsx +1 -0
- package/ui/docs/DocumentationKind.d.ts +1 -1
- package/ui/docs/DocumentationKind.js +9 -4
- package/ui/docs/DocumentationKind.tsx +10 -5
- package/ui/docs/DocumentationPage.d.ts +1 -0
- package/ui/docs/DocumentationPage.js +2 -0
- package/ui/docs/DocumentationPage.md +46 -0
- package/ui/docs/DocumentationPage.tsx +2 -0
- package/ui/form/Button.d.ts +1 -0
- package/ui/form/Button.js +1 -0
- package/ui/form/Button.md +88 -0
- package/ui/form/Button.module.css +10 -1
- package/ui/form/Button.tsx +1 -0
- package/ui/form/Field.d.ts +6 -1
- package/ui/form/Field.js +6 -1
- package/ui/form/Field.md +59 -0
- package/ui/form/Field.module.css +6 -1
- package/ui/form/Field.tsx +6 -1
- package/ui/form/Form.d.ts +1 -0
- package/ui/form/Form.md +118 -0
- package/ui/form/Form.module.css +2 -1
- package/ui/form/Form.tsx +1 -0
- package/ui/form/FormStore.md +47 -0
- package/ui/form/Input.module.css +10 -2
- package/ui/form/Popover.module.css +6 -1
- package/ui/form/Progress.module.css +4 -1
- package/ui/form/SchemaInput.d.ts +1 -0
- package/ui/form/SchemaInput.md +64 -0
- package/ui/form/SchemaInput.tsx +1 -0
- package/ui/inline/Code.d.ts +1 -0
- package/ui/inline/Code.js +1 -0
- package/ui/inline/Code.md +58 -0
- package/ui/inline/Code.module.css +7 -1
- package/ui/inline/Code.tsx +1 -0
- package/ui/inline/Deleted.module.css +4 -1
- package/ui/inline/Emphasis.module.css +1 -1
- package/ui/inline/Inserted.module.css +4 -1
- package/ui/inline/Link.d.ts +1 -0
- package/ui/inline/Link.js +1 -0
- package/ui/inline/Link.md +47 -0
- package/ui/inline/Link.module.css +4 -2
- package/ui/inline/Link.tsx +1 -0
- package/ui/inline/Mark.d.ts +1 -0
- package/ui/inline/Mark.js +1 -0
- package/ui/inline/Mark.md +40 -0
- package/ui/inline/Mark.module.css +3 -1
- package/ui/inline/Mark.tsx +1 -0
- package/ui/inline/Small.module.css +2 -1
- package/ui/inline/Strong.d.ts +1 -0
- package/ui/inline/Strong.js +1 -0
- package/ui/inline/Strong.md +34 -0
- package/ui/inline/Strong.module.css +2 -1
- package/ui/inline/Strong.tsx +1 -0
- package/ui/inline/Subscript.module.css +1 -1
- package/ui/inline/Superscript.module.css +1 -1
- package/ui/layout/CenteredLayout.d.ts +1 -0
- package/ui/layout/CenteredLayout.js +1 -0
- package/ui/layout/CenteredLayout.md +38 -0
- package/ui/layout/CenteredLayout.module.css +2 -1
- package/ui/layout/CenteredLayout.tsx +1 -0
- package/ui/layout/Layout.module.css +4 -2
- package/ui/layout/SidebarLayout.d.ts +1 -0
- package/ui/layout/SidebarLayout.js +1 -0
- package/ui/layout/SidebarLayout.md +65 -0
- package/ui/layout/SidebarLayout.module.css +5 -1
- package/ui/layout/SidebarLayout.tsx +1 -0
- package/ui/menu/Menu.d.ts +2 -0
- package/ui/menu/Menu.js +2 -0
- package/ui/menu/Menu.md +51 -0
- package/ui/menu/Menu.module.css +9 -1
- package/ui/menu/Menu.tsx +2 -0
- package/ui/menu/MenuItem.md +54 -0
- package/ui/misc/Catcher.d.ts +1 -0
- package/ui/misc/Catcher.js +1 -0
- package/ui/misc/Catcher.md +41 -0
- package/ui/misc/Catcher.tsx +1 -0
- package/ui/misc/Loading.d.ts +1 -0
- package/ui/misc/Loading.js +1 -0
- package/ui/misc/Loading.md +28 -0
- package/ui/misc/Loading.module.css +3 -1
- package/ui/misc/Loading.tsx +1 -0
- package/ui/misc/Mapper.md +40 -0
- package/ui/misc/Markup.d.ts +1 -0
- package/ui/misc/Markup.js +1 -0
- package/ui/misc/Markup.md +34 -0
- package/ui/misc/Markup.tsx +1 -0
- package/ui/misc/StatusIcon.d.ts +1 -0
- package/ui/misc/StatusIcon.js +1 -0
- package/ui/misc/StatusIcon.md +25 -0
- package/ui/misc/StatusIcon.module.css +2 -1
- package/ui/misc/StatusIcon.tsx +1 -0
- package/ui/misc/Tag.d.ts +1 -0
- package/ui/misc/Tag.js +1 -0
- package/ui/misc/Tag.md +47 -0
- package/ui/misc/Tag.module.css +9 -1
- package/ui/misc/Tag.tsx +1 -0
- package/ui/notice/Message.module.css +3 -1
- package/ui/notice/Notice.d.ts +1 -0
- package/ui/notice/Notice.js +1 -0
- package/ui/notice/Notice.md +53 -0
- package/ui/notice/Notice.module.css +7 -1
- package/ui/notice/Notice.tsx +1 -0
- package/ui/notice/Notices.d.ts +1 -0
- package/ui/notice/Notices.js +1 -0
- package/ui/notice/Notices.md +59 -0
- package/ui/notice/Notices.module.css +3 -1
- package/ui/notice/Notices.tsx +1 -0
- package/ui/page/HTML.d.ts +1 -0
- package/ui/page/HTML.js +1 -0
- package/ui/page/HTML.md +36 -0
- package/ui/page/HTML.tsx +1 -0
- package/ui/page/Head.d.ts +1 -0
- package/ui/page/Head.js +1 -0
- package/ui/page/Head.md +26 -0
- package/ui/page/Head.tsx +1 -0
- package/ui/page/Page.d.ts +1 -0
- package/ui/page/Page.js +1 -0
- package/ui/page/Page.md +42 -0
- package/ui/page/Page.tsx +1 -0
- package/ui/router/Navigation.d.ts +1 -0
- package/ui/router/Navigation.js +1 -0
- package/ui/router/Navigation.md +41 -0
- package/ui/router/Navigation.tsx +1 -0
- package/ui/router/NavigationStore.md +34 -0
- package/ui/router/Router.d.ts +1 -0
- package/ui/router/Router.js +1 -0
- package/ui/router/Router.md +143 -0
- package/ui/router/Router.tsx +1 -0
- package/ui/style/Color.module.css +28 -1
- package/ui/style/Duration.d.ts +24 -0
- package/ui/style/Duration.js +13 -0
- package/ui/style/Duration.module.css +23 -0
- package/ui/style/Duration.tsx +31 -0
- package/ui/style/Flex.module.css +3 -1
- package/ui/style/Font.d.ts +24 -0
- package/ui/style/Font.js +13 -0
- package/ui/style/Font.module.css +50 -0
- package/ui/style/Font.tsx +31 -0
- package/ui/style/Gap.module.css +2 -1
- package/ui/style/Padding.module.css +2 -1
- package/ui/style/Radius.d.ts +24 -0
- package/ui/style/Radius.js +13 -0
- package/ui/style/Radius.module.css +43 -0
- package/ui/style/Radius.tsx +31 -0
- package/ui/style/Scroll.module.css +4 -0
- package/ui/style/Shadow.d.ts +24 -0
- package/ui/style/Shadow.js +13 -0
- package/ui/style/Shadow.module.css +42 -0
- package/ui/style/Shadow.tsx +31 -0
- package/ui/style/Size.d.ts +24 -0
- package/ui/style/Size.js +13 -0
- package/ui/style/Size.module.css +69 -0
- package/ui/style/Size.tsx +31 -0
- package/ui/style/Space.module.css +19 -1
- package/ui/style/Status.module.css +2 -1
- package/ui/style/Stroke.d.ts +24 -0
- package/ui/style/Stroke.js +13 -0
- package/ui/style/Stroke.module.css +26 -0
- package/ui/style/Stroke.tsx +31 -0
- package/ui/style/TINT_CLASS.md +55 -0
- package/ui/style/Tint.module.css +4 -1
- package/ui/style/Typography.d.ts +10 -26
- package/ui/style/Typography.js +8 -3
- package/ui/style/Typography.module.css +2 -66
- package/ui/style/Typography.tsx +17 -31
- package/ui/style/Weight.d.ts +24 -0
- package/ui/style/Weight.js +13 -0
- package/ui/style/Weight.module.css +42 -0
- package/ui/style/Weight.tsx +31 -0
- package/ui/style/Width.module.css +7 -1
- package/ui/style/getColorClass.md +47 -0
- package/ui/style/getDurationClass.md +18 -0
- package/ui/style/getFontClass.md +39 -0
- package/ui/style/getRadiusClass.md +24 -0
- package/ui/style/getShadowClass.md +23 -0
- package/ui/style/getSizeClass.md +42 -0
- package/ui/style/getSpaceClass.md +35 -0
- package/ui/style/getStrokeClass.md +20 -0
- package/ui/style/getWeightClass.md +25 -0
- package/ui/style/getWidthClass.md +18 -0
- package/ui/style/index.d.ts +7 -0
- package/ui/style/index.js +7 -0
- package/ui/style/index.tsx +7 -0
- package/ui/style/layers.css +26 -0
- package/ui/transition/CollapseTransition.d.ts +1 -0
- package/ui/transition/CollapseTransition.js +1 -0
- package/ui/transition/CollapseTransition.md +34 -0
- package/ui/transition/CollapseTransition.tsx +1 -0
- package/ui/transition/FadeTransition.d.ts +1 -0
- package/ui/transition/FadeTransition.js +1 -0
- package/ui/transition/FadeTransition.md +36 -0
- package/ui/transition/FadeTransition.tsx +1 -0
- package/ui/transition/HorizontalTransition.d.ts +1 -0
- package/ui/transition/HorizontalTransition.js +1 -0
- package/ui/transition/HorizontalTransition.md +44 -0
- package/ui/transition/HorizontalTransition.tsx +1 -0
- package/ui/transition/Transition.d.ts +1 -0
- package/ui/transition/Transition.js +1 -0
- package/ui/transition/Transition.md +70 -0
- package/ui/transition/Transition.tsx +1 -0
- package/ui/transition/VerticalTransition.d.ts +1 -0
- package/ui/transition/VerticalTransition.js +1 -0
- package/ui/transition/VerticalTransition.md +34 -0
- package/ui/transition/VerticalTransition.tsx +1 -0
- package/ui/tree/TreeApp.d.ts +1 -0
- package/ui/tree/TreeApp.js +1 -0
- package/ui/tree/TreeApp.md +59 -0
- package/ui/tree/TreeApp.tsx +1 -0
- package/ui/tree/TreeMenu.d.ts +1 -0
- package/ui/tree/TreeMenu.js +1 -0
- package/ui/tree/TreeMenu.md +35 -0
- package/ui/tree/TreeMenu.tsx +1 -0
- package/ui/tree/TreeSidebar.d.ts +1 -0
- package/ui/tree/TreeSidebar.js +1 -0
- package/ui/tree/TreeSidebar.md +24 -0
- package/ui/tree/TreeSidebar.tsx +1 -0
- package/ui/util/getClass.md +55 -0
- package/ui/util/notify.md +50 -0
- package/ui/util/requireContext.md +24 -0
- package/ui/app/README.md +0 -32
- package/ui/block/README.md +0 -144
- package/ui/dialog/README.md +0 -80
- package/ui/docs/README.md +0 -71
- package/ui/form/README.md +0 -165
- package/ui/inline/README.md +0 -86
- package/ui/layout/README.md +0 -71
- package/ui/menu/README.md +0 -33
- package/ui/misc/README.md +0 -121
- package/ui/notice/README.md +0 -94
- package/ui/page/README.md +0 -56
- package/ui/router/README.md +0 -186
- package/ui/style/base.css +0 -161
- package/ui/transition/README.md +0 -80
- package/ui/tree/README.md +0 -78
- package/ui/util/README.md +0 -153
|
@@ -62,8 +62,15 @@ function _absorbIndex(element, index) {
|
|
|
62
62
|
// Recurse first so nested levels absorb their own indexes before we look at this one.
|
|
63
63
|
// Only descend into elements that actually have children — leaving childless leaves (e.g. files) untouched, including their `undefined` children.
|
|
64
64
|
const recursed = Array.from(walkElements(element.props.children)).map(child => notNullish(child.props.children) ? _absorbIndex(child, index) : child);
|
|
65
|
-
// Find the index child by
|
|
66
|
-
|
|
65
|
+
// Find the index child by pattern priority: the earliest `index` pattern with a matching child wins, so a
|
|
66
|
+
// README.md is preferred over an index.ts barrel even when the barrel is listed first on disk (a barrel carries
|
|
67
|
+
// no prose, so absorbing it instead of the README would silently drop the directory's documentation).
|
|
68
|
+
let indexChild;
|
|
69
|
+
for (const matcher of index) {
|
|
70
|
+
indexChild = recursed.find(child => anyMatch(child.key, matcher));
|
|
71
|
+
if (indexChild)
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
67
74
|
if (!indexChild)
|
|
68
75
|
return { ...element, props: { ...element.props, children: recursed } };
|
|
69
76
|
// Fold the index child into the parent, and drop it from children.
|
|
@@ -20,9 +20,11 @@ export interface MergingExtractorOptions {
|
|
|
20
20
|
/**
|
|
21
21
|
* Through extractor that walks a tree of `tree-element` nodes and merges sibling tree elements whose keys match a `merges` template pair.
|
|
22
22
|
* - Purely key-based: it doesn't care whether siblings are directories or files — any element with children is processed, at every level.
|
|
23
|
+
* - **Token-first:** a secondary (e.g. `Card.md`) is preferentially folded into the same-named documentation token declared by a sibling file (the `Card` symbol inside `Card.tsx`), not the file element. Exported names are unique across the package, so this is unambiguous — and it lands the prose on the published symbol page, which survives the module flatten (file-element content does not).
|
|
24
|
+
* - **File fallback:** when no same-named token exists, the secondary folds into a sibling *file* whose key matches a `merges` candidate (whole-file prose like `template.md` → `template.ts`).
|
|
23
25
|
* - The primary (winning) element keeps its `key`, `source`, and `type`; the secondary's `title`, `description`,
|
|
24
26
|
* `content`, and `children` are folded in via `mergeTreeElements()`.
|
|
25
|
-
* - A secondary with no matching
|
|
27
|
+
* - A secondary with no matching token or file is left in place — pure prose files (e.g. `concepts.md` with no `concepts.ts`) stand alone.
|
|
26
28
|
*
|
|
27
29
|
* @example
|
|
28
30
|
* ```ts
|
|
@@ -15,9 +15,11 @@ const DEFAULT_MERGES = {
|
|
|
15
15
|
/**
|
|
16
16
|
* Through extractor that walks a tree of `tree-element` nodes and merges sibling tree elements whose keys match a `merges` template pair.
|
|
17
17
|
* - Purely key-based: it doesn't care whether siblings are directories or files — any element with children is processed, at every level.
|
|
18
|
+
* - **Token-first:** a secondary (e.g. `Card.md`) is preferentially folded into the same-named documentation token declared by a sibling file (the `Card` symbol inside `Card.tsx`), not the file element. Exported names are unique across the package, so this is unambiguous — and it lands the prose on the published symbol page, which survives the module flatten (file-element content does not).
|
|
19
|
+
* - **File fallback:** when no same-named token exists, the secondary folds into a sibling *file* whose key matches a `merges` candidate (whole-file prose like `template.md` → `template.ts`).
|
|
18
20
|
* - The primary (winning) element keeps its `key`, `source`, and `type`; the secondary's `title`, `description`,
|
|
19
21
|
* `content`, and `children` are folded in via `mergeTreeElements()`.
|
|
20
|
-
* - A secondary with no matching
|
|
22
|
+
* - A secondary with no matching token or file is left in place — pure prose files (e.g. `concepts.md` with no `concepts.ts`) stand alone.
|
|
21
23
|
*
|
|
22
24
|
* @example
|
|
23
25
|
* ```ts
|
|
@@ -70,10 +72,24 @@ function _mergeElement(element, merges) {
|
|
|
70
72
|
}
|
|
71
73
|
/** Merge same-template siblings at one directory level. */
|
|
72
74
|
function _mergeChildren(children, merges) {
|
|
73
|
-
// Index children by key so we can look up primary candidates quickly.
|
|
75
|
+
// Index children by key so we can look up primary file candidates quickly.
|
|
74
76
|
const byKey = new Map();
|
|
75
77
|
for (const child of children)
|
|
76
78
|
byKey.set(child.key, child);
|
|
79
|
+
// Index exported documentation tokens by name, recording the key of the file element that declares each.
|
|
80
|
+
// Exported names are unique across the package, so a `Foo.md` can target the symbol `Foo` directly.
|
|
81
|
+
const tokensByName = new Map();
|
|
82
|
+
for (const child of children) {
|
|
83
|
+
if (child.type !== "tree-element")
|
|
84
|
+
continue;
|
|
85
|
+
for (const token of walkElements(child.props.children)) {
|
|
86
|
+
const t = token;
|
|
87
|
+
if (t.type === "tree-documentation")
|
|
88
|
+
tokensByName.set(t.props.name, { fileKey: child.key, token: t });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Accumulate token replacements per declaring file: fileKey → (tokenKey → merged token).
|
|
92
|
+
const tokenUpdates = new Map();
|
|
77
93
|
// Walk in original order, deciding for each whether it's a secondary that should fold into a primary.
|
|
78
94
|
const skip = new Set();
|
|
79
95
|
for (const secondary of children) {
|
|
@@ -81,6 +97,18 @@ function _mergeChildren(children, merges) {
|
|
|
81
97
|
const matches = matchTemplate(lhs, secondary.key);
|
|
82
98
|
if (!matches)
|
|
83
99
|
continue;
|
|
100
|
+
// Prefer folding a `.md` into the same-named documentation token (e.g. `Card.md` → the `Card` component) so the
|
|
101
|
+
// prose lands on the published symbol page — the module flatten keeps tokens but discards file-element content.
|
|
102
|
+
const base = Object.values(matches)[0];
|
|
103
|
+
const hit = base ? tokensByName.get(base) : undefined;
|
|
104
|
+
if (hit && hit.token !== secondary) {
|
|
105
|
+
const fileMap = tokenUpdates.get(hit.fileKey) ?? new Map();
|
|
106
|
+
fileMap.set(hit.token.key, mergeTreeElements(fileMap.get(hit.token.key) ?? hit.token, secondary));
|
|
107
|
+
tokenUpdates.set(hit.fileKey, fileMap);
|
|
108
|
+
skip.add(secondary);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
// Otherwise fall back to merging into a sibling file (whole-file prose like `template.md` → `template.ts`).
|
|
84
112
|
for (const rhs of candidates) {
|
|
85
113
|
const primaryKey = renderTemplate(rhs, matches);
|
|
86
114
|
const primary = byKey.get(primaryKey);
|
|
@@ -94,5 +122,15 @@ function _mergeChildren(children, merges) {
|
|
|
94
122
|
break;
|
|
95
123
|
}
|
|
96
124
|
}
|
|
97
|
-
|
|
125
|
+
// Rebuild: drop merged `.md` secondaries, apply file-level merges, and fold token updates into their file elements.
|
|
126
|
+
return children
|
|
127
|
+
.filter(c => !skip.has(c))
|
|
128
|
+
.map(c => {
|
|
129
|
+
const updated = byKey.get(c.key) ?? c;
|
|
130
|
+
const fileMap = tokenUpdates.get(c.key);
|
|
131
|
+
if (!fileMap)
|
|
132
|
+
return updated;
|
|
133
|
+
const merged = Array.from(walkElements(updated.props.children), child => fileMap.get(child.key) ?? child);
|
|
134
|
+
return { ...updated, props: { ...updated.props, children: merged } };
|
|
135
|
+
});
|
|
98
136
|
}
|
|
@@ -6,6 +6,7 @@ import { FileExtractor } from "./FileExtractor.js";
|
|
|
6
6
|
* - Extracts exported, public, non-`_`-prefixed declarations as `tree-documentation` children.
|
|
7
7
|
* - Overloaded declarations sharing a name are merged into a single `tree-documentation` with multiple `signatures`.
|
|
8
8
|
* - Class declarations synthesise their `signatures`, `params`, and `returns` from the constructor — `new ClassName<…>(…)` including generics, one signature per constructor overload, with `returns` set to the class type. Param descriptions come from the constructor's `@param` first, then the class's `@param`.
|
|
9
|
+
* - A `@kind` tag in a symbol's JSDoc overrides the inferred kind — e.g. `@kind component` relabels a React component (otherwise a `function`) so the docs site groups and colours it as a component. The override also drops the trailing `()` from the title, since a non-function kind reads as a bare name.
|
|
9
10
|
* - Top-of-file JSDoc comment becomes the file's `content`.
|
|
10
11
|
* - Sets `description` (a plain-text summary from the first JSDoc paragraph) on the file and every `tree-documentation` child.
|
|
11
12
|
* - Sets `title` on every `tree-documentation` child — `name()` for functions and methods, bare `name` for other kinds. Parent class context comes from the `class` prop ("member of …" affordance), never the title.
|
|
@@ -7,6 +7,7 @@ import { extractMarkdownProps } from "./MarkupExtractor.js";
|
|
|
7
7
|
* - Extracts exported, public, non-`_`-prefixed declarations as `tree-documentation` children.
|
|
8
8
|
* - Overloaded declarations sharing a name are merged into a single `tree-documentation` with multiple `signatures`.
|
|
9
9
|
* - Class declarations synthesise their `signatures`, `params`, and `returns` from the constructor — `new ClassName<…>(…)` including generics, one signature per constructor overload, with `returns` set to the class type. Param descriptions come from the constructor's `@param` first, then the class's `@param`.
|
|
10
|
+
* - A `@kind` tag in a symbol's JSDoc overrides the inferred kind — e.g. `@kind component` relabels a React component (otherwise a `function`) so the docs site groups and colours it as a component. The override also drops the trailing `()` from the title, since a non-function kind reads as a bare name.
|
|
10
11
|
* - Top-of-file JSDoc comment becomes the file's `content`.
|
|
11
12
|
* - Sets `description` (a plain-text summary from the first JSDoc paragraph) on the file and every `tree-documentation` child.
|
|
12
13
|
* - Sets `title` on every `tree-documentation` child — `name()` for functions and methods, bare `name` for other kinds. Parent class context comes from the `class` prop ("member of …" affordance), never the title.
|
|
@@ -127,7 +128,8 @@ function _extractStatement(statement, source) {
|
|
|
127
128
|
if (name.startsWith("_"))
|
|
128
129
|
return;
|
|
129
130
|
const jsDoc = _getJSDoc(statement, source);
|
|
130
|
-
|
|
131
|
+
// A `@kind` tag overrides the AST-inferred kind (e.g. `@kind component` for a React component declared as a function).
|
|
132
|
+
const kind = jsDoc?.kind ?? _getKind(statement);
|
|
131
133
|
if (!kind)
|
|
132
134
|
return;
|
|
133
135
|
const signatures = _getSignatures(statement, source, name);
|
|
@@ -442,10 +444,11 @@ function _getClassMembers(statement, source, className) {
|
|
|
442
444
|
}
|
|
443
445
|
/**
|
|
444
446
|
* `@rule` names handled (parsed or deliberately discarded) — everything else is appended to `unhandled` as raw markup.
|
|
447
|
+
* - `@kind` is parsed by `_parseJSDocKind` to override the AST-inferred kind, so it must not also leak into `unhandled`.
|
|
445
448
|
* - `@see` is recognised here purely to strip it: it's a VS Code hover affordance (a link back to the docs site) and must
|
|
446
449
|
* never leak into the rendered page content. It has no dedicated parser; it's simply discarded from the `unhandled` bucket.
|
|
447
450
|
*/
|
|
448
|
-
const _HANDLED_RULES = new Set(["param", "params", "return", "returns", "throw", "throws", "example", "examples", "see"]);
|
|
451
|
+
const _HANDLED_RULES = new Set(["kind", "param", "params", "return", "returns", "throw", "throws", "example", "examples", "see"]);
|
|
449
452
|
/** Extract JSDoc from a node. */
|
|
450
453
|
function _getJSDoc(node, source) {
|
|
451
454
|
const ranges = ts.getLeadingCommentRanges(source.text, node.pos);
|
|
@@ -460,6 +463,7 @@ function _getJSDoc(node, source) {
|
|
|
460
463
|
if (!text.startsWith("/**"))
|
|
461
464
|
continue;
|
|
462
465
|
const description = _parseJSDocComment(text);
|
|
466
|
+
const kind = _parseJSDocKind(text);
|
|
463
467
|
const params = _parseJSDocParams(text);
|
|
464
468
|
const returns = _parseJSDocReturns(text);
|
|
465
469
|
const throws = _parseJSDocThrows(text);
|
|
@@ -467,6 +471,7 @@ function _getJSDoc(node, source) {
|
|
|
467
471
|
const unhandled = _parseJSDocUnhandled(text);
|
|
468
472
|
return {
|
|
469
473
|
description: description || undefined,
|
|
474
|
+
kind,
|
|
470
475
|
params: params.length ? params : undefined,
|
|
471
476
|
returns: returns.length ? returns : undefined,
|
|
472
477
|
throws: throws.length ? throws : undefined,
|
|
@@ -529,6 +534,11 @@ function _parseJSDocComment(text) {
|
|
|
529
534
|
const result = description.join("\n").trim();
|
|
530
535
|
return result || undefined;
|
|
531
536
|
}
|
|
537
|
+
/** Parse a single `@kind` override from a JSDoc comment (e.g. `@kind component`), or `undefined` when absent. */
|
|
538
|
+
function _parseJSDocKind(text) {
|
|
539
|
+
// `@kind name` — a single identifier-ish token (letters, digits, hyphens).
|
|
540
|
+
return text.match(/@kind\s+([\w-]+)/)?.[1];
|
|
541
|
+
}
|
|
532
542
|
/** Parse `@param` tags from a JSDoc comment. Duplicates are kept (overloads). */
|
|
533
543
|
function _parseJSDocParams(text) {
|
|
534
544
|
const results = [];
|
package/package.json
CHANGED
package/ui/README.md
CHANGED
|
@@ -8,299 +8,67 @@ The `ui` module exists so an app never hand-rolls the same form field, card, or
|
|
|
8
8
|
|
|
9
9
|
## How components work
|
|
10
10
|
|
|
11
|
-
A few conventions run through every component
|
|
11
|
+
A few conventions run through every component:
|
|
12
12
|
|
|
13
|
-
- **Styling props
|
|
14
|
-
- **Composition.** Higher-level components — a `*Page`, a `*Card` — take their identity from library components like `Card
|
|
13
|
+
- **Styling props are for one-off overrides.** Visual options are props on the component — enumerated props for the scales (`color="red"`, `size="large"`, `space="none"`) and boolean props for on/off variants (`<Button strong>`, `<Section narrow>`, `<Flex wrap>`). Each maps to a class in a CSS Module. Reach for them when a component needs to look different in *one place* — the way the docs site tints its accents purple — not as the way to dress a whole app. You never pass `style` or raw `className`.
|
|
14
|
+
- **Composition.** Higher-level components — a `*Page`, a `*Card` — take their identity from library components like [`Card`](/ui/Card), [`Section`](/ui/Section), [`Button`](/ui/Button), and [`Tag`](/ui/Tag) rather than shipping their own styling.
|
|
15
15
|
- **Sentence case.** Titles, headings, and button labels capitalise only the first word.
|
|
16
|
-
- **
|
|
16
|
+
- **Theme with CSS.** An app-wide custom look is a CSS file, not a wall of props. Write a `theme.css` that overrides the base design-token variables (and, where needed, per-component hooks) at `:root`, and import it after the library styles. The recommended workflow is to spend time tuning those variables to match your design — see [Theming](#theming) below.
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## The styling system
|
|
19
19
|
|
|
20
|
-
The styling system has four moving parts
|
|
20
|
+
The styling system lives in `style/` and has four moving parts: design tokens, the tint scale, cascade layers, and the styling props. Components compose them in a predictable shape; consumers theme by overriding CSS custom properties at `:root`.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
**Design tokens.** Every design-token constant is defined at `:root`, split across the themed token modules in `style/` — each module owns one domain, documents the variables it defines, and is the page a theme author overrides. `style/layers.css` is the cascade-layer anchor; every `*.module.css` `@import`s it plus the specific token modules it references, so the tokens and the layer order reach every component regardless of bundle order. The domains are: colours ([`getColorClass`](/ui/getColorClass)), font sizes ([`getSizeClass`](/ui/getSizeClass)), font weights ([`getWeightClass`](/ui/getWeightClass)), font faces ([`getFontClass`](/ui/getFontClass)), spacing ([`getSpaceClass`](/ui/getSpaceClass)), widths ([`getWidthClass`](/ui/getWidthClass)), radii ([`getRadiusClass`](/ui/getRadiusClass)), strokes ([`getStrokeClass`](/ui/getStrokeClass)), shadows ([`getShadowClass`](/ui/getShadowClass)), and durations ([`getDurationClass`](/ui/getDurationClass)). Each also defines the semantic aliases a theme usually targets (`--color-primary`, `--color-link`, `--space-paragraph`, …). Components read tokens via `var(--token)`.
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
**The tint scale.** All colour flows from one anchor variable, `--tint-50`, from which a 21-step ladder is computed and *recomputed* under [`TINT_CLASS`](/ui/TINT_CLASS) — the heart of how `color=` and `status=` retint a whole subtree. The ladder, the recompute trick, the painting conventions, and the theming guide all live on the [`TINT_CLASS`](/ui/TINT_CLASS) page.
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
**Cascade layers.** Styles are ordered by `@layer`, lowest to highest priority: `defaults` (`:root` tokens, the tint ladder, body baseline) → `components` (the bulk of the CSS: `.card`, `.button`, …) → `variants` (cross-cutting opt-in modifiers, which always beat components) → `overrides` (top-priority structural fixes like `:first-child` / `:last-child` margin collapses). Unlayered rules beat all layered rules, so a theme should set tokens at `:root` or wrap its rules in `@layer`.
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
**Styling props.** The cross-cutting visual options are props, each backed by a helper in `style/` that maps the prop to a class. Colour and status move the tint anchor — [`getColorClass`](/ui/getColorClass) and [`getStatusClass`](/ui/getStatusClass); font size, weight, and family come from [`getSizeClass`](/ui/getSizeClass), [`getWeightClass`](/ui/getWeightClass), and [`getFontClass`](/ui/getFontClass), which [`getTypographyClass`](/ui/getTypographyClass) combines with text alignment and tint; spacing, padding, and gap from [`getSpaceClass`](/ui/getSpaceClass), [`getPaddingClass`](/ui/getPaddingClass), and [`getGapClass`](/ui/getGapClass); width constraints from [`getWidthClass`](/ui/getWidthClass); flex layout from [`getFlexClass`](/ui/getFlexClass); and opt-in scrolling from [`getScrollClass`](/ui/getScrollClass). Each helper's page lists its exact prop values and what they set. A component opts into the props it wants by extending the matching `*Props` interfaces and composing the `getXxxClass(props)` calls.
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
Each painting component also exposes its own theme hooks — a single tint hook (`--card-tint`) to recolour the whole component, plus per-property hooks (`--card-background`, `--card-radius`, …) for surgical overrides. Those are documented in each component's own **Styling** section (see [`Card`](/ui/Card) for the precedent).
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
## Theming
|
|
33
33
|
|
|
34
|
-
The
|
|
35
|
-
|
|
36
|
-
The ladder is computed at `:root` and *recomputed* under the `.tint` class (`TINT_CLASS` in [`style/Tint.tsx`](./style/Tint.tsx)). That recomputation is the whole trick: move the anchor at any scope, apply `.tint`, and all 21 shades rebuild from the new hue at that scope. Colour and status classes are exactly that —
|
|
34
|
+
The recommended way to give an app its own look is a **theme stylesheet**, not styling props. Create a `theme.css`, override the base design-token variables at `:root`, and import it after the library styles:
|
|
37
35
|
|
|
38
36
|
```css
|
|
39
|
-
/*
|
|
40
|
-
|
|
41
|
-
--
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
.success {
|
|
46
|
-
--tint-50: var(--color-success);
|
|
37
|
+
/* theme.css — imported after shelving/ui styles */
|
|
38
|
+
:root {
|
|
39
|
+
--color-primary: oklch(58% 0.25 300); /* purple brand */
|
|
40
|
+
--font-body: "Inter", system-ui;
|
|
41
|
+
--radius: 0.5rem; /* tighter corners everywhere */
|
|
42
|
+
--space: 1.125rem; /* roomier spacing scale */
|
|
47
43
|
}
|
|
48
44
|
```
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Components paint from the ladder by convention:
|
|
53
|
-
|
|
54
|
-
| Step | Used for |
|
|
55
|
-
|---|---|
|
|
56
|
-
| `--tint-00` | Body text, headings — maximum contrast |
|
|
57
|
-
| `--tint-50` | The hue itself — accents, labels, `Tag` backgrounds, `strong` button backgrounds |
|
|
58
|
-
| `--tint-80` | Borders |
|
|
59
|
-
| `--tint-90` | Surfaces — `Card`, `Preformatted`, `Button` backgrounds |
|
|
60
|
-
| `--tint-95` | Hover state of those surfaces |
|
|
61
|
-
| `--tint-100` | The page background; text on `--tint-50` backgrounds |
|
|
62
|
-
|
|
63
|
-
Pairings follow contrast: long text reads at `00`-on-`90` or `00`-on-`100`; short labels read at `100`-on-`50`.
|
|
64
|
-
|
|
65
|
-
### Cascade layers
|
|
66
|
-
|
|
67
|
-
Order, lowest to highest priority:
|
|
68
|
-
|
|
69
|
-
| Layer | What's in it |
|
|
70
|
-
|---|---|
|
|
71
|
-
| `defaults` | `:root` design tokens, the tint ladder, body baseline typography, low-priority opt-in defaults |
|
|
72
|
-
| `components` | Component-defining CSS — the bulk of the codebase: `.card`, `.button`, `.notice`, `.heading`, etc. |
|
|
73
|
-
| `variants` | Cross-cutting opt-in modifiers (Color, Status, Spacing, Padding, Gap, Width, Typography, Flex, Scroll). Always beat components. |
|
|
74
|
-
| `overrides` | Top-priority structural overrides — `:first-child` / `:last-child` margin collapses, which need to beat variant-set margins |
|
|
46
|
+
Each base token lives in a themed module that documents the variables it defines and which ones a theme usually overrides. Work from broadest (a palette colour or scale root) to narrowest (a single semantic alias):
|
|
75
47
|
|
|
76
|
-
|
|
48
|
+
- [weight](/ui/getWeightClass) · [size](/ui/getSizeClass) · [font](/ui/getFontClass) — typography (`--weight-*`, `--size-*`, `--font-*`, `--case-label`).
|
|
49
|
+
- [color](/ui/getColorClass) — palette, semantic, and brand colours (`--color-*`).
|
|
50
|
+
- [space](/ui/getSpaceClass) · [width](/ui/getWidthClass) — layout spacing and widths (`--space-*`, `--width-*`).
|
|
51
|
+
- [radius](/ui/getRadiusClass) · [stroke](/ui/getStrokeClass) · [shadow](/ui/getShadowClass) · [duration](/ui/getDurationClass) — surface tokens (`--radius-*`, `--stroke-*`, `--shadow-*`, `--duration-*`).
|
|
77
52
|
|
|
78
|
-
|
|
53
|
+
The **tint ladder** is the one exception that doesn't follow the override-a-variable pattern: its 21 steps are *recomputed* from a single anchor inside every tinted scope, so you move the anchor rather than overriding individual steps. See [`TINT_CLASS`](/ui/TINT_CLASS) for the full theming guide, and each component's **Styling** section for its per-component hooks.
|
|
79
54
|
|
|
80
|
-
|
|
55
|
+
## Finding your way around
|
|
81
56
|
|
|
82
|
-
|
|
57
|
+
The components below are listed in the index following this page; this is the short version of where to start reading.
|
|
83
58
|
|
|
84
|
-
|
|
85
|
-
|---|---|---|---|
|
|
86
|
-
| `color=` | [`Color`](./style/Color.tsx) | `"primary"`, `"secondary"`, `"tertiary"`, `"red"`, `"orange"`, `"yellow"`, `"green"`, `"aqua"`, `"blue"`, `"purple"`, `"pink"`, `"gray"` | The tint anchor — recolours the whole scope |
|
|
87
|
-
| `status=` | [`Status`](./style/Status.tsx) | `"info"`, `"success"`, `"warning"`, `"danger"`, `"error"`, `"loading"` | The tint anchor, via a semantic name |
|
|
88
|
-
| `size=` | [`Typography`](./style/Typography.tsx) | `"xxsmall"` … `"xxlarge"` | `font-size` |
|
|
89
|
-
| `tint=` | [`Typography`](./style/Typography.tsx) | `"00"`, `"05"`, … `"100"` | Text `color`, as a step of the current ladder |
|
|
90
|
-
| `space=` | [`Spacing`](./style/Spacing.tsx) | `"none"`, `"xxsmall"` … `"xxlarge"` | `margin-block` (top + bottom) |
|
|
91
|
-
| `padding=` | [`Padding`](./style/Padding.tsx) | `"none"`, `"xxsmall"` … `"xxlarge"` | `padding-block` (top + bottom) |
|
|
92
|
-
| `gap=` | [`Gap`](./style/Gap.tsx) | `"none"`, `"xxsmall"` … `"xxlarge"` | `gap` between children |
|
|
59
|
+
**Content.** Block-level structure starts with [`Card`](/ui/Card), [`Section`](/ui/Section), and the [`Heading`](/ui/Heading) / [`Title`](/ui/Title) family, with [`Table`](/ui/Table), [`List`](/ui/List), and [`Figure`](/ui/Figure) for specific shapes; wrap longform copy in [`Prose`](/ui/Prose). Inline pieces — [`Link`](/ui/Link), [`Code`](/ui/Code), [`Strong`](/ui/Strong), [`Mark`](/ui/Mark) — live inside that block content. To render a Markdown string as components, use [`Markup`](/ui/Markup).
|
|
93
60
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
| Props | Module | Purpose |
|
|
97
|
-
|---|---|---|
|
|
98
|
-
| `narrow`, `wide`, `full` | [`Width`](./style/Width.tsx) | Constrain (or unconstrain) `max-width` |
|
|
99
|
-
| `body`, `code`, `monospace`, `sans`, `serif` | [`Typography`](./style/Typography.tsx) | Font family |
|
|
100
|
-
| `left`, `center`, `right` | [`Typography`](./style/Typography.tsx) | Text alignment |
|
|
101
|
-
| `wrap`, `column`, `reverse`, justify/align (`left`, `middle`, `between`, …) | [`Flex`](./style/Flex.tsx) | Flex layout (composes `gap=`) |
|
|
102
|
-
| `horizontal`, `vertical` | [`Scroll`](./style/Scroll.tsx) | Opt-in scrolling (combinable) |
|
|
103
|
-
|
|
104
|
-
Status also keeps boolean aliases (`<Notice error>` ≡ `<Notice status="error">`) because they read naturally at call sites that hard-code one status.
|
|
105
|
-
|
|
106
|
-
A component using styling props looks like:
|
|
107
|
-
|
|
108
|
-
```tsx
|
|
109
|
-
export interface CardProps extends ColorProps, StatusProps, PaddingProps, SpacingProps, WidthVariants /* … */ {}
|
|
110
|
-
|
|
111
|
-
export function Card({ children, ...props }: CardProps): ReactElement {
|
|
112
|
-
return (
|
|
113
|
-
<article
|
|
114
|
-
className={getClass(
|
|
115
|
-
getModuleClass(CARD_CSS, "card"),
|
|
116
|
-
getStatusClass(props),
|
|
117
|
-
getColorClass(props),
|
|
118
|
-
getPaddingClass(props),
|
|
119
|
-
getSpacingClass(props),
|
|
120
|
-
getWidthClass(props),
|
|
121
|
-
)}
|
|
122
|
-
>
|
|
123
|
-
{children}
|
|
124
|
-
</article>
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
```tsx
|
|
130
|
-
// At a call site:
|
|
131
|
-
<Card color="purple" padding="large" space="none">…</Card>
|
|
132
|
-
```
|
|
61
|
+
**Structure.** Mount a client app with [`App`](/ui/App), or render a full server document with [`HTML`](/ui/HTML) and [`Page`](/ui/Page). Arrange the screen with [`CenteredLayout`](/ui/CenteredLayout) or [`SidebarLayout`](/ui/SidebarLayout), and drive URLs with [`Navigation`](/ui/Navigation) and [`Router`](/ui/Router).
|
|
133
62
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
Each component exposes per-component CSS custom properties for its overridable values, read with a `var(--component-hook, default)` fallback chain. A consumer overrides the hook at `:root` to retheme that one component without touching the rest of the design system.
|
|
137
|
-
|
|
138
|
-
Every painting component exposes two kinds of hook:
|
|
139
|
-
|
|
140
|
-
- **A tint hook** — the component rebinds the scale anchor once, at the top of its rule: `--tint-50: var(--card-tint, inherit);`. Setting `--card-tint: var(--color-purple)` recolours every card (and everything nested inside cards) while leaving the rest of the page alone. The `inherit` fallback is what lets `color=` / `status=` variants and parent scopes flow through when the hook is unset.
|
|
141
|
-
- **Per-property hooks** — `--card-background`, `--card-border`, `--card-padding`, `--card-radius`, `--card-shadow`, and so on, for surgical overrides of a single painted property: `background: var(--card-background, var(--tint-90))`.
|
|
142
|
-
|
|
143
|
-
Naming follows the file-prefix rule from [AGENTS.md](/AGENTS.md): hooks owned by a module file start with that file's kebab-case name — `Card.module.css` owns `--card-*`, `Button.module.css` owns `--button-*`. Design tokens declared at `:root` in `base.css` and the tint ladder itself are exempt — they're the global palette, not file-owned.
|
|
144
|
-
|
|
145
|
-
### Theming
|
|
146
|
-
|
|
147
|
-
A theme is a CSS file of custom-property overrides at `:root`, imported after the base styles. Work from broadest to narrowest:
|
|
148
|
-
|
|
149
|
-
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.
|
|
150
|
-
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.
|
|
151
|
-
3. **Override one property.** Per-property hooks are the scalpel: `--button-radius: 999px`, `--card-border: none`, `--tag-case: none`.
|
|
152
|
-
|
|
153
|
-
**Don't override individual ladder steps (`--tint-90`, etc.) at `:root`.** The ladder is *recomputed* from the anchor inside every `.tint` 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.
|
|
154
|
-
|
|
155
|
-
### How `:first-child` / `:last-child` margin overrides work
|
|
156
|
-
|
|
157
|
-
Every paragraph-level component zeros its outer margins when it's the first or last child of its container — otherwise a Heading at the top of a Card would leave a strip of unwanted space. These rules live in `@layer overrides`, which beats every other layer including `variants`, so a `<Paragraph space="large">` still collapses its abutting edges correctly.
|
|
158
|
-
|
|
159
|
-
Pattern:
|
|
160
|
-
|
|
161
|
-
```css
|
|
162
|
-
@layer components {
|
|
163
|
-
.paragraph { margin-block: var(--paragraph-space, var(--space-paragraph)); }
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
@layer overrides {
|
|
167
|
-
.paragraph {
|
|
168
|
-
&:first-child { margin-block-start: 0; }
|
|
169
|
-
&:last-child { margin-block-end: 0; }
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
```
|
|
63
|
+
**Interaction.** Forms start at [`Form`](/ui/Form), which wires [`Field`](/ui/Field) and the typed inputs to a [`FormStore`](/ui/FormStore); [`Button`](/ui/Button) is the standalone action. Overlays are [`Dialog`](/ui/Dialog) and [`Modal`](/ui/Modal); navigation menus are [`Menu`](/ui/Menu) and [`MenuItem`](/ui/MenuItem); transient feedback is [`Notice`](/ui/Notice) (and the global [`Notices`](/ui/Notices) list); animate enter/leave with [`Transition`](/ui/Transition).
|
|
173
64
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
A typical new block-level component looks like:
|
|
177
|
-
|
|
178
|
-
```tsx
|
|
179
|
-
// Address.tsx
|
|
180
|
-
import { type ColorProps, getColorClass } from "../style/Color.js";
|
|
181
|
-
import { getSpacingClass, type SpacingProps } from "../style/Spacing.js";
|
|
182
|
-
import { getTypographyClass, type TypographyProps } from "../style/Typography.js";
|
|
183
|
-
|
|
184
|
-
export interface AddressProps extends ColorProps, SpacingProps, TypographyProps, ChildProps {}
|
|
185
|
-
|
|
186
|
-
export function Address({ children, ...props }: AddressProps) {
|
|
187
|
-
return (
|
|
188
|
-
<address
|
|
189
|
-
className={getClass(
|
|
190
|
-
getModuleClass(styles, "address"),
|
|
191
|
-
getColorClass(props),
|
|
192
|
-
getSpacingClass(props),
|
|
193
|
-
getTypographyClass(props),
|
|
194
|
-
)}
|
|
195
|
-
>
|
|
196
|
-
{children}
|
|
197
|
-
</address>
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
```css
|
|
203
|
-
/* Address.module.css */
|
|
204
|
-
@import "../style/base.css";
|
|
205
|
-
|
|
206
|
-
@layer components {
|
|
207
|
-
.address {
|
|
208
|
-
/* Theme — rebind the tint anchor so `--address-tint` (and parent scopes) flow through. */
|
|
209
|
-
--tint-50: var(--address-tint, inherit);
|
|
210
|
-
|
|
211
|
-
/* Box */
|
|
212
|
-
display: block;
|
|
213
|
-
margin-inline: 0;
|
|
214
|
-
margin-block: var(--address-space, var(--space-paragraph));
|
|
215
|
-
|
|
216
|
-
/* Text — paint from the ladder, with a per-property hook in front. */
|
|
217
|
-
color: var(--address-color, var(--tint-00));
|
|
218
|
-
font-family: var(--address-font, inherit);
|
|
219
|
-
font-size: var(--address-size, inherit);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
@layer overrides {
|
|
224
|
-
.address {
|
|
225
|
-
&:first-child { margin-block-start: 0; }
|
|
226
|
-
&:last-child { margin-block-end: 0; }
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
Checklist:
|
|
232
|
-
|
|
233
|
-
- [ ] `@import "../style/base.css";` at the top.
|
|
234
|
-
- [ ] All rules inside `@layer components { … }`.
|
|
235
|
-
- [ ] All custom properties owned by this file start with the file name (`--address-*`, etc.), per [AGENTS.md](/AGENTS.md).
|
|
236
|
-
- [ ] If the component paints colour, rebind the tint anchor at the top of the rule (`--tint-50: var(--address-tint, inherit);`) and paint from ladder steps with per-property hooks in front.
|
|
237
|
-
- [ ] `:first-child` / `:last-child` overrides in a separate `@layer overrides { … }` block.
|
|
238
|
-
- [ ] TSX extends the styling-prop interfaces (`ColorProps`, `SpacingProps`, `TypographyProps`, etc.) you want to expose; composes the matching `getXxxClass(props)` calls.
|
|
239
|
-
|
|
240
|
-
## Module map
|
|
241
|
-
|
|
242
|
-
### Content
|
|
243
|
-
|
|
244
|
-
| Folder | What's inside |
|
|
245
|
-
|---|---|
|
|
246
|
-
| [block](/ui/block) | Block-level content — `Card`, `Section`, `Title`, `Heading`, `Table`, `List`, `Prose`, `Figure` |
|
|
247
|
-
| [inline](/ui/inline) | Inline content — `Code`, `Strong`, `Emphasis`, `Link`, `Mark`, `Small` |
|
|
248
|
-
| [misc](/ui/misc) | Cross-cutting pieces — `Markup`, `Tag`, `StatusIcon`, `Loading`, `Catcher`, `Mapper` |
|
|
249
|
-
| [style](./style/) | The styling system — design tokens, the tint scale, styling props, `Flex`, `Scroll` |
|
|
250
|
-
|
|
251
|
-
### Structure
|
|
252
|
-
|
|
253
|
-
| Folder | What's inside |
|
|
254
|
-
|---|---|
|
|
255
|
-
| [app](/ui/app) | The `<App>` root component |
|
|
256
|
-
| [page](/ui/page) | Document-level components — `<HTML>`, `<Head>`, `<Page>` |
|
|
257
|
-
| [layout](/ui/layout) | Page layouts — `SidebarLayout`, `CenteredLayout` |
|
|
258
|
-
| [router](/ui/router) | Client-side routing — `<Navigation>`, `<Router>` |
|
|
259
|
-
|
|
260
|
-
### Interaction
|
|
261
|
-
|
|
262
|
-
| Folder | What's inside |
|
|
263
|
-
|---|---|
|
|
264
|
-
| [form](/ui/form) | Forms and inputs — `<Form>`, `<Field>`, typed inputs, `<Button>`, `FormStore` |
|
|
265
|
-
| [dialog](/ui/dialog) | `<Dialog>` and `<Modal>` overlays |
|
|
266
|
-
| [menu](/ui/menu) | `<Menu>` and `<MenuItem>` |
|
|
267
|
-
| [notice](/ui/notice) | Inline and global notices |
|
|
268
|
-
| [transition](/ui/transition) | CSS enter / leave transitions |
|
|
269
|
-
|
|
270
|
-
### Documentation site
|
|
271
|
-
|
|
272
|
-
| Folder | What's inside |
|
|
273
|
-
|---|---|
|
|
274
|
-
| [tree](/ui/tree) | `<TreeApp>` and the components that turn a tree into a site |
|
|
275
|
-
| [docs](/ui/docs) | Page and card renderers for directories, files, and code symbols |
|
|
276
|
-
| [util](/ui/util) | UI helper functions — context, meta, CSS class composition |
|
|
277
|
-
|
|
278
|
-
## Quick start
|
|
279
|
-
|
|
280
|
-
A minimal single-screen app:
|
|
281
|
-
|
|
282
|
-
```tsx
|
|
283
|
-
import { App, CenteredLayout, Section, Title, Paragraph } from "shelving/ui";
|
|
284
|
-
|
|
285
|
-
function HelloApp() {
|
|
286
|
-
return (
|
|
287
|
-
<App app="My app">
|
|
288
|
-
<CenteredLayout>
|
|
289
|
-
<Section narrow>
|
|
290
|
-
<Title>Hello</Title>
|
|
291
|
-
<Paragraph>Welcome to the app.</Paragraph>
|
|
292
|
-
</Section>
|
|
293
|
-
</CenteredLayout>
|
|
294
|
-
</App>
|
|
295
|
-
);
|
|
296
|
-
}
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
For a routed, multi-page app, wrap the tree in [`<Navigation>` and `<Router>`](/ui/router). For a documentation site, hand an extracted tree to [`<TreeApp>`](/ui/tree) — see the [extract](/extract) guide.
|
|
65
|
+
**Documentation site.** Hand an extracted tree to [`TreeApp`](/ui/TreeApp) and you get a complete site — sidebar, routing, and a rendered page per element — using the renderers in `docs/`.
|
|
300
66
|
|
|
301
67
|
## See also
|
|
302
68
|
|
|
303
69
|
- [extract](/extract) — builds the tree that the documentation components render
|
|
304
|
-
- [markup](/markup) — Markdown rendering used by
|
|
305
|
-
- [store](/store) — reactive state behind `FormStore
|
|
70
|
+
- [markup](/markup) — Markdown rendering used by [`Markup`](/ui/Markup) and [`Prose`](/ui/Prose)
|
|
71
|
+
- [store](/store) — reactive state behind [`FormStore`](/ui/FormStore), `NavigationStore`, and notices
|
|
306
72
|
- [react](/react) — store and provider hooks used alongside these components
|
|
73
|
+
|
|
74
|
+
> Building or extending a component? The contributor walkthrough (file layout, the tint-anchor + per-property-hook pattern, `:first-child` / `:last-child` overrides, and the checklist) lives in the **React Components** section of `AGENTS.md`.
|
package/ui/app/App.d.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
|
-
import "../style/
|
|
2
|
+
import "../style/layers.css";
|
|
3
|
+
import "../style/Color.module.css";
|
|
4
|
+
import "../style/Duration.module.css";
|
|
5
|
+
import "../style/Font.module.css";
|
|
6
|
+
import "../style/Radius.module.css";
|
|
7
|
+
import "../style/Shadow.module.css";
|
|
8
|
+
import "../style/Size.module.css";
|
|
9
|
+
import "../style/Space.module.css";
|
|
10
|
+
import "../style/Stroke.module.css";
|
|
11
|
+
import "../style/Tint.module.css";
|
|
12
|
+
import "../style/Weight.module.css";
|
|
13
|
+
import "../style/Width.module.css";
|
|
3
14
|
import type { PossibleMeta } from "../util/index.js";
|
|
4
15
|
import type { ChildProps } from "../util/props.js";
|
|
5
16
|
/**
|
|
@@ -12,11 +23,12 @@ export interface AppProps extends PossibleMeta, ChildProps {
|
|
|
12
23
|
/**
|
|
13
24
|
* Root component for an application, providing the top-level `Meta` context and global styles.
|
|
14
25
|
* - Descendants can read or update metadata via the provided `<Meta>` context.
|
|
15
|
-
* - Design tokens and body baseline typography are set globally via `style
|
|
26
|
+
* - Design tokens and body baseline typography are set globally via the `style/` token modules (`Color`, `Size`, `Font`, …).
|
|
16
27
|
*
|
|
17
28
|
* @param children The application content.
|
|
18
29
|
* @param meta The root meta (app name, root URL, language, etc.).
|
|
19
30
|
* @returns The app root element wrapping `children`.
|
|
31
|
+
* @kind component
|
|
20
32
|
* @example <App app="My App" root="https://example.com/"><Navigation>…</Navigation></App>
|
|
21
33
|
* @see https://dhoulb.github.io/shelving/ui/app/App/App
|
|
22
34
|
*/
|
package/ui/app/App.js
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { MetaContext, requireMeta } from "../misc/MetaContext.js";
|
|
3
|
-
|
|
3
|
+
// Load the global design tokens, cascade-layer order, and body baseline. Each module owns its own
|
|
4
|
+
// `:root` tokens (and body rules where relevant); see `modules/ui/README.md` for the styling system.
|
|
5
|
+
import "../style/layers.css";
|
|
6
|
+
import "../style/Color.module.css";
|
|
7
|
+
import "../style/Duration.module.css";
|
|
8
|
+
import "../style/Font.module.css";
|
|
9
|
+
import "../style/Radius.module.css";
|
|
10
|
+
import "../style/Shadow.module.css";
|
|
11
|
+
import "../style/Size.module.css";
|
|
12
|
+
import "../style/Space.module.css";
|
|
13
|
+
import "../style/Stroke.module.css";
|
|
14
|
+
import "../style/Tint.module.css";
|
|
15
|
+
import "../style/Weight.module.css";
|
|
16
|
+
import "../style/Width.module.css";
|
|
4
17
|
/**
|
|
5
18
|
* Root component for an application, providing the top-level `Meta` context and global styles.
|
|
6
19
|
* - Descendants can read or update metadata via the provided `<Meta>` context.
|
|
7
|
-
* - Design tokens and body baseline typography are set globally via `style
|
|
20
|
+
* - Design tokens and body baseline typography are set globally via the `style/` token modules (`Color`, `Size`, `Font`, …).
|
|
8
21
|
*
|
|
9
22
|
* @param children The application content.
|
|
10
23
|
* @param meta The root meta (app name, root URL, language, etc.).
|
|
11
24
|
* @returns The app root element wrapping `children`.
|
|
25
|
+
* @kind component
|
|
12
26
|
* @example <App app="My App" root="https://example.com/"><Navigation>…</Navigation></App>
|
|
13
27
|
* @see https://dhoulb.github.io/shelving/ui/app/App/App
|
|
14
28
|
*/
|