The pdg book · Chapter 06 of 10 · Cards & overlays
Boxes, stacks & springs
<box> for a styled container, <stack> for layering (z-index), <padding> and <spacer> for breathing room, and <expander> — the spring that pushes things apart.
With <vertical> and <horizontal> you can arrange anything. This chapter adds
the supporting cast: a tidy way to make cards, a way to layer content, and the
spring that lets you pin things to edges.
box: a container with a box model
You can decorate anything with style: attributes, but when an element exists
only to be a styled container, <box> reads better. It
takes background, border, border-color, radius, and padding as plain
attributes — no style: prefix — and lays its children out in a column.
<box background="#ffffff" border="1px" border-color="#ddd5c4"
radius="10px" padding="16px">
<text size="16" bold="true">A card</text>
<text size="12" color="#3c392f">box is sugar for the common case: a padded,
bordered, rounded container. Its children stack like a vertical.</text>
</box>Anything <box> does, you could spell out with style: attributes on a
<vertical> — pick whichever reads more clearly.
Default layout: drop content straight in
Look again at that card: the text sits directly inside <box>, and the two
<text>s stacked without a <vertical> around them. That isn't special to
<box> — every container gives its content a sensible default layout, so you
write less scaffolding. Three rules cover it:
- Bare text becomes a paragraph.
<box padding="16px">Just text</box>needs no inner<text>, and inline markup —<b>,<i>,<a>,<font>— works right inside it. - One child is used as-is; several stack in a column. That's why the card's two paragraphs stacked on their own.
- Text and blocks interleave. Mix them and each run of text between blocks becomes its own paragraph, in document order:
<box background="#faf6ed" radius="8px" padding="14px">
A lead paragraph, written as bare text — no wrapper.
<line stroke="#ddd5c4" />
A second paragraph after the rule. Both are kept, in order.
</box>The same applies to the <vertical> and <horizontal> from the last chapter, to
table cells, and to the page itself. It is the terse path: the implicit column
has no gap and default alignment, so the moment you want spacing or alignment
between the pieces, reach for an explicit <vertical>/<horizontal> with gap,
main, and cross. The Default layout guide
spells out the full rules.
stack: layering content (z-index)
<stack> places every child in the same spot, layered
in document order — earlier children sit underneath later ones. It is your
position + z-index: a label over a colour field, a badge over an image.
<stack>
<rect width="240px" height="90px" fill="#1b1a17" />
<text size="20" bold="true" color="#faf6ed"
style:width="240px" style:height="90px"
style:align="center" style:valign="center">
Text over a filled panel
</text>
</stack>The <rect> paints first; the centred <text> lands on top. Sizing the text box
to match the rectangle lets style:align/valign centre the words over it.
padding: inset a single child
<padding> insets its content — a single child, or
several collapsed into a column like any container. all sets every side at once
(a CSS 1–4 length shorthand), and top/right/bottom/left override per side.
It is the explicit-element form of style:padding, handy when you want the padding
to be its own node in the tree.
<padding all="20px" top="8px">
<text size="14" style:background="#faf6ed">
8px above me, 20px on the other three sides.
</text>
</padding>spacer: a deliberate gap
<spacer> is empty space of a fixed height. Use it for
a one-off gap between elements where a container gap would be overkill.
<vertical>
<text size="16" bold="true">Section title</text>
<spacer height="18px" />
<text size="12">Eighteen points of air above me, placed by a spacer.</text>
</vertical>expander: the spring
<expander> eats all the leftover space along the main
axis, shoving whatever follows to the far edge. In a row it pushes an item to the
right; in a column it pushes one to the bottom. This is how you build a header
with a title on the left and an action on the right without measuring anything:
<horizontal cross="center">
<text size="16" bold="true">Dashboard</text>
<expander />
<text size="12" color="#c4341f">Edit →</text>
</horizontal>Put one <expander> between two items to push them apart, or one on each side
to centre an item:
<horizontal>
<expander />
<text size="14" bold="true">Perfectly centred</text>
<expander />
</horizontal>The same trick in a <vertical> pins a footer to the bottom of whatever height
the column is given — which is exactly how page footers work in
Chapter 9.
expander vs flex. They are two sides of one coin.
flexgrows a real child to share space;expanderis an invisible child that exists only to take up the slack. Reach forflexwhen a visible element should grow, andexpanderwhen you just need a gap that fills.
Next: the structured content elements — lists and tables.