Docs
/
Language
Objects
Everything on screen is an object: a widget, its config (how it looks and sizes), and its children (what is inside it). This page takes those three parts one at a time.
Config and children
.config(...) sets the object's features. .obj(...) holds the one object inside it.
Container(
.config(
w: 240
.border(color: line, width: 1)
color: panel
cornerRadius: 16
padding: all(20)
)
.obj(Text(.config(size: 17, weight: semibold) .obj('A card, 240 wide')))
)Every number is in design pixels. padding: all(20) is space on all four sides inside the edge; .border(...) is a group, several related keys written together.
The formatter keeps config in one order, sizes first (w, h, size, the min/max keys), then the rest alphabetically, so every file reads the same. Every widget's keys, with types and defaults, are in the API reference.
Slots: where children go
A widget with several places for children names them. Scaffold, the screen, has a .bar on top, a .body that scrolls, and a .footer. Each band below says which slot it is in:
Page(
.name(Slots)
.route('/')
Scaffold(
.config(color: canvas)
.bar(
Row(
.config(w: fill, color: sunken, padding: all(14))
Text(.config(weight: semibold) .obj('.bar'))
)
)
.body(
Column(
.config(w: fill, gap: 8, padding: all(20))
Text('.body: the page content')
Text(
.config(color: muted)
.obj('It scrolls when it is taller than the screen.')
)
)
)
.footer(
Row(
.config(w: fill, color: sunken, padding: all(14))
Text(.config(color: muted) .obj('.footer'))
)
)
)
).obj(x) is the default slot for one child and .objs(a, b) the list form. Children written straight into the call (Column(Text('One'), Text('Two'))) go into the default slot.
Identity: .id
.id(name) names an object. An id is unique inside its Page or Widget, and it is how other code reaches the object: Slots.card from any file, to read or change it with Export, Extract and Inject.
Container(.id(card) .config(w: 200, color: panel, padding: all(16)))Objects without an id are still reachable by their lookup path, one . per level (Slots.scaffold.body.column.text(1)). Ids survive edits that would change a path.
Presets: a name for a look
A preset is a named bundle of config for one widget. Define it once, use it anywhere:
Preset(
.name(pill)
.for(Button)
.config(
padding: sym(x: 16, y: 8)
cornerRadius: 999
color: sunken
.border(color: lineStrong, width: 1)
)
)
Page(
.name(Buttons)
.route('/')
Scaffold(
.config(color: canvas)
.body(
Row(
.config(gap: 12, padding: all(24))
Button(.preset(pill) .config(label: 'Save'))
Button(.preset(pill) .config(label: 'Share'))
Button(.preset(default) .config(label: 'Cancel'))
)
)
)
)Config written on the object wins over its preset: Button(.preset(pill) .config(color: accent)) keeps the pill shape with an accent fill. .preset(default) is LAYR's neutral, accessible look for interactive widgets; core widgets are otherwise unstyled.
Per-frame config: .at
.at(frame, key: value) changes config at a design frame: m (phone), t (tablet), w (desktop). The plain .config value is the one for phones.
Container(
.config(w: 160, color: accent, padding: all(16))
.at(w, w: 480)
.obj(
Text(
.config(color: onAccent, weight: semibold)
.obj('160 on phones, 480 on desktop')
)
)
)Between frames the number is interpolated: at a tablet width it is part way between 160 and 480, so nothing jumps. Write step for a value that switches at the frame instead: .at(t, step h: 60).
Conditions and lists
If shows one branch or the other (.fb(...) is the fallback). Each repeats an object for every item of a list:
Page(
.name(Lists)
.route('/')
var bool showAll = false
const list<txt> names = ['Ada', 'Grace', 'Linus', 'Margaret']
Scaffold(
.config(color: canvas)
.body(
Column(
.config(gap: 10, padding: all(24))
Button(
.preset(default)
.config(label: showAll ? 'Show fewer' : 'Show everyone')
.fnc { showAll = !showAll }
)
If(
.cnd(showAll)
.obj(Each(.of(names) .as(name, i) .obj(Text('${i + 1}. $name'))))
.fb(
Text(.config(color: muted) .obj('${names.length} people, hidden'))
)
)
)
)
)
)Accessibility
Text(.config(type: h1)) renders a real heading. Images need alt (or decorative: true) and inputs need a label: the compiler reports L6001 and L6002 otherwise. .a11y(label: ..., role: ...) sets an accessible name and role on any object.