Skip to content

HyDtcgTree ​

Usage examples on this page are written for Lit / plain HTML (<hy-dtcg-tree>). The same component ships as HyDtcgTree in @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-preview so 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-list or hy-table.
  • A color preview outside a tree: hy-swatch or hy-swatch-group.

Accessibility: your part ​

  • Set label to what this token set is. The default "Design tokens" does not say which set.
  • Give every control you add in the header slot, and every rowActions item, 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. ​

html
<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. ​

html
<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. ​

html
<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 ​

js
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) ​

js
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 ​

PropertyAttributeTypeDefaultDescription
tokens—DtcgDocument{}Parsed DTCG document.
selectedPathselected-pathstring''Dot-path of the currently selected token. Reflected.
filterTextfilter-textstring''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.
strictFilterstrict-filterbooleanfalseWhen 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.
copyablecopyablebooleantrueShow the copy-path icon button in each row's trailing slot.
valuePreviewvalue-previewbooleantrueShow inline value previews (swatch, dimension, shadow thumb, etc.).
showTypeBadgeshow-type-badgebooleanfalseRender 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).
showGroupMetashow-group-metabooleanfalseRender 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.
sizesize'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.
expandOnexpand-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.
emptyTextempty-textstring'No tokens'Text of the no-data state (no tokens in the document) when nothing is slotted into empty.
noResultsTextno-results-textstring'No tokens match'Text shown when the filter excludes every token.
copyLabelcopy-labelstring'Copy path'Accessible name of each row's copy button. Use for internationalization.
labellabelstring'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—SortKeysFnidentitySortHook 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 ​

EventDetailDescription
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 ​

SlotDescription
headerContent above the tree (a filter field, …) — shown in the empty state too.
emptyThe 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 ​

PartDescription
emptyContainer 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.

PropertyDefaultDescription
--hy-dtcg-tree-empty-foreground-subtle-restvar(--hy-foreground-subtle-rest)Empty-state message text color
--hy-dtcg-tree-mono-foreground-subtle-restvar(--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-transformuppercaseCase of the type badge (uppercase unless set; none shows the three letters as written). Display only.
--hy-dtcg-tree-row-badge-text-transformnoneCase 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.

Built with Lit. Documented with VitePress.