perf(images): WebP banners, text-only related cards, resized list thumbnails
Deploy / build (push) Successful in 56s

Measured before/after with headless Chrome + CDP (not by eye):

- article banner: <picture> WebP q78 with the PNG as fallback, because the PNG
  must stay for og:image (social scrapers do not reliably render WebP cards).
  70 banners: 6.02 MB -> 2.52 MB (42%). JPEG measured *larger* than PNG here
  (q85 = 129%) — the art is flat terminal-style with sharp text edges.
- post page related cards: dropped four 1200x630 PNGs (183 KB) to display
  526x276 where the embedded code is illegible; now text-only cards with a
  category badge, date and reading time.
- list thumbnails (home + archive): new /og/thumb/<name>-{400,800}.webp served
  via srcset/sizes. Rows display at ~398px but were pulling full 1200x630 PNGs.
  Homepage thumbnails ~450 KB -> 62 KB; archive (71 rows) ~3.2 MB -> 436 KB.
- desktop banner capped at 720px centred: 67% -> 45% of a 900px viewport,
  keeping the art intact instead of cropping it.

Post page totals: images 277 KB -> 38 KB; page height 5758 -> 5065 (desktop)
and 9423 -> 8807 (mobile). Build: 163 pages.

Script: scripts/optimize-images.py; policy: docs/design-guide.md §3; runbook
step added to docs/ops-runbook.md.
This commit is contained in:
hoelee committed 2026-10-16 17:11:27 +08:00
1 parent 9462ab2373
commit f364084284
219 files changed
+158 -15

No files matched your search

+29 -11
View File
@@ -182,6 +182,13 @@ time { color: var(--faint); font-size: 0.88rem; }
height: auto;
display: block;
}
/* Desktop: the banner is a 16:9 figure. At full column width (1070px) it stands
602px tall — 67% of a 900px viewport, pushing the whole article below the fold.
Capping the width (rather than cropping) keeps the art intact: 720px -> ~405px,
which is 45% of the viewport. */
@media (min-width: 900px) {
.article-banner { max-width: 720px; margin-left: auto; margin-right: auto; }
}
.article-head {
margin-bottom: 2rem;
padding-bottom: 1.5rem;
@@ -290,7 +297,8 @@ pre code {
margin-top: 1rem;
}
.related-card {
display: block;
display: flex;
flex-direction: column;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
@@ -299,17 +307,27 @@ pre code {
text-decoration: none;
}
.related-card:hover { border-color: var(--brand); }
.related-thumb {
width: 100%;
height: auto;
display: block;
aspect-ratio: 1200 / 630;
object-fit: cover;
border-bottom: 1px solid var(--border);
.related-body { padding: 0.9rem 1rem; display: flex; flex-direction: column; flex: 1; }
.related-badge {
align-self: flex-start;
display: inline-block;
font-size: 0.68rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--brand);
background: var(--surface);
border: 1px solid var(--border);
border-radius: 999px;
padding: 0.1em 0.6em;
margin-bottom: 0.45rem;
}
.related-body { padding: 0.9rem 1rem; }
.related-card h3 { margin: 0 0 0.3rem; font-size: 0.95rem; }
.related-card time { font-size: 0.78rem; }
.related-card h3 {
margin: 0 0 0.3rem;
font-size: 0.95rem;
/* keep card heights comparable without thumbnails */
display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.related-card time { font-size: 0.78rem; margin-top: auto; padding-top: 0.4rem; }
/* ---------- Post list ---------- */
.post-list { list-style: none; padding: 0; margin: 0; }