home3: small-joys rail (WOTD/QOTD crafted + 'good thing today' SOON), whole-word headline clamp, 16px gaps

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-22 10:18:00 -04:00
parent 2173802c70
commit 3c519a5f50
+42 -22
View File
@@ -8,6 +8,15 @@
let news = $state(null); // {id, title, summary, image}
let art = $state(null); // {title, artist, year, image}
// headline truncated on a whole-word boundary (no mid-word ellipsis)
function clampWords(s, max = 48) {
if (!s || s.length <= max) return s;
const cut = s.slice(0, max);
const i = cut.lastIndexOf(' ');
return (i > 0 ? cut.slice(0, i) : cut).replace(/[\s,;:.]+$/, '') + '…';
}
let headline = $derived(clampWords(news?.title ?? 'What went right this week: the good news that actually matters'));
onMount(async () => {
try {
const a = await getJSON('/api/art/today');
@@ -61,7 +70,7 @@
<div class="news-photo" style={news?.image ? `background-image:url(${news.image})` : ''}></div>
<div class="news-body">
<span class="label" style="color:#0083ad">GOOD NEWS</span>
<h2>{news?.title ?? 'What went right this week: the good news that actually matters'}</h2>
<h2>{headline}</h2>
<p class="summary">{news?.summary || "We read the week so you don't have to doomscroll it. Five quietly hopeful stories, summarised to the gist."}</p>
<div class="news-foot">
<a class="link news-link" href={news?.id ? `/a/${news.id}` : '/'}>Read article</a>
@@ -136,18 +145,23 @@
</div>
</div>
<!-- skinny Word / Quote of the day strip (placeholder content; real engine wired later) -->
<div class="card wotd">
<div class="wotd-half">
<!-- "small joys" rail: three evenly-weighted daily delights (placeholder content) -->
<div class="joys">
<div class="joy joy-word">
<span class="label">WORD OF THE DAY</span>
<span class="word">Serene</span>
<p class="word-line"><span class="word">Serene</span> <span class="word-meta">adj · /səˈriːn/</span></p>
<span class="def">Calm, peaceful, and untroubled.</span>
</div>
<div class="wotd-half">
<div class="joy joy-quote">
<span class="label">QUOTE OF THE DAY</span>
<span class="quote">“Very little is needed to make a happy life.”</span>
<span class="qglyph" aria-hidden="true"></span>
<span class="quote">Very little is needed to make a happy life.</span>
<span class="quote-by">Marcus Aurelius</span>
</div>
<div class="joy joy-fact">
<div class="joy-top"><span class="label">A GOOD THING TODAY</span><span class="soon">SOON</span></div>
<span class="fact">In 1928, penicillin was discovered by a happy accident.</span>
</div>
</div>
</div>
</main>
@@ -201,12 +215,12 @@
.bento {
max-width: 1180px; width: 100%; margin: 0 auto; box-sizing: border-box;
padding: 0 clamp(18px, 5vw, 44px) 16px;
display: grid; grid-template-columns: minmax(0, 1.18fr) minmax(0, 1.82fr); gap: 18px;
display: grid; grid-template-columns: minmax(0, 1.18fr) minmax(0, 1.82fr); gap: 16px;
}
/* right column matches the News height; Art stays pinned to the TOP and the Play/Moment
pair to the BOTTOM, with the extra space distributed BETWEEN them (FIX1). The cards
themselves keep their natural size and never stretch. */
.rightcol { display: flex; flex-direction: column; gap: 18px; }
.rightcol { display: flex; flex-direction: column; gap: 16px; }
.rightcol .art { flex: none; } /* Art pinned to the top */
.pair-wrap { flex: 1; display: flex; align-items: center; } /* fill the rest; pair vertically centered */
.card {
@@ -228,7 +242,7 @@
.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;
display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden;
display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
}
/* the gist fades out at the bottom (FIX 1) instead of a hard ellipsis — full read on /news */
.summary {
@@ -263,7 +277,7 @@
}
/* bottom pair */
.pair { width: 100%; display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.pair { width: 100%; display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.play { background: #FFF3DC; border: 1px solid #f6e2b8; display: flex; flex-direction: column; }
.play-top { padding: 22px 24px 0; }
.play h3 { font-size: clamp(1.25rem, 1.9vw, 23px); margin: 14px 0 0; color: #5c3d0c; }
@@ -299,15 +313,22 @@
.moment-line { font-family: 'Newsreader', Georgia, serif; font-style: italic; font-size: 18px; line-height: 1.3; color: #214a35; margin: 14px 0 0; }
.moment-meta { margin-top: auto; padding-top: 16px; font-size: 13px; color: #6f9683; }
/* skinny Word / Quote of the day strip — warm neutral, two halves split by a divider */
.wotd { flex: none; display: flex; align-items: stretch; background: #f1ece1; border: 1px solid #e6ddc9; }
.wotd-half { flex: 1; padding: 15px 22px; display: flex; flex-direction: column; justify-content: center; gap: 3px; min-width: 0; }
.wotd-half + .wotd-half { border-left: 1px solid #e6ddc9; }
.wotd .label { color: var(--muted); }
.wotd .word { font-family: 'Newsreader', Georgia, serif; font-weight: 600; font-size: 20px; color: var(--ink); }
.wotd .def { font-size: 13px; color: var(--body); }
.wotd .quote { font-family: 'Newsreader', Georgia, serif; font-style: italic; font-size: 15px; line-height: 1.3; color: #2d2a25; }
.wotd .quote-by { font-size: 12px; color: var(--muted); }
/* "small joys" rail — three evenly-weighted daily delights, each its own quiet tint */
.joys { flex: none; display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; }
.joy { border-radius: 18px; padding: 14px 18px; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.joy-word { background: #f1ece1; border: 1px solid #e6ddc9; }
.joy-quote { background: #f2ecf6; border: 1px solid #e4d8ee; }
.joy-fact { background: #e8f2ea; border: 1px solid #cfe5d4; }
.joy .label { color: var(--muted); }
.word-line { margin: 0; display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap; }
.joy .word { font-family: 'Newsreader', Georgia, serif; font-weight: 600; font-size: 19px; color: var(--ink); }
.word-meta { font-size: 12px; color: var(--muted); font-style: italic; }
.joy .def { font-size: 12.5px; color: var(--body); line-height: 1.4; }
.qglyph { font-family: 'Newsreader', Georgia, serif; font-size: 26px; line-height: 1; height: 13px; color: #b9a8d6; }
.joy .quote { font-family: 'Newsreader', Georgia, serif; font-style: italic; font-size: 14px; line-height: 1.3; color: #2d2a25; }
.quote-by { font-size: 11.5px; color: var(--muted); }
.joy-fact .joy-top { display: flex; align-items: center; justify-content: space-between; }
.fact { font-size: 13px; line-height: 1.4; color: var(--body); }
.foot {
text-align: center; max-width: 1180px; width: 100%; margin: 14px auto 0; box-sizing: border-box;
@@ -324,7 +345,6 @@
.art { flex-direction: column; }
.art-swatch { width: 100%; min-height: 130px; }
.pair { grid-template-columns: 1fr; }
.wotd { flex-direction: column; }
.wotd-half + .wotd-half { border-left: none; border-top: 1px solid #e6ddc9; }
.joys { grid-template-columns: 1fr; }
}
</style>