Generated from the compiler's registry (wf registry --json) by scripts/components-reference.py, with an example of each from scripts/component_examples.py; edit those, not this file.
Every component is written Name(positional, prop: value).flag { block }. A prop is passed by name; the one positional prop, where there is one, comes first and unnamed. A flag is written after the parentheses with a dot: a Bool prop, or a case of one of the element's enum props (Button("x").primary.lg). An enum prop takes a case written .case (Row(gap: .md)). Every element also takes the universal props and events listed at the end, and the HTML attribute families named on it (aria-*, data-*, and the global attributes such as id, class, title, role, tabindex).
- Layout:
Container,Row,Column,Grid,Stack,Spacer,VirtualList,Splitter,Divider,Host,Portal,Element - Navigation:
Navbar,Sidebar,Link,Tabs,Breadcrumb,Menu - Data display:
Card,Table,List,Badge,Tag,Avatar,Tooltip,Chart,QrCode - Form:
Input,Select,Checkbox,Radio,Switch,Slider,DatePicker,FileUpload,Form,Textarea - Feedback:
Alert,Toast,Modal,Popover,Dialog,Spinner,Progress,Skeleton - Actions:
Button,IconButton,ButtonGroup,Dropdown - Media:
Image,Video,Audio,Frame,Icon,Carousel - Typography:
Text,Heading,Code,Markdown,Unsafe,Blockquote - PDF:
TableOfContents,Document,Section,Paragraph,Header,Footer,Background,Watermark,PageBreak - Slides:
Presentation,Slide,TitleSlide,SectionSlide,TwoColumn,ImageSlide - Routing:
Router
Centred wrapper with a maximum width and horizontal padding.
Container(fluid: …) { … }
Container { Heading("Pricing").h1 }
Container.fluid { Text("Edge to edge") }
Renders with the class wf-container.
| Prop | Type | Meaning |
|---|---|---|
fluid: |
Bool |
Full width, no maximum |
Flags: .fluid .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Horizontal flex layout. It stays a row at every width unless .stacks or a responsive direction says otherwise.
Row(gap: …, align: …, justify: …) { … }
Row(gap: .md, align: .center, justify: .between) {
Text("Left")
Button("Right")
}
Row(direction: { base: .column, md: .row }) { Text("a") Text("b") }
Renders with the class wf-row.
| Prop | Type | Meaning |
|---|---|---|
gap: |
.xs .sm .md .lg .xl |
Space between children |
align: |
.start .center .end .stretch .baseline |
Cross-axis alignment |
justify: |
.start .center .end .between .around .evenly |
Main-axis distribution |
direction: |
.row .column |
Which way its children run; takes a value per breakpoint |
stacks: |
Bool |
Reflow on a narrow screen: a Row becomes a column, a Grid one column |
Flags: .xs .sm .md .lg .xl .stretch .baseline .between .around .evenly .row .column .stacks .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
A child of the 12-column grid, spanning span columns.
Column(span: …, stacks: …) { … }
Row {
Column(span: 8) { Text("Main") }
Column(span: 4) { Text("Aside") }
}
Renders with the class wf-col.
| Prop | Type | Meaning |
|---|---|---|
span: |
Number |
How many of the 12 grid columns to span |
stacks: |
Bool |
Reflow on a narrow screen: a Row becomes a column, a Grid one column |
Flags: .stacks .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
CSS grid with a fixed number of columns.
Grid(columns: …, gap: …, align: …) { … }
Grid(columns: { base: 1, md: 3 }, gap: .lg) {
Card { Text("One") }
Card { Text("Two") }
Card { Text("Three") }
}
Renders with the class wf-grid.
| Prop | Type | Meaning |
|---|---|---|
columns: |
Number |
Number of equal columns |
gap: |
.xs .sm .md .lg .xl |
Space between children |
align: |
.start .center .end .stretch .baseline |
Cross-axis alignment |
justify: |
.start .center .end .between .around .evenly |
Main-axis distribution |
stacks: |
Bool |
Reflow on a narrow screen: a Row becomes a column, a Grid one column |
Flags: .xs .sm .md .lg .xl .stretch .baseline .between .around .evenly .stacks .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Vertical flex layout.
Stack(gap: …, align: …, justify: …) { … }
Stack(gap: .sm) {
Text("First")
Text("Second")
}
Renders with the class wf-stack.
| Prop | Type | Meaning |
|---|---|---|
gap: |
.xs .sm .md .lg .xl |
Space between children |
align: |
.start .center .end .stretch .baseline |
Cross-axis alignment |
justify: |
.start .center .end .between .around .evenly |
Main-axis distribution |
Flags: .xs .sm .md .lg .xl .stretch .baseline .between .around .evenly .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Vertical space; md when no size is given.
Spacer(size: …)
Text("Above")
Spacer.lg
Text("Below")
Renders with the class wf-spacer.
| Prop | Type | Meaning |
|---|---|---|
size: |
.xs .sm .md .lg .xl |
How much space |
Flags: .xs .sm .md .lg .xl .dropInside .trapFocus
Attribute families: global, aria, data.
A scrolling box that draws only the rows in view of the one keyed for inside it — for a tree, search results or a diff of thousands of rows.
VirtualList(rowHeight: …, estimate: …, reveal: …) { … }
state rows = (0..10000).map((i) => "row {i}")
state picked = "row 0"
VirtualList(rowHeight: 32, reveal: picked, label: "Rows") {
style { height: 320px }
for r in rows by r { Text(r) { style { height: 32px; margin: 0 } } }
}
Renders with the class wf-virtual-list.
| Prop | Type | Meaning |
|---|---|---|
rowHeight: |
Number |
Every row's height, in px: the fast path. Unset, each row is measured |
estimate: |
Number |
The height a row not yet drawn stands for, in px. Unset, 32 |
reveal: |
Any |
A row's key: when it changes, that row is scrolled into view — the selection, the current match |
label: |
String |
What the list is, for a screen reader |
Flags: .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
A handle that sets the size of a region beside it: dragged, between min: and max:; double-clicked or Enter, back to reset:; the arrow keys move it.
Splitter(bind: …, min: …, max: …)
state side = 240
Row {
Container { style { width: {side}px } Text("Files") }
Splitter(bind: side, min: 160, max: 480, reset: 240, label: "Sidebar width")
Container { Text("Editor") }
}
Renders with the class wf-splitter.
| Prop | Type | Meaning |
|---|---|---|
bind: |
State |
State variable the control reads and writes (two-way) |
min: |
Number |
The smallest size, in px |
max: |
Number |
The largest size, in px |
reset: |
Number |
The size a double-click or Enter goes back to, taken as written: 0 can mean the region's default share |
label: |
String |
What it resizes, for a screen reader |
horizontal: |
Bool |
Between two regions one above the other: it moves up and down |
reverse: |
Bool |
On the start side of what it sizes — a pane on the right, a panel below — so a drag toward the start grows it |
Flags: .horizontal .reverse .dropInside .trapFocus
Attribute families: global, aria, data.
Horizontal rule.
Divider
Text("Section one")
Divider
Text("Section two")
Renders with the class wf-divider.
Flags: .dropInside .trapFocus
Attribute families: global, aria, data.
A bare node handed to somebody else's code, with a lifetime. mount: runs with the node each time it is made, update: again whenever the state it reads changes, and cleanup: when the page, branch or list item it belongs to leaves. Every other element takes the same three.
Host(tag: …)
Host(tag: "canvas", mount: (node) => node.getContext("2d"), cleanup: (ctx) => log("gone"))
Renders with the class wf-host.
| Prop | Type | Meaning |
|---|---|---|
tag: |
String |
The element to make: div (the default), span, canvas, svg, section, figure, pre, p, ul or table |
Flags: .dropInside .trapFocus
Attribute families: global, aria, data.
Its block, drawn into a node somebody else's code owns: an element a script hands over, or a ref: handle. When into: names another node the block moves there with its nodes and state; null keeps it aside until a node comes back; it goes when the page, branch or list item it is written in leaves. What is inside belongs to where it is written: its handlers, emit and lifetimes.
Portal(into: …) { … }
Stack(ref: panel)
Portal(into: panel) { Text("Drawn inside the stack") }
Renders with the class wf-portal.
| Prop | Type | Meaning |
|---|---|---|
into: (required) |
Any |
Where it draws: an element, a ref: handle, or null to keep it aside; read again whenever what it reads changes |
Flags: .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
A custom element a script defines, placed by its tag: Element("stripe-pricing-table", publishable-key: key) { on ready { … } }. Every named argument is an attribute, every on handler an event it fires, and the block its children.
Element(tag) { … }
Element("relative-time", datetime: "2026-03-14T09:30:00Z") { Text("March 14") }
Element("stripe-pricing-table", publishableKey: "pk_test_…") { on ready { log("prices shown") } }
| Prop | Type | Meaning |
|---|---|---|
tag (positional) |
String |
The custom element's tag: lower case, with a hyphen, as every custom element's is |
Flags: .dropInside .trapFocus
Takes a block of children.
Attribute families: any.
Top navigation bar with brand, links and action areas.
Navbar { … }
Navbar {
Navbar.Brand { Link("Acme", to: "/") }
Navbar.Links { Link("Docs", to: "/docs") Link("Pricing", to: "/pricing") }
Navbar.Actions { Button("Sign in").sm }
}
Renders with the class wf-navbar.
Flags: .dropInside .trapFocus
Parts:
Navbar.Brand { … }— The brand area, usually a link home.- flags:
.dropInside.trapFocus
- flags:
Navbar.Links { … }— The navigation links; collapses behind a toggle on small screens.- flags:
.dropInside.trapFocus
- flags:
Navbar.Actions { … }— Buttons at the end of the bar.- flags:
.dropInside.trapFocus
- flags:
Takes a block of children.
Attribute families: global, aria, data.
Side navigation. A Sidebar.Item whose to matches the current route is marked .active and aria-current="page".
Sidebar { … }
Sidebar {
Sidebar.Header { Text("Console").bold }
Sidebar.Item(to: "/", icon: "home") { Text("Overview") }
Sidebar.Divider
Sidebar.Item(to: "/settings", icon: "settings") { Text("Settings") }
}
Renders with the class wf-sidebar.
Flags: .dropInside .trapFocus
Parts:
Sidebar.Header { … }— The sidebar's heading area.- flags:
.dropInside.trapFocus
- flags:
Sidebar.Item(to: …, icon: …, active: …) { … }— One navigation entry.to:Path— Route to navigate to; the link whosetomatches the current route carries.activeandaria-current="page"icon:String— Icon shown before the labelactive:.prefix— How the link decides it is the current one- flags:
.prefix.dropInside.trapFocus
Sidebar.Divider— A rule between groups of items.- flags:
.dropInside.trapFocus
- flags:
Takes a block of children.
Attribute families: global, aria, data.
Client-side navigation link; its label or its block is the link text.
Link(label, to: …, active: …) { … }
Link("About us", to: "/about")
Link("Guide", to: "/docs", active: .prefix)
Link(to: "https://example.com", target: "_blank") { Text("Example") }
Renders with the class wf-link.
| Prop | Type | Meaning |
|---|---|---|
label (positional) |
String |
The link text |
to: |
Path |
Route to navigate to; the link whose to matches the current route carries .active and aria-current="page" |
active: |
.prefix |
How the link decides it is the current one |
Flags: .prefix .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data, anchor.
Tab strip switching between its pages.
Tabs { … }
Tabs {
Tabs.Page("Profile") { Text("Your details") }
Tabs.Page("Billing") { Text("Your plan") }
}
Renders with the class wf-tabs.
Flags: .dropInside .trapFocus
Parts:
Tabs.Page(label) { … }— One tab and its panel.label:String— The tab's label- flags:
.dropInside.trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Breadcrumb trail. An item without to is the current page.
Breadcrumb { … }
Breadcrumb {
Breadcrumb.Item(to: "/") { Text("Home") }
Breadcrumb.Item(to: "/docs") { Text("Docs") }
Breadcrumb.Item { Text("Components") }
}
Renders with the class wf-breadcrumb.
Flags: .dropInside .trapFocus
Parts:
Breadcrumb.Item(to: …) { … }— One step of the trail.to:Path— Route to navigate to; the link whosetomatches the current route carries.activeandaria-current="page"- flags:
.dropInside.trapFocus
Takes a block of children.
Attribute families: global, aria, data.
A menu above everything: opened by its trigger: button, or by visible: at an anchor: or a point at: — a context menu. Escape, a press outside or a choice close it, write false back to visible: and fire on close.
Menu(trigger: …, label: …, visible: …) { … }
Menu(trigger: "Options") {
Menu.Item(shortcut: "F2") { Text("Rename") }
Menu.Sub(label: "Move to") { Menu.Item { Text("Archive") } }
Menu.Divider
Menu.Item(disabled: true) { Text("Delete") }
}
state asked = false
state point = { x: 0, y: 0 }
Menu(visible: asked, at: point) { Menu.Item { Text("Copy") } }
Renders with the class wf-menu.
| Prop | Type | Meaning |
|---|---|---|
trigger: |
String |
Label of the button that opens the menu |
label: |
String |
Same as trigger |
visible: |
State |
State flag that opens and closes it |
anchor: |
Any |
What it is placed against: an element's ref:, or a point { x: e.clientX, y: e.clientY } |
at: |
Any |
A point to place it at, { x: e.clientX, y: e.clientY }: where the pointer asked for a context menu |
placement: |
.bottom .bottomStart .bottomEnd .top .topStart .topEnd .left .leftStart .leftEnd .right .rightStart .rightEnd |
Where it goes against its anchor; it flips to the other side when there is no room. Unset, .bottomStart |
offset: |
Number |
How far from the anchor, in px |
Flags: .bottom .bottomStart .bottomEnd .top .topStart .topEnd .left .leftStart .leftEnd .right .rightStart .rightEnd .dropInside .trapFocus
Events of its own: on close
Parts:
Menu.Item(shortcut: …, checked: …, disabled: …, danger: …) { … }— One entry of the menu;on clickis what choosing it does.shortcut:String— The keys that do the same,mod+b, shown at its end as this system writes them (⌘B,Ctrl+B)checked:Bool— A check mark: the item turns something on and offdisabled:Bool— Shown, skipped by the arrow keys, and cannot be chosendanger:Bool— What it does cannot be taken back — Delete, Discard: drawn in the danger colour- flags:
.checked.disabled.danger.dropInside.trapFocus
Menu.Sub(label: …) { … }— An entry that opens a menu of its own beside it.label:String— What the entry says- flags:
.dropInside.trapFocus
Menu.Divider— A rule between groups of entries.- flags:
.dropInside.trapFocus
- flags:
Takes a block of children.
Attribute families: global, aria, data.
Card surface with optional header, body and footer sections.
Card(surface: …) { … }
Card.elevated {
Card.Header { Heading("Pro").h3 }
Card.Body { Text("Everything in Free, and more.") }
Card.Footer { Button("Choose Pro").primary }
}
Renders with the class wf-card.
| Prop | Type | Meaning |
|---|---|---|
surface: |
.flat .elevated .outlined |
Surface treatment |
Flags: .flat .elevated .outlined .dropInside .trapFocus
Parts:
Card.Header { … }— The card's heading area.- flags:
.dropInside.trapFocus
- flags:
Card.Body { … }— The card's content area.- flags:
.dropInside.trapFocus
- flags:
Card.Footer { … }— The card's action area.- flags:
.dropInside.trapFocus
- flags:
Takes a block of children.
Attribute families: global, aria, data.
Data table. Cells inside Table.Head render as <th scope="col">; caption is the table's accessible name, visually hidden.
Table(caption: …) { … }
Table(caption: "Deploys") {
Table.Head { Table.Row { Table.Cell("Region") Table.Cell("Status") } }
Table.Body { Table.Row { Table.Cell("fra1") Table.Cell("Healthy") } }
}
Renders with the class wf-table.
| Prop | Type | Meaning |
|---|---|---|
caption: |
String |
Accessible name of the table (rendered visually hidden) |
Flags: .dropInside .trapFocus
Parts:
Table.Head { … }— Header section; its cells become<th scope="col">.- flags:
.dropInside.trapFocus
- flags:
Table.Body { … }— Body section.- flags:
.dropInside.trapFocus
- flags:
Table.Row { … }— A row.- flags:
.dropInside.trapFocus
- flags:
Table.Cell(content, header: …) { … }— A cell.headermakes it a<th>anywhere, asTable.Headdoes for its cells.content:Any— The cell's contentheader:Bool— Render as a<th>- flags:
.header.dropInside.trapFocus
Takes a block of children.
Attribute families: global, aria, data.
List of its children; ordered for a numbered list.
List(ordered: …) { … }
List {
List.Item { Text("Milk") }
List.Item { Text("Eggs") }
}
List.ordered { List.Item { Text("First") } }
Renders with the class wf-list.
| Prop | Type | Meaning |
|---|---|---|
ordered: |
Bool |
Numbered (<ol>) |
Flags: .ordered .dropInside .trapFocus
Parts:
List.Item { … }— One entry (<li>).- flags:
.dropInside.trapFocus
- flags:
Takes a block of children.
Attribute families: global, aria, data.
Small status label.
Badge(label, tone: …, pill: …)
Badge("Beta").info
Badge("3 new").danger.pill
Renders with the class wf-badge.
| Prop | Type | Meaning |
|---|---|---|
label (positional) |
String |
The badge text |
tone: |
.primary .secondary .success .danger .warning .info |
Colour |
pill: |
Bool |
Fully rounded ends |
Flags: .primary .secondary .success .danger .warning .info .pill .dropInside .trapFocus
Attribute families: global, aria, data.
Compact chip.
Tag(label)
Tag("design")
Renders with the class wf-tag.
| Prop | Type | Meaning |
|---|---|---|
label (positional) |
String |
The tag text |
Flags: .dropInside .trapFocus
Attribute families: global, aria, data.
A picture of a person, or their initials.
Avatar(src: …, alt: …, initials: …)
Avatar(src: "/ada.jpg", alt: "Ada Lovelace")
Avatar(initials: "AL").primary.lg
Renders with the class wf-avatar.
| Prop | Type | Meaning |
|---|---|---|
src: |
String |
Image URL |
alt: |
String |
Text alternative for the image |
initials: |
String |
Letters shown when there is no image |
size: |
.sm .md .lg |
Its size |
tone: |
.primary |
Colour of the initials |
Flags: .sm .md .lg .primary .dropInside .trapFocus
Attribute families: global, aria, data.
Shows text above everything when its child is hovered (after delay:) or focused from the keyboard.
Tooltip(text, shortcut: …, placement: …, delay: …) { … }
Tooltip("Copies the link") { IconButton(icon: "copy", label: "Copy link") }
Tooltip("Save", shortcut: "Ctrl+S", delay: 400, placement: .bottom) { Button("Save") }
Renders with the class wf-tooltip.
| Prop | Type | Meaning |
|---|---|---|
text (positional) |
String |
The tooltip text |
shortcut: |
String |
The keys that do the same, mod+s, shown as a chip as this system writes them |
placement: |
.bottom .bottomStart .bottomEnd .top .topStart .topEnd .left .leftStart .leftEnd .right .rightStart .rightEnd |
Where it goes against its anchor; it flips to the other side when there is no room. Unset, .bottomStart |
delay: |
Number |
How long a hover waits before it shows, in ms. Unset, 500 |
Flags: .bottom .bottomStart .bottomEnd .top .topStart .topEnd .left .leftStart .leftEnd .right .rightStart .rightEnd .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
A chart drawn as vector graphics when the page is built: bars, lines, areas, a pie or a donut over a list of records.
Chart(kind: …, data: …, x: …)
Chart(kind: .bar, data: [{ month: "Jan", web: 12 }, { month: "Feb", web: 18 }], x: "month", y: "web", unit: "k")
Renders with the class wf-chart.
| Prop | Type | Meaning |
|---|---|---|
kind: |
.bar .line .area .pie .donut |
What it draws |
data: |
Any |
The rows: a list of records, or of numbers |
x: |
String |
The key of each row's label |
y: |
Any |
The key of each row's value, or a list of keys for several series |
colors: |
Any |
The series' colours, in order |
width: |
Number |
Its width in user units (it scales to its box) |
height: |
Number |
Its height in user units |
ink: |
String |
The colour of its labels |
grid: |
String |
The colour of its grid lines |
legend: |
Bool |
Name the series |
stacked: |
Bool |
Stack a bar chart's series |
labels: |
Bool |
Write each bar's or slice's value on it |
unit: |
String |
A unit after each value on the axis: ms, % |
Flags: .bar .line .area .pie .donut .legend .stacked .labels .dropInside .trapFocus
Attribute families: global, aria, data.
A QR code for a URL or any text, drawn as vector graphics when the page is built.
QrCode(value, color: …, background: …)
QrCode("https://example.com/report/q1")
Renders with the class wf-qr.
| Prop | Type | Meaning |
|---|---|---|
value (positional) |
String |
What it encodes |
color: |
String |
The colour of the dark modules |
background: |
String |
The colour behind them |
Flags: .dropInside .trapFocus
Attribute families: global, aria, data.
Text field. With label, hint or error it is a labelled field.
Input(bind: …, label: …, hint: …)
state email = ""
Input(bind: email, label: "Email", hint: "We never share it").email.required
Renders with the class wf-input.
| Prop | Type | Meaning |
|---|---|---|
bind: |
State |
State variable the control reads and writes (two-way) |
label: |
String |
Visible label; also the control's accessible name |
hint: |
String |
Help text under the control, linked to it by aria-describedby |
error: |
String |
Error text (a string; empty when there is none): announced as it appears and sets aria-invalid |
type: |
.text .email .password .number .search .tel .url .date .time .datetime .color |
What the field holds |
placeholder: |
String |
Hint shown while the field is empty |
value: |
Any |
The control's value, when its state is not bound |
min: |
Any |
Lowest value |
max: |
Any |
Highest value |
step: |
Number |
Granularity |
required: |
Bool |
Whether the form refuses to submit without it |
disabled: |
Bool |
Whether the control is inert |
size: |
.sm .md .lg |
Its size |
width: |
.full |
Width |
rounded: |
Bool |
Rounded corners |
Flags: .text .email .password .number .search .tel .url .date .time .datetime .color .required .disabled .sm .md .lg .full .rounded .dropInside .trapFocus
Attribute families: global, aria, data, input.
Drop-down of Select.Option children.
Select(bind: …, value: …, label: …) { … }
state plan = "free"
Select(bind: plan, label: "Plan") {
Select.Option("Free", value: "free")
Select.Option("Team", value: "team")
}
Renders with the class wf-select.
| Prop | Type | Meaning |
|---|---|---|
bind: |
State |
State variable the control reads and writes (two-way) |
value: |
Any |
The control's value, when its state is not bound |
label: |
String |
Visible label; also the control's accessible name |
hint: |
String |
Help text under the control, linked to it by aria-describedby |
error: |
String |
Error text (a string; empty when there is none): announced as it appears and sets aria-invalid |
disabled: |
Bool |
Whether the control is inert |
multiple: |
Bool |
Allow several choices |
Flags: .disabled .multiple .dropInside .trapFocus
Parts:
Select.Option(label, value: …)— One choice: the visible label, and the value sent with the form.label:String— The visible labelvalue:Any— The valuebindtakes when this one is chosen; the label when absent- flags:
.dropInside.trapFocus
Takes a block of children.
Attribute families: global, aria, data, input.
Checkbox bound to a boolean state.
Checkbox(bind: …, checked: …, label: …)
state agree = false
Checkbox(bind: agree, label: "I agree to the terms")
Renders with the class wf-checkbox.
| Prop | Type | Meaning |
|---|---|---|
bind: |
State |
State variable the control reads and writes (two-way) |
checked: |
Bool |
Whether it is checked, when its state is not bound |
label: |
String |
Visible label; also the control's accessible name |
disabled: |
Bool |
Whether the control is inert |
Flags: .checked .disabled .dropInside .trapFocus
Attribute families: global, aria, data, input.
One choice of a group; every radio bound to the same state is one group.
Radio(bind: …, value: …, checked: …)
state size = "m"
Radio(bind: size, value: "s", label: "Small")
Radio(bind: size, value: "m", label: "Medium")
Renders with the class wf-radio.
| Prop | Type | Meaning |
|---|---|---|
bind: |
State |
State variable the control reads and writes (two-way) |
value: |
Any |
The value bind takes when this one is chosen |
checked: |
Bool |
Whether it is checked, when its state is not bound |
label: |
String |
Visible label; also the control's accessible name |
disabled: |
Bool |
Whether the control is inert |
Flags: .checked .disabled .dropInside .trapFocus
Attribute families: global, aria, data, input.
On/off toggle bound to a boolean state.
Switch(bind: …, checked: …, label: …)
state notify = true
Switch(bind: notify, label: "Email me about replies")
Renders with the class wf-switch.
| Prop | Type | Meaning |
|---|---|---|
bind: |
State |
State variable the control reads and writes (two-way) |
checked: |
Bool |
Whether it is checked, when its state is not bound |
label: |
String |
Visible label; also the control's accessible name |
disabled: |
Bool |
Whether the control is inert |
Flags: .checked .disabled .dropInside .trapFocus
Attribute families: global, aria, data, input.
Range input. aria-* and its other attributes reach the input; with aria-valuetext the raw number is not shown beside the track.
Slider(bind: …, min: …, max: …)
state volume = 50
Slider(bind: volume, min: 0, max: 100, step: 5, label: "Volume")
Renders with the class wf-slider.
| Prop | Type | Meaning |
|---|---|---|
bind: |
State |
State variable the control reads and writes (two-way) |
min: |
Number |
Lowest value |
max: |
Number |
Highest value |
step: |
Number |
Granularity |
label: |
String |
Visible label; also the control's accessible name |
disabled: |
Bool |
Whether the control is inert |
Flags: .disabled .dropInside .trapFocus
Attribute families: global, aria, data, input.
Date input.
DatePicker(bind: …, label: …, min: …)
state start: Date? = null
DatePicker(bind: start, label: "Start date", min: "2026-01-01")
Renders with the class wf-datepicker.
| Prop | Type | Meaning |
|---|---|---|
bind: |
State |
State variable the control reads and writes (two-way) |
label: |
String |
Visible label; also the control's accessible name |
min: |
String |
Earliest date, YYYY-MM-DD |
max: |
String |
Latest date, YYYY-MM-DD |
disabled: |
Bool |
Whether the control is inert |
Flags: .disabled .dropInside .trapFocus
Attribute families: global, aria, data, input.
File input; handle the chosen files in on change.
FileUpload(accept: …, label: …, multiple: …) { … }
FileUpload(accept: "image/*", label: "Choose a picture") { on change(e) { log(e.target.files[0].name) } }
Renders with the class wf-file-upload.
| Prop | Type | Meaning |
|---|---|---|
accept: |
String |
Accepted types, e.g. image/* or .pdf,.doc |
label: |
String |
Visible label; also the control's accessible name |
multiple: |
Bool |
Accept several files |
disabled: |
Bool |
Whether the control is inert |
Flags: .multiple .disabled .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data, input.
Groups controls; on submit runs when it is submitted, and the page never navigates away.
Form(bind: …, show: …) { … }
state name = ""
Form(bind: form) {
on submit { log(name) }
Input(bind: name, label: "Name").required
Button("Save", type: .submit, disabled: !form.valid).primary
}
Renders with the class wf-form.
| Prop | Type | Meaning |
|---|---|---|
bind: |
State |
A handle on the form: form.valid, form.errors, form.values by field name, form.reset(), form.apply(serverErrors) |
show: |
.onBlur .onSubmit .live |
When a validate block's message is shown |
Flags: .onBlur .onSubmit .live .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data, form.
Several lines of text. With label, hint or error it is a labelled field; with maxLength it counts what is left.
Textarea(bind: …, label: …, hint: …)
state note = ""
Textarea(bind: note, label: "Notes", rows: 4, maxLength: 200)
Renders with the class wf-input wf-textarea.
| Prop | Type | Meaning |
|---|---|---|
bind: |
State |
State variable the control reads and writes (two-way) |
label: |
String |
Visible label; also the control's accessible name |
hint: |
String |
Help text under the control, linked to it by aria-describedby |
error: |
String |
Error text (a string; empty when there is none): announced as it appears and sets aria-invalid |
placeholder: |
String |
Hint shown while the field is empty |
rows: |
Number |
How many lines it shows |
maxLength: |
Number |
The most characters it takes |
required: |
Bool |
Whether the form refuses to submit without it |
disabled: |
Bool |
Whether the control is inert |
Flags: .required .disabled .dropInside .trapFocus
Attribute families: global, aria, data, input.
Inline notice; a danger or warning one is announced as an alert.
Alert(message, tone: …) { … }
Alert("Your changes were saved.").success
Alert("The server is not answering.").danger
Renders with the class wf-alert.
| Prop | Type | Meaning |
|---|---|---|
message (positional) |
String |
The message |
tone: |
.success .danger .warning .info |
Kind of notice |
Flags: .success .danger .warning .info .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Temporary notification.
Toast(message, tone: …)
state saved = false
if saved { Toast("Saved").success }
Renders with the class wf-toast.
| Prop | Type | Meaning |
|---|---|---|
message (positional) |
String |
The message |
tone: |
.success .danger .warning .info |
Kind of notice |
Flags: .success .danger .warning .info .dropInside .trapFocus
Attribute families: global, aria, data.
Modal <dialog>, opened by a state flag; the browser supplies focus trapping and Escape. on close runs when it closes, however it was closed.
Modal(visible: …, title: …, closable: …) { … }
state open = false
Button("Delete") { on click { open = true } }
Modal(visible: open, title: "Delete deploys.wf?").closable {
Modal.Title { Markdown("Delete `deploys.wf`?") }
Text("This cannot be undone.")
Modal.Footer { Button("Cancel") { on click { open = false } } }
}
Renders with the class wf-modal.
| Prop | Type | Meaning |
|---|---|---|
visible: |
State |
State flag that opens and closes it |
title: |
String |
Heading shown at the top |
closable: |
Bool |
A close button in the heading row, which closes it as Escape does |
Flags: .closable .dropInside .trapFocus
Events of its own: on close
Parts:
Modal.Title { … }— What the modal's heading draws, in place of itstitle:'s words, which stay its name:Modal.Title { Text { "Rename " Code(name) } }.- flags:
.dropInside.trapFocus
- flags:
Modal.Footer { … }— The modal's action row.- flags:
.dropInside.trapFocus
- flags:
Takes a block of children.
Attribute families: global, aria, data.
A layer at a control: drawn above everything, placed against its anchor:, closed by Escape or a press outside, which write false back to visible: and fire on close.
Popover(visible: …, anchor: …, at: …) { … }
state open = false
Button("Branches", ref: branches) { on click { open = !open } }
Popover(visible: open, anchor: branches, placement: .bottomStart, label: "Branches").arrow {
on close(reason) { log(reason) }
Text("main")
}
Renders with the class wf-popover.
| Prop | Type | Meaning |
|---|---|---|
visible: |
State |
State flag that opens and closes it |
anchor: |
Any |
What it is placed against: an element's ref:, or a point { x: e.clientX, y: e.clientY } |
at: |
Any |
A point to place it at, { x: e.clientX, y: e.clientY }: where the pointer asked for a context menu |
placement: |
.bottom .bottomStart .bottomEnd .top .topStart .topEnd .left .leftStart .leftEnd .right .rightStart .rightEnd |
Where it goes against its anchor; it flips to the other side when there is no room. Unset, .bottomStart |
offset: |
Number |
How far from the anchor, in px |
label: |
String |
What it is, for a screen reader: its accessible name |
arrow: |
Bool |
A point toward the anchor |
Flags: .bottom .bottomStart .bottomEnd .top .topStart .topEnd .left .leftStart .leftEnd .right .rightStart .rightEnd .arrow .dropInside .trapFocus
Events of its own: on close
Takes a block of children.
Attribute families: global, aria, data.
Confirmation <dialog>, opened by a state flag. on close runs when it closes, however it was closed.
Dialog(visible: …, title: …, closable: …) { … }
state asking = false
Dialog(visible: asking, title: "Leave without saving notes.md?") {
Dialog.Title { Markdown("Leave without saving `notes.md`?") }
Text("Your draft will be lost.")
}
Renders with the class wf-dialog.
| Prop | Type | Meaning |
|---|---|---|
visible: |
State |
State flag that opens and closes it |
title: |
String |
Heading shown at the top |
closable: |
Bool |
A close button in the heading row, which closes it as Escape does |
Flags: .closable .dropInside .trapFocus
Events of its own: on close
Parts:
Dialog.Title { … }— What the dialog's heading draws, in place of itstitle:'s words, which stay its name.- flags:
.dropInside.trapFocus
- flags:
Takes a block of children.
Attribute families: global, aria, data.
Loading indicator, announced as a status.
Spinner(size: …)
Spinner
Spinner.lg
Renders with the class wf-spinner.
| Prop | Type | Meaning |
|---|---|---|
size: |
.sm .md .lg |
Its size |
Flags: .sm .md .lg .dropInside .trapFocus
Attribute families: global, aria, data.
Progress bar; a value that reads state moves with it.
Progress(value: …, max: …)
Progress(value: 40, max: 100)
Renders with the class wf-progress.
| Prop | Type | Meaning |
|---|---|---|
value: |
Number |
Current value |
max: |
Number |
Value at 100% |
Flags: .dropInside .trapFocus
Attribute families: global, aria, data.
Placeholder shape shown while content loads.
Skeleton(height: …, width: …, size: …)
Skeleton(height: "20px", width: "200px")
Skeleton(size: "48px").circle
Renders with the class wf-skeleton.
| Prop | Type | Meaning |
|---|---|---|
height: |
String |
CSS height |
width: |
String |
CSS width |
size: |
String |
Diameter of a circle |
circle: |
Bool |
A round placeholder |
Flags: .circle .dropInside .trapFocus
Attribute families: global, aria, data.
Button. Its block holds what it shows; on click what it does.
Button(label, tone: …, size: …, shape: …) { … }
Button("Save").primary { on click { log("saved") } }
Button("Delete").danger.outlined.sm
Button("Send", type: .submit).primary.full
Renders with the class wf-btn.
| Prop | Type | Meaning |
|---|---|---|
label (positional) |
String |
The label |
tone: |
.primary .secondary .success .danger .warning .info |
Colour |
size: |
.sm .md .lg |
Its size |
shape: |
.rounded .pill |
Corner shape |
width: |
.full |
Width |
outlined: |
Bool |
A border and no fill |
type: |
.button .submit .reset |
Its role inside a form |
to: |
Path |
Route to navigate to; the link whose to matches the current route carries .active and aria-current="page" |
disabled: |
Bool |
Whether the control is inert |
Flags: .primary .secondary .success .danger .warning .info .sm .md .lg .rounded .pill .full .outlined .button .submit .reset .disabled .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Icon-only button. label is its accessible name, never visible text.
IconButton(icon: …, label: …, size: …) { … }
IconButton(icon: "close", label: "Close")
IconButton(icon: "edit", label: "Edit the title").primary.sm
Renders with the class wf-icon-btn.
| Prop | Type | Meaning |
|---|---|---|
icon: |
String |
One of the built-in icon names |
label: |
String |
Accessible name (aria-label and title) |
size: |
.sm .md .lg |
Its size |
tone: |
.primary .danger |
Colour |
type: |
.button .submit .reset |
Its role inside a form |
disabled: |
Bool |
Whether the control is inert |
Flags: .sm .md .lg .primary .danger .button .submit .reset .disabled .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Buttons joined into one row.
ButtonGroup { … }
ButtonGroup {
Button("Day")
Button("Week")
Button("Month")
}
Renders with the class wf-btn-group.
Flags: .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Button that opens a menu of Dropdown.Items.
Dropdown(label) { … }
Dropdown(label: "Actions") {
Dropdown.Item { Text("Duplicate") }
Dropdown.Divider
Dropdown.Item { Text("Archive") }
}
Renders with the class wf-dropdown.
| Prop | Type | Meaning |
|---|---|---|
label (positional) |
String |
Label of the button that opens it |
Flags: .dropInside .trapFocus
Parts:
Dropdown.Item { … }— One entry of the menu.- flags:
.dropInside.trapFocus
- flags:
Dropdown.Divider— A rule between groups of entries.- flags:
.dropInside.trapFocus
- flags:
Takes a block of children.
Attribute families: global, aria, data.
Image; alt is required for accessibility (rule A01). Given an image the program declares, it becomes a <picture> with every width the build wrote.
Image(source, src: …, sizes: …, placeholder: …)
Image(src: "/team.jpg", alt: "The team at the launch", width: 1200, height: 630)
Image(src: "/flourish.svg", alt: "")
Renders with the class wf-image.
| Prop | Type | Meaning |
|---|---|---|
source (positional) |
Any |
An image the program declares, or a URL |
src: |
String |
Image URL |
sizes: |
String |
How wide it will be shown, so the browser picks a width: (max-width: 768px) 100vw, 1200px |
placeholder: |
.blur .color .none |
What fills the box while the image loads |
alt: |
String |
Text alternative; empty for a purely decorative image |
width: |
Number |
Intrinsic width |
height: |
Number |
Intrinsic height |
loading: |
.lazy .eager |
Unset, the first image on a page is eager at high priority and the rest lazy |
shape: |
.rounded .circle |
Shape |
Flags: .blur .color .none .lazy .eager .rounded .circle .dropInside .trapFocus
Attribute families: global, aria, data.
Video player. Without captions it draws an A09 warning: a video nobody can hear is a video nobody can follow.
Video(src: …, controls: …, autoplay: …)
Video(src: "/demo.mp4", captions: "/demo.en.vtt", poster: "/demo.jpg").controls
Renders with the class wf-video.
| Prop | Type | Meaning |
|---|---|---|
src: |
String |
Video URL |
controls: |
Bool |
Show the browser's playback controls |
autoplay: |
Bool |
Start playing when shown |
poster: |
String |
The frame shown before it plays |
muted: |
Bool |
Start with the sound off |
loop: |
Bool |
Start again when it ends |
playsinline: |
Bool |
Play where it sits, rather than full screen |
captions: |
String |
A WebVTT file of captions, which becomes a <track> |
width: |
Number |
Intrinsic width |
height: |
Number |
Intrinsic height |
Flags: .controls .autoplay .muted .loop .playsinline .dropInside .trapFocus
Attribute families: global, aria, data, media.
Audio player. transcript links what was said, which is what a reader who cannot hear it needs.
Audio(src: …, controls: …, autoplay: …)
Audio(src: "/episode-1.mp3", transcript: "/episode-1.txt").controls
Renders with the class wf-audio.
| Prop | Type | Meaning |
|---|---|---|
src: |
String |
Audio URL |
controls: |
Bool |
Show the browser's playback controls |
autoplay: |
Bool |
Start playing when shown |
loop: |
Bool |
Start again when it ends |
transcript: |
String |
A link to what was said, shown beneath the player |
Flags: .controls .autoplay .loop .dropInside .trapFocus
Attribute families: global, aria, data.
Another page, shown inside this one: an iframe — the page at src:, or the HTML srcdoc: gives it, sandboxed as sandbox: says. on message(m) hears what that page posts to this one (m.data), and nothing any other page posts. Without title: it draws an A17 warning: a frame is announced by its title.
Frame(src: …, srcdoc: …, title: …)
Frame(src: "https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ", title: "The launch talk", allow: "fullscreen")
state heard = ""
Frame(src: "/preview/", title: "Preview", sandbox: "allow-scripts") {
on message(m) { heard = m.data }
}
state version = 1
Frame(srcdoc: "<p>Version {version}</p>", title: "Example", sandbox: "")
Renders with the class wf-frame.
| Prop | Type | Meaning |
|---|---|---|
src: |
String |
The address of the page it shows |
srcdoc: |
String |
The HTML it draws in place of an address, sandboxed as sandbox: says; a value that reads state draws the new HTML as it changes |
title: |
String |
What it shows, in a few words: its accessible name |
sandbox: |
String |
What the framed page may do: "allow-scripts allow-forms"; empty, it may do nothing |
allow: |
String |
The features it may use: "fullscreen; clipboard-write" |
loading: |
.lazy .eager |
Unset, at once |
Flags: .lazy .eager .dropInside .trapFocus
Events of its own: on message, on load
Attribute families: global, aria, data.
One of the built-in SVG icons, rendered inline.
Icon(name, size: …, tone: …, muted: …)
Icon("search")
Icon("check").lg.success
Renders with the class wf-icon.
| Prop | Type | Meaning |
|---|---|---|
name (positional) |
String |
The icon name |
size: |
.sm .md .lg |
Its size |
tone: |
.primary .secondary .success .danger .warning .info |
Colour |
muted: |
Bool |
Dimmed, for secondary text |
Flags: .sm .md .lg .primary .secondary .success .danger .warning .info .muted .dropInside .trapFocus
Attribute families: global, aria, data.
Slide track with named controls and dots; optional autoplay.
Carousel(label: …, autoplay: …, interval: …) { … }
Carousel(label: "Recent work", autoplay: true, interval: 5000) {
Carousel.Slide { Image(src: "/one.jpg", alt: "A bridge at dusk") }
Carousel.Slide { Image(src: "/two.jpg", alt: "The harbour") }
}
Renders with the class wf-carousel.
| Prop | Type | Meaning |
|---|---|---|
label: |
String |
Accessible name of the region |
autoplay: |
Bool |
Advance automatically |
interval: |
Number |
Milliseconds between slides |
Flags: .autoplay .dropInside .trapFocus
Parts:
Carousel.Slide { … }— One slide.- flags:
.dropInside.trapFocus
- flags:
Takes a block of children.
Attribute families: global, aria, data.
A paragraph of text. {…} in the string reads state and stays live.
Text(content, tone: …, size: …, align: …)
Text("Hello, world")
Text("Last updated today").muted.sm
Text("Important").bold.danger
Renders with the class wf-text.
| Prop | Type | Meaning |
|---|---|---|
content (positional) |
Any |
The text |
tone: |
.primary .secondary .success .danger .warning .info |
Colour |
size: |
.sm .md .lg |
Its size |
align: |
.left .center .right |
Text alignment |
transform: |
.uppercase .lowercase |
Letter case |
kind: |
.heading .subtitle |
Typographic role |
bold: |
Bool |
Bold |
italic: |
Bool |
Italic |
underline: |
Bool |
Underlined |
muted: |
Bool |
Dimmed, for secondary text |
Flags: .primary .secondary .success .danger .warning .info .sm .md .lg .left .center .right .uppercase .lowercase .heading .subtitle .bold .italic .underline .muted .dropInside .trapFocus
Attribute families: global, aria, data.
Heading; level picks the tag (h2 when none is given).
Heading(content, level: …, align: …, muted: …)
Heading("Pricing").h1
Heading("Frequently asked").h2
Renders with the class wf-heading.
| Prop | Type | Meaning |
|---|---|---|
content (positional) |
Any |
The heading text |
level: |
.h1 .h2 .h3 .h4 .h5 .h6 |
Heading level: the tag, and its place in the outline |
align: |
.left .center .right |
Text alignment |
muted: |
Bool |
Dimmed, for secondary text |
Flags: .h1 .h2 .h3 .h4 .h5 .h6 .left .center .right .muted .dropInside .trapFocus
Attribute families: global, aria, data.
Inline code, or a code block with block; language: colours it.
Code(content, block: …, language: …)
Text("Run ")
Code("wf serve")
Code("page Home(path: \"/\") { Text(\"Hi\") }", language: "wf").block
Renders with the class wf-code.
| Prop | Type | Meaning |
|---|---|---|
content (positional) |
Any |
The code |
block: |
Bool |
A multi-line block |
language: |
String |
Colours the code as this language: wf, json, bash or css |
Flags: .block .dropInside .trapFocus
Attribute families: global, aria, data.
Markdown text rendered as HTML: headings, paragraphs, lists, quotes, code, links, emphasis. The text is escaped first, so HTML in it is shown, not run, and a link or picture whose URL a browser would run is drawn as its words. .inert is for text somebody else wrote.
Markdown(text, inert: …)
state docs = "Returns <T>, as [the guide](/docs) says."
Markdown("Some **bold** text and a [link](/docs).")
Markdown(docs).inert
Renders with the class wf-markdown.
| Prop | Type | Meaning |
|---|---|---|
text (positional) |
String |
The Markdown text |
inert: |
Bool |
Text somebody else wrote, such as a language server's documentation: its entities are decoded, a link is drawn as its words and a picture is left out |
Flags: .inert .dropInside .trapFocus
Attribute families: global, aria, data.
The one door for markup a page did not write. It has a single part, Unsafe.Html.
Unsafe
state body = "<p>From the CMS</p>"
Unsafe.Html(sanitize(body))
Flags: .dropInside .trapFocus
Parts:
Unsafe.Html(markup)— Markup, put in as markup. It is not sanitised — the name says so — andsanitize(…)is one call away for anything the project did not write itself.markup:String— The HTML to insert- flags:
.dropInside.trapFocus
Attribute families: global, aria, data.
Quotation: the text, or its block as the quoted content.
Blockquote(content) { … }
Blockquote { Text("Make it work, make it right, make it fast.") }
Renders with the class wf-blockquote.
| Prop | Type | Meaning |
|---|---|---|
content (positional) |
Any |
The quoted text |
Flags: .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
The document's headings with the page each lands on, each a link to it.
TableOfContents(levels: …, title: …)
TableOfContents(levels: 2)
Renders with the class wf-toc.
| Prop | Type | Meaning |
|---|---|---|
levels: |
Number |
How deep it lists: 1 is the h1s only (default 3) |
title: |
String |
A heading above the list |
Flags: .dropInside .trapFocus
Attribute families: global, aria, data.
Root of a PDF document: the paper, the margins and what the file says about itself.
Document(page_size: …, size: …, landscape: …) { … }
Document(page_size: "A4") {
Section { Heading("Report").h1 Paragraph("Revenue grew.") }
}
Renders with the class wf-document.
| Prop | Type | Meaning |
|---|---|---|
page_size: |
.A4 .A3 .A5 .Letter .Legal |
Paper size (also size:) |
size: |
String |
Paper size: A4, Letter, Tabloid, or two lengths, 210mm 297mm |
landscape: |
Bool |
Turn the paper on its side |
margin: |
Any |
The page margins: one length, or top right bottom left (72, 20mm 25mm) |
title: |
String |
The file's title, shown by a reader in place of its name |
author: |
String |
The file's author; several separated by commas |
subject: |
String |
What the document is about |
keywords: |
String |
Comma-separated keywords |
lang: |
String |
The document's language, for screen readers and hyphenation |
Flags: .A4 .A3 .A5 .Letter .Legal .landscape .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Groups content with spacing; on the web, a <section>.
Section { … }
Section {
Heading("Terms").h2
Text("Payment within 30 days.")
}
Renders with the class wf-section.
Flags: .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Block of text with paragraph spacing.
Paragraph(content, tone: …, size: …, align: …)
Paragraph("A block of prose, with the spacing a paragraph has.")
Renders with the class wf-text.
| Prop | Type | Meaning |
|---|---|---|
content (positional) |
Any |
The text |
tone: |
.primary .secondary .success .danger .warning .info |
Colour |
size: |
.sm .md .lg |
Its size |
align: |
.left .center .right |
Text alignment |
transform: |
.uppercase .lowercase |
Letter case |
kind: |
.heading .subtitle |
Typographic role |
bold: |
Bool |
Bold |
italic: |
Bool |
Italic |
underline: |
Bool |
Underlined |
muted: |
Bool |
Dimmed, for secondary text |
Flags: .primary .secondary .success .danger .warning .info .sm .md .lg .left .center .right .uppercase .lowercase .heading .subtitle .bold .italic .underline .muted .dropInside .trapFocus
Attribute families: global, aria, data.
Repeated at the top of every PDF page (or those on: names), where page and pages are the page's number and the count; a <header> on the web.
Header(on: …) { … }
Header { Text("Company Inc.").muted.sm }
Renders with the class wf-header.
| Prop | Type | Meaning |
|---|---|---|
on: |
.all .first .rest .odd .even |
Which pages it is drawn on |
Flags: .all .first .rest .odd .even .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Repeated at the bottom of every PDF page (or those on: names), where page and pages are the page's number and the count; a <footer> on the web.
Footer(on: …) { … }
Footer { Text("© Company Inc.").muted.sm }
Renders with the class wf-footer.
| Prop | Type | Meaning |
|---|---|---|
on: |
.all .first .rest .odd .even |
Which pages it is drawn on |
Flags: .all .first .rest .odd .even .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Drawn behind each PDF page's content, the size of the page: a cover, a band, a full-bleed picture.
Background(on: …) { … }
Background(on: .first) { Stack { style { height: 100%; background: linear-gradient(160deg, #1d4ed8, #0f172a) } } }
Renders with the class wf-background.
| Prop | Type | Meaning |
|---|---|---|
on: |
.all .first .rest .odd .even |
Which pages it is drawn on |
Flags: .all .first .rest .odd .even .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Large, faint, rotated text behind each PDF page's content.
Watermark(text, on: …)
Watermark("DRAFT")
Renders with the class wf-watermark.
| Prop | Type | Meaning |
|---|---|---|
text (positional) |
String |
The text |
on: |
.all .first .rest .odd .even |
Which pages it is drawn on |
Flags: .all .first .rest .odd .even .dropInside .trapFocus
Attribute families: global, aria, data.
Forces a new PDF page.
PageBreak
Text("End of chapter one.")
PageBreak
Heading("Chapter two").h2
Renders with the class wf-page-break.
Flags: .dropInside .trapFocus
Attribute families: global, aria, data.
Root of a slide deck; its children must be slides.
Presentation { … }
Presentation {
TitleSlide("Q1 review", subtitle: "March 2026")
Slide { Heading("Highlights").h1 }
}
Renders with the class wf-presentation.
Flags: .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
One slide, one PDF page; content is top-aligned and clipped at the bottom margin.
Slide { … }
Presentation {
Slide {
Heading("Revenue").h1
Text("Up 18% on last quarter.")
}
}
Renders with the class wf-slide.
Flags: .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Cover slide: title and subtitle, vertically centred.
TitleSlide(title, subtitle: …)
Presentation { TitleSlide("Q1 review", subtitle: "Acme, March 2026") }
Renders with the class wf-title-slide.
| Prop | Type | Meaning |
|---|---|---|
title (positional) |
String |
The title |
subtitle: |
String |
The subtitle |
Flags: .dropInside .trapFocus
Attribute families: global, aria, data.
Full-bleed coloured band with a centred label.
SectionSlide(label, tone: …)
Presentation { SectionSlide("Numbers").primary }
Renders with the class wf-section-slide.
| Prop | Type | Meaning |
|---|---|---|
label (positional) |
String |
The label |
tone: |
.primary .success .danger .warning .info |
Colour of the band |
Flags: .primary .success .danger .warning .info .dropInside .trapFocus
Attribute families: global, aria, data.
Two equal columns; takes exactly two Container children.
TwoColumn { … }
Presentation {
TwoColumn {
Container { Heading("Wins").h3 }
Container { Heading("Risks").h3 }
}
}
Renders with the class wf-two-column.
Flags: .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
Image slide with an optional caption.
ImageSlide(src: …, caption: …)
Presentation { ImageSlide(src: "chart.png", caption: "Revenue by month") }
Renders with the class wf-image-slide.
| Prop | Type | Meaning |
|---|---|---|
src: |
String |
Image path (required) |
caption: |
String |
Caption under the image |
Flags: .dropInside .trapFocus
Attribute families: global, aria, data.
Where the current page renders. May sit at any depth inside app; pages declare their own paths.
Router(transition: …, duration: …) { … }
app {
Navbar { Navbar.Brand { Link("Acme", to: "/") } }
Router(transition: .fade, duration: "200ms")
}
Renders with the class wf-router.
| Prop | Type | Meaning |
|---|---|---|
transition: |
.none .fade .slide |
How a route change moves between pages |
duration: |
String |
How long each half of the transition plays, e.g. 200ms |
Flags: .none .fade .slide .dropInside .trapFocus
Takes a block of children.
Attribute families: global, aria, data.
| Prop | Type | Meaning |
|---|---|---|
animate: |
.fadeIn .fadeOut .slideUp .slideDown .slideLeft .slideRight .scaleIn .scaleOut .bounce .shake .pulse .spin .expand .collapse |
Animation played when the element appears |
exit: |
.fadeIn .fadeOut .slideUp .slideDown .slideLeft .slideRight .scaleIn .scaleOut .bounce .shake .pulse .spin .expand .collapse |
Animation played when the element leaves |
speed: |
.fast .normal .slow |
How fast its animation runs |
duration: |
String |
Animation length, e.g. 300ms |
delay: |
String |
Wait before the animation starts |
easing: |
.standard .spring .ease .easeIn .easeOut .easeInOut .linear .bouncy .smooth |
How the animation is paced — a named easing, or any CSS timing function |
stagger: |
String |
Delay added per item of a list, e.g. 50ms |
on: |
.mount .enterView |
When its animation plays |
count: |
String |
How long the number it shows takes to count to a new value, e.g. 600ms |
shared: |
String |
A name this element keeps across a route change, so the browser carries it from one page to the next |
class: |
String |
Classes added beside the engine's, from the project's own stylesheets |
ref: |
State |
A handle on the element, usable as the element: ref: nameInput, then nameInput.focus() |
drag: |
Any |
What it carries when it is picked up — by a mouse moved a few pixels, or a finger held still — and dropped on something with on receive(d) |
dragGroup: |
String |
Which drops it belongs to: a drag goes only to targets of its own group, so tabs and tree rows stay apart |
dropValue: |
Any |
What this target is, as a drag sees it: d.over in the drag's on dragmove(d) and in on receive(d) |
dropAxis: |
.y .x |
Which way its list runs, for where a drop lands |
dropInside: |
Bool |
A drop on its middle lands inside it — a folder, a container — as well as before or after |
trapFocus: |
Bool |
While true, focus moves into it and Tab and Shift+Tab stay inside; when it turns false or the element leaves, focus goes back where it was |
mount: |
Any |
Run with the element each time it is made — mount: (n) => initTilt(n) — and what it returns is the handle update: and cleanup: get |
update: |
Any |
Run with the handle again whenever the state it reads changes: update: (h) => h.setMax(max) |
cleanup: |
Any |
Run with the handle when the element leaves — a route change, an if branch, a list item: cleanup: (h) => h.destroy() |
The cases of animate: and speed: are also flags on every element: .fadeIn … .spin, .fast .normal .slow.
Events every element accepts in an on … { } handler:
on click— The element was clicked or activatedon input— The value changed, on every keystrokeon change— The value was committedon submit— The form was submittedon focus— The element received focuson blur— The element lost focuson keydown— A key went down;e.keynames iton longpress— Held still for half a second;p.x,p.ysay where. The click it ends with is not a clickon swipe— A finger or a pen swept across it;s.directionis "left", "right", "up" or "down"on receive— Something withdrag:was dropped on it;d.valueis what,d.position"before", "after" or "inside"on dragmove— On what is dragged, while it moves:d.overis the target'sdropValueor null,d.positionwhere it would landon key— One key, named:on key("ctrl+k") { }on keyup— A key came upon keypress— A character key was pressedon mouseenter— The pointer entered the elementon mouseleave— The pointer left the elementon dblclick— The element was double-clickedon auxclick— A button other than the main one was clicked;e.buttonis which — 1 is the middle, which closes a tabon focusin— Focus moved onto the element or something inside iton focusout— Focus left the element or something inside iton contextmenu— The context menu was asked for;e.preventDefault()keeps the browser'son mousedown— A mouse button went down over the elementon mouseup— A mouse button came up over the elementon mousemove— The mouse moved over the elementon mouseover— The pointer moved onto the element or one of its childrenon mouseout— The pointer moved off the element or one of its childrenon pointerdown— A pointer — mouse, pen or touch — went downon pointerup— A pointer came upon pointermove— A pointer movedon pointerenter— A pointer entered the elementon pointerleave— A pointer left the elementon pointercancel— The browser took the pointer back, for a scroll or a gestureon touchstart— A finger touched the elementon touchend— A finger left the elementon touchmove— A finger moved across the elementon wheel— The wheel or trackpad scrolled over the elementon scroll— The element's content scrolledon focusin— The element or something inside it received focuson focusout— The element or something inside it lost focuson reset— The form was reseton invalid— A control failed the browser's own validationon select— Text in a field was selectedon copy— Something was copied from the elementon cut— Something was cut from the elementon paste— Something was pasted into the element;e.clipboardDataholds iton dragstart— The element started being draggedon drag— The element is being draggedon dragend— A drag of the element endedon dragenter— Something dragged entered the elementon dragover— Something dragged is over the element;e.preventDefault()allows a dropon dragleave— Something dragged left the elementon drop— Something was dropped on the element;e.dataTransferholds iton load— An image, video or frame finished loadingon error— An image, video or script failed to loadon play— A video or audio started playingon pause— A video or audio pausedon ended— A video or audio reached its endon timeupdate— A playing video or audio moved onon animationend— A CSS animation on the element endedon transitionend— A CSS transition on the element endedon toggle— A details element opened or closed
The names Icon(…), IconButton(icon:) and Sidebar.Item(icon:) draw; any other name shows as the word, and the compiler warns.
close menu search home user settings check plus minus edit trash star heart mail bell download upload eye link calendar filter chevron-down chevron-right chevron-left info warning arrow-left arrow-right logout copy sun moon folder folder-plus folder-off file file-plus refresh alert-circle clock dot diff book git-pull-request pin git-branch more-horizontal window-minimize window-maximize outline help-circle collapse-all locate chevron-up globe external-link panel-down panel-up code monitor git-commit play layers insert grid list square pill image sparkle lock
CLI.