view-contracts
v0.5.0
Published
Contract-first UI design toolchain — restricted TSX to View IR to platform renderers
Downloads
1,787
Maintainers
Readme
view-contracts
Contract-first UI development for AI-native applications.
view-contracts is a UI definition system that separates:
- Screen semantics → OpenAPI
- Visual structure → Restricted TSX
Instead of mixing API contracts, business logic, and UI implementation inside React components, view-contracts treats UI as a compilable contract.
Why?
Modern UI development has a structural problem.
React Component
├─ API knowledge
├─ ViewModel mapping
├─ Business rules
├─ Layout
└─ StylingAs applications grow:
- UI becomes difficult to regenerate
- AI-generated code drifts from design rules
- Multi-platform support becomes expensive
- Design systems become guidelines instead of contracts
view-contracts introduces a contract-first approach:
OpenAPI
↓
Screen Contract
Restricted TSX
↓
View Definition
Compiler
↓
Generated UIThe goal is to make UI generation deterministic.
Core Concepts
OpenAPI owns semantics
OpenAPI defines:
- screens
- actions
- navigation
- ViewModels
Example:
/admin/dashboard:
get:
operationId: getAdminDashboard
x-screen-id: SCR-ADMIN-DASHBOARDThis is the source of truth for screen behavior.
TSX owns layout
A .view.tsx file defines:
- component hierarchy
- layout structure
- visual composition
variantnames (appearance resolved fromviews/design/theme.yaml)
Design tokens and theme own appearance
Color, spacing, typography, and component variants are defined in project YAML — not in view source:
views/design/tokens.yaml— DTCG design tokensviews/design/theme.yaml—Button.primary,Text.muted, etc.views/design/extensions.css— optionalext-*styles for extension components
See docs/design-tokens-and-theme.md.
Compile instead of hand-coding
.view.tsx
↓
Compiler (in-memory View IR)
↓
Renderer
↓
generated/react/ (*.generated.tsx, components/, runtime/)The generated output is deterministic.
Current Status
Supported
- React generation (
generated/react/— screens,runtime.ts,theme.css, …) - SwiftUI generation (
generated/swiftui/— full DSL element coverage, theme variant dictionary,SampleData.jsonfrom mock YAML SSoT) - Design tokens + theme variants (
views/design/tokens.yaml,theme.yaml) - Live DSL preview (
dev:preview) withviews/preview/*.mock.yamlsample data - Extension registry (
extensions.yaml) with plain HTML/CSS implementations - In-memory View IR compilation
- Standalone app scaffold (
.vite/per example project) - Table DSL with configurable
separator/separatorColor/border/borderColor - Dynamic variant resolution via
IrConcat(runtime theme dictionary in SwiftUI)
Planned
- Jetpack Compose renderer
- PDF renderer
The contract model is designed for multi-target generation. React and SwiftUI are production-ready targets.
Architecture
Two sources of truth converge through bindings, type-checking, and View IR.
flowchart TB
subgraph ia ["Information architecture"]
openapi["contracts/openapi.yaml<br/>screens · ViewModels · actions"]
end
subgraph design ["Visual design"]
viewTsx["views/*.view.tsx<br/>layout · composition · variant names"]
tokens["views/design/tokens.yaml"]
theme["views/design/theme.yaml"]
end
openapi --> microContracts["micro-contracts generate"]
microContracts --> contractsTs["generated/contracts.ts"]
openapi --> bindings["view-bindings.yaml"]
viewTsx --> bindings
tokens --> build
theme --> build
contractsTs -->|"imports ViewModel type<br/>validates vm.* and operationId"| viewTsx
bindings -->|"screen ↔ view mapping"| build["view-contracts build"]
viewTsx --> build
build --> viewIr["View IR (in-memory)"]
viewIr --> reactRenderer["renderComponents"]
reactRenderer --> generatedReact["generated/react/*.generated.tsx"]
build --> runtimeCopy["renderRuntime → generated/"]
build --> bridgeGen["render bridge"]
runtimeCopy --> generatedTheme["generated/react/theme.css"]
bridgeGen --> bridgeTs["generated/react/view-bridge.generated.ts"]
runtimeCopy --> generatedRuntime["generated/react/runtime.ts"]
generatedReact --> app["Standalone app (src/main.tsx)"]
generatedTheme --> app
bridgeTs --> app
generatedRuntime --> appWhere the two designs meet
| Stage | What merges | Output |
| --- | --- | --- |
| Authoring | OpenAPI types flow into .view.tsx via generated/contracts.ts | Type-safe view source |
| Binding | view-bindings.yaml links a screen (operationId, ViewModel) to a view file | No schema duplication |
| Compilation | Compiler reads layout from TSX and lowers vm.* / action to language-neutral IR | In-memory View IR |
| Design | tokens.yaml + theme.yaml validate and emit generated/react/theme.css | CSS variables + .vc-* variant rules |
| Generation | Renderer copies runtime + emits React from IR + bridge registry | generated/ (no @view-contracts imports) |
Preview path (no compile step)
flowchart LR
openapi["openapi.yaml"] --> microContracts["micro-contracts"]
microContracts --> contractsTs["contracts.ts"]
contractsTs --> viewTsx["*.view.tsx"]
mockYaml["views/preview/*.mock.yaml"] --> previewPlugin["preview mock plugin"]
viewTsx --> preview["Vite preview"]
previewPlugin --> preview
preview --> reactDev["Vite dev server<br/>design verification"]Designers and developers validate layouts without running the full build. Preview loads sample ViewModels from views/preview/*.mock.yaml (not from generated/). Production apps load real data via operationId in view-bindings.
See docs/ARCHITECTURE.md, docs/design-tokens-and-theme.md, docs/extensions.md, and docs/SPEC_COVERAGE.md.
Quick Start
npm install
npm run build:views # compile + generate + scaffold .vite/
npm run dev # preview (5173) + dev app (5174)| URL | Purpose |
| --- | --- |
| http://localhost:5173/.vite/preview.html | DSL preview — views/*.view.tsx only |
| http://localhost:5174/.vite/index.html | Dev app — generated/ + src/main.tsx |
Ports are fixed in examples/admin-dashboard/dev.ports.ts (strictPort: true).
Other useful commands:
npm run dev:preview # preview only
npm run dev:app # standalone dev app only
npm run check:both # build + preview dev + production bundle (4174)Example Project
The repository includes an example admin dashboard:
examples/admin-dashboard/
├── views/ # restricted .view.tsx (preview compiles live)
│ ├── design/ # tokens.yaml, theme.yaml, extensions.css
│ └── preview/ # *.mock.yaml — preview sample ViewModels only
├── src/ # app wiring, extension components (HTML/CSS), handlers
├── generated/ # build output (standalone — no @view-contracts imports)
│ ├── react/ # React generated code
│ └── swiftui/ # SwiftUI generated code + SampleData.json
├── ios-preview/ # SwiftUI preview app (XcodeGen project)
│ ├── Sources/ # ContentView, ViewModels (Codable, loads SampleData.json)
│ └── project.yml # XcodeGen definition
├── contracts/ # OpenAPI + micro-contracts config
├── extensions.yaml # extension registry (Grid, Table, …)
├── dev.ports.ts # fixed dev server ports
├── scripts/ # dev-both.sh, check-both.sh, ports.sh
└── .vite/ # auto-generated Vite configs (gitignored)See examples/admin-dashboard/README.md for local verification steps.
Extensions (Grid, Table, …) are declared in extensions.yaml and implemented in src/components/extensions.tsx with plain HTML/CSS — not DSL. See docs/extensions.md.
Appearance for vocabulary elements comes from views/design/theme.yaml and tokens.yaml. See docs/design-tokens-and-theme.md.
Project Layout
src/
├── compiler/ # TSX → View IR
├── design/ # tokens/theme parse, validate, CSS emit
├── extensions/ # extension registry load + validation
├── preview/ # Vite plugins (live views, theme, mock YAML)
├── scaffold/ # .vite/ + package.json for example projects
└── commands/ # CLI handlers (build orchestrator)
packages/
├── core/ # screen(), types (@view-contracts/core)
└── runtime/ # legacy preview shim
renderers/
├── react/ # lowering, element map, runtime, templates
├── swiftui/ # lowering, theme dictionary, modifier cascade
└── compose/ # (planned)
docs/
├── design-tokens-and-theme.md
├── extensions.md
├── ARCHITECTURE.md
└── SPEC_COVERAGE.md # auto-generated vocabulary referenceVision
UI should be treated like an API contract.
The long-term goal of view-contracts is to make UI:
- deterministic
- compilable
- AI-friendly
- portable across platforms
while keeping designers and developers working from the same source of truth.
view-contracts is not a React replacement — it is contract-first UI for the AI era.
npm
npm install -D view-contracts
npx view-contracts build -c view-contracts.config.yamlReleases are published to npm when a GitHub Release is published (v* tag).
