Elements
Cuttlefish UI supports a focused set of HTML elements. Every UI has one or more <screen> roots; inside them you compose containers, text, controls, and media. Multiple screens can coexist in one file — see Screen navigation.
The <screen> root
<screen>
...children...
</screen>Required — at least one per UI; add more to define additional screens. Each screen’s box fills the display viewport (e.g. 320×240 landscape for an ILI9341, a common TFT display controller). All other elements live inside a screen.
Screen navigation
Any tap target can navigate to another screen with an href="#screenId":
<screen id="home">
<button href="#settings">Settings</button>
</screen>
<screen id="settings">
<a href="#home">Back</a>
</screen><a href="#id"> and <button href="#id"> auto-wire the tap to a screen switch at build time — no script code needed.
Containers and text
<view> — generic container
A block container. Supports display: flex for layout. Exposes .value plus onToggle/onChange for pin-driven state.
<view id="row" style="display: flex; flex-direction: row; gap: 8px;">
<text>Hello</text>
<text>World</text>
</view><text> — static or dynamic text
<text id="label">Temperature</text>Has a numeric .value property for state. To change the displayed string reactively, use ui.bind(node, 'text', compute) (see Authoring API).
Interactive elements
| Element | Purpose | .value |
|---|---|---|
<button> | Tappable button (with :pressed state) | 0 / 1 |
<check> | Checkbox — tap to toggle | 0 / 1 |
<radio name="g"> | Radio — mutually exclusive within a name group | 0 / 1 |
<select> | Tap to cycle options | 0..N-1 |
<progress> | Progress bar | 0-100 (fill %) |
<range> | Draggable slider | between min and max |
<input> | Text input (opens on-screen keyboard) | — (use .text) |
<list> | Virtualized (only the visible items are drawn), data-bound list | — |
<canvas> | User-drawn graphics via ui.drawCanvas | — |
<drawer> | Edge panel that slides in from a screen edge | — |
<dialog> | Centered modal overlay | — |
<toast> | Auto-dismissing notification | — |
<button>
<button id="ok">OK</button>Supports the :pressed pseudo-state and transition animations (see CSS). Events: onClick (short tap, up within 600ms), onHold (press ≥ 600ms), onRelease.
<check>
<check id="enable">Enable feature</check>Content text is the label. .value is 0 (unchecked) or 1 (checked). onToggle(pin, onChange?) watches a GPIO pin for falling edges and flips .value automatically.
<radio>
<radio name="mode">Auto</radio>
<radio name="mode">Manual</radio>Radios sharing the same name are mutually exclusive — selecting one deselects the others. .value is 0 or 1.
<select>
<select id="mode">
<option>Auto</option>
<option>Manual</option>
<option>Off</option>
</select>Tapping cycles through the <option> children. .value is the index of the current option (0..N-1); the element’s displayed text automatically reflects the current option.
<progress>
<progress id="load" value="40"></progress>Read-only progress bar. .value is 0-100 (percentage filled).
<range>
<range id="brightness" min="0" max="100"></range>A draggable slider. .value sits between min and max (defaults 0-100). onChange(callback?) fires whenever .value changes during a drag. Drag the thumb or write .value from code.
<input>
<input id="ssid" type="text" placeholder="Network name" maxlength="32">Text input. Tapping opens an on-screen keyboard. The string value lives in .text (not .value). onChange(callback?) fires after the keyboard commits. Attributes: id, type, placeholder, maxlength, keyboard.
Use type="number" to open a numeric keypad instead of the alpha keyboard. To open a custom keyboard, set keyboard="mykeys" to the id of a <keyboard> template defined in the same file (see below).
<list>
<list id="networks" item-height="28px"></list>A virtualized (only the visible items are drawn), data-bound list. Bind it to dynamic data with ui.bindList(node, countFn, itemFn, onTap?). Attribute: item-height.
<canvas>
<canvas id="spark" width="120" height="40"></canvas>A user-drawn region. Register a per-frame draw callback with ui.drawCanvas(node, callback); the callback receives a CanvasCtx (see Authoring API). Attributes: width, height (drawing buffer size in pixels).
On-screen keyboards
Two built-in keyboards ship with the runtime — an alpha keyboard (QWERTY-style, themeable via the ui-key* CSS classes) and a number keypad. <input> opens the alpha one by default, or the number keypad with type="number".
You can also define custom keyboards with <keyboard>, <row>, and <key>:
<keyboard id="hexkeys">
<row><key>0</key><key>1</key><key>2</key><key>3</key></row>
<row><key>4</key><key>5</key><key>6</key><key>7</key></row>
<row><key>8</key><key>9</key><key>A</key><key>B</key></row>
<row><key>C</key><key>D</key><key>E</key><key>F</key></row>
</keyboard>
<input id="addr" keyboard="hexkeys" placeholder="Hex address">Keyboard templates are parsed from the top level of the .ui file and referenced by id from any <input>.
Overlays
<drawer> — edge panel
<drawer id="nav" side="left">
<view class="dialog-scrim" on:click={closeNav}></view>
<view class="dialog-card"> ...menu content... </view>
</drawer>A panel that slides in from an edge of the screen. side is left, right, top, or bottom (default bottom). Open and close it from script with ui.drawer.open(id) / ui.drawer.close(id); a tap on the scrim (an overlay child covering the rest of the screen) closes it. Slide frames are band-composited, so the animation doesn’t tear on SPI panels.
<dialog> — centered modal
<dialog id="confirm">
<view class="dialog-scrim" on:click={closeConfirm}></view>
<view class="dialog-card">
<text>Delete this network?</text>
<view class="dialog-footer">
<button class="btn btn-outline">Cancel</button>
<button class="btn btn-destructive">Delete</button>
</view>
</view>
</dialog>A centered modal overlay. Open and close it with ui.dialog.open(id) / ui.dialog.close(id); tapping the scrim closes it. Internally it uses the same slot machinery as the drawer. The <dialog> element itself lays out at zero height with absolutely-positioned children — style the .dialog-card child, not the <dialog> tag.
<toast> — transient notification
<toast id="saved" side="bottom" duration="2500" class="toast">
<text class="toast-title">Saved</text>
<text class="toast-description">Settings written to flash</text>
</toast>Shows with ui.toast(id) — a single call, no open/close pair. side is top or bottom; after the panel is fully open it auto-closes duration milliseconds later (re-showing it restarts the timer, and a manual close wins over the auto-close). Multiple toasts stack as authored siblings. When closed it slides fully off the display edge — no remnant strip.
Tables
<table>
<table>
<tr><th>Sensor</th><th>Value</th></tr>
<tr><td>Temp</td><td>22.4 °C</td></tr>
</table>An approximation of an HTML table via the UA stylesheet (it is not a full table layout engine) — useful with the kit’s table styling. See CSS & Styling.
Template directives
Elements support Svelte-style declarative wiring in the template itself, so most UIs need little or no script code:
| Directive | Example | Meaning |
|---|---|---|
on:click / on:hold / on:release / on:change | <button on:click={save}> | Call the script function save on the event |
bind:text | <input bind:text="name"> | Two-way bind the input’s text to the signal name |
bind:value | <range bind:value="level"> | Two-way bind .value to the signal level |
ref | <text ref="temp"> | Give the node a TypeScript handle name independent of its CSS id |
{expr} interpolation | <text>Level: {level()}</text> | Live-updating text bound to a signal — no id needed |
Images
<img>
<img id="logo" src="assets/logo.img" width="64" height="64">Embeds an image as a static const uint16_t[] (RGB565 — a compact color format). Common formats — png, jpg, webp, gif, svg, ico, tiff, avif — are detected by magic bytes, downscaled to fit the display, and converted automatically at build time; the raw .img format (flat row-major, width × height × 2 bytes) also still works. Use object-fit: contain | cover | fill to control scaling. Attributes: id, src, width, height.
HTML tag aliases
Common HTML tags map to Cuttlefish primitives:
| HTML tag | Maps to | Notes |
|---|---|---|
body, div, header, footer, nav, main, section, article, aside | <view> | Block container |
span, p, h1–h6 | <text> | Inline/heading text |
b, strong, i, em, u | <text> inline runs | Mixed bold/italic/underline spans within one text node |
label, a, br | first-class tags | label groups with a control, a is a link (supports href), br breaks a line |
table, tr, th, td, hr | first-class tags | UA-styled table approximation; hr draws a rule (see the kit’s .separator / .vseparator) |
Global attributes
All elements support hidden — a hidden element (and its descendants) is kept in memory but takes no space on screen and ignores taps.
Next: CSS & Styling for how to style these elements.
On This Page