UI
Displays get the same treatment as everything else: author with HTML and CSS, and the build compiles it — layout solved at build time (Yoga), styles folded into draw lists, fonts subsetted to the glyphs you use, pixels as RGB565. Nothing like a browser runs on the board.
the .ui file
A single file: markup, style, and a script. Point the config's entry at it and the transpiler does the rest. The syntax is Svelte-compatible — braces interpolate, on: and bind: wire events and values.
Example first
<screen>
<view class="col">
<text class="title">{count} taps</text>
<button on:click={() => count++}>tap me</button>
</view>
</screen>
<style>
.col { flex-direction: column; gap: 12; align-items: center; }
.title { font-size: 24; }
button { padding: 10 20; }
</style>
<script lang="ts">
import { ui } from '@typecad/ui';
const count = ui.signal(0);
</script>import type { TypecadConfig } from '@typecad/hal/config';
const config: TypecadConfig = {
entry: './src/app.ui',
display: {
driver: 'ilitek,ili9341', // any panel your Zephyr SDK binds — drop-in
width: 320, height: 240,
rotation: 90,
backlightPin: 23,
},
};
export default config;the ui API
The script side: reactive state, bindings, input, and canvases. Everything is a compile-time construct — each call lowers to specific draw or read code.
Example first
| Call | What it does |
|---|---|
| ui.mount(tree, opts?) | Mount a screen — display facts usually live in the config’s display block |
| ui.signal(initial) | Reactive value: call it to read, .set() to write — screens re-render on change |
| ui.bind(node, prop, compute) | Bind a node property to a computed value |
| ui.bindList(node, countFn, itemFn, onTap?) | Virtualized list — only visible items render |
| ui.bindInput(node, onCommit) | On-screen keyboard input |
| ui.watchPin(pin, onFalling) | Poll a physical button (~20 ms, no ISRs) |
| await ui.onTap(node?) | Awaitable taps — menus and flows read top to bottom |
| ui.drawCanvas(node, draw) | Canvas drawing — CSS colors become RGB565 at build time |
Elements
| Group | Elements |
|---|---|
| Layout | <screen> root · <view> containers · <text> · <img> |
| Input | <button> · <check> · <radio> · <select> · <input> · <range> |
| Display | <progress> · <range> · <canvas> |
| Wiring | {expr} interpolation · on:click/hold/release/change · bind:text / bind:value · ref= |
the CSS subset
The layout-critical part of CSS, resolved entirely at build time. Unsupported properties are build warnings — errors with --strict-css.
Example first
| Area | Supported |
|---|---|
| Layout | Flexbox — direction, gap, grow/shrink, align/justify, wrap, position, z-index |
| Boxes | Padding/margin shorthands, borders, border-radius, aspect-ratio, overflow scroll |
| Type | Font shorthand, size, weight, text-align/decoration, custom fonts with build-time glyph subsetting |
| Color | All CSS formats plus the named colors |
| States & themes | :pressed, CSS variables, theme imports |
| Cost | Zero at runtime — layout resolves at build time, styles compile to draw lists |