home3: small-joys rail -> rotating two-up shelf (option A: label + dots + arrows)

Two jewel cards at a time, reader-advanced (no auto-motion); 3 cells total, wraps. Keeps
each card at generous size instead of cramming three.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-22 10:58:55 -04:00
parent 8cf061d214
commit 89352e7123
+74 -27
View File
@@ -17,6 +17,12 @@
}
let headline = $derived(clampWords(news?.title ?? 'What went right this week: the good news that actually matters'));
// small-joys shelf: 3 cells shown two at a time, rotated by the reader (no auto-motion)
const JOY_ACCENTS = ['#D2861B', '#8857C2', '#3F9A66'];
let joyIdx = $state(0);
const prevJoy = () => (joyIdx = (joyIdx + 2) % 3);
const nextJoy = () => (joyIdx = (joyIdx + 1) % 3);
onMount(async () => {
try {
const a = await getJSON('/api/art/today');
@@ -42,6 +48,40 @@
<meta name="description" content="A calmer, brighter corner of the internet: good news, daily art, small games, and little resets." />
</svelte:head>
{#snippet joyCard(i)}
{#if i === 0}
<div class="joy joy-word">
<span class="wm" aria-hidden="true">S</span>
<div class="joy-in">
<div class="tag"><span class="rule"></span><span class="tag-label">Word of the day</span></div>
<p class="word-line"><span class="word">Serene</span> <span class="word-pos">adj.</span></p>
<p class="word-pron">/səˈriːn/</p>
<p class="def">Calm, peaceful, and untroubled. The quiet after a storm passes.</p>
</div>
</div>
{:else if i === 1}
<div class="joy joy-quote">
<span class="wm wm-q" aria-hidden="true"></span>
<div class="joy-in">
<div class="tag"><span class="rule"></span><span class="tag-label">Quote of the day</span></div>
<p class="quote">Very little is needed to make a happy life.</p>
<div class="attrib"><span class="attrib-rule"></span><span class="attrib-by">Marcus Aurelius</span></div>
</div>
</div>
{:else}
<div class="joy joy-fact">
<div class="joy-in">
<div class="joy-top">
<div class="tag"><span class="rule"></span><span class="tag-label">A good thing today</span></div>
<span class="soon">SOON</span>
</div>
<p class="fact-hero"><span class="year">1928</span> <span class="onthis">ON THIS DAY</span></p>
<p class="fact">Penicillin was discovered by a happy accident.</p>
</div>
</div>
{/if}
{/snippet}
<div class="page">
<header class="bar">
<a class="brand" href="/home3" aria-label="upbeatBytes home">
@@ -145,35 +185,26 @@
</div>
</div>
<!-- "small joys" rail — little jewels: focal point + watermark + tag + soft gradient -->
<div class="joys">
<div class="joy joy-word">
<span class="wm" aria-hidden="true">S</span>
<div class="joy-in">
<div class="tag"><span class="rule"></span><span class="tag-label">Word of the day</span></div>
<p class="word-line"><span class="word">Serene</span> <span class="word-pos">adj.</span></p>
<p class="word-pron">/səˈriːn/</p>
<p class="def">Calm, peaceful, and untroubled. The quiet after a storm passes.</p>
</div>
</div>
<div class="joy joy-quote">
<span class="wm wm-q" aria-hidden="true"></span>
<div class="joy-in">
<div class="tag"><span class="rule"></span><span class="tag-label">Quote of the day</span></div>
<p class="quote">Very little is needed to make a happy life.</p>
<div class="attrib"><span class="attrib-rule"></span><span class="attrib-by">Marcus Aurelius</span></div>
</div>
</div>
<div class="joy joy-fact">
<div class="joy-in">
<div class="joy-top">
<div class="tag"><span class="rule"></span><span class="tag-label">A good thing today</span></div>
<span class="soon">SOON</span>
<!-- "small joys" — two jewels at a time, rotated by the reader (3 cells total) -->
<div class="joys-shelf">
<div class="joys-head">
<div class="joys-title"><span class="jt-label">Small joys for today</span><span class="jt-count">· {joyIdx + 1} of 3</span></div>
<div class="joys-nav">
<div class="joys-dots" aria-hidden="true">
{#each [0, 1, 2] as d}
<span class="dot" class:on={d === joyIdx} style={d === joyIdx ? `background:${JOY_ACCENTS[joyIdx]}` : ''}></span>
{/each}
</div>
<div class="joys-arrows">
<button class="arrow" onclick={prevJoy} aria-label="Previous small joys"></button>
<button class="arrow" onclick={nextJoy} aria-label="More small joys"></button>
</div>
<p class="fact-hero"><span class="year">1928</span> <span class="onthis">ON THIS DAY</span></p>
<p class="fact">Penicillin was discovered by a happy accident.</p>
</div>
</div>
<div class="joys">
{@render joyCard(joyIdx)}
{@render joyCard((joyIdx + 1) % 3)}
</div>
</div>
</div>
</main>
@@ -327,7 +358,23 @@
/* "small joys" rail — little jewels: one big focal point per card, a faint oversized
watermark glyph, an accent-tag label, soft diagonal gradient + long low shadow. */
.joys { flex: none; display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; }
.joys-shelf { flex: none; }
.joys-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.joys-title { display: flex; align-items: baseline; gap: 8px; }
.jt-label { font-family: 'Newsreader', Georgia, serif; font-style: italic; font-size: 17px; color: #7a6f5b; }
.jt-count { font-size: 12px; color: #b3a890; }
.joys-nav { display: flex; align-items: center; gap: 14px; }
.joys-dots { display: flex; align-items: center; gap: 6px; }
.dot { width: 6px; height: 6px; border-radius: 50%; background: #d9cdb8; transition: width 0.2s ease, background 0.2s ease; }
.dot.on { width: 18px; border-radius: 3px; }
.joys-arrows { display: flex; gap: 8px; }
.arrow {
width: 30px; height: 30px; border-radius: 50%; border: 1px solid #e0d3b8; background: transparent;
color: #b09a6e; font-size: 14px; cursor: pointer; display: flex; align-items: center; justify-content: center;
padding: 0; line-height: 1; transition: background 0.15s ease, color 0.15s ease;
}
.arrow:hover { background: #fff; color: #9a7b3e; }
.joys { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.joy { position: relative; overflow: hidden; border-radius: 20px; padding: 22px 24px; }
.joy-in { position: relative; } /* content sits above the watermark */
.wm { position: absolute; font-family: 'Newsreader', Georgia, serif; line-height: 1; pointer-events: none; }