Dazzle Runtime Architecture¶
How the Dazzle runtime works internally.
Naming note (2026-05-25): Earlier drafts of this doc and adjacent files used "DNR" / "Dazzle Native Runtime" as an internal product name. That label has been retired in favour of plain "the Dazzle runtime" — the framework code lives under
src/dazzle/back/andsrc/dazzle/ui/. Some autogenerated API-reference pages still carry "DNR" in docstrings; those propagate from source-file docstrings and will be cleaned up incrementally.
Overview¶
The Dazzle runtime transforms your DSL into working applications through a multi-stage pipeline:
DSL Files (.dsl)
↓ parse
AppSpec (Dazzle IR)
↓ convert
├── BackendSpec ──→ Backend runtime (dazzle/back) ──→ FastAPI App
└── Fragment tree ──→ UI runtime (dazzle/ui) ──→ HTML + HTMX
Core Principles¶
1. Spec-First¶
BackendSpec and UISpec are the source of truth: - Language-agnostic specifications - Multiple runtimes can consume them - No framework lock-in
2. LLM-First¶
Specifications are structured for: - Deterministic generation - Easy patching by LLMs - Semantic clarity
3. Framework-Agnostic¶
Backend and frontend frameworks are outputs, not inputs: - FastAPI is the current backend - Pure JS is the frontend (React optional)
Backend Architecture (dazzle/back)¶
Layers¶
BackendSpec (language-agnostic specification)
↓
Runtime Generators
├── Model Generator → Pydantic models
├── Service Generator → CRUD + custom services
├── Route Generator → FastAPI routes
└── Server → Complete FastAPI application
BackendSpec Structure¶
BackendSpec:
name: str
version: str
entities: list[EntitySpec] # Data models
services: list[ServiceSpec] # Business logic
endpoints: list[EndpointSpec] # HTTP routes
auth_rules: list[AuthRuleSpec] # Security
Runtime Generation¶
The runtime generates:
- Pydantic Models from EntitySpec
- Field types mapped to Python types
- Validators from constraints
-
Create/Update schemas auto-generated
-
Services from ServiceSpec
- CRUD operations with PostgreSQL persistence
- Custom operations with handlers
-
Business rule enforcement
-
FastAPI Routes from EndpointSpec
- RESTful endpoints
- Request/response validation
- OpenAPI documentation
Frontend Architecture (dazzle/ui)¶
Design Decision: Server-Rendered HTMX¶
Why server-rendered templates over client-side JS: - Server is the single source of truth — no client/server state sync - HTMX declarative attributes replace custom JavaScript - Vanilla JS handles ephemeral UI state (toggles, modals, transitions) - Zero build toolchain — two CDN script tags, no node_modules - LLM-friendly — templates are plain HTML with predictable attributes
Rendering Pipeline¶
AppSpec (Dazzle IR)
↓ converters (surface_converter, workspace_converter)
typed Fragment tree (frozen dataclasses)
↓ HTML emission (dazzle.render.html.esc by default, ADR-0023 Pattern A)
HTML + hx-* attributes
↓ browser
HTMX swaps partial HTML from server
Post-#1042 (v0.67.92) the framework no longer ships Jinja2. Surfaces declare render: fragment in DSL; rendering goes through the typed Fragment substrate (frozen-dataclass primitive tree, validated against an HTML5 element/attribute allowlist, escaped at the emission boundary by default). The few remaining template paths use either f-strings + dazzle.render.html.esc (Pattern A — framework writes HTML) or string.Template (Pattern B — framework executes user-authored templates). See ADR-0023 for the full rationale.
Technology Stack¶
| Technology | Role |
|---|---|
| HTMX | Declarative server interactions (hx-get, hx-post, hx-swap) |
| Typed Fragments | Frozen-dataclass primitive tree for server-rendered HTML — dazzle/ui/runtime/ |
| Dazzle CSS | Bundled native stylesheet (/styles/dazzle.css) — tokens + components in @layer order, no third-party CSS framework |
| dz.js | Lightweight client-side state (modals, toggles, transitions) |
| Alpine.js | Local component state (filter bars, popovers, modals) — narrowly scoped per ADR-0022 |
The workspace runtime no longer loads Tailwind or DaisyUI. Site/marketing pages still consume the legacy CDN tags via
site_renderer.get_shared_head_htmlfor back-compat withstat-value/bg-base-*class names — seedocs/CSS_MIGRATION_GUIDE.mdfor the rename map.
Rendering Structure¶
The UI runtime emits HTML through the typed Fragment primitive tree. The DSL layer is unchanged — surfaces, sections, regions still declare layout; the render: fragment annotation tells the renderer to produce HTML from the typed tree rather than (the now-retired) Jinja2 templates. See docs/typed-fragment-pilot-guide.md for the migration notes.
Fragment Rendering¶
Fragments are the key interaction primitive. When HTMX fires a request, the server renders a fragment and returns partial HTML:
# In a FastAPI route handler
@app.get("/_dazzle/tasks")
async def list_tasks(request: Request, search: str = ""):
rows = filter_tasks(search)
return templates.TemplateResponse(
"fragments/table_rows.html",
{"request": request, "table": table_context, "rows": rows}
)
The browser swaps the fragment into the target element — no full page reload.
Interaction Patterns¶
| Pattern | Technology | Fragment |
|---|---|---|
| Search with debounce | HTMX hx-trigger="keyup changed delay:300ms" |
search_input.html |
| Inline editing | HTMX hx-put + JS toggle state |
inline_edit.html |
| Bulk actions | JS selection state + HTMX submit | bulk_actions.html |
| Slide-over detail | CSS transitions + HTMX content load | slide_over.html |
| Form submission | HTMX hx-post with validation fragments |
form.html + form_errors.html |
| Pagination | HTMX hx-get with page parameter |
table_pagination.html |
Conversion Pipeline¶
AppSpec → BackendSpec¶
from dazzle_back.converters import convert_appspec_to_backend
backend_spec = convert_appspec_to_backend(appspec)
Transforms:
- EntitySpec → BackendEntitySpec with field types
- SurfaceSpec → ServiceSpec + EndpointSpec
- Infers CRUD operations from surface modes
AppSpec → UISpec¶
Transforms:
- WorkspaceSpec → UIWorkspaceSpec with layouts
- SurfaceSpec → ComponentSpec with views
- Generates default theme from app metadata
Extensibility¶
Adding New Runtimes¶
Backend runtimes implement:
UI runtimes implement:
Custom Components¶
Register custom components in UISpec:
ComponentSpec(
name="CustomWidget",
category="custom",
props_schema=PropsSchema(fields=[...]),
view=ElementNode(...)
)
Performance Considerations¶
Backend¶
- PostgreSQL with async drivers (asyncpg)
- Async route handlers
- Pydantic validation caching
- Auto-migration on startup
Frontend¶
- No virtual DOM (direct DOM manipulation)
- Fine-grained signal subscriptions
- CSS variables for theme switching (no recomputation)
- Lazy component rendering
Future Directions¶
- Mobile runtime: UISpec → React Native / Flutter
- WebSocket support: Real-time updates in specs