Skip to content
ilinxa/pro-ui

Card Tree

betav0.5.0

JSON-driven recursive card tree with a full structural editor — drag and drop, multi-select, permissions, search, validation, and undo.

Category: Data DisplayUpdated: 2026-08-11Created: 2026-04-28Author: ilinxa

Context

Card Tree renders deeply nested structured content — agent transcripts, configuration trees, decision records, runbooks, requirement docs — as a card-tree where each card has typed scalar flat fields (string/number/boolean/null/date), five predefined content blocks (codearea, image, table, quote, list), child cards, and per-card meta. v0.4 completes the safety net: sync validation hooks via 3-layer pipeline (built-in → per-action → master) with `onValidationFailed` event, plus per-commit undo/redo (state-snapshot strategy with structural sharing, default 50-step history, `Cmd+Z` / `Cmd+Shift+Z` / `Cmd+Y` keyboard shortcuts, optional `<CardTreeUndoToolbar>` sibling export). Markdown adapter (v0.5) deferred indefinitely as a separate companion module — card-tree itself is JSON-native.

Installation

Initialize shadcn (once per project)Seeds lib/utils.ts and components.json. Skip if you've already used any shadcn component.
pnpm dlx shadcn@latest init
Register the @ilinxa namespace (once per project)Add to your components.json. Merge with existing config.
"registries": {
  "@ilinxa": "https://ui.ilinxa.com/r/{name}.json"
}
Install the component
pnpm dlx shadcn@latest add @ilinxa/card-tree

Add -fixtures for dummy data:

pnpm dlx shadcn@latest add @ilinxa/card-tree-fixtures

Preview

v0.4 demo: validators + per-commit undo/redo on v0.3's structural management foundation. Toggle edit mode to enable inline editing, drag-drop reordering, multi-select (shift-click range, cmd/ctrl-click toggle), the bulk toolbar (≥2 selected), and the undo toolbar (Cmd+Z / Cmd+Shift+Z / Cmd+Y). Use the search bar to find content in collapsed subtrees and meta entries.

Preview

v0.3 · 6 levels · all features
  • Thesis outline

    title
    Adaptive UI Components for Data-Heavy Applications
    abstract
    A study of dynamic component patterns for hierarchical structured content in modern web apps.
    word_count
    28400
    status
    in-progress
    approved
    true
    defense_date
    Jun 15, 2026, 2:00 PM
    last_edited
    Apr 28, 2026
    reviewer_count
    • structured-content
    • tree-rendering
    • json-native
    • accessibility
    • round-trip
    Architecture diagram cover
    Architecture diagram cover
    • introduction

      pages
      8
      completed
      true
      The web demands components that adapt to their data, not the other way around.
      • motivation

        summary
        Why hierarchical structured content needs a first-class viewer
        relevance_score
        8.5
        addresses_gap
        true
        component-classstatic-shapeadaptive-shape
        table
        card-tree
        form-builder
        json-tree
        • industry_need

          summary
          Industry surveys 2024-2026
          sample_size
          1240
          confidence
          0.95
          peer_reviewed
          false
          ts
          interface AdaptiveProps<T> {
            data: T;
            schema?: Schema<T>;
            // shape inferred when schema is absent
          }
          • survey_result

            finding
            73% of devs build custom tree-card UIs per project
            methodology
            online survey + 14 interviews
            response_rate
            0.42
            completed
            true
            • shadcn covers primitives, not compositions
            • json-tree libs render as code, not content
            • Notion-likes lock you into a block schema
            • top_excerpt

              attribution
              Senior FE, public-traded SaaS
              interview_id
              7
              recorded
              Jan 22, 2026, 4:30 PM
              We rebuild this same hierarchical view for every product. Six weeks each, accessibility skipped, never reusable.
      • contributions

        summary
        What this thesis adds to the field
        novel_findings
        3
        • JSON-native data model with stable identity keys
        • ARIA tree contract from day one
        • Round-trip-safe serialization at every depth
    • conclusion

      pages
      4
      written
      false
      summary
      Bring it home
    • methodology

      pages
      12
      approach
      design + implementation + evaluation
      Three components, three teams, six weeks each — measured against a single shared baseline.
    • results

      pages
      18
      significant
      true
      p_value
      0.003
      effect_size
      0.81
      metricbaselinecard-treedelta
      dev-time-days212-90%
      a11y-score7698+29%
      bundle-kb4518-60%
      round-trip-fidelity

Live playground

card-tree renders a recursive card tree from a JSON CardTreeJsonNode (scalar keys are fields, nested objects are subcards, __rcmeta holds metadata). Edit it on the left, press Submit, and the editable card renders on the right.

CardTreeJsonNode · JSON
valid

23 lines · valid

