Keep a post inside the window, and stop letterboxing it
CI / Typecheck, test, build (pull_request) Successful in 34s

On a desktop a portrait video came with black bars all the way around and sat
partly below the fold, so you had to scroll to find the thing you had opened.

The bars were the figure, not the video. It carried the media's aspect ratio,
so at the full width of the column a 720x1280 reel forced a box 1204px tall
inside a 982px window, and the video -- capped at 78vh -- floated in the middle
of it with 438px of black above and below and 247px either side. The ratio now
goes on the media itself, which sizes to its own proportions instead of being
stretched to the column, and the background is the card's rather than black, so
what space is left reads as page instead of as a border.

The height cap is the window less the chrome around it. Deriving that from the
layout is the obvious approach and does not work: the card is sized by its
contents, so every percentage height inside resolves to auto and clamps
nothing. Measured across the posts this serves, the header, caption and footer
come to 270-350px, so the cap subtracts 350 and the whole post fits. Smaller
than it might be on a short window, but a video can be full-screened and a post
you have to hunt for cannot.

Threads keep the looser cap and scroll, which a column of several posts was
always going to do.

Grid thumbnails were quietly broken and the measuring turned it up: the cell
takes its height from an aspect ratio, which a percentage height will not
resolve against, and `aspect-ratio: auto` asks for the image's own ratio rather
than none, so a portrait thumbnail rendered at twice the height of its cell and
spilled out. They fill their cell absolutely now.

Verified at 1512x982, 1280x800, 1440x700 and 390x844: no letterboxing anywhere,
and the media sits above the fold on every one.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01BGkRmLfiWuJHx6tQ12EELY
This commit is contained in:
2026-08-26 17:24:22 -03:00
co-authored by Claude Opus 5
parent 60a9468875
commit df50fa7e93
2 changed files with 73 additions and 8 deletions
+9 -2
View File
@@ -4,6 +4,11 @@ import { html, raw, type Raw } from './html.ts';
import { badge, layout, originalUrlBlock } from './layout.ts';
import { linkify } from './text.ts';
/**
* Put the ratio on the media itself, never on the figure around it. On the
* figure, at the full width of the column, a portrait video forces a box
* taller than the window and the rest of the post gets pushed off screen.
*/
function aspect(item: Media): string {
return item.width && item.height ? `aspect-ratio: ${item.width} / ${item.height};` : '';
}
@@ -13,11 +18,12 @@ function renderItem(item: Media, index: number): Raw {
if (item.kind === 'video') {
const poster = item.poster ? proxyUrlFor(item.poster) : undefined;
return html`<figure class="item item--video" data-index="${index}" style="${aspect(item)}">
return html`<figure class="item item--video" data-index="${index}">
<video
controls
playsinline
preload="metadata"
style="${aspect(item)}"
${poster ? html`poster="${poster}"` : ''}
><source src="${src}"></video>
${item.hls
@@ -26,10 +32,11 @@ function renderItem(item: Media, index: number): Raw {
</figure>`;
}
return html`<figure class="item item--image" data-index="${index}" style="${aspect(item)}">
return html`<figure class="item item--image" data-index="${index}">
<img
src="${src}"
alt="${item.alt ?? ''}"
style="${aspect(item)}"
loading="${index === 0 ? 'eager' : 'lazy'}"
decoding="async"
>