pdg XML → PDF

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.

Try it — edit the source Open in Playground
<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:
Try it — edit the source Open in Playground
<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.

Try it — edit the source Open in Playground
<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.

Try it — edit the source Open in Playground
<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.

Try it — edit the source Open in Playground
<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:

Try it — edit the source Open in Playground
<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:

Try it — edit the source Open in Playground
<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. flex grows a real child to share space; expander is an invisible child that exists only to take up the slack. Reach for flex when a visible element should grow, and expander when you just need a gap that fills.

Next: the structured content elements — lists and tables.