Layout Language
Glance layouts are JSON trees rendered natively on iOS. This guide covers the design system every layout and Glance AI follow — including mapping, views, and the v2 density nodes.
Design principles
- Glanceable — one clear takeaway in under two seconds
- Native — SF typography, semantic colors, calm density like Apple system widgets
- One hero — every size has a single focal point (hero stat, headline, or image)
- Intentional spacing — pick 8 / 12 / 16 pt rhythm and stick to it
Canvas sizes
| Size | Canvas (pt) | Safe zone inset |
|---|---|---|
| small | 170 × 170 | 22 pt from each edge |
| medium | 364 × 170 | 22 pt from each edge |
| large | 364 × 382 | 22 pt from each edge |
Root container padding is typically 12 pt (16 pt on airy large layouts). Use spacer with flex: 1 to push footers to the bottom on large widgets.
Bindings
| Syntax | Meaning |
|---|---|
| {{field_name}} | Dynamic — appears in upload form & automations |
| Plain string | Static label — fixed in the layout |
| https://… | Static image URL on image components |
| bag.fill | SF Symbol name on icon components |
Node shape
Every node is a JSON object. Containers nest children. Leaf nodes carry bindings.
| Field | Where |
|---|---|
| id | Required. Unique in the tree. |
| type | A registry type — 19 v1 nodes plus shape, calendar, chart, grid, table. |
| variant | v2 nodes. Shape, calendar, chart, and grid pick a visual form here — not as a separate type. |
| action | Pro+ family_views. set_view (another screen) or toggle_flag (same-screen reveal). XOR with href. |
| visibleFlag | Pro+. Slug on any node. Shown when that flag is true. Flags default false. Pair with toggle_flag — not a named view. |
| layout | Containers only: vertical | horizontal | z-stack. Not a style key. |
| style | Optional object. Fields depend on type — see Components. |
| children | Containers and gradient only. |
| binding, value, label, href, … | Type-specific. Static string or {{field}}. |
Style
style is a static object. Extra keys are ignored. The interpreter only reads the fields listed for that type. CSS names from the web do not map over.
| Ignored key | Use instead |
|---|---|
| gap | spacing on container and gradient. gap is valid only on status_dot. |
| textAlign | alignment |
| borderRadius | cornerRadius |
| backgroundColor | background on container. backgroundColor is valid on badge, button, and avatar. |
| margin / flexGrow / objectFit / numberOfLines | padding, flex, contentMode, maxLines |
Allowed values
alignment—leading|center|trailing. Containers also acceptstretch.fontWeight—regular(default),medium,semibold,bold,light,heavyfontFamily—sf-pro(default),sf-rounded,sf-monocontentMode—fill(default) |fit
Bindable style keys
Most style values are literals. These keys may also take a binding so color or a background image can change with the feed:
- Color —
background,color,fillColor,trackColor,dotColor,borderColor,gradientStart,gradientEnd, or any key ending inColor - URL —
backgroundImage
Typography
Use sf-pro (or omit fontFamily). At most 3 font sizes and 2 weights per widget. Set maxLines: 1 on titles and hero lines in small/medium widgets.
Color
- iOS renderer accepts 6-digit hex only (
#RRGGBB) — 8-digit alpha hex is ignored - Primary text:
#000000or omit for system primary - Secondary labels:
#8E8E93 - One accent hue per widget unless the user requests a branded look
- Color,
opacity, andvisiblemay be a map object instead of a hex or binding — see Mapping
Mapping
A closed function of a number. No JavaScript. from is {{field}}. Choose stops (interpolate) or when (first match: lt / lte / gt / gte / eq), never both. visible may only use when.
colorScale on chart, grid, and table is the same object, evaluated per bar or cell. Zero on a grid uses emptyColor.
Charts
Pro+ type: "chart" with variant bar | line | pie | candle | scatter. showAxes: true draws the plot plane (grid, Y ticks, X labels).
On bar only, showAverage: true draws a dashed line at the arithmetic mean of the current y series — use it when each bar should be judged against a typical day (weekday counts, new users, tickets). Optional averageColor (default #636366). Pair with showAxes: true. It is not a second binding or a second chart; line, pie, scatter, and candle ignore it. Web preview and the iOS Home Screen widget both paint it.
Views
Pro+ family_views is two patterns. Pick one per tap.
| Intent | Mechanism | Golden |
|---|---|---|
| Tap shows/hides content on the same screen | toggle_flag + visibleFlag. No views map. | v2-vocab-flip |
| Tap goes to another screen | set_view + views | v2-mission-control |
tree is always home. Optional sibling views holds up to four named screens. Slugs are a-z plus digits and underscore. Reload returns home and clears flags. The next ingest keeps the current view. Discover tiles stay on home.
In-place reveal — flags default false:
{
"action": { "type": "toggle_flag", "flag": "show_answer" },
"children": [
{ "id": "prompt", "type": "text", "binding": "{{prompt}}" },
{ "id": "answer", "type": "text", "binding": "{{answer}}", "visibleFlag": "show_answer" }
]
}Plans
Shape, mapping, and calendar are Free. Views (named screens and in-place reveal), charts, activity grids, and tables are Pro and Power — authoring only. A subscriber still sees a Pro creator's chart. Downgrade keeps rendering; the next edit that still contains a premium family 403s. bar_chart / pie_chart are invalid; use type: "chart" and a variant.
Next steps
- Component reference — bindings and style fields per type
- Golden examples
- Create a layout