data-ui 0.1.0

One dataset, one .base file, many views.

A drop-in kit for viewing any data in your apps: framework-free custom elements, Obsidian's Bases grammar for the views, and tokens that default to shadcn's names. Not an app: you copy it into yours.

Open the galleryDrop it into an app

Live: robotparts' 150 photographed actuators, one JSON file and one .base file. Every tab is the same rows; the panel shows the entry the tab draws. Hover a dot, drag a card, open a row.

Thirteen views, one row model

Each thumbnail is the kit running on a gallery case's real data. Filters, search, sort and grouping run once, in the core; a view only draws. Click one for its case.

Custom elements

<data-views>, <data-facets>, <data-value>. Framework-free; React 19 uses the same tags. Light DOM, so the page's fonts and its no-JS table still apply.

One grammar

Obsidian Bases .base files: filters, order, sort, groupBy, summaries, plus data-ui's keys. Every Bases file is a data-ui file; inside chart: the vocabulary is Vega-Lite's.

Tokens, not theming knobs

Set --data-*, or nothing: each token falls back to shadcn's variable name, so a shadcn app needs zero lines and a vanilla app writes one block.

Files are the agent interface

No AI inside. A JSON Schema and data-ui check make a .base file safe for Claude Code or Codex to write; the page re-renders when the file changes.

Drop it into an app

Copy it in, pinned

shadcn/ui copies a component's source into your app, and from then on you own it and edit it. data-ui copies the built kit instead: dist/ goes into your static folder whole, with manifest.json listing every file's sha256, and you never edit it there. To update, re-copy. You change data-ui from the outside: tokens for its look, .base files for its views, registerView() for a view of your own.

Why not the shadcn way? Copies that apps edited are how Clank's views froze, one fork per app. A pinned copy is one version everywhere, and still nothing loads from a CDN. From a bundler, the same files are a pinned package: import 'data-ui', 'data-ui/kpi'.

npm --prefix ~/data-ui run build
cp -r ~/data-ui/dist/{data-ui.js,data-ui.css,views,LICENSES.md,manifest.json} static/vendor/data-ui/

Two tags, one file

<link rel="stylesheet" href="/static/vendor/data-ui/data-ui.css">
<script type="module" src="/static/vendor/data-ui/data-ui.js"></script>

<data-facets for="parts"></data-facets>
<data-views id="parts" src="/data/actuators.base"></data-views>

Only the core loads up front; each view's code loads the first time it shows. <data-kpi> and <data-sparkline> work on their own with views/kpi.js (a plain page).

A .base file

data: actuators.json          # JSON rows, CSV, JSONL or a field schema
filters: '!photo.isEmpty()'
views:
  - type: table
    name: Table
    order: [model, brand, peak_torque, mass]
    sort:
      - { property: peak_torque, direction: DESC }
  - type: chart                  # inside chart: it is Vega-Lite
    name: Chart
    chart:
      encoding:                  # no mark: Show Me picks one
        x: { field: mass, scale: { type: log } }
        y: { field: peak_torque }

Tokens, or nothing

Every colour, radius and font is a --data-* token that falls back to shadcn's variable, so a shadcn app maps nothing. Anything else maps one block, light and dark:

:root {
  --data-bg: var(--bg);       --data-fg: var(--ink);
  --data-muted: var(--ink2);  --data-border: var(--rule);
  --data-accent: var(--accent);
  --data-font: var(--sans);   --data-radius: 6px;
}

Bring your own agent: the .base file is the API

data-ui has no AI and no agent protocol inside. Its agent interface is its files: the data, its field schema and one .base file of views, which schemas/view.schema.json describes and data-ui check verifies, one line per mistake with a suggestion an agent can act on. Your own Claude Code or Codex edits the file with your account; the page draws it on its next load, or at once when the host calls reload() on a file change. Going the other way, ⋯ copies the view a person built in the browser as YAML, and #view= carries it in a link. Live page state (what is selected, what is on screen) stays optional and never goes through a localhost socket (robotparts' agent-bridge proposals, 2026-10-25).

