Docs
Language reference
Core 0.3.5. REGISTRY v2.1 and COMPONENTS v2.0 are JSON Schema contracts. DATA is an envelope, and the author supplies DATA_SCHEMA.
Page model
A WDL page is JSON. REGISTRY, COMPONENTS, and DATA are the document. DATA_SCHEMA sits beside DATA and describes the author fields. The host-agnostic renderer turns the document into HTML given an injected store.
Shape
{
// REGISTRY: semantic id to a utility entry or CSS rules
"REGISTRY": {},
// COMPONENTS: layers trees or component refs
"COMPONENTS": [],
// DATA: author fields plus reserved keys
"DATA": {},
// DATA_SCHEMA: Draft-07 schema for those author fields
"DATA_SCHEMA": {}
}COMPONENTS and Layers
Each COMPONENTS entry is a layers tree or a component reference. A node is tag.semantic_id, with one semantic id. Child >, sibling +, climb <, repeat *N, and data loops *items. <@N climbs to a depth and the digit is required: <@0 is the root and <@1 is depth 1. A bare <@ climbs one level and core prints the tag <@name></@name>.
Forms
{ "layers": "section>div.hero>h1.title+p.lead",
"attr": {
".title": { "text": "Hello ${name}" },
".lead": { "text": "${tagline}" }
}
}
{ "component": "card",
"data_overrides": { "title": "..." },
"style_overrides": { ".card": { "class": "..." } }
}
// Bare <@ climbs one. Printed tag keeps the @.
// div.drawer>aside.panel>div.header>button.close<@vertical-menuLayers restrictions
Parse throws with a hint on unsupported syntax. Extra CSS classes belong in REGISTRY or attr.class.
Not allowed
.class1.class2 // a second dot class
{ } // inline text
[ ] // inline attributes
^ // climb with ^, use <
( ) // groupingREGISTRY
v2.1 is the active registry. The normative file is specifications/registry/v2.1.schema.json. An entry is a utility class string, an attribute object, or a structured entry. states, breakpoints, and containers prefix the map key onto every class. rules compile to @scope (.semantic_id). The HTML tag stays in the layers string. scopes is not a registry key.
Utility and CSS together
"card": {
"base": "shadow-md",
"states": { "hover": "shadow-lg" },
"breakpoints": { "md": "p-8 hover:bg-blue" },
"rules": [
{ "selector": ":scope", "css": { "display": "flex" } },
{ "selector": "& .button", "css": { "background": "#e5e7eb" } }
]
}
// class: shadow-md hover:shadow-lg md:p-8 md:hover:bg-blue
// css: @scope (.card) { :scope { display:flex } }The attr object
Keyed by .semantic_id or a tag name. tag.semantic_id never matches. Special keys: class, text, attr-ref, alpine, htmx.
Matchers
attr: {
".project-title": { "text": "**${title}**" },
"meta": { }
}
{ "layers": "meta.charset+meta.viewport",
"attr": {
".charset": { "charset": "UTF-8" },
".viewport": { "name": "viewport", "content": "width=device-width, initial-scale=1" }
}
}DATA and DATA_SCHEMA
${key} and ${nested.path} bind values. A loop value is an array of objects. Core does not publish a schema of your fields. Write Draft-07 DATA_SCHEMA beside DATA for those fields. Core reads only the reserved keys.
Author schema
DATA: {
"name": "World",
"tags": [ { "label": "Workers" }, { "label": "Edge" } ]
}
DATA_SCHEMA: {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": { "name": { "type": "string" } },
"required": ["name"]
}
// layers: span.tag*tags
// attr: { ".tag": { "text": "${label}" } }Layouts and fullPage
A page may set layout. Layouts chain with extends. The slot is the literal string {{content}}. fullPage true skips the default document shell.
Slot
{ "layers": "div.shell",
"attr": { ".shell": { "text": "{{content}}" } }
}Reserved DATA keys
These keys are read by the renderer. Other DATA keys belong to the author. Brand tokens are emitted after design tokens. The shell injection is skipped when fullPage is true.
Keys
__seo object __head string or string[] __design_tokens string or string[] __brand_tokens string or string[] DATA_SCHEMA Draft-07 schema, beside DATA
Contracts
The registry schema, the component schema, and this reference are in the v0.3.5 repository.