Style
The closed list of visual keys a document may set, what each one accepts, and the five kinds of value you can write.
A style block sits on any element, and on the document itself. It looks like a React inline-style object, because that is exactly what it is modelled on:
"style": { "fontSize": 14, "fontWeight": "semibold", "color": "var(--color-muted-foreground)" }
The key list is closed. Everything the language can say is on this page, and a key that is not on it comes back as an error rather than being quietly ignored — you would otherwise never learn that boxShadowColor does nothing.
The five kinds of value
Before the tables, the value shapes. There are only five, and they are the reason a document can be checked before it is saved.
A number is pixels. "fontSize": 14, "gap": 8. Whole numbers, and each key has its own sensible range — the Accepts column below gives it. A number outside the range is rejected, not clamped silently.
A preset is a named step. "fontSize": "lg", "borderRadius": "full", "gap": "md". Presets resolve through your theme's scale, so a preset follows a theme change and a hard-coded pixel value does not. A preset and a pixel value are equally safe; pick whichever says what you mean.
Spacing can be per side. padding and margin take a single number, a preset, or an object:
"padding": { "x": 14, "top": 2, "bottom": 12 }
x is left and right, y is top and bottom, and a named side wins over the shorthand it overlaps. Any side you do not mention is 0. What you cannot write is the CSS shorthand string "2px 14px" — a phone cannot parse it, so it is not part of a language both screens speak.
Colours and fonts are tokens, not paint. The form to reach for is your theme's own variable — "color": "var(--color-foreground)", "background": "var(--color-primary)", "fontFamily": "var(--font-heading)". Those follow whatever theme is active, including one you switch to next month. A strict hex ("#16a34a") and a colour name from your theme's palette are accepted too, and stay exactly that colour forever.
Everything else is a closed token or a boolean. "textAlign": "center", "objectFit": "cover", "boxShadow": "md", "stretch": true. Each one's full list is in the Accepts column.
One document, two screens
Every key below is honoured by both renderers — the browser and the phone app — and that is checked automatically rather than promised. A few land differently under the hood: lineHeight is a ratio the phone multiplies out into pixels, span and columns become real CSS Grid on the web and arithmetic on the phone, maxLines becomes a line clamp on one and a line limit on the other. You write the same thing either way.
Typography
How words are set: size, weight, family, colour, alignment.
| Key | Value | Accepts | What it does | Example |
|---|---|---|---|---|
fontSize | pixels or a preset token | integer 8–96 or xs | sm | base | lg | xl | 2xl | md | Text size in pixels, or a step on the theme's type scale | "fontSize": 14 |
fontWeight | whole number or a preset token | integer 100–900 or normal | medium | semibold | bold | Stroke weight on the CSS 100–900 ladder, or a preset word | "fontWeight": "semibold" |
fontFamily | font | var(--font-<slot>) or a bundled font id | A theme font slot (`var(--font-heading)`) or a bundled font id | "fontFamily": "var(--font-heading)" |
color | colour | var(--color-<role>), role:<name>, a palette name, or #rrggbb | Text colour — a theme variable, a role, a palette name, or a hex | "color": "var(--color-foreground)" |
textAlign | closed token | left | center | right | Horizontal alignment of the text inside its box | "textAlign": "center" |
lineHeight | number | number 0.8–3 | Line spacing as a MULTIPLE of the font size | "lineHeight": 1.4 |
letterSpacing | number | number -2–8 | Extra space between glyphs, in pixels | "letterSpacing": 0.5 |
textTransform | closed token | none | uppercase | lowercase | capitalize | Recase the text without changing the stored value | "textTransform": "uppercase" |
fontStyle | closed token | normal | italic | Upright or italic | "fontStyle": "italic" |
textDecorationLine | closed token | none | underline | line-through | Underline or strike the text | "textDecorationLine": "underline" |
maxLines | whole number | integer 1–24 | Clamp the text to at most this many lines | "maxLines": 2 |
Box
The element's own box — fill, border, spacing, size, and how a picture fills it.
| Key | Value | Accepts | What it does | Example |
|---|---|---|---|---|
background | colour | var(--color-<role>), role:<name>, a palette name, or #rrggbb | Fill behind the element | "background": "var(--color-primary)" |
borderRadius | pixels or a preset token | integer 0–48 or none | sm | md | lg | xl | full | Corner rounding in pixels, or a preset step (`full` is a pill) | "borderRadius": 12 |
borderWidth | pixels or a preset token | integer 0–12 or none | thin | medium | thick | Border thickness in pixels, or a preset step | "borderWidth": 1 |
borderColor | colour | var(--color-<role>), role:<name>, a palette name, or #rrggbb | Border colour | "borderColor": "var(--color-border)" |
padding | pixels, or a per-side object | integer 0–96, none | xs | sm | md | lg, or { top, right, bottom, left, x, y } | Inner spacing — one number, a preset, or per side with `x` / `y` shorthands | "padding": {"x":14,"top":2,"bottom":12} |
margin | pixels, or a per-side object | integer 0–96, none | xs | sm | md | lg, or { top, right, bottom, left, x, y } | Outer spacing — one number, a preset, or per side with `x` / `y` shorthands | "margin": {"y":8} |
width | pixels | integer 0–2000 | Fixed width in pixels | "width": 120 |
height | pixels | integer 0–2000 | Fixed height in pixels | "height": 44 |
minWidth | pixels | integer 0–2000 | Smallest width the element may shrink to, in pixels | "minWidth": 44 |
minHeight | pixels | integer 0–2000 | Smallest height the element may shrink to, in pixels | "minHeight": 44 |
maxWidth | pixels | integer 0–2000 | Largest width the element may grow to, in pixels | "maxWidth": 320 |
maxHeight | pixels | integer 0–2000 | Largest height the element may grow to, in pixels | "maxHeight": 320 |
aspectRatio | number or a closed token | number 0.1–10, or square | video | portrait | auto | Width ÷ height — a ratio, or a named box (`square`, `video`, `portrait`, `auto`) | "aspectRatio": "square" |
objectFit | closed token | cover | contain | How a picture fills its box | "objectFit": "cover" |
Layout
How an element arranges its children, and how it sits among its siblings.
| Key | Value | Accepts | What it does | Example |
|---|---|---|---|---|
gap | pixels or a preset token | integer 0–96 or none | xs | sm | md | lg | Space between children, in pixels or a preset step | "gap": 8 |
flexDirection | closed token | row | column | Lay the children out across or down | "flexDirection": "row" |
alignItems | closed token | start | center | end | stretch | Where the children sit on the cross axis | "alignItems": "center" |
justifyContent | closed token | start | center | end | between | around | evenly | How the children are distributed along the main axis | "justifyContent": "between" |
flexWrap | closed token | wrap | nowrap | Whether a row may break onto another line | "flexWrap": "wrap" |
span | whole number | integer 1–12 | How many columns of the parent grid this element covers | "span": 2 |
rowSpan | whole number | integer 1–12 | How many rows of the parent grid this element covers | "rowSpan": 2 |
order | whole number | integer 0–999 | Where this element sits among its siblings, lowest first | "order": 2 |
columns | whole number | integer 1–8 | Lay the children out in this many equal columns | "columns": 3 |
stretch | boolean | true or false | Fill the space available (with `columns`, stretch the tracks) | "stretch": true |
Effects
Depth and transparency, from the one app-wide table.
| Key | Value | Accepts | What it does | Example |
|---|---|---|---|---|
opacity | number | number 0–1 | How opaque the element is, 0 to 1 | "opacity": 0.6 |
boxShadow | closed token | none | sm | md | lg | Depth under the element, from the one app-wide shadow table | "boxShadow": "md" |
Next
- Events — the
onblock - Commands — what an element can do
- Cards — where style blocks actually live
- Themes and appearance — where
var(--color-*)andvar(--font-*)come from