Merge branch 'refactor/task-08-route-cards'

This commit is contained in:
Marcos Paulo
2026-09-05 07:49:03 +00:00
2 changed files with 140 additions and 0 deletions
+48
View File
@@ -0,0 +1,48 @@
---
// GridGroup — gap:1px hairline-separated card grid wrapper.
//
// The separator technique is deliberate house style: a `gap: 1px` over a
// coloured parent background fakes borders without `border` shorthand. Each
// direct child paints its own background, which is what makes the 1px seam
// show. Do not "fix" this into `border`.
//
// Built from `.agents/templates/components/grid-group.astro` per task 08 of
// the Astro refactor. The landing page wraps six `RouteCard` instances in
// this group; the route-grid layout also pushes each card's link to the
// bottom, which `RouteCard` carries itself so this wrapper stays generic.
interface Props {
/** Accessible label for the group, surfaced as `aria-label` on the section. */
label: string;
/** Number of columns at the widest breakpoint. */
columns?: number;
}
const { label, columns = 3 } = Astro.props;
---
<section class="group" aria-label={label}>
<div class="grid" style={`--columns: ${columns}`}>
<slot />
</div>
</section>
<style>
.grid {
display: grid;
grid-template-columns: repeat(var(--columns), 1fr);
gap: 1px; /* hairline separators, drawn by the parent background */
background: var(--line);
}
/* Children paint their own background, which is what makes the 1px show. */
.grid > :global(*) {
background: var(--paper);
}
@media (max-width: 800px) {
.grid {
grid-template-columns: 1fr;
}
}
</style>
+92
View File
@@ -0,0 +1,92 @@
---
// RouteCard — one chapter card in the landing page's six-up route grid.
//
// Markup mirrors `index.html`'s `<article class="card">` block:
// <b>{number}</b><h2>{title}</h2><p>{summary}</p><a href={href}>…</a>
// The `route-grid` wrapper in landing.css used to push each card's link to the
// bottom with `display:flex; flex-direction:column; margin-top:auto` on the
// anchor. That responsibility lives on the card itself now, so the card is
// self-contained — drop it into any gap:1px group and it lays out the same way.
//
// Built from `.agents/templates/components/static-block.astro` per task 08 of
// the Astro refactor. Steps on:
// - number — the "01" … "06" prefix
// - title — the chapter name (h2)
// - summary — the lede line under the title
// - href — link target for the chapter
// - cta — link text (the index page varies this: "Open chapter →",
// "Open lab →", "Open desk →"). Defaults to "Open chapter →".
interface Props {
/** Two-digit prefix rendered in the chapter surface red, e.g. "01". */
number: string;
/** Chapter name shown as the card's h2. */
title: string;
/** One-line summary under the title. */
summary: string;
/** Link target for the chapter. */
href: string;
/** Link text. Varies per destination in the current landing page. */
cta?: string;
}
const { number, title, summary, href, cta = 'Open chapter →' } = Astro.props;
---
<article class="card">
<b>{number}</b>
<h2>{title}</h2>
<p>{summary}</p>
<a href={href}>{cta}</a>
</article>
<style>
/* Mirror chapters.css `.card` + landing.css `.route-grid .card` exactly. */
.card {
display: flex;
min-width: 0;
flex-direction: column;
min-height: 220px;
padding: 28px;
background: var(--paper);
}
.card b {
color: var(--red);
/* token-gap: legacy 24px fixed from chapters.css .card b; no --step-* covers 24px; owner design-system-keeper */
font-size: 24px;
font-family: ui-monospace, monospace;
}
.card h2 {
margin: 18px 0 8px;
/* token-gap: legacy 25px fixed from chapters.css .card h2; --step-5 is clamp(24px,3vw,38px) and grows at wide viewports; owner design-system-keeper */
font-size: 25px;
letter-spacing: -0.04em;
}
.card p {
margin: 0 0 14px;
color: var(--muted);
}
.card a {
/* Push the link to the bottom — carried from landing.css
`.route-grid .card a { margin-top: auto }`. */
margin-top: auto;
color: var(--blue);
font-weight: 700;
text-decoration: none;
}
.card a:focus-visible {
outline: 3px solid var(--red);
outline-offset: 2px;
}
@media (max-width: 560px) {
.card {
min-height: 0;
}
}
</style>