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 | scroll enables touch-drag scrolling |
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 (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 | |
border (shorthand) | 2px solid #808080 | |
border-width / border-color / border-style | 2px / color / solid dashed none | Dashed is approximated with segments |
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) | Draw-time offset; no flex relayout |
opacity | number | Blending requires a PSRAM framebuffer (PSRAM is extra memory on some ESP32 boards) |
Transitions 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).
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, :disabled, :checked, :focus |
Pseudo-states: :pressed (button held), :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', /* … */ }@media performs compile-time variant selection (no runtime resizing). Supported conditions: min-width, max-width, min-height, max-height (in px). 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 flexbox- Full inline rich text (mixed inline spans)
background-image/ CSS sprites — use<img>insteadposition: fixed::before/::afterpseudo-elementstext-shadowon the built-in font- Per-corner
border-radius(uniform only) - Runtime theme switching (themes are resolved at transpile time)
@import,@supports
Next: Display Configuration for wiring a physical display.
On This Page