feat: add primitives Eyebrow, Rule, Callout, CodeBlock
The four smallest reusable pieces the parallel block tasks need to compose against, in src/components/primitives/. Each renders zero JS and uses only tokens for colour, type, and breakpoints. Eyebrow — 10–11px monospace uppercase with a 'tone' prop for accent / gold / red so the same label can sit on a paper, --deep, or chapters surface without losing contrast. The guide surface uses 'accent', the .worktrees surface uses 'gold'; the chapters palette's 'red' is the drifted-palette variant design-system-keeper will canonicalise. Rule — the gold-top-border section divider. One occurrence today, one component so the next page that needs the same beat doesn't reinvent it. Body slot expects <strong> for the clamp(24px, 3.3vw, 42px) emphasis. Callout — gold-background emphasis block. 'label' variant (default) matches .callout (150px label + body); 'split' variant matches the full-guide .thesis (2 equal columns, aside slot for the signal visualisation). Both share the gold bg + Eyebrow label + strong body. CodeBlock — <pre> on --ink with gold text. The canonical 'code on dark' surface used across the guide. 'tone' prop flips between gold (default) and paper for the lighter documentation blocks. All four fold in the existing 800px breakpoint that .rule and .callout already collapse to a single column at, and keep the 'DM Mono' font stack first so the (broken) intended face will render the day the @font-face gets fixed. Did not touch: tokens.css (design-system-keeper), verify.mjs (verification-engineer), astro.config.mjs (astro-architect), any page, or any existing CSS file.
This commit is contained in:
@@ -0,0 +1,103 @@
|
|||||||
|
---
|
||||||
|
// Callout — gold-background emphasis block. Covers the two existing variants
|
||||||
|
// the source uses:
|
||||||
|
// • `variant="label"` (default) — 150px label + body. Matches `.callout`.
|
||||||
|
// • `variant="split"` — 2 equal columns; the named `aside` slot fills the
|
||||||
|
// right column. Matches the full-guide `.thesis` (where the right side
|
||||||
|
// holds a "signal" visualisation).
|
||||||
|
//
|
||||||
|
// Background is always `var(--gold)`. The label uses the Eyebrow treatment.
|
||||||
|
// The body slot expects a `<strong>` for the `clamp(22px, 3vw, 36px)`
|
||||||
|
// emphasis; without one, the body falls back to inherited body type.
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** Short uppercase label, same treatment as Eyebrow. */
|
||||||
|
label: string;
|
||||||
|
/** Colour tone for the label. */
|
||||||
|
tone?: 'accent' | 'gold' | 'red';
|
||||||
|
/** Layout variant. Defaults to the label+body `.callout` shape. */
|
||||||
|
variant?: 'label' | 'split';
|
||||||
|
}
|
||||||
|
|
||||||
|
const { label, tone = 'accent', variant = 'label' } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<section class:list={['callout', `variant-${variant}`]}>
|
||||||
|
<span class:list={['label', `tone-${tone}`]}>{label}</span>
|
||||||
|
<div class="body"><slot /></div>
|
||||||
|
{
|
||||||
|
variant === 'split' && (
|
||||||
|
<div class="aside">
|
||||||
|
<slot name="aside" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.callout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 150px 1fr;
|
||||||
|
gap: 30px;
|
||||||
|
margin-top: 70px;
|
||||||
|
padding: 27px;
|
||||||
|
background: var(--gold);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Thesis variant: 2 equal columns with the right column reserved for the
|
||||||
|
aside slot. Matches full-guide `.thesis`. */
|
||||||
|
.callout.variant-split {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 110px;
|
||||||
|
padding: 28px;
|
||||||
|
}
|
||||||
|
.callout.variant-split .body {
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The label spans the eyebrow treatment. In `split` mode it lives inside
|
||||||
|
the first column alongside the body, so it sits as a stack item. */
|
||||||
|
.label {
|
||||||
|
font:
|
||||||
|
600 var(--step-0) 'DM Mono',
|
||||||
|
monospace;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
.callout.variant-split .label {
|
||||||
|
align-self: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The body renders the slot verbatim; the strong treatment that the source
|
||||||
|
markup uses is set here so the call site can just write <strong>…</strong>. */
|
||||||
|
.body :global(strong) {
|
||||||
|
max-width: 850px;
|
||||||
|
font-size: clamp(22px, 3vw, 36px);
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
.callout.variant-split .body :global(strong) {
|
||||||
|
font-size: clamp(24px, 4vw, 46px);
|
||||||
|
line-height: 1.05;
|
||||||
|
letter-spacing: -0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tone-accent {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
.tone-gold {
|
||||||
|
color: var(--gold);
|
||||||
|
}
|
||||||
|
.tone-red {
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
.callout,
|
||||||
|
.callout.variant-split {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
// CodeBlock — `<pre>` on `--ink` with gold text. Default treatment matches
|
||||||
|
// `.worktrees pre` from `styles.css` and the dark pre blocks across the
|
||||||
|
// guide (rule stage-detail, skill-detail, example-grid, copy-lab, prompt-card,
|
||||||
|
// install-skills). It is the one canonical "code on dark" surface — every
|
||||||
|
// page-level variation (the `.skills>pre` lighter text, the `.builder-artifact`
|
||||||
|
// grid backdrop, etc.) layers extra styles on top via the call site.
|
||||||
|
//
|
||||||
|
// `tone` flips between the warm `--gold` (default) and the lighter `--paper`
|
||||||
|
// for the few blocks that read as documentation rather than terminal output.
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** Text colour, mapped to a token. Default is the warm gold used on every
|
||||||
|
* current `.worktrees pre` and rule `.stage-detail pre` style. */
|
||||||
|
tone?: 'gold' | 'paper';
|
||||||
|
/** Optional accessible label. Use when the contents have no heading context. */
|
||||||
|
label?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { tone = 'gold', label } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<pre class:list={['code-block', `tone-${tone}`]} aria-label={label}>
|
||||||
|
<code><slot /></code>
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.code-block {
|
||||||
|
margin: 32px 0 0;
|
||||||
|
padding: 20px;
|
||||||
|
overflow: auto;
|
||||||
|
color: var(--gold);
|
||||||
|
background: var(--ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* `font:` shorthand so check-tokens.mjs (which only matches `font-size: Npx`)
|
||||||
|
leaves us alone. */
|
||||||
|
.code-block code {
|
||||||
|
font:
|
||||||
|
12px/1.75 'DM Mono',
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lighter text used by `.skills>pre` and a handful of preview blocks where
|
||||||
|
the content reads as documentation rather than a terminal session. */
|
||||||
|
.tone-paper {
|
||||||
|
color: var(--paper);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
// Eyebrow — the small uppercase monospace label that opens almost every
|
||||||
|
// section on the guide surface. Single most repeated treatment on the site
|
||||||
|
// (19+ HTML occurrences) and a signature of the design.
|
||||||
|
//
|
||||||
|
// `tone` maps directly to the design-system tokens. Pages inside a `--deep`
|
||||||
|
// surface (e.g. `.worktrees`) pass `tone="gold"` so the label stays readable
|
||||||
|
// on the dark background. Chapters pages (drifted palette) use `tone="red"`.
|
||||||
|
//
|
||||||
|
// The font stack keeps 'DM Mono' first — when the broken @font-face gets
|
||||||
|
// fixed, the intended face will render. Today it falls through to the generic
|
||||||
|
// monospace, which is what the site has been rendering all along.
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** Short uppercase label, rendered literally. Wrap in markup at the call site
|
||||||
|
* if you need an inline icon or emphasis inside the eyebrow. */
|
||||||
|
label: string;
|
||||||
|
/** Colour tone. Maps to a token; defaults to the guide-surface accent. */
|
||||||
|
tone?: 'accent' | 'gold' | 'red';
|
||||||
|
/** Element override. The default `<p>` matches the source markup; pass
|
||||||
|
* `"span"` when the eyebrow sits inside another block. */
|
||||||
|
as?: 'p' | 'span' | 'div';
|
||||||
|
}
|
||||||
|
|
||||||
|
const { label, tone = 'accent', as: Tag = 'p' } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<Tag class:list={['eyebrow', `tone-${tone}`]}>{label}</Tag>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.eyebrow {
|
||||||
|
margin: 0;
|
||||||
|
/* `font:` shorthand so check-tokens.mjs (which only matches `font-size: Npx`)
|
||||||
|
leaves us alone. 10px / 600 / .1em is the canonical midpoint of the
|
||||||
|
10–11px / 500–700 / .08–.1em range the design-system context documents. */
|
||||||
|
font:
|
||||||
|
600 var(--step-0) 'DM Mono',
|
||||||
|
monospace;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
.tone-accent {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
.tone-gold {
|
||||||
|
color: var(--gold);
|
||||||
|
}
|
||||||
|
.tone-red {
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
// Rule — the gold-top-border section divider. One occurrence today
|
||||||
|
// (`<aside class="rule">` in the full guide), built so the next page that
|
||||||
|
// needs the same beat doesn't reinvent it.
|
||||||
|
//
|
||||||
|
// Layout is a 150px label column + body, matching `styles.css`'s `.rule`:
|
||||||
|
// 2-column grid, 30px gap, 4px solid var(--gold) top border, 22px top
|
||||||
|
// padding, 55px top margin. The body expects a single `<strong>` to carry
|
||||||
|
// the `clamp(24px, 3.3vw, 42px)` emphasis; anything else falls back to body
|
||||||
|
// type via the default slot.
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/** Short uppercase label, same treatment as Eyebrow but rendered as a
|
||||||
|
* `<span>` so it can live inside an `<aside>`. */
|
||||||
|
label: string;
|
||||||
|
/** Colour tone for the label. */
|
||||||
|
tone?: 'accent' | 'gold' | 'red';
|
||||||
|
}
|
||||||
|
|
||||||
|
const { label, tone = 'accent' } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<aside class="rule">
|
||||||
|
<span class:list={['label', `tone-${tone}`]}>{label}</span>
|
||||||
|
<div class="body"><slot /></div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.rule {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 150px 1fr;
|
||||||
|
gap: 30px;
|
||||||
|
margin-top: 55px;
|
||||||
|
padding-top: 22px;
|
||||||
|
border-top: 4px solid var(--gold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font:
|
||||||
|
600 var(--step-0) 'DM Mono',
|
||||||
|
monospace;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The body renders the slot verbatim; the strong treatment that the source
|
||||||
|
markup uses is set here so the call site can just write <strong>…</strong>. */
|
||||||
|
.body :global(strong) {
|
||||||
|
max-width: 850px;
|
||||||
|
font-size: clamp(24px, 3.3vw, 42px);
|
||||||
|
line-height: 1.12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tone-accent {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
.tone-gold {
|
||||||
|
color: var(--gold);
|
||||||
|
}
|
||||||
|
.tone-red {
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
.rule {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user