57 lines
1.6 KiB
TypeScript
57 lines
1.6 KiB
TypeScript
---
|
|
// Interactive page template — for pages that genuinely need client-side
|
|
// behaviour (the full guide, the review desk).
|
|
//
|
|
// The page itself is still server-rendered. Only the islands hydrate.
|
|
// If you are copying this for a page that has no interaction, use
|
|
// chapter.astro instead.
|
|
|
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
|
import Island from '../components/islands/Island.astro';
|
|
import { getCollection } from 'astro:content';
|
|
|
|
const entries = await getCollection('guide');
|
|
const lang = 'en'; // TODO: wire to the language toggle decision (task 03)
|
|
|
|
const items = entries.map((entry) => ({
|
|
id: entry.id,
|
|
label: entry.data.title[lang],
|
|
body: entry.data.copy[lang],
|
|
}));
|
|
---
|
|
|
|
<BaseLayout title="Full field guide" description="TODO">
|
|
<!-- Static content is server-rendered. No hydration cost. -->
|
|
<section class="hero">
|
|
<p class="eyebrow">The full guide</p>
|
|
<h1>Ship the <em>system.</em></h1>
|
|
</section>
|
|
|
|
<!--
|
|
client:visible, not client:load — this is below the fold and the page must
|
|
stay interactive-free until it matters. Justification belongs in the PR:
|
|
"tab panel requires click-driven state; no server equivalent."
|
|
-->
|
|
<Island client:visible items={items} />
|
|
|
|
<!-- More static content after the island. Islands are leaves, not wrappers. -->
|
|
<slot />
|
|
</BaseLayout>
|
|
|
|
<style>
|
|
.hero {
|
|
max-width: 780px;
|
|
padding: clamp(75px, 12vh, 145px) 0 85px;
|
|
}
|
|
h1 {
|
|
font-size: var(--step-display);
|
|
line-height: 0.86;
|
|
letter-spacing: -0.08em;
|
|
}
|
|
h1 em {
|
|
color: var(--blue);
|
|
font-family: Georgia, serif;
|
|
font-weight: 400;
|
|
}
|
|
</style>
|