home3 news: typographic category cover for pictureless articles

~half the brief has no image, leaving a blank well above the headline. When an
article has no image_url, fill the well with the topic word (e.g. "science") in
lowercase Newsreader on a soft topic-tinted field, color-coded per topic
(science/tech/environment/health/community/culture/world/space + neutral
default). Same 5:4 footprint as the photo, so card height stays consistent.
Threads `topic` through the news object.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-26 22:05:41 -04:00
parent 022908392b
commit e3e6f24753
+32 -3
View File
@@ -6,9 +6,24 @@
// /home3 — the Claude Design "Frame A" direction (editorial, with colour), rebuilt in
// our codebase with our real logo + self-hosted fonts, wired to live data. Hidden
// prototype (noindex), alongside /home2 so we can compare.
let news = $state(null); // {id, title, summary, image}
let news = $state(null); // {id, title, summary, image, topic, ...}
let art = $state(null); // {title, artist, year, image}
let newsFit = $state('cover'); // 'cover' = full-bleed photo; 'contain' = framed-plate figure
// Pictureless articles (~half the feed) get a typographic category cover instead of a blank
// well: the topic word on a soft topic-tinted field, color-coded across the feed.
const TOPIC_TINT = {
science: { bg: '#e2edf1', ink: '#2f6d86' },
technology: { bg: '#e8e9f4', ink: '#5a5fa0' },
environment: { bg: '#e6efe2', ink: '#3a7d5b' },
health: { bg: '#f4e8e8', ink: '#a8566a' },
community: { bg: '#f3ece0', ink: '#a9763f' },
culture: { bg: '#ece5f3', ink: '#7e51b0' },
world: { bg: '#e6ecf3', ink: '#4a6da0' },
space: { bg: '#e7e7f2', ink: '#4a4e8a' },
};
const TOPIC_DEFAULT = { bg: '#e6edef', ink: '#3f6378' };
let topicTint = $derived(TOPIC_TINT[(news?.topic || '').toLowerCase()] || TOPIC_DEFAULT);
let word = $state(null); // /api/word/today
let quote = $state(null); // /api/quote/today
let fact = $state(null); // /api/onthisday/today
@@ -91,7 +106,7 @@
} catch { /* global brief */ }
try {
const it = (await getJSON(`/api/brief?limit=1${homeq}`))?.items?.[0];
if (it) news = { id: it.id, title: it.title, summary: it.summary || it.description || '', image: it.image_url || null, source_read_minutes: it.source_read_minutes };
if (it) news = { id: it.id, title: it.title, summary: it.summary || it.description || '', image: it.image_url || null, topic: it.topic || null, source_read_minutes: it.source_read_minutes };
// Photos display full (cover); only wide/tall figures (diagrams) get the framed plate.
if (news?.image) {
const probe = new Image();
@@ -159,9 +174,16 @@
<!-- Good News (tall) — a card with TWO links, so it's a div, not a single anchor -->
<div class="card news">
<a class="news-photo-a" href={news?.id ? `/a/${news.id}` : '/'} aria-label="Read this article">
{#if news?.image}
<div class="news-photo {newsFit}">
<div class="news-plate" style={news?.image ? `background-image:url(${news.image})` : ''}></div>
<div class="news-plate" style={`background-image:url(${news.image})`}></div>
</div>
{:else}
<!-- pictureless: typographic category cover -->
<div class="news-typo" style={`background:${topicTint.bg}`}>
<span class="news-typo-word" style={`color:${topicTint.ink}`}>{news?.topic || 'good news'}</span>
</div>
{/if}
</a>
<div class="news-body">
<span class="label" style="color:#0083ad">GOOD NEWS</span>
@@ -371,6 +393,13 @@
box-shadow: 0 6px 18px -10px rgba(30, 60, 70, 0.28);
background-size: contain; background-origin: content-box;
}
/* pictureless fallback: topic word on a soft topic-tinted field, same footprint as the photo */
.news-typo { aspect-ratio: 5/4; display: flex; align-items: center; justify-content: center; padding: 16px; box-sizing: border-box; }
.news-typo-word {
font-family: 'Newsreader', Georgia, serif; font-weight: 500; font-size: clamp(1.9rem, 5vw, 2.9rem);
line-height: 1.05; letter-spacing: -0.01em; text-transform: lowercase; text-align: center; max-width: 100%;
}
.news-body { padding: 24px 26px; flex: 1; display: flex; flex-direction: column; }
.news h2 {
font-size: clamp(1.55rem, 2.6vw, 30px); line-height: 1.14; margin: 12px 0 0;