@frontify/fondue 13.5.0 → 13.6.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 (46) hide show
  1. package/dist/components/AssetInput/AssetInput.es.js.map +1 -1
  2. package/dist/components/AssetInput/AssetThumbnail.es.js.map +1 -1
  3. package/dist/components/AssetInput/MultiAssetPreview/MultiAssetPreview.es.js.map +1 -1
  4. package/dist/components/AssetInput/MultiAssetPreview/SelectedAssetsThumbnail.es.js.map +1 -1
  5. package/dist/components/AssetInput/SingleAsset/AssetSubline.es.js.map +1 -1
  6. package/dist/components/AssetInput/SingleAsset/SelectedAsset.es.js.map +1 -1
  7. package/dist/components/AssetInput/SingleAsset/SpinningCircle.es.js.map +1 -1
  8. package/dist/components/AssetInput/types.es.js.map +1 -1
  9. package/dist/index.cjs.js.map +1 -1
  10. package/dist/index.d.ts +48 -0
  11. package/dist/index.umd.js.map +1 -1
  12. package/dist/packages/charts/style.css +1 -1
  13. package/dist/packages/components/style.css +1 -1
  14. package/dist/packages/locales/de-CH.js +4 -2
  15. package/dist/packages/locales/de-CH.js.map +1 -1
  16. package/dist/packages/locales/de-DE.js +2 -0
  17. package/dist/packages/locales/de-DE.js.map +1 -1
  18. package/dist/packages/locales/en-US.d.ts +2 -0
  19. package/dist/packages/locales/en-US.js +2 -0
  20. package/dist/packages/locales/en-US.js.map +1 -1
  21. package/dist/packages/locales/es-ES.js +2 -0
  22. package/dist/packages/locales/es-ES.js.map +1 -1
  23. package/dist/packages/locales/fr-CH.js +2 -0
  24. package/dist/packages/locales/fr-CH.js.map +1 -1
  25. package/dist/packages/locales/fr-FR.js +2 -0
  26. package/dist/packages/locales/fr-FR.js.map +1 -1
  27. package/dist/packages/locales/it-CH.js +2 -0
  28. package/dist/packages/locales/it-CH.js.map +1 -1
  29. package/dist/packages/locales/it-IT.js +2 -0
  30. package/dist/packages/locales/it-IT.js.map +1 -1
  31. package/dist/packages/locales/nl-NL.js +2 -0
  32. package/dist/packages/locales/nl-NL.js.map +1 -1
  33. package/dist/packages/locales/pl-PL.js +2 -0
  34. package/dist/packages/locales/pl-PL.js.map +1 -1
  35. package/dist/packages/locales/pt-PT.js +4 -2
  36. package/dist/packages/locales/pt-PT.js.map +1 -1
  37. package/dist/packages/rte/style.css +1 -1
  38. package/dist/packages/sdk/fondue-sdk.js +1 -0
  39. package/dist/packages/sdk/sdk.d.ts +1 -0
  40. package/dist/packages/tokens/manifest.json +2747 -0
  41. package/dist/tools/codemod/index.js +16 -0
  42. package/dist/tools/internal/index.js +16 -16
  43. package/dist/tools/sdk-cli/adapters/claude-skill/skill/SKILL.md +207 -0
  44. package/dist/tools/sdk-cli/adapters/claude-skill/skill/reference.md +235 -0
  45. package/dist/tools/sdk-cli/index.js +224 -0
  46. package/package.json +17 -10
