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.
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.
Table table
Two group levels with fold and summaries, bars in cells, units in the header.
robotparts actuators · By brand →
Pivot pivot
Groups × split columns, one value per cell, the records behind it.
Models × benches · Benchmarks →
List and tree list
One line per row; with tree:, an outline that folds.
Vibe Tracks feature tree · Spec tree →
Record and form record
One row as a sheet in sections; edit: true makes it a form.
Actuators side by side · Part page →
Compare compare
Rows in columns, the better value marked, a photo slider.
Actuators side by side · Side by side →
Cards and media grid cards
Bases' cards, virtualised; lightbox: true opens a media grid.
robotparts photos · Actuators →
Kanban kanban
Columns from groupBy; a drag proposes an edit, the host decides.
Vibe Tracks feature board · By status →
Chart chart
Vega-Lite inside chart:, Show Me defaults, Ashby envelopes.
Actuator charts · Ashby · torque vs mass →
KPI kpi
The number, its basis, a delta coloured by which way is better.
Vibe Tracks KPIs · KPIs →
Calendar calendar
Month, week and list grids; drag to propose a new date.
Releases and daily notes · Month →
Timeline timeline
Bars and points on a zoomable axis, in lanes from groupBy.
robotparts releases and lanes · Lanes by kind →
Graph graph
React Flow and dagre: edges from a property, groups as subflows.
1-DOF rack wiring · Wiring →
Map map
MapLibre on a bundled world map; no tile server.
robotparts sellers by country · Where sellers ship →
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.
● 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.
| App | Hand-rolled today | Rebuilt 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 |