node ~/data-ui/bin/data-ui.mjs check views.base --data actuators.json

Every value says how sure it is

A cell can be a value with provenance, {value, unit, basis, source, asOf}. <data-value> draws it the same way in a table, a card or a report; its text is the no-JS truth. Hover a value.

Stall torque, SteadyWin's datasheet3.82 N·m
Coulomb friction, BAM's rack (CAN 14)0.0551 N·m
Peak torque, a seller's listing5.5 N·m
Rotor inertia—

● measuredD datasheetS seller~ derived○ estimateA assumedC chosen≈ sampled◌ stand-in” claimed

Replaces

Every gallery case rebuilds a screen someone hand-rolled in one of Zach's apps. Same data, a .base file instead of the code.

AppHand-rolled todayRebuilt as
robotparts
~/robotparts
Splice's hand-drawn rack canvas (robotparts reports/media/rack/splice-rack-2026-10-22.png) for browsing the wiring. Not its analysis: the voltages, currents and findings here are Splice's own results, copied.1-DOF rack wiring
graph table
robotparts
~/robotparts
robotparts.dev's hand-rolled Chart view (site/static/site.js scatter(): one log-log SVG with an X and a Y picker, no colour, no presets, no hover card) and the views lane's demo chart (docs/views/demo/views/chart.mjs).Actuator charts
chart table
robotparts
~/robotparts
nothing yet: the models proposal had no per-model page (Zach, 2026-10-26: "click it and get information on that actual model, perhaps … it renders the README of the model").Actuator model page
record table
robotparts
~/robotparts
the hand-built tables of robotparts' models proposal round 2 (lane/datasheet @ ec9a13c, docs/proposals/build_models_report_r2.py: compare_table, model_rows, dist_svg, the verdict cards), the rack's hand-rendered model cards (model_cards/can<N>.md), the table lane's models.base and Vibe Tracks' Perspective results pivot.Actuator models · benches, leaderboard, parameters
pivot table chart
robotparts
~/robotparts
robotparts' hand-rolled compare page, /search/compare/ (site/static/site.js in ~/robotparts, compare(), about 150 lines, and build/site_pages.py, compare_page()): a fetch per part, a table built by hand, a "(remove)" link that rewrites the query string. Here the same side by side is a compare view in a .base file, and a pick is a view you can save.Actuators side by side
compare record table
robotparts
~/robotparts
the three-kind rail (values, range, has) robotparts.dev's category pages use today (robotparts/site/views/actuator.base), and each site's own hand-built filter widgets that the kinds copy: McMaster-Carr's pictorial facets, Algolia InstantSearch's widgets, Airbnb's price histogram, Amazon's rating and price bands, Digi-Key's parametric columns, Booking.com's popular filters.Filters
table cards chart
robotparts
~/robotparts
the datasheet proposal's hand-built tables (robotparts lane/datasheet).GIM4305-10 datasheet
table
robotparts
~/robotparts
the hand-built HTML tables and SVG strips of robotparts' models report (lane/datasheet docs/proposals/build_models_report_r2.py: dist_svg, the per-test rows, the floor and twin-grade lines drawn by hand) and the table lane's models.html prototype (design/tables-2026-10-25/presets/models.base).Models × benches
pivot chart table
robotparts
~/robotparts
scanning Sources/Daily Notes/ by file name and git tag -l 'release/*'; there was no calendar of either.Releases and daily notes
calendar table
robotparts
~/robotparts
robotparts' hand-rolled category table, its Rows view and its left rail (site.js), and the table lane's prototype page.robotparts actuators
table cards pivot
robotparts
~/robotparts
robotparts' hand-rolled picture cards: the round-2 design babble's image-first card grid (design/babble-2026-10-22b/w5-gallery/gallery.js, cardHtml / cardMedia) and the PCPartPicker variant's photo cells (w2-pcpartpicker/app.js), each its own grid, image well and spec lines.robotparts photos
cards table
robotparts
~/robotparts
reading git log --graph and the release tags by hand; robotparts has no timeline page.robotparts releases and lanes
timeline table
robotparts
~/robotparts
the part page's "Ships to <region>: yes / no" column (robotparts build/site_pages.py) and the seller table in docs/sellers.md for the question "who can ship to me".robotparts sellers by country
map table
robotparts
~/robotparts
robotparts' hand-rolled /sources/ page: sources_page() in build/site_pages.py in ~/robotparts (the table, its facet form and its counts) and the freshness script in site/static/site.js that adds the "needs refresh" badges in the browser. Here the table, the grouping, the rail and the badges are the kit's.robotparts sources
table list record
Vibe Tracks
~/vibetracks
Vibe Tracks' hand-rolled Results tab (the Perspective-based runs pivot and its leaderboard) and the per-experiment metric charts its tracks draw by hand; the same shape as W&B's or MLflow's runs table and charts.ML experiment tracking
pivot chart kpi table
Vibe Tracks
~/vibetracks
the sunset Obsidian Vibe Kanban plugin's board (its .base files in Zach's vault now fall back to a stock table grouped by a formula.status that hard-codes the column order) and the Board display of Vibe Tracks' own home (VT-018), whose rule this board keeps: "derived columns still refuse drops: state is derived, never claimed" (VT-025).Vibe Tracks feature board
kanban table
Vibe Tracks
~/vibetracks
the Spec tab's hand-rolled tree in Vibe Tracks, VT.ui.specTree in vibetracks~/spec.js in ~/vibetracks (story: SpecTree.stories.js; docs/peps/0007 "the feature tree"). Vibe Tracks keeps computing the tree (vt spec tree) and running the suites; drawing it, folding it, filtering it and the side peek are the kit's.Vibe Tracks feature tree
list table record
Vibe Tracks
~/vibetracks
Vibe Tracks' hand-rolled KPI section (vibetracks~/kpi-chart.js: <vt-kpi-progress>, the headline and its driver tiles on ECharts, about 2,000 lines) and the tiles of kpi-panel.js (VT.ui.kpiTile, Storybook's Components/KPI tile).Vibe Tracks KPIs
kpi chart table
dataclass viewer
~/py_dataclass_viewer
the hand-rolled data views of py_dataclass_viewer (static/app.js in ~/py_dataclass_viewer): the Outline (outlineView, treeNodes), the Sheet and the Controls (controlsView, ctlRow). The viewer keeps its file tree, its CodeMirror source and its byte-splice writes; the views over the values are the kit's.Dataclass viewer: facility inputs
list record table
SystemSketch
~/systemsketch
the hand-placed tldraw board (systemsketch/sketches/review/communication-projection-prototype.systemsketch) for reading the system's structure; SystemSketch stays the editor.SystemSketch blocks
graph table
Money
~/money
money has no transaction list of its own: its Flask pages (statements.html and home.html in ~/money, src/money/templates) show totals by category, and the transactions themselves are only in Fava's journal. This is the list those totals come from, grouped the way the statements group them, with the category as a field you can propose a change to (money's own fix is a rule in rules.toml).Money transactions (illustrative)
list table record
design work
~/robotparts
the babble skill's hand-built comparison galleries for those rounds (design/babble-2026-10-22*/ build_gallery_spec.py writing gallery-spec.json, whose media entries inline every still as base64 HTML).robotparts design babble
cards table
design work
~/robotparts
the hand-built logo gallery reports, one per round (reports/media/robotparts-logos-r2…r11-2026-10-25.html in robotparts, each written by its round's own build_gallery.py, which inlined every SVG into a page of its own).robotparts logo rounds
cards table