Cuttlefish UI

Build the display screen using HTML and CSS — Cuttlefish turns it into the drawing commands a small TFT or OLED screen understands. You write a .ui file (an HTML template, CSS, and an optional TypeScript script), and when you build, Cuttlefish turns it into C++ that draws directly to a display over SPI or I2C. No browser, DOM, or CSS engine runs on the device. Layout, style resolution, and font subsetting (keeping only the characters you use) all happen at build time; the firmware only runs the resulting draw calls.

The authoring library is @typecad/ui (compile-time only — none of its code ships to the device). It pairs with the display driver support built into @typecad/cuttlefish.


Topics

TopicWhat you’ll learn
ElementsThe full HTML element catalog (<screen>, <view>, <text>, <button>, <check>, <radio>, <select>, <progress>, <range>, <input>, <list>, <canvas>, <img>, plus the <drawer>, <dialog>, and <toast> overlays)
CSS & StylingBox model, flexbox via Yoga, colors, gradients, typography, selectors, transitions, @keyframes animations, CSS variables, the built-in shadcn-style component kit, theming
Display ConfigurationThe display wizard, wiring displays in cuttlefish.config.ts, built-in profiles, touch input, display drivers
Authoring APIThe ui.* functions (mount, signal, bind, drawCanvas, …) and the .ui file format

First example

A minimal .ui file — a screen with a label and a button. The template, styles, and script live in one file:

Template:

<screen>
  <text id="label">Hello, Cuttlefish</text>
  <button id="ok">OK</button>
</screen>

Styles:

screen { display: flex; flex-direction: column; gap: 12px; padding: 16px; }
#label { font-size: 20px; color: #fff; }
#ok { background: #2563eb; color: #fff; padding: 10px; text-align: center; }

Script:

import { ui } from '@typecad/ui';
ui.mount(screen);
screen.ok.onClick(() => { screen.label.value = 1; });

Build with cuttlefish build --compile --upload --port COM4. When you build, Cuttlefish resolves the layout, keeps only the font characters you use, and emits the draw calls; ui.mount(screen) is intercepted and becomes display initialization plus the first-frame draw.

How it works

  • Author in HTML + CSS. Use the elements catalog and a CSS subset that includes flexbox, the box model, gradients, colors, typography, animations, and selectors. A shadcn-style component kit (btn, card, badge, …) is built in, with pre-packaged themes (@import "@typecad/ui/themes/blue.css") and dark mode.
  • Resolved at build time. Layout runs through Yoga (a layout library), styles are resolved, fonts keep only the characters you actually use, and images (png, jpg, webp, svg, …) are embedded as RGB565 byte arrays (a compact color format used by most TFT displays). Bundled anti-aliased DejaVu sans and mono fonts work out of the box.
  • Retained-mode runtime. The device holds a tree of elements and only redraws what changed; ui_tick() polls inputs, evaluates reactive bindings, and repaints only the dirty regions (no full-screen flicker on small updates). Scrolling uses small band-shaped repair strips (~10 KB SRAM), and offscreen canvases (list viewports, repair buffers, on-screen keyboards) are allocated once and reused — on boards with PSRAM (extra memory on some ESP32 boards), those canvases are placed in PSRAM.
  • Display-agnostic. The same UI drives ILI9341 and ST7796 SPI TFTs and an SDL desktop window for development.

Read Elements next for the full component catalog.