v3: the journal feed breathes — one mosaic running unbroken down the page reads as a slab, so the composer now cuts it into sub-blocks, each its own grid with a themable rule between. A break can only fall where a row starts clean and isn't reached into from above, which the exact tiling makes cheap to test: every row closes flush, so the only boundaries ruled out are the ones a portrait or a hero straddles.
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 42s

Two things set the rhythm, and neither is a row counter — that beats like a metronome. It comes due on a count of *cards*: a block is a set of stories and a hero is one story however many cells it takes, so a stretch thick with big footprints reaches the count over more rows than a stretch of squares does, and block heights end up tracking the size mix (measured: four to five rows through square-heavy runs, six to seven where art clusters). And the break *makes* its own section front rather than waiting for one, promoting the card that opens a new block to a hero. Waiting was the first attempt and it failed on the numbers — a banner lands at the start of a row on its own two to five times in thirty-six, so the preference almost never fired and a max-rows cap did all the work. Promotion always fits, since a break only falls on a row with all four cells free. A tail guard keeps the last cut from leaving a rule with a fragment under it.

The break sits between two grids rather than inside one: a grid item would be stretched to the 9rem row track, and out here its height is its own, down to nothing. Three custom properties are the whole contract a theme writes against — the air around it, the rule through it, an optional glyph centred on that rule — with `none` on the last two meaning the real absence of each, so a theme can remove the separation entirely rather than leave an empty band. Themes wanting something a rule can't be reach past the properties to plain CSS: nyt pairs hairlines in the grid's own pen and clips them to the art inset (which moves up to the feed, since a property on the grid can't reach a sibling); brutalist spends air around the 2px border it already divides everything else with; kanagawa takes a comment divider, geocities the rainbow its header ends on, melange a thinner band of the mélange. The page gutter moves to a feed wrapper so every block lines up on the same edges, and nyt's masking frame now runs once per block, which is what makes each of them read frameless and not just the feed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
khannurien
2026-09-20 08:01:42 +00:00
parent 6449569b34
commit e4d50f047c
8 changed files with 224 additions and 45 deletions

View File

@@ -5443,17 +5443,63 @@ body.has-player .chat-fab {
so the four columns close on every row with no gap to backfill — and
without `dense` moving a later card up into one, the order cards are read
in stays the order they were ranked in. */
.journal-grid {
list-style: none;
/* The lane and its gutter belong to the feed, not to any one block: the
composer cuts the mosaic into several grids and they all have to line up
on the same edges. */
.journal-feed {
margin: 0 auto;
padding: 1.25rem;
max-width: 1180px;
width: 100%;
box-sizing: border-box;
}
.journal-grid {
list-style: none;
margin: 0;
padding: 0;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 9rem;
gap: 0.75rem;
max-width: 1180px;
width: 100%;
box-sizing: border-box;
}
/* ── Sub-block breaks ──────────────────────────────────────────────
Three custom properties are the whole contract a theme writes against:
the air around the break, the rule through it, and a glyph centred on
that rule. `none` on the last two is the real absence of each — no
border, and no pseudo-element at all — so a theme that wants no
separation zeroes the space and drops the rule, and the break collapses
to nothing rather than to an empty band. Themes wanting something a rule
can't be (a gradient, a short centred pill) set height and background
here directly; the properties are the convenient path, not the only one. */
.journal-break {
--journal-break-space: 1.6rem;
--journal-break-rule: 1px solid var(--color-border);
--journal-break-ornament: none;
position: relative;
height: 0;
margin: var(--journal-break-space) 0;
border: none;
border-top: var(--journal-break-rule);
overflow: visible;
}
/* Page-coloured, so the glyph opens a gap in the rule exactly as wide as
itself instead of sitting on top of it. */
.journal-break::before {
content: var(--journal-break-ornament);
position: absolute;
top: 50%;
left: 50%;
translate: -50% -50%;
padding: 0 0.8em;
background: var(--color-bg);
color: var(--color-text-muted);
font-family: var(--font-display, inherit);
font-size: var(--text-sm);
line-height: 1;
}
/* ── Footprints ── */
@@ -5745,10 +5791,13 @@ body.has-player .chat-fab {
}
@media (max-width: 460px) {
.journal-feed {
padding: 0.85rem 1rem;
}
.journal-grid {
grid-template-columns: 1fr;
grid-auto-rows: auto;
padding: 0.85rem 1rem;
}
/* Collapse the mosaic into a single readable column. */