Runtime UI Capabilities¶
What the Dazzle runtime actually renders for each DSL construct. Use this to understand what UI features your DSL declarations produce — the gap between "what can I declare?" and "what will the user see?"
Display locale (#1597)¶
List/detail cells use DisplayLocaleProfile (not browser Accept-Language):
| Layer | Role |
|---|---|
| Product default | en-GB + Europe/London + GBP + friendly D MMM YYYY |
| Tenant override | DSL param locale.timezone / locale.date_format (ParamResolver cascade) |
| Browser cookie / Accept-Language | gettext only — never cell dates/numbers |
- date fields: calendar day, never TZ-shifted (filing/VAT-safe).
- datetime fields: stored UTC (naive = UTC), displayed in tenant TZ.
today()/days_until/ attention: sharedcalendar_today()(tenant midnight).money(CODE): code is authoritative; locale does not convert.
Editors vs display: C2.3 list edit for dates uses input type=date /
datetime-local (ISO value). Display is day-first en-GB; the edit payload stays
ISO so day/month swap cannot corrupt storage. Do not force format: raw on
temporal list fields (dazzle validate warns — #1597 E).
List Surfaces (mode: list)¶
A surface with mode: list renders a DataTable component. The base table always includes:
- Rows with click-to-navigate to detail view
- Row action menu (View, Edit, Delete) with confirmation dialog on delete
- Pagination (page buttons below table)
- HTMX-powered partial updates (no full page reloads)
With sort: directive¶
Renders: - All column headers become clickable with hover state - Active sort column shows a direction indicator arrow (up/down) - Clicking a sorted column toggles direction (asc/desc) - Clicking a different column sorts by that column (asc default) - Sort state is preserved across pagination
The first sort: entry becomes the default sort on page load.
With filter: directive¶
Renders a filter bar above the table with per-field inputs:
| Field Type | Rendered As |
|---|---|
| Enum field | <select> dropdown with all enum values + "All" option |
| Bool field | <select> dropdown with Yes/No/All options |
| State machine field | <select> dropdown with all states + "All" option |
| Text/other fields | Debounced text input (300ms delay) |
- Filter changes trigger an HTMX request that swaps just the table body
- Multiple filters combine (AND logic)
- Filter state is preserved across pagination
- Empty filter values are ignored (selecting "All" clears that filter)
With search: directive¶
Renders:
- Search input field with debounced type-ahead (300ms delay)
- Clear button appears when search has text
- Search targets the fields listed in the directive
- Combines with active filters and sort
- Supported repository operators: __contains (text), __gte/__lte (dates/numbers), __in (lists)
Without search:, no search input is rendered.
With empty: directive¶
Renders: Custom message in the empty state row when no items match the current filters/search. Without this directive, defaults to "No items found."
Column Visibility¶
When a table has more than 3 columns, a "Columns" dropdown button appears next to the "New" button. Users can toggle column visibility. Preferences persist in localStorage.
Combined Example¶
surface task_list "Tasks":
uses entity Task
mode: list
section main:
field title "Title"
field status "Status"
field priority "Priority"
field due_date "Due"
ux:
sort: due_date asc
filter: status, priority
search: title, description
empty: "No tasks yet. Create your first task!"
Produces: A table with sortable headers, status dropdown filter, priority dropdown filter, search bar, custom empty message, column visibility toggle, and stateful pagination.
Attention Signals¶
ux:
attention critical:
when: due_date < today and status != done
message: "Overdue task"
action: task_edit
Renders: Row-level visual indicators based on data conditions. Signal levels map to CSS severity classes:
| Level | CSS Class | Typical Use |
|---|---|---|
critical |
danger (red) |
Data loss risk, compliance violations |
warning |
warning (amber) |
Approaching deadlines, threshold breaches |
notice |
info (blue) |
Stale data, items needing review |
info |
secondary (grey) |
Informational flags |
Signals with an action: link to the referenced surface for quick resolution.
Persona Variants¶
ux:
as admin:
scope: all
purpose: "Manage all tasks"
action_primary: task_create
as member:
scope: assigned_to = current_user
purpose: "View my tasks"
read_only: true
Renders: Role-filtered views from a single surface definition:
scope:— Server-side data filtering (admin sees all, member sees own)show:/hide:— Controls which columns/fields are visible per personaread_only: true— Hides create/edit/delete actionsaction_primary:— Highlights the primary CTA for that personadefaults:— Pre-populates form fields on create surfaces
Form Surfaces (mode: create / mode: edit)¶
Base form rendering:
| Field Type | Rendered As |
|---|---|
str |
Text input |
text |
Textarea |
int, decimal, money |
Number input |
bool |
Checkbox |
date |
Date picker |
datetime |
Datetime picker |
email |
Email input |
enum |
Select dropdown with enum values |
| State machine field | Select dropdown with valid states |
Field with source: |
Search-select widget (debounced autocomplete from external API) |
Search-Select Widget (source=)¶
Renders: Autocomplete input that queries an external API with debounce. Shows results in a dropdown with primary/secondary display fields. Selecting a result can autofill other form fields.
Supported field key=value options on create/edit forms: source,
search_trigger (alias — see below), widget, accept, capture,
rich_text_toolbar, rich_text_max_length. Other keys are ignored at
render; dazzle validate warns.
search_trigger=<pack> (#1599) is an alias for the pack’s primary search
operation (e.g. search_trigger=companies_house_lookup →
source=companies_house_lookup.search_companies). Prefer explicit
source=<pack>.<op> when you need a non-default search op.
Pack fragment field maps (#1600 P1) — API packs may declare per-operation
[operations.<op>.fragment] keys (display_key, value_key, secondary_key,
items_key, query_param, detail_url) and [operations.<op>.field_map]
aliases (e.g. Companies House search title → company_name, profile type →
company_type). Fragment search/select apply the map before reading display
keys, so host dual-lock remappers are not required for shape mismatches.
Detail Surfaces (mode: view)¶
- Read-only field display
- Edit and Delete action buttons
- Back navigation link
- State machine transitions — rendered as action buttons for valid transitions from current state
- Related groups —
related name "Title": display: table|status_cards|file_list; show: ChildEntity(reverse-FK tabs under the detail body). Optionalcolumns: field1, field2, …projects scannable tabs instead of every entity field. - Multi-section overview (#1600 Wedge B) — multiple
sectionblocks stack as titled field groups (identity / role / timeline …), then related work below. Pair with listopen: Entity via fk(#1603) for queue → context hub. Polymorphic clients:open: first_non_null(company, sole_trader, partnership)or pipe-chainedopen: Company via company | SoleTrader via sole_trader(first non-null FK wins; else same-entity fallback). - Status / RAG strip —
layout: stripon a VIEW section renders fields as a horizontal badge row (compliance status, lifecycle flags).
surface client_overview "Client overview":
uses entity Company
mode: view
section header "Client":
field name "Name"
section compliance "Compliance":
layout: strip
field vat_status "VAT"
field aml_status "AML"
related work "Work":
display: table
show: Engagement
columns: name, status, due_date
surface engagement_list "Engagements":
uses entity Engagement
mode: list
open: Company via company # row → /app/company/{company} hub
# Polymorphic client FKs — first non-null hop (#1600 P2)
surface subscription_list "Subscriptions":
uses entity ClientSubscription
mode: list
open: first_non_null(company, sole_trader, partnership)
# or: open: Company via company | SoleTrader via sole_trader | Partnership via partnership
One mode: view surface per entity owns the detail route (/app/<entity>/{id}).
Workspace Layouts¶
Workspaces group surfaces into navigable sections. The runtime renders:
- Sidebar navigation with workspace items
- Active state highlighting on current route
- Collapsible sidebar on mobile
- App name and branding in the header
System Endpoints¶
/health¶
Always-public endpoint. Returns a JSON object with the current health status of the running app:
{
"status": "healthy",
"app": "my_app",
"version": "0.48.2",
"dsl_hash": "a3f9c1e2",
"uptime_seconds": 142.3
}
| Field | Description |
|---|---|
status |
Always "healthy" when the server is up |
app |
The module name from your DSL |
version |
Dazzle framework version |
dsl_hash |
First 8 hex chars of the SHA-256 of the compiled IR — changes whenever the DSL changes |
uptime_seconds |
Seconds since server startup |
Use dsl_hash in deployment pipelines to confirm a new DSL has been loaded without downtime.
/_diagnostics¶
Admin-only endpoint (requires a session with admin, super_admin, or trust_admin role). Returns a richer snapshot for operational use:
{
"entities": 12,
"surfaces": 8,
"workspaces": 3,
"features": {
"auth": true,
"files": false,
"feedback_widget": true
},
"database": {
"connected": true,
"pending_migrations": 0
},
"dsl_hash": "a3f9c1e2",
"version": "0.48.2"
}
Returns HTTP 403 for any authenticated user without an admin role. Returns HTTP 401 when no session is present.
Feedback Widget¶
When feedback_widget: enabled is declared at the module level, the runtime:
- Auto-generates a
FeedbackReportentity with fields for category, severity, description, and auto-captured context (URL, persona, viewport, user agent, console errors, navigation history, page snapshot, screenshot, and annotation data). - Injects the widget JavaScript and CSS into every authenticated page via the base template.
- Exposes the widget status in
/_diagnosticsunderfeatures.feedback_widget.
feedback_widget: enabled
position: bottom-right
shortcut: backtick
categories: [bug, ux, visual, behaviour, enhancement, other]
severities: [blocker, annoying, minor]
capture: [url, persona, viewport, user_agent, console_errors, nav_history, page_snapshot]
All fields except enabled are optional — the defaults above are used when omitted.
| Option | Default | Description |
|---|---|---|
position |
bottom-right |
Screen corner for the floating button |
shortcut |
backtick |
Keyboard shortcut to open the panel |
categories |
6 values | Dropdown options for feedback type |
severities |
3 values | Dropdown options for severity |
capture |
7 fields | Context automatically attached to each report |
The widget is only injected on authenticated pages. Public surfaces (declared with access: public) do not receive the widget.