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

Three tiers: Views → Dashboards → Analyses

  1. ViewsLiveOne dataset, many ways to see it: a table, a chart, a board, a map, all from one .base file.
  2. DashboardsLiveMany views laid out on a grid over linked datasets: one rail filters them all, a click in one narrows the rest.
  3. AnalysesLiveA report page that answers one question: prose with views and dashboards embedded, a sticky contents list and decision callouts.

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
data-uirobotparts.dev's own analysis renderer (its PEP 0002), now the kit's <data-analysis> and <data-analyses> (docs/peps/0007-analyses.md); W&B reports, as a file format an agent writes.Analyses · reports that mix text with live views
table cards kanban
ObjectDBObjectDB's home page (object-database site/), which draws its own category sections and links; the grouped gallery is the cards view with headers and sub-headers, so ObjectDB's home becomes one .base view.Object collection
cards table
object database~/bam-waste-objects/views/Objects.base (Cards, Real scans) and the FiftyOne App's grid on port 5162Object library · 3D shelf (proposal 1)
cards table source
object database~/bam-waste-objects/views/Types.base and Materials.base (flat tables with a backlink formula)Object library · family tree (proposal 3)
graph table cards
object database~/bam-waste-objects/views/Materials.base and the spectra CSVs nobody can see without a scriptObject library · material lab (proposal 5)
table chart
object database~/bam-waste-objects/views/composition.html (object-db composition) and FiftyOne's saved views per mixObject library · stream board (proposal 4)
chart table pivot
object databasenothing: it is the same files, so Obsidian and data-ui show one library the same way. The verbatim copies are in library/.Object library · the dataset's own Bases (proposal 2)
cards table
BAM rigpidtuna.com's per-robot result tables and the rack tuner's printed summaries (bam_pid_tunning/docs/build_report.py); the MuJoCo replays stay on pidtuna.com (~/pidtuna-website/site) and the design rationale in ~/bam_ws/reports/pid-tuner-design-rationale-2026-10-26.html.PID tuning results
kpi chart table pivot
data-uiopening the data file in a text editor or a spreadsheet to see what a view is really drawing from (a spreadsheet re-reads 1.50 as 1.5 and trims spaces; an editor shows a 2,000-row JSON file as one line).Raw source: a CSV as it is
source table
BAM rigthe rack motor models report's tables and its per-motor JSON (bam_ws reports/media/rack-motor-models-2026-10-23/report.html, built by bam_rack_twin/docs/) and the model cards' markdown tables (model_cards/*.md), for browsing; the report keeps the figures and video.Sim-to-real · rack motor twins
kpi chart table
BAM rigthe summary tables of traj_compare's reports (traj_compare.write_comparison_report, in bam_ws trajectory_generation/traj_compare) and the per-run numbers inside budget_can*.json, for comparing across runs; traj_compare stays the renderer of every time series (the trajectory-comparison skill: never a bespoke renderer).Trajectory tracking
kpi chart table
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 ladder drawn in prose in robotparts' models proposal (docs/proposals/models-2026-10-25.md, "Rung k adds a layer of physics") and the rung column of the models-benches pivot.Model lineage
lineage 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
robotparts
~/robotparts
robotparts' part page header (its Specs / User data / Config tabs over a separate search box and filter words).The bar, Notion's pattern: visible views, folded filters, a search icon
table cards chart
robotparts
~/robotparts
Notion's database header (view tabs with "N more…" and +, the Filter / Sort words and the View settings side panel), as robotparts' tab mock drew it (zach_brain Sources/Images/Pasted image 20261026122336.png and …122558.png).The bar, Notion's way: tabs and pills
table cards chart pivot
robotparts
~/robotparts
Obsidian Bases' toolbar (the view chip and its Configure view menu, and the Sort / Group / Filter / Properties words; zach_brain Sources/Images/Pasted image 20261026122137.png, …122154.png and …122424.png).The bar, Obsidian's way: one chip and spread-out words
table cards chart pivot
robotparts
~/robotparts
Shopify admin's index table bar ("All ⌃⌄ · Search and filter · columns") and its bulk-action band (zach_brain Sources … images 86 and 87, 2026-10-26).The bar, Shopify's way: three elements
table cards chart
robotparts
~/robotparts
the full toolbar's row of words (Layout, Group, Sort, Filter, Properties, Units, ⋯) and the toolbar proposals' six mocks (lane/toolbar-proposals @ 4481dfe).The bar: data-ui's toolbar
table cards chart pivot
robotparts
~/robotparts
Shopify admin's order search (its query builder suggesting properties, operators and values) and its bulk bar ("2 selected ▾ · actions · ⋯ · Show all selected"); Obsidian Bases' advanced filter expression.The query bar: tokens, autocomplete and bulk actions
table cards chart pivot
Vibe Tracks
~/vibetracks
nothing (new): Vibe Tracks has no Decisions page yet; its records are read as files in docs/peps/.Decisions (Vibe Tracks)
table graph
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 track page's Overview KPI section and its Spec tab's run list (vibetracks~/track.js, kpi-panel.js, the PEP 0012 history view's run list), for reading.Vibe Tracks · one track
table kpi chart
Vibe Tracks
~/vibetracks
the Results tab's FINOS Perspective pivot over a track's runs (PEP 0006, vibetracks~/pivot.js) and the hand-built run lists; Perspective stays only as the million-row escape hatch (SPEC §8.10).Vibe Tracks · run explorer
kpi chart table pivot
Vibe Tracks
~/vibetracks
the home's track rows and KPI tiles (vibetracks~/views.js, cards.js, kpi-panel.js) and the hand-written roll-up in vt state (rollup), for reading; Vibe Tracks keeps computing its own state.Vibe Tracks · track overview dashboard
kpi table chart pivot
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: a synthetic facility
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
Brandbook's hand-built phylogeny prototype (~/brandbook/src/variants/v3-phylogeny/, React + d3-hierarchy over one brand at a time) and the per-round logo galleries it links to.Brand lineage
lineage table
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