The pdg book · Chapter 05 of 10 · The flexbox of pdg
Layout — vertical & horizontal
<vertical> and <horizontal> are pdg's flexbox — gap, main/cross alignment, and flex weights. This is how you build any arrangement on the page.
This is the chapter that unlocks the rest. Almost every layout you will ever build
is a nesting of two containers: <vertical> stacks its
children top to bottom, <horizontal> places them
left to right. They are pdg's flexbox, and they behave the way display: flex
trained you to expect.
Stacking with gap
<vertical> is flex-direction: column. The gap attribute is exactly CSS
gap — space between children, with no leading or trailing gap.
<vertical gap="10px">
<text size="16" bold="true">First</text>
<text size="13">Second, ten points below the first.</text>
<text size="13">Third, ten points below the second.</text>
</vertical><horizontal> is flex-direction: row:
<horizontal gap="12px">
<rect width="50px" height="50px" fill="#c4341f" />
<rect width="50px" height="50px" fill="#1b1a17" />
<rect width="50px" height="50px" fill="#9a2615" />
</horizontal>main and cross alignment
Two attributes position children, and they map straight onto flexbox:
mainis alignment along the stacking axis — CSSjustify-content. Values:start,center,end,space-between,space-around,space-evenly.crossis alignment across it — CSSalign-items. Values:start,center,end,stretch.
A row that pushes its ends apart and centres them vertically — the classic header bar:
<horizontal main="space-between" cross="center"
style:padding="12px" style:background="#1b1a17">
<text size="16" bold="true" color="#faf6ed">Acme Co.</text>
<text size="12" color="#9a948a">Invoice #0042</text>
</horizontal>cross="center" is why the small date sits on the same midline as the big title,
even though they are different heights — align-items: center to the letter.
flex: sharing leftover space
There is no width: 50% in pdg. Instead, give children a flex weight and they
share the leftover space along the main axis in proportion — precisely CSS
flex-grow. Two equal columns:
<horizontal gap="12px">
<text flex="1" size="12" style:padding="10px" style:background="#ece5d5">
I take one share of the width.
</text>
<text flex="1" size="12" style:padding="10px" style:background="#faf6ed">
I take the other. Together we fill the row.
</text>
</horizontal>Change one to flex="2" and it grows to twice the width of the other. A fixed
sidebar beside a flexible main column is just a fixed width next to a flex:
<horizontal gap="12px">
<rect width="60px" height="80px" fill="#c4341f" />
<text flex="1" size="12" style:padding="10px" style:background="#faf6ed">
The red bar is a fixed 60px. I am flex="1", so I absorb everything that's
left — resize the page and only I change width.
</text>
</horizontal>stretch: filling the cross axis
cross="stretch" makes children fill the container's width (in a <vertical>) or
height (in a <horizontal>) — like align-items: stretch. Here three banners
stretch to a common width instead of each shrinking to its own text:
<vertical gap="6px" cross="stretch">
<text size="13" style:padding="8px" style:background="#1b1a17" color="#fff">One</text>
<text size="13" style:padding="8px" style:background="#c4341f" color="#fff">Two</text>
<text size="13" style:padding="8px" style:background="#9a2615" color="#fff">Three</text>
</vertical>Wrapping rows
When a <horizontal> holds more rigid children than fit across the width, it
wraps them onto a new line instead of overflowing — like flexbox with
flex-wrap: wrap. The gap applies both between items on a line and between the
wrapped lines.
<horizontal gap="8px">
<rect width="70px" height="30px" fill="#c4341f" />
<rect width="70px" height="30px" fill="#1b1a17" />
<rect width="70px" height="30px" fill="#9a2615" />
<rect width="70px" height="30px" fill="#447ac4" />
<rect width="70px" height="30px" fill="#1a7f3c" />
</horizontal>Wrapping is for rows of rigid items. The moment any child has a flex weight,
the row commits to a single line and shares the width instead — a flex child is
asking to fill the row, not to wrap off the end of it.
Nesting is the whole game
Real layouts are containers inside containers. A vertical page that holds a horizontal header, then a two-column body:
<vertical gap="14px">
<horizontal main="space-between" cross="center">
<text size="18" bold="true">Report</text>
<text size="11" color="#847d6c">June 2026</text>
</horizontal>
<line stroke="#ddd5c4" />
<horizontal gap="14px">
<text flex="2" size="12">The wide main column carries the body copy and
takes two shares of the width.</text>
<text flex="1" size="11" style:padding="10px" style:background="#faf6ed">
A narrow aside, one share wide.</text>
</horizontal>
</vertical>That nesting — verticals holding horizontals holding verticals — is how you build any page. If you want the precise mechanics of how space is offered and split, the layout protocol spells it out, but the flexbox intuition above will carry you a long way.
Two more layout tools — overlapping content and springs — are in the next chapter.