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

SizeCanvas (pt)Safe zone inset
small170 × 17022 pt from each edge
medium364 × 17022 pt from each edge
large364 × 38222 pt from each edge
extra_large378 × 611.320.2 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. extra_large is the iOS 27 portrait page. Its inset is 20.2 pt, the corner measured on the iPhone 18 Pro Max. The other sizes stay at 22 pt. Use the extra height as more than one region. A large layout scaled up does not fill the page. The reference layout is xl-morning-brief.

Bindings

SyntaxMeaning
{{field_name}}Dynamic — appears in upload form & automations
Plain stringStatic label — fixed in the layout
https://…Static image URL on image components
bag.fillSF Symbol name on icon components

Node shape

Every node is a JSON object. Containers nest children. Leaf nodes carry bindings.

FieldWhere
idRequired. Unique in the tree.
typeA registry type — 19 v1 nodes plus shape, calendar, chart, grid, table.
variantv2 nodes. Shape, calendar, chart, and grid pick a visual form here — not as a separate type.
actionPro+ family_views. set_view (another screen) or toggle_flag (same-screen reveal). XOR with href.
visibleFlagPro+. Slug on any node. Shown when that flag is true. Flags default false. Pair with toggle_flag — not a named view.
layoutContainers only: vertical | horizontal | z-stack. Not a style key.
styleOptional object. Fields depend on type — see Components.
childrenContainers 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 keyUse instead
gapspacing on container and gradient. gap is valid only on status_dot.
textAlignalignment
borderRadiuscornerRadius
backgroundColorbackground on container. backgroundColor is valid on badge, button, and avatar.
margin / flexGrow / objectFit / numberOfLinespadding, flex, contentMode, maxLines

Allowed values

  • alignment — leading | center | trailing. Containers also accept stretch.
  • fontWeight — regular (default), medium, semibold, bold, light, heavy
  • fontFamily — sf-pro (default), sf-rounded, sf-mono, heebo, rubik, frank_ruhl_libre, varela_round, secular_one, playpen_sans. Details are under Typography.
  • contentMode — 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 in Color
  • URL — backgroundImage

Typography

Omit fontFamily and the text draws in SF Pro. Reserve sf-rounded for playful widgets and sf-mono for code and hashes. At most 3 font sizes and 2 weights per widget. Set maxLines: 1 on titles and hero lines in small/medium widgets.

Six more faces are bundled on every plan. Use the id. An unknown name still draws as SF Pro. The family applies on text, timer, clock, date, and relative_time. stat, badge, and status_dot stay on the system face.

IDSampleScripts in the file
heeboGood morningשלוםLatin, Hebrew
rubikGood morningשלום مرحبا ПриветLatin, Hebrew, Arabic, Cyrillic
frank_ruhl_libreGood morningשלוםLatin, Hebrew
varela_roundGood morningשלוםLatin, Hebrew
secular_oneGood morningשלוםLatin, Hebrew
playpen_sansGood morningΓεια Привет שלוםLatin, Cyrillic, Greek, and Hebrew

fontWeight changes the stroke on Heebo, Rubik, Frank Ruhl Libre, and Playpen Sans. It does not change Varela Round or Secular One. Chinese, Japanese, Korean, Thai, and Hindi are not in these files. Those characters stay on the system font.

Color

  • iOS renderer accepts 6-digit hex only (#RRGGBB) — 8-digit alpha hex is ignored
  • Primary text: #000000 or omit for system primary
  • Secondary labels: #8E8E93
  • One accent hue per widget unless the user requests a branded look
  • Color, opacity, and visible may 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.

IntentMechanismGolden
Tap shows/hides content on the same screentoggle_flag + visibleFlag. No views map.v2-vocab-flip
Tap goes to another screenset_view + viewsv2-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. An extra-large widget or layout is also Pro and Power to create. Dropping to Free locks the extra-large widgets that account owns. People who already follow one keep the last content.

Next steps