Components
Blog Grid
The subdivision grid as an editorial layout. From left to right the areas halve — and with them the weight of each article. Far left the newest one at full height, far right the archive as a list of rows. A mono timeline runs underneath.
Default
The principle
Five columns of equal width, drawn 1, 2, 3, 6 and 6 — the lead card, two columns of cards with a text block, two of single lines. Each column stands for a period: the further right, the older and the terser the presentation. The layout explains how an archive works without a single word.
A column is a year, and the numbers above are how it draws, not how much it
holds. On patterns/news.html the newest post is the lead and
every post after it stands in the column of its own year; the last column takes every
year older than the four that fit, which is why its tick is a range. A year that runs
long is not paged away — the column scrolls. See The port.
| Column | Class | Articles | Presentation |
|---|---|---|---|
| 1 — current | .cf-blog-col--1 | 1 | picture, large title, author, date, reading time |
| 2 — this year | .cf-blog-col--2 | 2 | picture, title, date, reading time |
| 3 — last year | .cf-blog-col--3 | 3 | picture, title, date, reading time |
| 4 — before that | .cf-blog-col--6 | 6 | title only, single line |
| 5 — archive | .cf-blog-col--6 | 6 | title only, single line |
The counter in the section header names what stands in the grid, which is now the whole archive: nothing is held back on a second page for the counter to point at.
The port
.cf-blog-grid--port is the archive's own form of this grid: a fixed height
of 36.5rem, and each column its own scroll. It is worn on
patterns/news.html and the topic pages, and deliberately not on the
Weiterlesen grid under an article — three cards have nothing to scroll and a port there
is a fixed height with air under it.
- 584 px is twice what the grid drew. Measured at 1440 and 1920: 293.66 px, six compact rows stretched to 48.6 each. Twice that is 587.3, and 584 is the number on the 4 px unit under it.
- Not a whole number of rows, on purpose. A compact row is 40.59 px of its own accord at every width this grid opens at. The port's two hairlines take 2 px, and the 582 left hold fourteen rows and 14.7 px of the fifteenth — a year with more posts than fit always shows one cut off by the bottom edge. On a static page that cut row is the only thing that says “keep going” before anyone has touched anything.
-
The rows are the cards' own heights.
--rank's equal1frrows are what fills a content-sized column; in a port they would divide 584 px by six and give each single-line headline 96 px of box to float in. So the port setsgrid-auto-rows: min-contentandalign-content: start, and a thin year leaves air at the bottom of its column — which is what a thin year looks like. The lead is the exception: one card, one1frrow, and its picture absorbs the height. - No tabindex on the columns, and that is the rule rather than an oversight. A scrollable region needs to be keyboard-operable; one whose content is focusable already is, because tabbing between the cards scrolls it. Every card in these columns is a link or a listing next to one.
-
Folded, there is no port. Below the fold the columns stack and the
height goes back to
auto: a fixed-height box nested inside the page's own scroll is the pattern that takes a thumb hostage. Both fold widths are base.css's —44remfor the primitive,56remfor the five-column form.
The title picture
A post names one picture that stands for it — bild: in its file, the
Titelbild property in Notion — and the card is drawn from it:
.cf-blog-card--media on the card, an <img> with
.cf-blog-card__image as its first child.
scripts/build-news.py writes both, and the same call writes the
Weiterlesen cards under an article, so the two cannot drift apart.
- Optional, and the layout does not reserve a hole for it. A card without a picture is the card this grid has always drawn. An archive where half the posts carry a photograph reads as an archive; a row of grey placeholders reads as a fault.
-
The picture pays the padding. A card with one drops its own
12 pxbox padding and pays it on the title and the meta line instead, so the photograph meets the cell's hairline on three sides. That is what makes the left of the grid read as a wall of pictures rather than as framed stamps. -
It takes the slack.
flex: 1 1 autowithmin-height: 0: in the tall lead cell the picture grows and the headline sits under it, instead ofspace-betweenspreading three items down a column of air.aspect-ratio: 3 / 2is the basis,object-fit: coverthe crop. -
Columns 4 and 5 carry none. Those rows are
2remhigh. A picture in one is a 48 px stamp painted from a file sized for the 1008 px reading plate — the wastescripts/check-image-scale.pyexists to catch, and bytes a reader on a train pays for something nobody can read. Terser to the right is the layout's whole argument; the picture is the first thing to go, not the last. -
alt="", always. The picture sits inside the link whose text is the headline. A description of the photograph makes a screen reader read every card twice, so there is no caption field on the post either — the title is the text alternative, and the<img>steps out of the way. -
Its own dimensions on the tag.
widthandheightcome from the file's header, so the box is reserved before the bytes arrive — eighteen cards on one page is eighteen chances to move the grid under the reader.loading="lazy"anddecoding="async"for the same page.
Behaviour
-
Below 896 px of container —
56rem, a viewport of about 1006 on a page that is one.containerwide — the grid collapses to one column and the timeline is dropped. It is a container threshold, not a viewport one: the same grid is readable in 896 px of page and unreadable in 896 px of viewport behind a 272 px documentation sidebar, and only the container can tell those apart. Binary-searched onpatterns/news.html: folded at 1006, open at 1007. -
The five-column form carries
.subdivide--late, always. The primitive's own fold at44remis the number for three columns; five columns of a 44 rem container give each single-line title 116 px of text, and the widest first words in this grid — “Smart-Home-Systeme”, “Erneuerbaren-Ausbau” — need 120 or more, so two of the twelve tail headlines rendered as a fragment of one word plus an ellipsis.base.csscarries the measurement,scripts/check-subdivide-late.pyholds every five-column call site to the modifier. →foundations/layout.html - Single-line titles are truncated with
text-overflow: ellipsis— thetitleattribute carries the full text. - Hover lightens the cell (white 45 %); nothing moves.
- Every cell is an
<a>— the whole area is clickable, not just the title.
.subdivide__row bands of one .subdivide, so the axis
adopts the grid's columns through subgrid and cannot drift from them. They
used to be siblings, each declaring five equal columns independently — which held only
because both happened to say five. patterns/blog-artikel.html already runs the
same grid at --subdivide-count:3; adding an axis there would have put five
ticks under three columns. → foundations/layout.html
A tick stands on the leading rule of the column it names, which is where the application plate draws it.
writing-mode: vertical-rl starts its text at the
block-start edge — the right — so a stretched tick landed on the trailing rule and read as
labelling the column next to it. justify-self: start is what puts it back.
aria-hidden because the
years already appear in each article's metadata. Without that, a screen reader announces
“Aktuell 2026 2025 2024 2022–2023” with no context at all.