Universal UI from semantic data. Describe what your data means — the compiler figures out how it looks.
Project description
ViewSpec
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.
What ViewSpec Does
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
Three Invariants
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.
Install
pip install viewspec
Requires Python 3.11+.
Demos
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.
Core Concepts
Semantic Substrate
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"})
ViewSpec
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")
CompositionIR
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.
Emitters
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.
Motif Types
| 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.
Compilation
Reference Compiler (free, offline)
Handles the four standard motifs locally. No API, no network, no LLM. Deterministic.
ast = compile(builder.build_bundle())
Hosted Compiler (api.viewspec.dev)
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 |
Wire Format
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-trip
Examples
See 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
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file viewspec-0.1.0.tar.gz.
File metadata
- Download URL: viewspec-0.1.0.tar.gz
- Upload date:
- Size: 116.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
152d0a29044cca00b29adc69deed093c40fa8e94af1d3d0fc47c59846cf41a53
|
|
| MD5 |
9369b7dae48b62bf56fe3a1e319c52f6
|
|
| BLAKE2b-256 |
efe99c1457e8340be2309f011976ea71a6d5629128bc3ade0711c847bb3562ad
|
File details
Details for the file viewspec-0.1.0-py3-none-any.whl.
File metadata
- Download URL: viewspec-0.1.0-py3-none-any.whl
- Upload date:
- Size: 26.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
332e6cda8a3edd35b6d1cd658eb64087de3ef51704a92d368d3b8380051fb45a
|
|
| MD5 |
ad8ba538666b375fad06dfe649831fd0
|
|
| BLAKE2b-256 |
ada96f5c4218ccbbab829e224391d4ceafefb5133de5f157a954a512f649a5c5
|