Keep a post inside the window, and stop letterboxing it #2
+64
-6
@@ -86,7 +86,9 @@ main { max-width: 680px; margin: 0 auto; }
|
||||
|
||||
/* ---------- media ---------- */
|
||||
|
||||
.media { border-block: 1px solid var(--line); background: #000; }
|
||||
/* Card-coloured, not black: whatever space the media does not fill should
|
||||
read as part of the page rather than as a border around it. */
|
||||
.media { border-block: 1px solid var(--line); background: var(--card); }
|
||||
.post__head + .media, .text + .media { border-top: 1px solid var(--line); }
|
||||
|
||||
.media__bar {
|
||||
@@ -135,14 +137,21 @@ main { max-width: 680px; margin: 0 auto; }
|
||||
place-items: center;
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* Sized by its own proportions rather than stretched to the column: a
|
||||
portrait video filling the width is mostly empty space either side of it.
|
||||
The cap keeps any single item within the window. */
|
||||
.item img, .item video {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-height: 78vh;
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
max-height: 70vh;
|
||||
max-height: 70dvh;
|
||||
object-fit: contain;
|
||||
background: #000;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.item__note {
|
||||
@@ -162,9 +171,31 @@ main { max-width: 680px; margin: 0 auto; }
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.media[data-view="grid"] .item { aspect-ratio: 1 / 1 !important; cursor: zoom-in; }
|
||||
.media[data-view="grid"] .item {
|
||||
aspect-ratio: 1 / 1;
|
||||
cursor: zoom-in;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/*
|
||||
* Filled absolutely rather than with height: 100%. The cell's height comes
|
||||
* from its aspect-ratio, which a percentage height will not resolve against,
|
||||
* and `aspect-ratio: auto` means "use the image's own" rather than "ignore
|
||||
* ratios" -- between them a portrait thumbnail ends up twice the height of
|
||||
* its cell.
|
||||
*/
|
||||
.media[data-view="grid"] .item img,
|
||||
.media[data-view="grid"] .item video { height: 100%; max-height: none; object-fit: cover; }
|
||||
.media[data-view="grid"] .item video {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
min-height: 0;
|
||||
object-fit: cover;
|
||||
aspect-ratio: auto !important;
|
||||
}
|
||||
.media[data-view="grid"] .item__note { display: none; }
|
||||
|
||||
/* ---------- footer ---------- */
|
||||
@@ -272,3 +303,30 @@ main { max-width: 680px; margin: 0 auto; }
|
||||
without hiding the rest. */
|
||||
background: color-mix(in srgb, var(--accent, #888) 7%, transparent);
|
||||
}
|
||||
|
||||
/* ---------- fitting the window ---------- */
|
||||
|
||||
/*
|
||||
* On a large screen a single post should be readable without scrolling to
|
||||
* find it -- a video especially, which you can full-screen if you want it
|
||||
* bigger.
|
||||
*
|
||||
* The cap is the window less the chrome around the media: header, caption and
|
||||
* footer come to roughly 270-350px on the posts this serves. Deriving it
|
||||
* instead of subtracting a constant is the obvious thing to try and does not
|
||||
* work: the card is sized by its contents, so a percentage height inside it
|
||||
* resolves to auto and clamps nothing.
|
||||
*
|
||||
* A thread is a column of several posts and cannot fit whatever we do, so it
|
||||
* keeps the looser cap above and scrolls.
|
||||
*/
|
||||
@media (min-width: 700px) and (min-height: 560px) {
|
||||
.post:not([data-segments]) .item img,
|
||||
.post:not([data-segments]) .item video {
|
||||
max-height: calc(100vh - 350px);
|
||||
max-height: calc(100dvh - 350px);
|
||||
/* Below that the media would be too small to be worth showing; better to
|
||||
let a very long caption scroll. */
|
||||
min-height: 220px;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-2
@@ -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"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user