A Swiss layout is mostly decided before any text arrives. The grid comes first, and the words fall into it.
One grid, used everywhere
The kit has one set of columns for lists: a narrow one for numbers, a wide one for names, and a slim one at the end for an indicator or a star. The section heading above a list uses the same columns, so its range label sits over the numbers and its title over the names.
Here is the whole thing as Tailwind utilities:
<li class="row grid md:grid-cols-[3rem_1fr_2.75rem]">
<span class="num small muted">01</span>
<span class="title">Field notes</span>
<span><!-- indicator --></span>
</li>
On phones the row stacks. From 768px it becomes the three columns.
The section header’s range label comes from the same numbers as the rows:
// "01–06": the first and last row numbers, padded to two digits.
const pad = (n: number) => String(n).padStart(2, '0');
export function range(items: { num: number }[]): string {
if (items.length === 0) return '';
return `${pad(items[0].num)}–${pad(items.at(-1)!.num)}`;
}
The steps, in order
- Pick the content width. This kit uses 88rem, centered, with a 1rem gutter that grows to 2rem.
- Pick the list columns, and reuse them for every section header.
- Pick the measure for running text. About 42rem, or 65 to 75 characters a line.
- Only then write the page.
Where each width goes
| Width | Used for |
|---|---|
| 88rem | The page: hero, page title, lists, footer |
| 42rem | Running text, like this post |
| 28rem | A hero’s lead line and the footer’s note |
A table is a grid too. It gets the same rules: a 2px line under the header, thin ones between rows, and small uppercase labels at the top.