Live preview

Nothing rendered yet

Edit the JSON on the left, then press Submit to render the live result on the right.

Demo source

demo.tsxtsx

Usage

When to use

Reach for CardTree when you have JSON-shaped, deeply nested, structured content — agent transcripts, configuration trees, decision records, runbooks, requirement docs — and want a card-tree view with typed-scalar fields, predefined content blocks (code, image, table, quote, list), and full keyboard accessibility.

Skip it for prose-only writing (use a markdown editor) or flat lists (use a table). Markdown source is not supported in v0.1 — card-tree is JSON-native.

Basic example

import { CardTree } from "@/components/card-tree";

export function Example() {
  return (
    <CardTree
      defaultValue={{
        title: "ADR-0042",
        status: "accepted",
        priority: 2,
        codearea: { format: "ts", content: "const x = 1;" },
        context: { reason: "..." },
      }}
    />
  );
}

Reserved keys

__rcid, __rcorder, and __rcmeta are reserved. __rcid auto-generates via crypto.randomUUID() if absent. __rcorder controls sibling order (integer, gaps allowed). __rcmeta is a per-card scalar map exposed via the metaPresentation prop.

Predefined keys

Five reserved-name fields render as styled blocks:

  • codearea{ format, content }
  • image{ src, alt? }
  • table { headers: string[], rows: scalar[][] }
  • quote — a string
  • list — an array of scalars

Add a key to disabledPredefinedKeys to opt out — the parser then treats it as a flat field instead.

Field value typing

Flat-field values are JSON scalars: string, number, boolean, null. Type is inferred at parse time and rendered per type (numbers right-aligned mono; booleans as check / dash icons; ISO-8601 date strings formatted via Intl.DateTimeFormat; null as a muted em-dash).

Pass dateDetection="never" to disable date inference, or a custom predicate function for fine control.

Children

Any non-reserved, non-predefined property whose value is a plain object becomes a child card. Arrays of objects are rejected in v0.1 — convert to object-keyed form (e.g. { items: { item_0: a, item_1: b } }) or use the list predefined key for scalar arrays.

State model

The component is uncontrolled: defaultValue is the seed. To reset, remount via the key prop. Read the current state imperatively via a ref:

const ref = useRef<CardTreeHandle>(null);
// ...
const json = ref.current?.getValue();   // canonical JSON string
const tree = ref.current?.getTree();    // object form with auto-IDs

Accessibility

The tree implements the full ARIA tree contract: role="tree", role="treeitem", aria-level, aria-expanded. Keyboard: arrows navigate visible cards, expands / descends, collapses / ascends, Home / End jump to first / last, Enter / Space toggles collapse on a card with children.

Features

  • JSON-native: accepts any plain object as a card; auto-attaches __rcid + __rcorder
  • Typed flat-field rendering: numbers right-aligned mono, booleans as icons, ISO-8601 dates formatted
  • Five predefined-key content blocks (codearea, image, table, quote, list) + custom-key registration
  • Per-level + per-predefined-key slot styling
  • Full ARIA tree contract with keyboard nav (arrows, home/end, expand/collapse, multi-select)
  • Three meta presentation modes (hidden, inline, popover) with custom renderers + audit trail
  • Inline editor: click-to-edit fields, keys, titles, predefined blocks, and meta entries
  • Drag-drop reordering with 2 scopes (same-level + cross-level), keyboard alternative via @dnd-kit
  • Multi-select with shift-click range + cmd-click toggle; bulk delete / duplicate / set-field / toggle-lock
  • Permission matrix with declarative shorthand + 11 predicate escape hatches; meta-locked cascade
  • Native data-model search: finds matches in collapsed cards and meta — auto-expands path
  • Configurable delete policy (cascade / promote) + collision strategy (suffix / qualify / reject)
  • Root-removal opt-in with onRootRemoved callback + emptyTreeRenderer prop
  • v0.4 sync validation hooks (per-action + master); onValidationFailed event for analytics
  • v0.4 per-commit undo/redo with state-snapshot (default 50-step history) + Cmd+Z keyboard binding + optional UndoToolbar sibling export
  • Imperative handle: getValue / getTree / isDirty / markClean / setSelection / focusCard / addCardAt / removeCard / replaceRoot / getEffectivePermissions / findNext / findPrevious / scrollToMatch / clearSearch / undo / redo / canUndo / canRedo / clearHistory

Tags

card-treetreeoutlinejsonviewereditordrag-droppermissionssearchstructured-contentdata

Dependencies

shadcn primitives: popover
npm peer deps: lucide-react@^1.11.0, @dnd-kit/core@^6.3.1, @dnd-kit/sortable@^10.0.0, @dnd-kit/utilities@^3.2.2