Perch
Building modsStyling & state

Styling & state

The shared props every node takes — layout, color roles, gradients, conditionals, interaction, and motion.

4 min readUpdated Jul 21, 2026

Every component takes the shared prop set on top of its own props. This page is the complete list — anything not here doesn’t exist, and the validator will tell you so by path.

Layout

PropRangeMeaning
w h min_w min_h max_w max_h0–2000 pxCSS-like dims, still flex-shrinkable
flexnumbergrow factor; 0 = natural size, never grow
padtoken or {t,r,b,l}xs s m l or px
margin{t,r,b,l}, -50–50negatives overlap — badges biting into avatars
align_selfstart center end stretch baselineoverride the parent’s cross-axis
at / anchorframe childrenposition; anchor = "center" centers on at

A node that must hold its size against flex pressure sets flex = 0.

Color

A color value anywhere is a role token, a palette name, or a hex literal:

RoleUse
primary secondary half tertiarytext tiers, brightest → faintest
accent accent-warmthe two product accents
live redpresence green / the red
fillthe generic filled element
islandthe notch’s own black (separator rings)
wellthe recessed inset (bar tracks)
currentcurrentColor — follows the node’s color, including through style_when
  • @name reads your manifest palette — themeable identity colors.
  • #rrggbb or #rrggbbaa — 8-digit hex carries alpha ("#d9d9d924" is 14% white, the translucent-white workhorse).

bg additionally takes surface roles — none recessed raised track well deep — or a gradient object:

bg = { dir = 150, stops = { "#9464d8", "#652fb1" } }                    -- linear
bg = { radial = "80% 65% at 32% 24%",
       stops = { "#ffffff66", "#ffffff00" }, offsets = { 0, 62 } }      -- radial

Gradients take 2–4 stops; offsets are percentages.

Chrome

PropValues
radiuspx, or a named token (chip, card, art)
bordernone rim rim-strong
shadownone float recess soft
ring{ width 0–6, color } — a crisp outline
glow{ blur 0–40, color } — a soft halo (the speaking-face bloom)
opacity0–1

State — when & style_when

when gates presence:

when = "speaking"                          -- truthy
when = { field = "k", is = "hdr" }         -- equals
when = { field = "k", ["not"] = "empty" }  -- not-equals

style_when flips looks only — 1–4 rules, never relayout. The settable whitelist: color, bg, opacity, weight, ring, glow, z, gray (grayscale — paused media art).

style_when = {
  { field = "onBreak", set = { color = "live" } },
  { field = "paused",  set = { gray = true, opacity = 0.7 } },
}

Data-bound colors

bg_bind / color_bind name a payload field whose value is a CSS color Lua computed — a user’s monogram gradient, a per-tile hue. Only #hex, hsl()/hsla(), and the strict monogram-gradient form are honoured; anything else is ignored and the declared bg/color stands.

{ type = "image", bind = "avatar", bg_bind = "hue_css", children = { mono } }

Interaction

PropMeaning
action≤64-char command string fired to on_command; {i} = repeater index, {frac} = a seek bar’s release fraction
hoverlift (pop — dock icons), soft (button-weight brightness), scope (a region whose descendants swap)
hover_show / hover_hideon descendants of a hover = "scope" node — cross-fade while hovered (the dock label swapping name → usage)

Motion & identity

PropMeaning
motionhalo nod pulse spin — lifetime-scoped presets
rotatestatic -360–360°
rotate_binda payload field of degrees — live clock hands
pivotcenter bottom — rotation origin
flipa glide anchor: nodes sharing it across contexts morph into each other
showsingle dual page_left page_right — this node exists only in that layout situation

show lets one tree serve multiple situations — the media card renders in both page slots, with side-specific nodes gated by show = "page_left" / "page_right".


The full worked catalog of these props in real trees is the builtin set — see recipes.

esc
Type to search
navigate open