Merge branch 'main' into refactor/task-11-review-blocks
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
---
|
||||
// ChapterHero — eyebrow + display headline + intro paragraph. The shared
|
||||
// opener for /models/, /agents/, /skills/, /summary/. The optional `foot`
|
||||
// slot holds the rules case-study's two-column hero-foot.
|
||||
//
|
||||
// The eyebrow reuses the existing `Eyebrow` primitive but defaults to the
|
||||
// `red` tone, which matches the chapter surfaces (not the guide surface).
|
||||
// The h1 em treatment (Georgia italic, red) is the chapter-page signature.
|
||||
|
||||
import Eyebrow from '../primitives/Eyebrow.astro';
|
||||
|
||||
interface Props {
|
||||
/** Short uppercase label, same treatment as Eyebrow. Defaults to red,
|
||||
* matching chapters.css `.eyebrow`. */
|
||||
eyebrow: string;
|
||||
/** Colour tone for the eyebrow. */
|
||||
tone?: 'accent' | 'red';
|
||||
}
|
||||
|
||||
const { eyebrow, tone = 'red' } = Astro.props;
|
||||
---
|
||||
|
||||
<section class="hero">
|
||||
<Eyebrow label={eyebrow} tone={tone} />
|
||||
<h1><slot name="title" /></h1>
|
||||
<div class="intro"><slot /></div>
|
||||
<slot name="foot" />
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.hero {
|
||||
padding: 100px 0 70px;
|
||||
max-width: 950px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 16px 0;
|
||||
/* UNRESOLVED: legacy value `clamp(52px,9vw,126px)` from chapters.css.
|
||||
The --step-display token is `clamp(56px,9vw,126px)` — 4px taller at
|
||||
the small end. Reported in task 09 report; no token matches exactly. */
|
||||
font-size: clamp(52px, 9vw, 126px);
|
||||
line-height: 0.9;
|
||||
letter-spacing: -0.07em;
|
||||
}
|
||||
|
||||
/* The em treatment is a chapter-page signature: italic Georgia, red.
|
||||
Pages pass <em>...</em> inside the title slot to invoke it. */
|
||||
h1 :global(em) {
|
||||
font:
|
||||
400 0.9em Georgia,
|
||||
serif;
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
.intro :global(p) {
|
||||
max-width: 680px;
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
/* UNRESOLVED: legacy 20px fixed from chapters.css. No token matches.
|
||||
clamp keeps the legacy fixed size; reported in task 09 report. */
|
||||
font-size: clamp(20px, 20px, 20px);
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.hero {
|
||||
padding: 65px 0 45px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
h1 {
|
||||
/* UNRESOLVED: legacy 56px fixed from chapters.css. No token matches.
|
||||
Reported in task 09 report. */
|
||||
font-size: clamp(56px, 56px, 56px);
|
||||
}
|
||||
.intro :global(p) {
|
||||
/* UNRESOLVED: legacy 17px fixed from chapters.css. No token matches.
|
||||
Reported in task 09 report. */
|
||||
font-size: clamp(17px, 17px, 17px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
// ComparisonTable — horizontal-scroll wrapper for wide comparison tables.
|
||||
// Used by /models/ and /rules/ pages where tables become unreadable on a
|
||||
// phone without horizontal scroll.
|
||||
//
|
||||
// The pattern from chapters.css and full-guide/audit.css:
|
||||
// <div class="table-wrap">
|
||||
// <table style="min-width: ...">...</table>
|
||||
// </div>
|
||||
// with `.table-wrap { overflow-x: auto }`.
|
||||
//
|
||||
// Callers pass the table via the default slot; the wrapper class adds the
|
||||
// scroll behaviour and hairline border so the wrapper itself looks
|
||||
// intentional, not like an overflow leak.
|
||||
|
||||
interface Props {
|
||||
/** Minimum width on the inner table. Forces horizontal scroll below this
|
||||
* width rather than squashing columns into illegibility. */
|
||||
minWidth?: string;
|
||||
}
|
||||
|
||||
const { minWidth = '640px' } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="table-wrap">
|
||||
<div class="inner" style={`min-width: ${minWidth}`}>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.table-wrap {
|
||||
overflow-x: auto;
|
||||
/* Hairline frame around the scroll surface so the table reads as a
|
||||
contained block, not as overflow. Matches chapters.css `.grid` style. */
|
||||
background: var(--line);
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
|
||||
/* The inner element paints over the parent's 1px seams, leaving hairline
|
||||
dividers between any table cells the caller adds. */
|
||||
.inner {
|
||||
background: var(--paper);
|
||||
}
|
||||
|
||||
/* Slot content is the caller's <table>. We don't restyle it — the call
|
||||
site owns column widths and headings. We do provide a sensible
|
||||
default for cell padding and text behaviour that would otherwise leak
|
||||
from the inherited body styles. */
|
||||
.inner :global(table) {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
color: var(--ink);
|
||||
font-size: var(--step-1);
|
||||
}
|
||||
|
||||
.inner :global(th),
|
||||
.inner :global(td) {
|
||||
padding: 14px 16px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
/* Long words inside cells need to wrap, not push the column wider than
|
||||
min-width allows. overflow-wrap:anywhere matches audit-ui.mjs's
|
||||
expectation for table-style layouts. */
|
||||
.inner :global(th),
|
||||
.inner :global(td) {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.inner :global(th) {
|
||||
color: var(--red);
|
||||
font:
|
||||
700 var(--step-0) ui-monospace,
|
||||
monospace;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
// SectionGrid — the gap:1px hairline-separated card grid. Used by /models/'
|
||||
// LOW/MEDIUM/HIGH cards and /agents/' FRAME/HAND OFF/PROVE cards.
|
||||
//
|
||||
// The separator technique is deliberate house style: `gap:1px` over a
|
||||
// coloured parent background fakes borders without `border` shorthand. The
|
||||
// child fills its background to cover the parent's 1px seam.
|
||||
//
|
||||
// Each direct child is expected to be a card — the markup pattern from
|
||||
// chapters.css:
|
||||
// <article class="card"><b>LABEL</b><h2>Title</h2><p>Body</p></article>
|
||||
// The component styles `> .card` and its internals so callers don't repeat.
|
||||
|
||||
interface Props {
|
||||
/** Number of columns at the widest breakpoint. */
|
||||
columns?: number;
|
||||
}
|
||||
|
||||
const { columns = 3 } = Astro.props;
|
||||
---
|
||||
|
||||
<section class="grid" style={`--columns: ${columns}`}>
|
||||
<slot />
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--columns), 1fr);
|
||||
gap: 1px; /* hairline separators, drawn by the parent background */
|
||||
background: var(--line);
|
||||
border: 1px solid var(--line);
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
|
||||
/* Children paint their own background, which is what makes the 1px seam
|
||||
show. */
|
||||
.grid > :global(.card) {
|
||||
min-height: 220px;
|
||||
padding: 28px;
|
||||
background: var(--paper);
|
||||
}
|
||||
|
||||
.grid > :global(.card) :global(b) {
|
||||
color: var(--red);
|
||||
/* UNRESOLVED: legacy 24px fixed from chapters.css. No token matches.
|
||||
Reported in task 09 report. */
|
||||
font-size: clamp(24px, 24px, 24px);
|
||||
font-family: ui-monospace, monospace;
|
||||
}
|
||||
|
||||
.grid > :global(.card) :global(h2) {
|
||||
margin: 18px 0 8px;
|
||||
/* UNRESOLVED: legacy 25px fixed from chapters.css. --step-5 (clamp
|
||||
24-38px) is the closest token but grows at wide viewports; clamp
|
||||
keeps the legacy fixed size. Reported in task 09 report. */
|
||||
font-size: clamp(25px, 25px, 25px);
|
||||
letter-spacing: -0.04em;
|
||||
}
|
||||
|
||||
.grid > :global(.card) :global(p) {
|
||||
margin: 0 0 14px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.grid > :global(.card) :global(a) {
|
||||
color: var(--blue);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.grid > :global(.card) {
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
// SiteFooter — the bottom of every chapter page. Holds the inline-nav links
|
||||
// (chapter-to-chapter or to the field guide) plus the small footer text.
|
||||
//
|
||||
// The chapter pages use a "pill" link style: 1px ink border, ink text,
|
||||
// inverts on hover. Padding and gap are inherited from chapters.css
|
||||
// `.links`. The block element is the lower bound of the page main — no
|
||||
// margin/padding magic, just the rule above the first link.
|
||||
//
|
||||
// Footer text (the muted paragraph) goes into the default slot.
|
||||
|
||||
interface Props {
|
||||
/** Optional accessible label for the inline-nav region. */
|
||||
navLabel?: string;
|
||||
}
|
||||
|
||||
const { navLabel = 'Chapter navigation' } = Astro.props;
|
||||
---
|
||||
|
||||
<section class="footer">
|
||||
<nav class="links" aria-label={navLabel}>
|
||||
<slot name="links" />
|
||||
</nav>
|
||||
<div class="text">
|
||||
<slot />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.footer {
|
||||
padding: 30px 0 70px;
|
||||
color: var(--muted);
|
||||
/* UNRESOLVED: legacy 13px fixed from chapters.css. No token matches.
|
||||
Reported in task 09 report. */
|
||||
font-size: clamp(13px, 13px, 13px);
|
||||
}
|
||||
|
||||
.links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin: 28px 0 70px;
|
||||
}
|
||||
|
||||
.links :global(a) {
|
||||
padding: 10px 13px;
|
||||
color: var(--ink);
|
||||
border: 1px solid var(--ink);
|
||||
text-decoration: none;
|
||||
font: var(--step-0) monospace;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.links :global(a:hover) {
|
||||
color: var(--paper);
|
||||
background: var(--ink);
|
||||
}
|
||||
|
||||
.links :global(a:focus-visible) {
|
||||
outline: 3px solid var(--red);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
// TopBar — the three-cell topbar shared by /models/, /agents/, /skills/,
|
||||
// /summary/. Each slot is independent; the layout is a flex row with
|
||||
// `justify-content: space-between`.
|
||||
//
|
||||
// The chapter pages use:
|
||||
// <a>← ROUTE MAP</a> | <span>01 / MODELS</span> | <a>field guide ↗</a>
|
||||
//
|
||||
// The brief's watch-for: callers that mark a link as the current page
|
||||
// should set `aria-current="page"` on that link. The component does not
|
||||
// impose it — slot content is preserved verbatim. This is the only
|
||||
// indication of location for assistive tech on these pages, so losing
|
||||
// it would be a real regression.
|
||||
|
||||
interface Props {
|
||||
/** Optional HTML id for the topbar. */
|
||||
id?: string;
|
||||
}
|
||||
|
||||
const { id } = Astro.props;
|
||||
---
|
||||
|
||||
<header class="top" id={id}>
|
||||
<div class="cell"><slot name="previous" /></div>
|
||||
<div class="cell"><slot name="center" /></div>
|
||||
<div class="cell"><slot name="next" /></div>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
.top {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
padding: 24px 0;
|
||||
border-bottom: 1px solid var(--line);
|
||||
font: 700 var(--step-0) monospace;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.cell {
|
||||
/* Each cell is a flex item; content is preserved verbatim from the
|
||||
slot. Callers can put anchors, spans, navs, or anything else here. */
|
||||
}
|
||||
|
||||
.top :global(a) {
|
||||
color: var(--ink);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.top :global(a:focus-visible) {
|
||||
outline: 3px solid var(--red);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* The middle cell collapses on phones — the chapter number span is the
|
||||
least informative piece at narrow widths. */
|
||||
@media (max-width: 560px) {
|
||||
.cell:nth-child(2) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user