Layout and spacing
You never set spacing in a template. Modules do not look at their neighbours, and there is no margin or padding prop. All vertical space between modules is decided in one place, layout.Arrange, from rules taken from the Quiet design's reference frames. The same template always gets the same spacing, and a new combination of modules still looks deliberate.
func Arrange(mods []ir.Module, header HeaderVariant) []Section
type Section struct {
Module ir.Module
PadTop, PadBottom int // px
}
Arrange runs three passes over the template's modules: class defaults, then pair rules, then position rules. The HTML renderer writes each section as a table row, <td class="px" style="padding:{top}px 56px {bottom}px 56px;text-align:…">, so a section's padding is the space it adds above and below itself. Bands (images and maps) are full-bleed and ignore the side padding.
Know the module classes
Every module belongs to one layout class:
| Class | Modules |
|---|---|
| Hero | statement, figure, offer |
| Band | image, map |
| Tabular | receipt, breakdown, compare, table, stock |
| Body | letter, meta, quote |
| Notice | notice |
| Tail | progress, code, actions |
| Editorial | stories, products |
| Signoff | signature, signoff |
| Rule | divider |
Start from the class defaults
| Class | Top / bottom (px) |
|---|---|
| Hero | 40 / 36 |
| Band | 0 / 0 |
| Tabular | 32 / 32 |
Body (letter) | 32 / 28 |
Body (meta) | 0 / 32 |
Body (quote) | 40 / 40 |
| Notice | 0 / 32 |
Tail (progress) | 0 / 8 |
Tail (code, actions) | 0 / 40 |
| Editorial | 0 / 0 (the tiles carry their own 32 / 36 padding) |
| Signoff | 0 / 36 |
| Rule | 0 / 0 |
The actions default is 40, not SPEC §8's 36: the handoff's Q2 frame wins on visual values (D10).
Apply the pair rules
Pair rules look at two adjacent modules and override the bottom of the first, the top of the second, or both. They are data ([]layout.Rule, in layout/rules.go), each citing the reference frame its value comes from. They apply in order, and a later match overrides an earlier one on the same side.
| Previous → next | Effect | Source |
|---|---|---|
Hero → Tail (progress) | Hero bottom 28 | Q1 |
Hero (statement) → Tabular (compare) | Hero bottom 24 | Q7 |
Hero (statement) → Tabular (table) | Hero bottom 16 | Q9 (D10: SPEC says 24) |
Hero (statement) → Tabular (receipt) | Hero bottom 24 | SPEC §8 |
Hero (figure) → Tabular | Hero bottom 32, Tabular top 0 | Q2 (D10) |
Hero → Tail (code) | Hero bottom 0 | Q3 |
Body (meta) → Tabular | Tabular top 0 | Q6 |
Band → Body (meta) | Meta top 32 | Q6 |
Tabular → Tail (actions) | Actions top 8 | Q2 |
A statement followed by breakdown or stock matches no rule and keeps the Hero default of 36.
Apply the position rules
Last, a few rules look at where a module sits in the whole email:
| Position | Effect | Source |
|---|---|---|
A statement that is the first module under an H3 header | Bottom 32 | Q4 |
| A Hero that is the last module | Bottom 40 | Q5 (D10) |
| A notice that is the last module, or follows a Band | Bottom 40 | SPEC §8 |
A letter that is the first module | Top 36 | SPEC §8 |
Check the nine reference stacks
The nine Quiet examples are the conformance suite for these rules. layout/arrange_test.go asserts every value below.
| Example | Header | Sections (top / bottom) |
|---|---|---|
| 01 order | H1 | statement 40/28 · progress 0/8 · receipt 32/32 · meta 0/32 |
| 02 payout | H1 | figure 40/32 · breakdown 0/32 · actions 8/40 · signoff 0/36 |
| 03 reset | H1 | statement 40/0 · code 0/40 · notice 0/40 |
| 04 journal | H3 | statement 40/32 · image 0/0 · stories 0/0 · divider 0/0 · quote 40/40 |
| 05 campaign | H1 | statement 40/36 · image 0/0 · products 0/0 · divider 0/0 · offer 40/40 |
| 06 job offer | H1 | statement 40/36 · map 0/0 · meta 32/32 · breakdown 0/32 |
| 07 fee change | H1 | statement 40/24 · compare 32/32 · divider 0/0 · letter 32/28 · signoff 0/36 |
| 08 outreach | H1 | letter 36/28 · signature 0/36 |
| 09 internal | H2 | statement 40/16 · table 32/32 · notice 0/32 · signoff 0/36 |
Here is 02. The figure → breakdown rule gives the figure a 32px bottom and the breakdown no top padding, and the Tabular → actions rule drops the actions' top padding to 8:
Change the spacing
The rules are part of the library, not the theme, so a consumer cannot change them per brand. If a combination of modules looks wrong, the fix is a new pair rule in layout/rules.go with its source, plus a case in layout/arrange_test.go, and a golden update (see Goldens and testing).