Universal UI from semantic data.
ViewSpec is a declarative language for describing what data means. The compiler figures out how it looks. Every pixel has a birth certificate.
🌐 viewspec.dev — Live demos and interactive playground
from viewspec import ViewSpecBuilder, compile
from viewspec.emitters.html_tailwind import HtmlTailwindEmitter
builder = ViewSpecBuilder("invoice")
table = builder.add_table("items", region="main", group_id="rows")
table.add_row(label="Design System Audit", value="$4,200")
table.add_row(label="Component Library", value="$8,500")
table.add_row(label="API Integration", value="$3,100")
ast = compile(builder.build_bundle())
HtmlTailwindEmitter().emit(ast, "output/")
# That's it. Full UI. Full provenance. No CSS.Before ViewSpec: You manually bridge the gap between data and UI. Every component, every prop, every layout decision — hand-wired by a developer.
After ViewSpec: You declare what the data means. The compiler determines the visual structure. Rendering is a pluggable backend.
Data → ViewSpec (semantic intent) → Compiler → CompositionIR → Emitter
├── HTML/Tailwind (shipped)
├── Canvas/Pretext
├── PDF
├── Native mobile
└── Your custom emitter
ViewSpec enforces three mathematical guarantees:
-
Exactly-once provenance. Every data binding is routed exactly once. Nothing dropped. Nothing duplicated. Nothing hallucinated.
-
Semantic grouping. Data is grouped by meaning, not by visual adjacency.
-
Strict ordering. The original data order is preserved as a mathematical guarantee.
pip install viewspecRequires Python 3.11+.
Six interactive demos at viewspec.dev:
| Demo | What it shows |
|---|---|
| Same Data, Three Motifs | One dataset → table, dashboard, or comparison. Change one parameter. |
| Provenance Inspector | Hover any element. Trace DOM → IR → binding → address → raw data. |
| Live Builder | Browse ViewSpec JSON, IR tree, and rendered output in sync. |
| The Invariants | Watch the compiler enforce — and refuse — each guarantee. |
| 15 Lines → Full UI | An invoice table builds itself from 15 lines of Python. |
| Style Derivation | Same structure, different feel. Toggle four visual presets. |
Text rendering powered by Pretext canvas surfaces.
The raw data graph. Nodes with typed attributes, slots, and edges. This is WHAT the data is — no visual intent.
builder = ViewSpecBuilder("my_view")
builder.add_node("user_1", "person", attrs={"name": "Alice", "role": "Engineer"})
builder.add_node("user_2", "person", attrs={"name": "Bob", "role": "Designer"})The declarative intent layer. Regions (WHERE data can go), bindings (WHICH data goes WHERE), motifs (HOW it should be structured), and styles (how it should FEEL).
table = builder.add_table("team", region="main", group_id="members")
table.add_row(label="Alice", value="Engineer")
table.add_row(label="Bob", value="Designer")The compiler's output. A strict hierarchical tree of 12 UI primitives (root, stack, grid, cluster, surface, text, label, value, badge, image_slot, rule, svg) with full provenance tracking. Every IR node knows which semantic addresses and intent refs produced it.
Pluggable renderers that turn CompositionIR into concrete output. Subclass EmitterPlugin:
from viewspec.emitters.base import EmitterPlugin
class MyEmitter(EmitterPlugin):
def emit(self, ast_bundle, output_dir):
# Walk ast_bundle.result.root.root and produce output
...The included HTML/Tailwind emitter produces standalone HTML with full Tailwind styling, provenance data attributes on every DOM element, action event dispatch, and a JSON provenance manifest.
| Builder | Motif | Use case |
|---|---|---|
add_table() |
table |
Tabular data with label-value rows |
add_dashboard() |
dashboard |
KPI cards with label-value pairs |
add_outline() |
outline |
Hierarchical outlines and trees |
add_comparison() |
comparison |
Side-by-side comparisons |
Each builder returns a chained sub-builder. Compose them freely within a single ViewSpec.
Handles the four standard motifs locally. No API, no network, no LLM. Deterministic.
ast = compile(builder.build_bundle())For complex layouts, novel data shapes, and advanced derivation. The hosted compiler was evolved (not hand-written) using reinforcement learning:
- 13/13 on a static validation suite
- 50/50 on novel, randomized out-of-distribution layouts (one-shot)
- Level 2 derivation tokens — data-aware emphasis, narrative routing, palette energy
- Zero LLM cost at runtime — deterministic Python, ~3ms per compile
from viewspec import compile_auto
# Try local first, fall back to hosted for unsupported motifs
ast = compile_auto(builder.build_bundle())| Tier | Price | Hosted Calls/Day |
|---|---|---|
| Free | $0 | 500 |
| Pro | $39/mo | 25,000 |
| Scale | $99/mo | 250,000 |
| Enterprise | Contact | Custom |
Protocol Buffers for language-agnostic serialization. The same ViewSpec can be constructed in Python, Rust, Go, TypeScript, or any language with protobuf support.
bundle = builder.build_bundle()
json_data = bundle.to_json() # JSON round-trip
proto_bytes = bundle.to_proto().SerializeToString() # Protobuf round-tripSee examples/:
invoice_table.py— Build a table in 15 lineskpi_dashboard.py— KPI dashboard with style tokenscomparison_view.py— Side-by-side comparisonemit_html.py— Load a compiled AST and emit HTML
MIT