HyDtcgTree
Usage examples on this page are written for Lit / plain HTML (
<hy-dtcg-tree>). The same component ships asHyDtcgTreein@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
A hy-tree specialized for DTCG (W3C Design Tokens Community Group) documents. Pass a parsed DTCG JSON object via tokens, and the wrapper walks it, renders each group as a non-leaf tree item and each token as a leaf item with an inline value preview.
The wrapper owns expansion state (not the underlying hy-tree-items) so filter changes can transparently expand ancestors of matches without fighting the user's manual expansion.
Filter semantics: an item is visible when (1) its own path contains the filter text, (2) any descendant's path does, or (3) an ancestor's path does — in the last case, the ancestor's whole subtree "opens up" (every descendant renders, matching or not). Set strict-filter to disable the third case so only own/descendant matches remain.
When to use
- Browsing or picking tokens in a design token document.
Do
- Show
value-previewso people see each token's value.
When not to use
- Hierarchical data that is not a DTCG token document:
hy-tree. - A few tokens with no groups:
hy-listorhy-table. - A color preview outside a tree:
hy-swatchorhy-swatch-group.
Accessibility: your part
- Set
labelto what this token set is. The default "Design tokens" does not say which set. - Give every control you add in the
headerslot, and everyrowActionsitem, a clear name.
Examples
Browser for a DTCG file — value previews follow $type (color swatch, dimension mono, shadow thumb, typography summary, alias chip). Previews show by default; set the valuePreview property to false to hide them.
<hy-dtcg-tree id="browser"></hy-dtcg-tree>
<script>
const browser = document.getElementById('browser');
browser.tokens = await (await fetch('/tokens.json')).json();
browser.addEventListener('select', (event) => {
console.log(event.detail.path, event.detail.token);
});
</script>Filterable token picker — non-matching items hide; ancestors of matches stay visible and auto-expand. A matching group "opens up" every descendant regardless of self-match.
<hy-text-input id="filter" placeholder="Filter tokens" clearable></hy-text-input>
<hy-dtcg-tree id="picker"></hy-dtcg-tree>
<script>
const filter = document.getElementById('filter');
const picker = document.getElementById('picker');
picker.tokens = await (await fetch('/tokens.json')).json();
filter.addEventListener('input', (event) => {
picker.filterText = event.detail.value;
});
</script>Selection mirrored in the URL — selectedPath is a reflected attribute, so setAttribute and property assignment stay in sync.
<hy-dtcg-tree id="router"></hy-dtcg-tree>
<script>
const tree = document.getElementById('router');
tree.tokens = await (await fetch('/tokens.json')).json();
const fromHash = () => tree.setAttribute('selected-path', location.hash.slice(1));
fromHash();
window.addEventListener('hashchange', fromHash);
tree.addEventListener('select', (event) => {
if (location.hash.slice(1) !== event.detail.path) {
location.hash = event.detail.path;
}
});
</script>Overriding the clipboard default — the copy event is cancelable and fires before the internal clipboard write
tree.addEventListener('copy', (event) => {
event.preventDefault();
navigator.clipboard.writeText(`{${event.detail.path}}`);
});Domain-specific key ordering — pass sortKeys to reorder children per parent path (type-scale shorthands, font-weight words, numeric z-index)
tree.sortKeys = (parentPath, keys) => {
if (parentPath === 'spacing') {
const order = ['xs', 'sm', 'md', 'lg', 'xl'];
return [...keys].sort((a, b) => order.indexOf(a) - order.indexOf(b));
}
return keys;
};API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
tokens | — | DtcgDocument | {} | Parsed DTCG document. |
selectedPath | selected-path | string | '' | Dot-path of the currently selected token. Reflected. |
filterText | filter-text | string | '' | Case-insensitive substring filter. When non-empty, items whose path or any descendant's path contains the text stay visible; the rest are hidden. Ancestors of matches are auto-expanded. |
strictFilter | strict-filter | boolean | false | When true, the default "ancestor match opens the whole subtree" semantic is disabled: only items whose own path matches the filter (or that have a descendant that matches) stay visible. Children of a matching ancestor are hidden unless they also match. |
copyable | copyable | boolean | true | Show the copy-path icon button in each row's trailing slot. |
valuePreview | value-preview | boolean | true | Show inline value previews (swatch, dimension, shadow thumb, etc.). |
showTypeBadge | show-type-badge | boolean | false | Render a compact 3-letter badge of the token's $type in the row's leading slot (opt-in). Disabled by default to keep the tree minimal. Colors come from typeColor(type) when set, otherwise fall back to the shared --hy-dtcg-tree-type-badge-* custom properties (see styles). |
showGroupMeta | show-group-meta | boolean | false | Render a compact child-count and inherited $type label in the trailing slot of each group row (opt-in). Good for token browsers where "how many children" and "what type does this group contain" are useful at-a-glance facts. |
size | size | 'small' | 'medium' | 'large' | 'medium' | Size forwarded to the internal hy-tree (and through to every hy-tree-item). small produces the dense rows typical of token browsers; medium is the default. |
expandOn | expand-on | 'chevron' | 'row' | 'double-click' | 'row' | Activation policy for row clicks — forwarded to the inner hy-tree. Defaults to 'row' (not 'chevron', the underlying tree's default) because token browsers have no "select a group without expanding it" use case: every group exists to be opened. See the expand-on matrix on hy-tree for the full behavior. |
emptyText | empty-text | string | 'No tokens' | Text of the no-data state (no tokens in the document) when nothing is slotted into empty. |
noResultsText | no-results-text | string | 'No tokens match' | Text shown when the filter excludes every token. |
copyLabel | copy-label | string | 'Copy path' | Accessible name of each row's copy button. Use for internationalization. |
label | label | string | 'Design tokens' | Accessible name of the tree. A name on the host — `aria-labelledby` (its text) or `aria-label` — wins over it, since the role sits on the inner tree a host name can't otherwise reach. Use for internationalization. |
sortKeys | — | SortKeysFn | identitySort | Hook for domain-specific key ordering. Called with a parentPath (empty string at the root) and the list of child keys at that path. Return a reordered list. Default is identity (preserves source ordering from Object.keys). |
typeColor | — | TypeColorFn | undefined | — | Optional per-type color override for the type badge. Return null or undefined to fall back to the CSS custom-property defaults. Consumers with a pre-existing color map can pass it directly: |
decorateRow | — | RowDecorationFn | undefined | — | Optional per-row badge decorator. Called once per rendered row; the returned { badge, variant } is rendered in the row's leading slot (after the type badge, if any). Return null / undefined to skip. |
rowActions | — | RowActionsFn | undefined | — | Optional per-row context-menu builder. When set, right-clicking a row calls this function with { path, isToken, token }; if it returns a non-empty MenuAction[], the wrapper opens an internal hy-context-menu at the pointer and fires action with { path, action, isToken, token } when the user picks an item. |
Events
| Event | Detail | Description |
|---|---|---|
select | — | { path, token } — fires when the user clicks a tree item. |
copy | — | { path } — cancelable; fires before the internal clipboard write. Call preventDefault() to suppress the default write and own it. |
action | — | { path, action, isToken, token } — fires when the user picks an item from the per-row context menu (when rowActions is set). |
Slots
| Slot | Description |
|---|---|
header | Content above the tree (a filter field, …) — shown in the empty state too. |
empty | The no-data state, shown when the document has no tokens and no filter is set (the built-in empty-text when nothing is slotted). A filter that excludes every token shows no-results-text, never the slot. |
CSS Parts
| Part | Description |
|---|---|
empty | Container for the empty-state message (shown when the filter excludes every item). |
CSS Custom Properties
The documented hooks. Every override hook the component reads — one per token it paints — is listed in @whitespaceux/harmony/theme-api.json, and @whitespaceux/harmony/component-bindings.json (the Harmony MCP get-bindings tool) says which part and state each one paints.
| Property | Default | Description |
|---|---|---|
--hy-dtcg-tree-empty-foreground-subtle-rest | var(--hy-foreground-subtle-rest) | Empty-state message text color |
--hy-dtcg-tree-mono-foreground-subtle-rest | var(--hy-foreground-subtle-rest) | Monospace value-preview text color (dimension, typography, raw values) |
--hy-dtcg-tree-swatch-size | — | Edge length of a color token's swatch (1rem unless set) |
--hy-dtcg-tree-type-badge-text-transform | uppercase | Case of the type badge (uppercase unless set; none shows the three letters as written). Display only. |
--hy-dtcg-tree-row-badge-text-transform | none | Case of a row-decoration badge (none, as written, unless set — the consumer's words, as on hy-badge; uppercase shows it in capitals). Display only. |
Methods
expandAll()
Expose the internal tree's imperative methods for advanced use.
resetExpansion()
Clear all expansion state. Useful when the consumer knows the document content changed meaningfully without a reference-swap.