Make article layout fluid: CSS-driven column count, uncapped width

Replace the fixed 100rem two-column breakpoint with 'columns: 30rem' so
CSS fits as many >=30rem columns as the article's width allows, and let
long (.multicol) articles grow past the 78rem cap (4:1 share against the
gutters, keeping them symmetric for the .wide breakout math). Also treat
mid-article h1s as full-width column separators like h2s.
This commit is contained in:
2026-08-18 05:14:21 +00:00
parent f9e314e5ea
commit d03ee7fa8f
2 changed files with 36 additions and 15 deletions
+31 -12
View File
@@ -228,6 +228,17 @@ body {
transition: margin-left 0.25s ease; transition: margin-left 0.25s ease;
} }
/* Long articles (.multicol is added by pagerite.js based on content length)
lift the 78rem cap: the center track keeps 78rem as its minimum and then
takes a 4:1 share of any extra space against each gutter, so the article
grows without limit and its column count follows its width (see the
`columns: 30rem` rule below). The gutters stay symmetric, so the .wide
breakout math (50% 50vw) still centers on the viewport — and at very
wide windows the gutters still hold the 12rem sidebar. */
body:has(.multicol) #content {
grid-template-columns: minmax(0, 1fr) minmax(78rem, 4fr) minmax(0, 1fr);
}
body.editing #content { body.editing #content {
margin-left: var(--editor-w); margin-left: var(--editor-w);
padding-left: 1rem; padding-left: 1rem;
@@ -464,17 +475,17 @@ article dd {
hyphens: auto; hyphens: auto;
} }
/* Multi-column reading on wide displays, but only for long articles /* Multi-column reading, but only for long articles (pagerite.js adds
(pagerite.js adds .multicol based on content length and splits the body .multicol based on content length and splits the body into .colseg segments
into .colseg segments separated by full-width h2s and wide figures; separated by full-width h2s and wide figures; only segments with enough
only segments with enough text get .cols and thus columns). Columns only text get .cols). No fixed breakpoint: `columns: 30rem` lets CSS fit as
reflow text inside the article; the article's width never changes. */ many columns of at least 30rem as the article's current width allows —
@media (min-width: 100rem) { since .multicol also uncaps the article width (see #content above), a
.multicol .colseg.cols { wider window simply yields more columns. */
columns: 2; .multicol .colseg.cols {
column-gap: 3.5rem; columns: 30rem;
column-rule: 1px solid var(--line); column-gap: 3.5rem;
} column-rule: 1px solid var(--line);
} }
.multicol .colseg { .multicol .colseg {
@@ -664,7 +675,7 @@ figure:has(.wide) img {
/* Floats take a fixed share of the text column rather than sizing by the /* Floats take a fixed share of the text column rather than sizing by the
image's intrinsic width, which varies wildly (SVGs have none, and image's intrinsic width, which varies wildly (SVGs have none, and
min-content collapses them). A percentage also scales correctly inside min-content collapses them). A percentage also scales correctly inside
two-column segments, where the column is the containing block. The multi-column segments, where the column is the containing block. The
caption wraps within that width. A width attribute ({.right width=300}) caption wraps within that width. A width attribute ({.right width=300})
overrides the default on uncaptioned images. */ overrides the default on uncaptioned images. */
figure:has(.right), figure:has(.right),
@@ -692,6 +703,14 @@ article h2 {
grid-template-columns: 12rem minmax(0, 78rem) minmax(0, 1fr); grid-template-columns: 12rem minmax(0, 78rem) minmax(0, 1fr);
} }
/* Long articles stay uncapped here too: same 12rem reservation for the
sidebar, but the center track keeps growing (see the .multicol rule
at #content). The article's left edge stays at 12rem either way, so
the constant .wide breakout margin below remains correct. */
body:has(#sidebar):has(.multicol):not(.editing) #content {
grid-template-columns: 12rem minmax(78rem, 4fr) minmax(0, 1fr);
}
body:has(#sidebar) figure:has(.wide), body:has(#sidebar) figure:has(.wide),
body:has(#sidebar) img.wide:not(figure img) { body:has(#sidebar) img.wide:not(figure img) {
margin-inline: -13.25rem 0; margin-inline: -13.25rem 0;
+5 -3
View File
@@ -232,7 +232,7 @@ import { showAuthIframe } from 'paskia'
renderAuthUi(); renderAuthUi();
placeEditPen(); placeEditPen();
// Multi-column layout only when there is enough text to justify it. // Multi-column layout only when there is enough text to justify it.
// Split the body into columned segments: h2s and wide figures are // Split the body into columned segments: h1s, h2s and wide figures are
// full-width separators and never go inside columns. // full-width separators and never go inside columns.
const article = main.querySelector("article"); const article = main.querySelector("article");
if (article) { if (article) {
@@ -243,9 +243,11 @@ import { showAuthIframe } from 'paskia'
); );
if (body && article.classList.contains("multicol") if (body && article.classList.contains("multicol")
&& !body.querySelector(".colseg")) { && !body.querySelector(".colseg")) {
// h2s and anything holding a wide image are full-width separators // h1s, h2s and anything holding a wide image are full-width
// separators
const isSeparator = (el) => const isSeparator = (el) =>
el.tagName === "H2" || el.querySelector("img.wide") !== null; el.tagName === "H1" || el.tagName === "H2"
|| el.querySelector("img.wide") !== null;
let seg = null; let seg = null;
for (const el of [...body.children]) { for (const el of [...body.children]) {
if (isSeparator(el)) { if (isSeparator(el)) {