CSS & Styling
Style your UI with CSS, the same way you’d style a web page. Cuttlefish understands a practical subset of CSS — the box model, flexbox, colors, typography, and selectors. All of it is resolved when you build (no CSS engine runs on the device): flexbox is laid out with Yoga (a layout library), styles are resolved, and the resulting draw commands are what the firmware runs.
Box model
| Property | Values | Notes |
|---|---|---|
padding | 8px, 8px 16px | Shorthand supported |
margin | 8px, 8px 16px | Shorthand supported |
width / height | 100px, 50px | Explicit size |
min-width / max-width / min-height / max-height | 100px | Yoga constraints |
aspect-ratio | 16 / 9, 1 / 1, 1.5 | Infers the missing dimension |
box-sizing | border-box | Yoga border-box |
overflow | hidden, scroll | Both values make the container a touch-drag scroll viewport |
Length units
px, bare numbers, and rem/em (multiplied by 16, the root font size — so 0.625rem = 10px). Percentages are honored where applicable. calc() evaluates + - * / on lengths after var() substitution, with operator precedence.
Flexbox (via Yoga)
| Property | Values |
|---|---|
display | flex, none |
flex-direction | row, row-reverse, column, column-reverse |
gap | 8px (sets both row and column gap) |
row-gap / column-gap | 8px (per-axis; overrides uniform gap) |
flex-grow | 1 |
flex-shrink | 0 |
flex-basis | auto, 120px, 50% |
flex (shorthand) | 1, 1 0 auto, none |
align-items | flex-start, center, flex-end, stretch |
align-self | flex-start, center, flex-end, stretch, baseline |
align-content | flex-start, center, flex-end, stretch, space-between, space-around, space-evenly |
justify-content | flex-start, center, flex-end, space-between, space-around, space-evenly |
flex-wrap | wrap, nowrap, wrap-reverse |
order | 1, 2, … |
position | relative, absolute, static |
top / right / bottom / left | 10px |
z-index | numeric layers (inherited by descendants) |
display: noneremoves the element subtree from layout, drawing, and hit-testing while preserving generated node indices.
Colors
All standard CSS formats: #rrggbb, #rgb, #rrggbbaa (alpha ignored), rgb(r,g,b), rgba(r,g,b,a) (alpha ignored), hsl(...), hsla(...). Both comma and CSS4 space syntaxes work. The 147 CSS named colors (red, dodgerblue, transparent, …) are supported.
Typography
| Property | Values | Notes |
|---|---|---|
color | any color | Text foreground |
font-family | "MyFont" | Uses a generated font when matched by @font-face; otherwise the built-in bitmap font |
font-size | 16px | Generated fonts rasterize at this pixel size |
font | italic bold 18px DeviceSans | Shorthand for style/weight/size/family |
text-align | left, center, right | |
text-decoration | underline, line-through, none | Combine: underline line-through |
text-overflow | ellipsis, clip | Truncates overflowing single-line text |
text-transform | uppercase, lowercase, capitalize, none | Applied at transpile time |
line-height | 1.5, 150%, 24px | normal = font default |
letter-spacing | 2px, -1px | |
white-space | normal, nowrap, pre, pre-line | |
font-weight | normal, bold, 400, 700 | Selects the matching @font-face variant |
font-style | normal, italic, oblique | |
font-smoothing | anti aliased, none | Overrides display-level antialiasing |
font-subset | exact, fallback | Controls glyph selection for generated fonts |
Fonts (@font-face)
Declare variants with @font-face. When you build, Cuttlefish keeps only the font characters you actually use:
@font-face {
font-family: "DeviceSans";
src: url("./assets/DeviceSans-Bold.ttf");
font-weight: bold;
}font-subset: exact— include only the exact glyphs used (smallest binary).font-subset: fallback— include fallback glyphs for dynamic text that may contain unanticipated characters.font-smoothing: antialiased— enables per-pixel alpha for the matched text.
Visual properties
| Property | Values | Notes |
|---|---|---|
background / background-color | any color, linear-gradient(…) | Two-stop linear gradients: background: linear-gradient(to bottom, #111, #333) |
border (shorthand) | 2px solid #808080 | |
border-width / border-color / border-style | 2px / color / solid dashed dotted double none | Dashed is approximated with segments |
border-left / border-top / border-right / border-bottom | 2px solid #808080 | Per-side borders |
border-radius | 4px | Uniform only (no per-corner) |
outline | 1px solid #fff | Drawn outside the element box |
visibility | visible, hidden | |
box-shadow | inset 0 1px 0 #fff, 0 10px 0 #333 | Up to 4 rect shadows; approximated |
transform | translateY(10px), translate(0, 10px), scale(1.1), scaleX(0.9), rotate(15deg) | Draw-time offset/transform; no flex relayout. Percent-based translate resolves against the element’s own size |
transform-origin | center, top left, 50% 100% | Anchor for scale/rotate |
opacity | number | Blended toward the parent/background color (approximate — no framebuffer needed) |
Transitions, animations, and :pressed
transition: background 300ms;
transition: color 120ms;Smoothly transitions the property over the duration. The :pressed pseudo-class applies while a button’s .value is 1:
#ok { background: #2563eb; transition: background 120ms; }
#ok:pressed { background: #1e40af; transform: translateY(2px); }:pressed rules may include top/left/right/bottom or transform: translate(...) as draw-time offsets (outset shadows stay anchored).
@keyframes animations
Define keyframe sets and attach them with the animation property (or its long forms animation-name, -duration, -iteration-count, -delay, -timing-function):
@keyframes pulse {
from { opacity: 1; }
to { opacity: 0.4; }
}
#indicator { animation: pulse 1.2s infinite ease-in-out; }Animatable properties are background, color, opacity, transform, and size (width/height). The shorthand accepts name duration iterations delay timing in any order; infinite loops forever, and linear/ease/ease-in/ease-out/ease-in-out shape the interpolation. direction (normal, reverse, alternate, alternate-reverse), fill-mode, and running/paused tokens are parsed for compatibility. Animations advance in ui_tick() alongside transitions.
Selectors
| Kind | Example |
|---|---|
| Element | screen |
| ID | #title |
| Class | .card |
| Compound | .card.active, button.primary |
| Descendant | view text |
| Child | view > text |
| Adjacent sibling | .first + .second |
| General sibling | .first ~ .later |
| Attribute | [disabled], [type="number"] |
| Negation | button:not(.disabled), .a:not(.b.c) |
| Pseudo-state | :pressed, :active, :disabled, :checked, :focus |
Pseudo-states: :pressed (button held; :active is an alias), :disabled, :checked (check/radio .value is 1), :focus.
Inline style="…" attributes and <style> blocks are both supported.
CSS variables and theming
Define variables in :root and reference with var():
:root {
--bg: #111;
--fg: #eee;
}
screen { background: var(--bg); color: var(--fg); }Class-scoped themes select a variant at build time via themeClass in your cuttlefish.config.ts:
:root { --bg: #fff; --fg: #000; }
.dark { --bg: #111; --fg: #eee; }display: { themeClass: 'dark', /* … */ }The built-in component kit
A shadcn-style component kit is built into @typecad/ui and always included — its tokens and class recipes are prepended ahead of your stylesheets, so anything you write overrides them by normal cascade order. No imports or scaffolding: put the classes on native elements and they work.
<button class="btn btn-primary">Save</button>
<view class="card">
<view class="card-header">
<text class="card-title">Title</text>
<text class="card-description">Subtitle</text>
</view>
<view class="card-content"> ... </view>
<view class="card-footer"> ... </view>
</view>The kit covers btn (variants -secondary -outline -ghost -destructive, sizes .btn-sm/.btn-lg, .btn-block), badge, card, input, form-label, row, separator/vseparator, alert, skeleton, spinner, dialog and toast recipes (dialog-scrim, dialog-card, dialog-footer, toast, toast-title, toast-description), progress, avatar, switch (a styled <check>), tabs, and accordion. Validation states (.input-error, .field-error) are runtime-driven — bind borderColor/visible to a signal.
The kit’s design tokens are ordinary CSS variables (--primary, --background, --card, --muted, --destructive, --border, --radius, …), themed through the checked pseudo-state as well: :checked rules resolve a --primary/--primary-foreground pair, so switches, checkboxes, radios, and select option rows recolor automatically per theme.
Themes
A theme is any CSS file. Pre-packaged ones ship with @typecad/ui — @import one by bare specifier from a <style> block (or the sidecar .ui.css file):
@import "@typecad/ui/themes/blue.css";Available themes: zinc, slate, stone, gray, neutral, blue, green, red. Dark mode activates with themeClass: 'dark' in the config’s display block. To use your own, save a ui.shadcn.com / tweakcn export into the project and @import it by path — its :root/.dark token blocks override the kit defaults (later definitions win). Both dialects parse: classic HSL channel triplets and Tailwind-v4 oklch(). Alpha in colors is ignored (no blending on bare metal). Redefine any kit class in your own stylesheet and your definition wins.
@media performs compile-time variant selection (no runtime resizing). Supported conditions: min-width, max-width, min-height, max-height (in px), exact-match (width: N)/(height: N), and the feature queries (e-ink), (update: slow|fast), (monochrome) / (monochrome: N), and (color-gamut: srgb|p3). orientation is unsupported (warned and skipped).
@media (max-width: 200px) {
screen { flex-direction: column; }
}Unsupported (and why)
These are intentionally out of scope for microcontroller rendering:
display: grid— use flexboxbackground-image/ CSS sprites — use<img>instead, or a two-stoplinear-gradient()onbackgroundposition: fixed::before/::afterpseudo-elements- Per-corner
border-radius(uniform only) - Runtime theme switching (themes are resolved at transpile time)
@supports(@importis supported for theme files — see Themes)
Inline rich text (mixed bold/italic/underline spans via <b>/<i>/<u>/<a>, line breaks with <br>) and text-shadow are supported — see Elements.
Next: Display Configuration for wiring a physical display.
On This Page