Homepage hub: /home2 prototype — sections-as-data + bigger shell + hybrid room cards
- New sections registry (lib/rooms.js): each room is one data entry (title/blurb/href/cta/ size/preview/icon) — add or resize by editing the list. - Reusable RoomCard (lib/components/RoomCard.svelte) with size variants and hybrid previews: Art shows today's live thumbnail, News shows today's top headline, others are blurb+CTA. - /home2 hidden prototype (noindex, unlinked) with a bigger top bar (logo 50px, larger nav labels) and a reflowing grid hub of the four rooms (News/Art/Play/Daily Moment). Iterate the look here, then promote to / and remove the clone. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { getJSON } from '$lib/api.js';
|
||||
import { ROOMS } from '$lib/rooms.js';
|
||||
import RoomCard from '$lib/components/RoomCard.svelte';
|
||||
|
||||
// Hidden prototype of the new homepage hub (/home2). Iterate the look here, then
|
||||
// promote to / and remove this clone — same approach we used for /art.
|
||||
let artImg = $state(null);
|
||||
let newsHeadline = $state('');
|
||||
|
||||
onMount(async () => {
|
||||
try { artImg = (await getJSON('/api/art/today'))?.image_url ?? null; } catch { /* card falls back to blurb */ }
|
||||
try {
|
||||
const b = await getJSON('/api/brief?limit=1');
|
||||
newsHeadline = b?.items?.[0]?.title ?? '';
|
||||
} catch { /* card falls back to blurb */ }
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>upbeatBytes — a calmer corner of the internet</title>
|
||||
<meta name="robots" content="noindex" />
|
||||
<meta name="description" content="A calmer corner of the internet: good news, daily art, small games, and little resets." />
|
||||
</svelte:head>
|
||||
|
||||
<div class="room">
|
||||
<header class="bar">
|
||||
<a class="brand" href="/home2" aria-label="upbeatBytes home">
|
||||
<img src="/logo.svg" alt="upbeatBytes" width="586" height="196" />
|
||||
</a>
|
||||
<nav class="nav">
|
||||
<a href="/">News</a>
|
||||
<a href="/play">Games</a>
|
||||
<a href="/art">Art</a>
|
||||
<a class="acct" href="/account" aria-label="Your account">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor"
|
||||
stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<circle cx="12" cy="8" r="3.3" />
|
||||
<path d="M5.5 19.2a6.5 6.5 0 0 1 13 0" />
|
||||
</svg>
|
||||
</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="hub">
|
||||
<div class="intro">
|
||||
<h1>A calmer corner of the internet.</h1>
|
||||
<p>Good news, daily art, small games, and little resets.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
{#each ROOMS as r (r.id)}
|
||||
<RoomCard room={r} {artImg} {newsHeadline} />
|
||||
{/each}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="foot">upbeatBytes — no ads, no paywalls, no doomscrolling.</footer>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.room {
|
||||
--canvas: #faf6ee; --surface: #ffffff; --ink: #232a31; --muted: #707b86;
|
||||
--line: #ece5d8; --accent: #0a93c0; --accent-deep: #066c8e;
|
||||
min-height: 100vh; background: var(--canvas); color: var(--ink);
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
|
||||
/* Bigger, more present top bar — logo + nav labels scaled up. */
|
||||
.bar {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 20px clamp(18px, 5vw, 64px);
|
||||
max-width: 1180px; width: 100%; margin: 0 auto; box-sizing: border-box;
|
||||
}
|
||||
.brand { display: block; line-height: 0; }
|
||||
.brand img { height: 50px; width: auto; display: block; }
|
||||
.nav { display: flex; align-items: center; gap: clamp(18px, 3vw, 38px); }
|
||||
.nav a { color: var(--ink); text-decoration: none; font-weight: 600; font-size: 1.12rem; }
|
||||
.nav a:hover { color: var(--accent); }
|
||||
.acct {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
width: 44px; height: 44px; border-radius: 50%; color: var(--muted);
|
||||
}
|
||||
.acct:hover { color: var(--accent); background: #eef6f9; }
|
||||
|
||||
.hub {
|
||||
flex: 1; width: 100%; max-width: 1180px; margin: 0 auto; box-sizing: border-box;
|
||||
padding: clamp(16px, 3vw, 40px) clamp(18px, 5vw, 64px) clamp(40px, 6vw, 72px);
|
||||
}
|
||||
.intro { text-align: center; margin: clamp(8px, 2vw, 22px) 0 clamp(28px, 4vw, 44px); }
|
||||
.intro h1 {
|
||||
font-family: Georgia, "Iowan Old Style", "Times New Roman", serif;
|
||||
font-size: clamp(1.9rem, 4.5vw, 3rem); margin: 0; letter-spacing: -0.015em; line-height: 1.08;
|
||||
}
|
||||
.intro p { color: var(--muted); margin: 12px 0 0; font-size: clamp(1rem, 2vw, 1.2rem); }
|
||||
|
||||
.grid {
|
||||
display: grid; gap: clamp(14px, 1.8vw, 22px);
|
||||
grid-template-columns: repeat(4, 1fr); grid-auto-flow: dense;
|
||||
}
|
||||
/* size variants → grid spans (RoomCard owns min-heights) */
|
||||
:global(.grid .card--large) { grid-column: span 2; grid-row: span 2; }
|
||||
:global(.grid .card--wide) { grid-column: span 2; }
|
||||
:global(.grid .card--tall) { grid-row: span 2; }
|
||||
|
||||
@media (max-width: 860px) {
|
||||
.grid { grid-template-columns: repeat(2, 1fr); }
|
||||
:global(.grid .card--large), :global(.grid .card--wide) { grid-column: span 2; }
|
||||
:global(.grid .card--large), :global(.grid .card--tall) { grid-row: auto; }
|
||||
}
|
||||
@media (max-width: 540px) {
|
||||
.grid { grid-template-columns: 1fr; }
|
||||
:global(.grid .card--large), :global(.grid .card--wide), :global(.grid .card--tall) { grid-column: span 1; }
|
||||
}
|
||||
|
||||
.foot {
|
||||
text-align: center; color: var(--muted); font-size: 0.86rem;
|
||||
padding: 28px 16px 40px; border-top: 1px solid var(--line); margin-top: auto;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user