@@ -573,6 +573,19 @@ const deprecated = [
573
573
  "AccordionHeaderProps",
574
574
  "AccordionItem",
575
575
  "ActionMenuProps",
576
+ "AssetInput",
577
+ "AssetInputMenuBlock",
578
+ "AssetInputMenuDefaultItemType",
579
+ "AssetInputMenuItemContentProps",
580
+ "AssetInputMenuItemContentSize",
581
+ "AssetInputMenuItemProps",
582
+ "AssetInputMenuItemStyle",
583
+ "AssetInputMenuItemType",
584
+ "AssetInputMenuSwitchItemType",
585
+ "AssetInputProps",
586
+ "AssetInputSelectionIndicatorIcon",
587
+ "AssetInputSize",
588
+ "AssetType",
576
589
  "Breadcrumb",
577
590
  "Breadcrumbs",
578
591
  "CollapsibleWrap",
@@ -597,9 +610,11 @@ const deprecated = [
597
610
  "FormControlStyle",
598
611
  "FrontifyPattern",
599
612
  "HelperPosition",
613
+ "ImageAsset",
600
614
  "Item",
601
615
  "LegacyTextarea",
602
616
  "LegacyTextareaProps",
617
+ "LibrarySource",
603
618
  "MenuItem",
604
619
  "MenuItemContentSize",
605
620
  "MenuItemProps",
@@ -624,6 +639,7 @@ const deprecated = [
624
639
  "Toast",
625
640
  "ToastAnimationDirection",
626
641
  "ToastStyle",
642
+ "UploadSource",
627
643
  "Validation",
628
644
  "useCopy",
629
645
  "useForwardedRef",
@@ -11,6 +11,19 @@ const deprecated = [
11
11
  "AccordionHeaderProps",
12
12
  "AccordionItem",
13
13
  "ActionMenuProps",
14
+ "AssetInput",
15
+ "AssetInputMenuBlock",
16
+ "AssetInputMenuDefaultItemType",
17
+ "AssetInputMenuItemContentProps",
18
+ "AssetInputMenuItemContentSize",
19
+ "AssetInputMenuItemProps",
20
+ "AssetInputMenuItemStyle",
21
+ "AssetInputMenuItemType",
22
+ "AssetInputMenuSwitchItemType",
23
+ "AssetInputProps",
24
+ "AssetInputSelectionIndicatorIcon",
25
+ "AssetInputSize",
26
+ "AssetType",
14
27
  "Breadcrumb",
15
28
  "Breadcrumbs",
16
29
  "CollapsibleWrap",
@@ -35,9 +48,11 @@ const deprecated = [
35
48
  "FormControlStyle",
36
49
  "FrontifyPattern",
37
50
  "HelperPosition",
51
+ "ImageAsset",
38
52
  "Item",
39
53
  "LegacyTextarea",
40
54
  "LegacyTextareaProps",
55
+ "LibrarySource",
41
56
  "MenuItem",
42
57
  "MenuItemContentSize",
43
58
  "MenuItemProps",
@@ -62,6 +77,7 @@ const deprecated = [
62
77
  "Toast",
63
78
  "ToastAnimationDirection",
64
79
  "ToastStyle",
80
+ "UploadSource",
65
81
  "Validation",
66
82
  "useCopy",
67
83
  "useForwardedRef",
@@ -69,19 +85,6 @@ const deprecated = [
69
85
  "useMobileDetection"
70
86
  ];
71
87
  const active = [
72
- "AssetInput",
73
- "AssetInputMenuBlock",
74
- "AssetInputMenuDefaultItemType",
75
- "AssetInputMenuItemContentProps",
76
- "AssetInputMenuItemContentSize",
77
- "AssetInputMenuItemProps",
78
- "AssetInputMenuItemStyle",
79
- "AssetInputMenuItemType",
80
- "AssetInputMenuSwitchItemType",
81
- "AssetInputProps",
82
- "AssetInputSelectionIndicatorIcon",
83
- "AssetInputSize",
84
- "AssetType",
85
88
  "CollapsibleWrap",
86
89
  "CollisionPosition",
87
90
  "DragHandle",
@@ -92,12 +95,10 @@ const active = [
92
95
  "ExpandButtonProps",
93
96
  "FilterableMultiSelect",
94
97
  "INDENTATION_WIDTH",
95
- "ImageAsset",
96
98
  "InputLabelProps",
97
99
  "InternalTreeItemMultiSelectProps",
98
100
  "InternalTreeItemProps",
99
101
  "LegacyTextarea",
100
- "LibrarySource",
101
102
  "MultiSelect",
102
103
  "OnExpandCallback",
103
104
  "OnSelectCallback",
@@ -144,7 +145,6 @@ const active = [
144
145
  "TreeProps",
145
146
  "TreeState",
146
147
  "TreeStateAction",
147
- "UploadSource",
148
148
  "addSelectedIds",
149
149
  "addSelectedItemsFromSelection",
150
150
  "cleanOrphanSelectedIds",
@@ -0,0 +1,207 @@
1
+ ---
2
+ name: fondue
3
+ description: Queries the locally installed `@frontify/fondue/sdk` to surface accurate Fondue component metadata. ALWAYS use when picking a Fondue component (button, dialog, dropdown, tabs, …), finding a design token or utility class, or setting up the `@frontify/fondue` package.
4
+ allowed-tools: Bash(node *)
5
+ ---
6
+
7
+ # Fondue
8
+
9
+ Use `@frontify/fondue/sdk` as the source of truth for every Fondue question. Never invent component names, props, token ids, or imports from memory — query the SDK.
10
+
11
+ **Runs against the project's locally installed Fondue.** Invoke `node` from the project root (or a subdirectory) so `@frontify/fondue` resolves from `node_modules` — results reflect the exact version this project depends on, not the version the skill was authored against.
12
+
13
+ ## Verify the SDK is available
14
+
15
+ ```bash
16
+ node -e "import('@frontify/fondue/sdk').then((m) => console.log(JSON.stringify({ components: m.components.size, tokens: m.tokens.size, guides: m.guides.size })))"
17
+ ```
18
+
19
+ If that errors with `Cannot find package`, the project hasn't installed Fondue yet:
20
+
21
+ ```bash
22
+ pnpm add @frontify/fondue # or npm i / yarn add
23
+ ```
24
+
25
+ ## How to query
26
+
27
+ The SDK is a synchronous, zero-I/O ES module that resolves from the user's project. Three singletons share the same query surface:
28
+
29
+ ```ts
30
+ import { components, tokens, guides } from '@frontify/fondue/sdk';
31
+
32
+ // Same shape on each domain:
33
+ components.list();
34
+ components.get('Button'); // ComponentNode | undefined (never throws)
35
+ components.has('Button'); // boolean
36
+ components.where({ text: 'dropdown' });
37
+ components.size; // number
38
+ ```
39
+
40
+ `where(filter)` clauses AND-combine; array-valued clauses OR within the clause. `get` and `where` never throw; missing ids return `undefined` / `[]`.
41
+
42
+ Nodes have **scalar fields** (read as properties) and **graph edges** (call as methods):
43
+
44
+ ```ts
45
+ const button = components.get('Button');
46
+ button?.importStatement; // scalar
47
+ button?.props; // scalar (array)
48
+ button?.category(); // edge → ComponentFacetNode { name, list, where, … }
49
+ button?.related(); // edge → ComponentNode[]
50
+ ```
51
+
52
+ ### Running queries from Claude
53
+
54
+ For one-liners, use `node -e`:
55
+
56
+ ```bash
57
+ node -e "import('@frontify/fondue/sdk').then(({components}) => console.log(JSON.stringify(components.where({text:'dropdown'}).map((c)=>({name:c.name,import:c.importStatement,canonical:c.examples.find((e)=>e.isCanonical)?.code})))))"
58
+ ```
59
+
60
+ For anything multi-line, pipe a module via stdin to avoid quoting hell:
61
+
62
+ ```bash
63
+ node --input-type=module <<'EOF'
64
+ import { components } from '@frontify/fondue/sdk';
65
+ const candidates = components.where({ text: 'dropdown', status: 'released' }).slice(0, 5);
66
+ console.log(JSON.stringify(candidates.map((c) => ({
67
+ name: c.name,
68
+ category: c.category().name,
69
+ tags: c.tags().map((t) => t.name),
70
+ import: c.importStatement,
71
+ canonical: c.examples.find((e) => e.isCanonical)?.code ?? c.examples[0]?.code ?? null,
72
+ })), null, 2));
73
+ EOF
74
+ ```
75
+
76
+ Always emit `JSON.stringify(...)` so the response is parseable. Never `console.log(node)` directly — facet methods serialize as `[Function]`.
77
+
78
+ For the formal contract (every filter clause, node shape, facet method), see [`reference.md`](./reference.md).
79
+
80
+ ---
81
+
82
+ ## Workflow 1 — Setting up Fondue
83
+
84
+ Read the canonical setup prose from the SDK rather than paraphrasing — the steps change between releases (font URLs, package layout, theme provider API).
85
+
86
+ ```bash
87
+ node -e "import('@frontify/fondue/sdk').then(({guides}) => console.log(guides.get('getting-started')?.content))"
88
+ ```
89
+
90
+ For upgrades:
91
+
92
+ ```bash
93
+ node -e "import('@frontify/fondue/sdk').then(({guides}) => console.log(guides.get('upgrading')?.content))"
94
+ ```
95
+
96
+ To discover other bundled guides:
97
+
98
+ ```bash
99
+ node -e "import('@frontify/fondue/sdk').then(({guides}) => console.log(JSON.stringify(guides.list().map((g)=>({id:g.id,title:g.title})))))"
100
+ ```
101
+
102
+ The output is the same markdown the Storybook docs render. Use it as the source for token imports, component-style imports, font face definitions, the Tailwind preset, `ThemeProvider`.
103
+
104
+ ## Workflow 2 — Finding a component
105
+
106
+ Start with intent-based search:
107
+
108
+ ```bash
109
+ node --input-type=module <<'EOF'
110
+ import { components } from '@frontify/fondue/sdk';
111
+ console.log(JSON.stringify(components.where({ text: 'dropdown' }).map((c) => ({
112
+ name: c.name,
113
+ category: c.category().name,
114
+ status: c.status,
115
+ description: c.description,
116
+ })), null, 2));
117
+ EOF
118
+ ```
119
+
120
+ Narrow by category or tag when the use case maps cleanly:
121
+
122
+ ```ts
123
+ components.where({ category: 'overlay', status: 'released' });
124
+ components.where({ tag: 'cta' });
125
+ components.where({ category: 'input', tag: 'cta' });
126
+ ```
127
+
128
+ Once you have a candidate, pull the recommendation shape:
129
+
130
+ ```bash
131
+ node --input-type=module <<'EOF'
132
+ import { components } from '@frontify/fondue/sdk';
133
+ const c = components.get('Button');
134
+ console.log(JSON.stringify({
135
+ name: c.name,
136
+ description: c.description,
137
+ importStatement: c.importStatement,
138
+ canonical: c.examples.find((e) => e.isCanonical)?.code ?? c.examples[0]?.code,
139
+ requiredProps: c.props.filter((p) => p.required).map((p) => p.name),
140
+ subComponents: c.subComponents.map((sc) => sc.name),
141
+ related: c.related().map((r) => r.name),
142
+ instructions: c.instructions,
143
+ }, null, 2));
144
+ EOF
145
+ ```
146
+
147
+ **Recommendation shape** — when you suggest a component, output: the name, what it's for, the import statement, and the canonical example. If the use case isn't a clean match, list 2–3 candidates with their categories and let the user pick.
148
+
149
+ **Icons** live in the components graph under `category: 'icon'`. They have empty `status`, empty `props`, empty `related()` — don't probe those fields. Find them with:
150
+
151
+ ```ts
152
+ components.where({ category: 'icon', tag: 'arrow' });
153
+ components.get('IconAdobeCreativeCloud')?.importStatement;
154
+ ```
155
+
156
+ When nothing matches:
157
+ - Try a related tag — `components.tags().map((t) => t.name)` lists every tag.
158
+ - Look at the closest hit's `related()`.
159
+ - If there truly is no component for the use case, say so. Don't recommend hand-rolling something Fondue already provides, but don't invent a component name either.
160
+
161
+ ## Workflow 3 — Picking tokens for a custom component
162
+
163
+ Two layers exist:
164
+
165
+ - `tokens` — atomic design tokens (colors, sizes, shadows, strings). Each has a CSS variable and a Tailwind class.
166
+ - `tokens.utilities` — composed Tailwind utilities (typography classes like `tw-body-large-strong`) bundling multiple token references.
167
+
168
+ ```ts
169
+ tokens.where({ text: 'primary' });
170
+ tokens.where({ category: 'colors', themeable: true });
171
+ tokens.where({ keyPathStartsWith: 'colors.text' });
172
+ tokens.type('color')?.where({ themeable: true });
173
+
174
+ const t = tokens.get('color-text-weak');
175
+ t?.value; // 'var(--color-text-weak)'
176
+ t?.cssVariable; // '--color-text-weak'
177
+ t?.tailwindClass; // 'tw-text-weak'
178
+ t?.themeable; // true
179
+
180
+ // Typography: always use the utility class, not raw font tokens
181
+ tokens.utilities.where({ keyPathStartsWith: 'utilities.text' });
182
+ ```
183
+
184
+ Rules:
185
+ - Prefer `themeable: true` tokens for any user-facing surface so dark mode and theming work out of the box.
186
+ - Prefer the Tailwind class when the project uses `@frontify/fondue/tokens/tailwind`; prefer the CSS variable otherwise.
187
+ - For typography, recommend the **utility class** — not raw font-size / line-height / weight tokens.
188
+ - Never invent token ids. If `tokens.get(id)` returns `undefined`, search again.
189
+ - If the design calls for a value with no matching token, surface that explicitly — don't silently hardcode. The right answer is usually a token the user didn't know existed.
190
+
191
+ ---
192
+
193
+ ## Common pitfalls
194
+
195
+ | Mistake | Fix |
196
+ | ------- | --- |
197
+ | Calling `.where()` / `.get()` on the array returned by `list()` / `where()` | Arrays are arrays. Use native `.filter` / `.find`, or navigate back to a facet to query. |
198
+ | Assuming a component exists ("there must be a `Combobox`") | `components.has('Combobox')` first, or `components.where({ text: 'combobox' })` |
199
+ | Hardcoding a hex / px value in custom code | `tokens.where({ text: '<intent>' })` — most "obvious" values have a token |
200
+ | Treating icons like normal components, reading `status` / `props` | Detect with `node.category().name === 'icon'`. Icons have empty `status`, `props`, `related`. |
201
+ | Importing `Button` from the wrong path | `components.get('Button')?.importStatement` is authoritative |
202
+ | Suggesting setup steps from memory | `guides.get('getting-started')?.content` — always the live text |
203
+ | `console.log(node)` instead of `JSON.stringify` | Facet methods serialize as `[Function]`; always stringify before logging |
204
+
205
+ ## Going deeper
206
+
207
+ [`reference.md`](./reference.md) is the formal contract: every export, every filter clause per domain, every node and facet type, error semantics. Load it when you need to verify a filter clause is valid (`ComponentFilter`, `TokenFilter`, `GuideFilter`) or that a method exists. Prefer it over web lookups or upstream GitHub.
@@ -0,0 +1,235 @@
1
+ ## Contents
2
+
3
+ - [Exports](#exports) — the three singletons and the shared `QueryApi` shape
4
+ - [Components](#components) — `ComponentFilter`, `ComponentNode`, icons
5
+ - [Tokens](#tokens) — `TokenFilter`, `TokenNode`, utilities
6
+ - [Guides](#guides) — guide ids and shape
7
+ - [Facets](#facets) — `FacetNode` shape and how to reach them
8
+ - [Error semantics](#error-semantics) — what throws, what returns `undefined`
9
+ - [Runtime guarantees](#runtime-guarantees) — module type, Node version, deps
10
+
11
+ ## Exports
12
+
13
+ ```ts
14
+ import { components, tokens, guides } from '@frontify/fondue/sdk';
15
+ ```
16
+
17
+ Three singletons. Each has the same query surface; each returns its own node types.
18
+
19
+ ```ts
20
+ interface QueryApi<Node, Filter> {
21
+ list(): readonly Node[];
22
+ get(id: string): Node | undefined; // never throws; returns undefined for unknown ids
23
+ has(id: string): boolean;
24
+ where(filter: Filter): readonly Node[]; // AND-combined; array-valued clauses OR within
25
+ readonly size: number;
26
+ }
27
+ ```
28
+
29
+ Plain arrays returned from `list()` / `where()` / `node.related()` are arrays — they don't carry the query surface. Use native `.filter` / `.find`, or navigate back to a facet to re-query.
30
+
31
+ ## Components
32
+
33
+ ```ts
34
+ components: QueryApi<ComponentNode, ComponentFilter> & {
35
+ categories(): readonly ComponentFacetNode[];
36
+ category(name: string): ComponentFacetNode | undefined;
37
+ tags(): readonly ComponentFacetNode[];
38
+ tag(name: string): ComponentFacetNode | undefined;
39
+ };
40
+ ```
41
+
42
+ ### `ComponentFilter`
43
+
44
+ | Clause | Type | Notes |
45
+ | ---------- | ----------------------------- | ------------------------------------------------------------------- |
46
+ | `category` | `string \| readonly string[]` | OR within array |
47
+ | `status` | `string \| readonly string[]` | OR within array |
48
+ | `tag` | `string \| readonly string[]` | Matches if component carries any tag in the array |
49
+ | `text` | `string` | Case-insensitive substring across name, description, category, tags |
50
+
51
+ All clauses AND-combine.
52
+
53
+ ### `ComponentNode`
54
+
55
+ ```ts
56
+ interface ComponentNode {
57
+ // scalar fields
58
+ name: string;
59
+ description: string;
60
+ status: string; // '' for icons
61
+ importStatement: string;
62
+ instructions: string; // hand-written usage notes, often empty
63
+ props: readonly ComponentProp[];
64
+ subComponents: readonly ComponentSubComponent[];
65
+ examples: readonly ComponentExample[];
66
+ typeDefinitions: Readonly<Record<string, string>>;
67
+
68
+ // graph edges (methods)
69
+ category(): ComponentFacetNode; // throws on data inconsistency only
70
+ tags(): readonly ComponentFacetNode[];
71
+ related(): readonly ComponentNode[]; // unknown names silently skipped
72
+ toJSON(): ComponentDetails;
73
+ }
74
+
75
+ interface ComponentProp {
76
+ name: string;
77
+ type: string;
78
+ required: boolean;
79
+ defaultValue: string | null;
80
+ description: string;
81
+ deprecated: boolean;
82
+ deprecationMessage: string;
83
+ }
84
+
85
+ interface ComponentExample {
86
+ name: string;
87
+ description: string;
88
+ code: string;
89
+ isCanonical: boolean; // there is at most one canonical example per component
90
+ }
91
+
92
+ interface ComponentSubComponent {
93
+ name: string; // e.g. 'Dialog.Header'
94
+ props: readonly ComponentProp[];
95
+ }
96
+ ```
97
+
98
+ ### Icons
99
+
100
+ Icons are components with `category: 'icon'`. They have:
101
+
102
+ - empty `status`, empty `props`, empty `related()`, empty `subComponents`
103
+ - a non-empty `importStatement` (e.g. `import { IconAdobeCreativeCloud } from '@frontify/fondue/icons';`)
104
+ - tags (e.g. `'arrow'`, `'brand'`)
105
+
106
+ Filter to icons with `components.where({ category: 'icon' })`. Detect at the node with `node.category().name === 'icon'`.
107
+
108
+ ## Tokens
109
+
110
+ ```ts
111
+ tokens: QueryApi<TokenNode, TokenFilter> & {
112
+ categories(): readonly TokenFacetNode[];
113
+ category(name: string): TokenFacetNode | undefined;
114
+ types(): readonly TokenFacetNode[];
115
+ type(name: TokenValueType): TokenFacetNode | undefined;
116
+ utilities: TokenUtilitiesApi;
117
+ };
118
+
119
+ type TokenValueType = 'color' | 'float' | 'shadow' | 'string';
120
+ ```
121
+
122
+ ### `TokenFilter`
123
+
124
+ | Clause | Type | Notes |
125
+ | ------------------- | --------------------------------------------- | --------------------------------------------------- |
126
+ | `category` | `string \| readonly string[]` | e.g. `'colors'`, `'sizes'` |
127
+ | `type` | `TokenValueType \| readonly TokenValueType[]` | |
128
+ | `themeable` | `boolean` | |
129
+ | `keyPathStartsWith` | `string` | Dot-joined keyPath prefix, e.g. `'colors.charts'` |
130
+ | `text` | `string` | Case-insensitive against id, tailwindClass, keyPath |
131
+
132
+ ### `TokenNode`
133
+
134
+ ```ts
135
+ interface TokenNode {
136
+ id: string; // e.g. 'color-charts-primary-default'
137
+ value: string; // often `var(--token)` or a literal
138
+ cssVariable: string; // 'var(--color-charts-primary-default)'
139
+ tailwindClass: string; // e.g. '*-charts-primary'
140
+ themeable: boolean;
141
+ keyPath: readonly string[]; // ['colors','charts','primary','default']
142
+
143
+ category(): TokenFacetNode;
144
+ type(): TokenFacetNode;
145
+ toJSON(): Token;
146
+ }
147
+ ```
148
+
149
+ ### Utilities
150
+
151
+ ```ts
152
+ tokens.utilities: QueryApi<TokenUtilityNode, TokenUtilityFilter> & {
153
+ classes(): readonly string[];
154
+ };
155
+ ```
156
+
157
+ No `categories()` / `types()` on utilities — they're a leaf domain.
158
+
159
+ ```ts
160
+ interface TokenUtilityFilter {
161
+ themeable?: boolean;
162
+ keyPathStartsWith?: string; // typography utilities live under 'utilities.text'
163
+ text?: string;
164
+ }
165
+
166
+ interface TokenUtilityNode {
167
+ id: string;
168
+ tailwindClass: string; // e.g. 'tw-body-large-strong'
169
+ themeable: boolean;
170
+ keyPath: readonly string[];
171
+ properties: readonly TokenUtilityProperty[];
172
+ }
173
+
174
+ interface TokenUtilityProperty {
175
+ id: string;
176
+ type: TokenValueType;
177
+ value: string;
178
+ cssVariable: string;
179
+ }
180
+ ```
181
+
182
+ ## Guides
183
+
184
+ ```ts
185
+ guides: QueryApi<Guide, GuideFilter>;
186
+
187
+ interface Guide {
188
+ id: string; // slug from filename, e.g. 'getting-started'
189
+ title: string; // extracted from the first `# Title` line
190
+ content: string; // raw markdown body, includes the leading `# Title`
191
+ }
192
+
193
+ interface GuideFilter {
194
+ text?: string; // case-insensitive against id, title, content
195
+ }
196
+ ```
197
+
198
+ Known ids include `getting-started`, `contributing`, `upgrading`. The bundled set may grow; call `guides.list()` to see the current corpus.
199
+
200
+ ## Facets
201
+
202
+ `ComponentFacetNode` and `TokenFacetNode` share this shape:
203
+
204
+ ```ts
205
+ interface FacetNode<Node, Filter> {
206
+ name: string;
207
+ list(): readonly Node[];
208
+ get(id: string): Node | undefined;
209
+ has(id: string): boolean;
210
+ where(filter: Filter): readonly Node[];
211
+ size: number;
212
+ }
213
+ ```
214
+
215
+ Reach a facet via the domain's accessor or via a node's edge:
216
+
217
+ ```ts
218
+ components.category('input'); // facet
219
+ components.tag('cta'); // facet
220
+ components.get('Button')?.category(); // facet from a node
221
+ tokens.category('colors')?.where({ themeable: true });
222
+ ```
223
+
224
+ ## Error semantics
225
+
226
+ - `get(id)` never throws — returns `undefined` for unknown ids.
227
+ - `node.related()` silently skips unknown names.
228
+ - `node.category()` throws only on internal data inconsistency (a component referencing a category that doesn't exist) — treat as a bug to report, not a runtime case to handle.
229
+ - `where(filter)` with an empty filter returns every node in the domain.
230
+
231
+ ## Runtime guarantees
232
+
233
+ - Synchronous, zero file I/O.
234
+ - Pure ES module; Node 18+.
235
+ - No peer dependencies, no React, no DOM.