docs: add .agents workspace and the Astro refactor plan
Adds the agent-facing workspace and a 20-task plan for migrating the site to Astro. Nothing here implements the refactor; these are briefs, rules and templates that the task agents read. - .agents/ holds context, rules, checklists, skills, specialist agents, component/page/config templates and gate scripts. It is vendor-neutral so MiniMax, Gemini and Codex can all read it; CLAUDE.md just points at AGENTS.md. - .husky/ plus .lintstagedrc.json wire the three gate tiers. gate.sh locks on the shared git-common-dir so parallel worktrees serialise, and guards the assertion count in scripts/verify.mjs against a coverage drop. - plans/astro-refactor/ carries the phase graph, per-task briefs and the model-routing recommendation. These files must be tracked before fanning out: a worktree only checks out tracked files, so an untracked plan is invisible to every agent working in one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
# Rule: accessibility
|
||||
|
||||
## The gate is a keyboard, not a scanner
|
||||
|
||||
Automated tooling catches roughly **57%** of accessibility defects, and the
|
||||
misses cluster exactly where usability is decided: focus visibility, focus
|
||||
obscured by sticky elements, target size, and drag alternatives. Run axe, then
|
||||
do the manual sweep anyway.
|
||||
|
||||
**Manual sweep, every interactive component:**
|
||||
|
||||
1. Tab through it. Every control reachable, in a sensible order.
|
||||
2. Focus ring visible at every stop — this site uses
|
||||
`outline: 3px solid #a7483f; outline-offset: 2px`. Keep it.
|
||||
3. Operate it with Enter and Space. Escape closes anything that opened.
|
||||
4. Nothing is reachable only by hover or only by pointer.
|
||||
5. Zoom to 200%. Nothing clipped, nothing overlapping.
|
||||
|
||||
## Semantics
|
||||
|
||||
- Native elements first. `<button>` for actions, `<a>` for navigation. A `<div>`
|
||||
with a click handler is a defect, not a style choice.
|
||||
- ARIA only when HTML cannot express it. The current code does this well —
|
||||
`role="tablist"`, `aria-pressed`, `aria-current="page"`, `aria-label` on
|
||||
regions. Preserve every one during migration; they are asserted in
|
||||
`verify.mjs`.
|
||||
- One `<h1>` per page. Heading levels never skip.
|
||||
- Every image needs `alt`. Decorative images get `alt=""`.
|
||||
|
||||
## State
|
||||
|
||||
Interactive state must be exposed, not just painted:
|
||||
|
||||
```html
|
||||
<!-- wrong: only colour says it is selected -->
|
||||
<button class="active">Improved draft</button>
|
||||
<!-- right -->
|
||||
<button class="active" aria-pressed="true">Improved draft</button>
|
||||
```
|
||||
|
||||
The vote widget and preview switcher already do this. Match them.
|
||||
|
||||
## Contrast
|
||||
|
||||
Body text ≥ 4.5:1, large text ≥ 3:1, UI boundaries ≥ 3:1. Check any new
|
||||
combination against the token palette — `--muted` on `--paper` is the pair most
|
||||
likely to fail; verify before shipping.
|
||||
|
||||
## Bilingual content
|
||||
|
||||
`<html lang>` must change with the language toggle, not just the text. Screen
|
||||
readers pick pronunciation from it. This already works today — do not regress it.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Rule: animation
|
||||
|
||||
This is an editorial-print design. Motion is punctuation, not decoration.
|
||||
|
||||
## Budget
|
||||
|
||||
- **Purpose or nothing.** Motion may signal a state change, direct attention to
|
||||
what just changed, or smooth a layout shift. Nothing else.
|
||||
- Duration: **150–250ms** for UI feedback, up to 400ms for a page transition.
|
||||
Longer reads as sluggish; shorter reads as a glitch.
|
||||
- Easing: `cubic-bezier(.2,0,0,1)` for entrances, `ease-out` for exits. Never
|
||||
`linear` for anything a person watches. Never bounce/elastic — wrong register
|
||||
for this design.
|
||||
- One thing moves at a time. Staggered cascades of cards are a template default;
|
||||
this site has a point of view and does not do them.
|
||||
|
||||
## `prefers-reduced-motion` is mandatory
|
||||
|
||||
Several current stylesheets already honour it. Every new animation must:
|
||||
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* { animation-duration: .01ms !important; animation-iteration-count: 1 !important;
|
||||
transition-duration: .01ms !important; scroll-behavior: auto !important; }
|
||||
}
|
||||
```
|
||||
|
||||
Reduced motion means *reduced*, not *broken*: the end state must still be
|
||||
correct and the interface still usable. Test it — in DevTools, Rendering →
|
||||
Emulate `prefers-reduced-motion`.
|
||||
|
||||
## Performance
|
||||
|
||||
- Animate **`transform` and `opacity` only.** They composite on the GPU.
|
||||
Animating `width`, `height`, `top`, `left`, or `margin` forces layout on every
|
||||
frame and will show up as a failed INP.
|
||||
- `will-change` only on an element about to animate, removed after. Leaving it
|
||||
on permanently costs memory and can *hurt* performance.
|
||||
- Prefer CSS transitions. Reach for the Web Animations API only for sequencing
|
||||
that CSS cannot express. Do not add an animation library — it is a runtime
|
||||
dependency on a site whose thesis is having none.
|
||||
- INP budget is **200ms**. An animation that delays interaction response fails.
|
||||
|
||||
## Astro view transitions
|
||||
|
||||
If page transitions are wanted, use Astro's `<ClientRouter />`. It is the only
|
||||
sanctioned motion dependency, and it must:
|
||||
|
||||
- degrade cleanly with JS disabled (it does — full navigation)
|
||||
- respect `prefers-reduced-motion`
|
||||
- not break the review desk's query-param deep links or browser back/forward
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Never animate anything that conveys information on its own. Motion is
|
||||
redundant reinforcement.
|
||||
- Nothing flashes more than three times per second.
|
||||
- Focus must stay visible throughout a transition, and focus order must not
|
||||
change because of one.
|
||||
@@ -0,0 +1,72 @@
|
||||
# Rule: Astro
|
||||
|
||||
Binding for every `.astro` file.
|
||||
|
||||
## Zero JS is the default
|
||||
|
||||
A component ships no JavaScript unless it has a `client:*` directive. Seven of
|
||||
this site's ten pages ship no JS today and must continue to.
|
||||
|
||||
- Never add `client:load` without justifying it in the PR description.
|
||||
- Prefer, in order: no JS → `client:visible` → `client:idle` → `client:load`.
|
||||
- An island is a **leaf**, not a wrapper. Hydrate the tab panel, not the page.
|
||||
|
||||
## Islands in this project
|
||||
|
||||
Only these need interactivity. Anything else claiming island status is wrong:
|
||||
|
||||
| Island | Why | Directive |
|
||||
| --- | --- | --- |
|
||||
| Guide phase/tab switchers | click-driven panel swap | `client:visible` |
|
||||
| Review desk catalog + file viewer | search, filter, fetch source files | `client:load` |
|
||||
| Vote widget | talks to `vote-service/` | `client:visible` |
|
||||
| Language toggle | swaps EN/PT across the page | `client:idle` |
|
||||
|
||||
## Structure
|
||||
|
||||
```astro
|
||||
---
|
||||
// 1. imports
|
||||
// 2. Props interface
|
||||
// 3. destructure Astro.props
|
||||
// 4. derived values — no side effects, no fetch in components
|
||||
---
|
||||
<!-- markup -->
|
||||
<style>/* component-scoped */</style>
|
||||
```
|
||||
|
||||
- Typed props always: `interface Props { … }`, then `const { … } = Astro.props`.
|
||||
- Data loading belongs in `src/content/` collections or the page frontmatter,
|
||||
not inside a component.
|
||||
- No barrel files (`index.ts` re-export hubs). They cost tree-shaking and invite
|
||||
cycles.
|
||||
|
||||
## Content collections
|
||||
|
||||
All copy lives in `src/content/`, typed with a Zod schema in
|
||||
`src/content/config.ts`. The review desk's `catalog.js` maps onto a collection
|
||||
almost one-to-one — do that rather than importing a 27 KB JS file.
|
||||
|
||||
## Styles
|
||||
|
||||
- Component styles go in the component's `<style>` block. Astro scopes them.
|
||||
- Only tokens and true resets live in global CSS.
|
||||
- Do not port `responsive.css` verbatim. It is an override layer whose reason
|
||||
for existing disappears once layout is componentized. Port what a component
|
||||
needs, prove the rest is dead, delete it.
|
||||
|
||||
## URLs and the base path
|
||||
|
||||
The site is served from `/ai-for-dummies/`. Set `base` in `astro.config.mjs` and
|
||||
never hand-write an absolute internal path. Use `import.meta.env.BASE_URL`.
|
||||
|
||||
Existing routes are load-bearing and must not change, including trailing
|
||||
slashes and the review desk's query params.
|
||||
|
||||
## Never
|
||||
|
||||
- No UI framework (React/Vue/Svelte) unless a task brief explicitly calls for it.
|
||||
Astro components plus a little vanilla JS cover everything here.
|
||||
- No CSS framework. This site has a hand-built visual identity — see
|
||||
[`theming.md`](theming.md).
|
||||
- No external runtime requests. Self-host. `audit-ui.mjs` enforces it.
|
||||
@@ -0,0 +1,48 @@
|
||||
# Rule: code style
|
||||
|
||||
## Match what is there
|
||||
|
||||
This codebase has a real voice: dense one-liner CSS, terse ES modules, comments
|
||||
that explain *why* and never *what*. Do not reformat it into someone else's
|
||||
house style as a side effect of a task.
|
||||
|
||||
The one exception is CSS minification-by-hand — `styles.css` is single-line and
|
||||
unreadable. Component `<style>` blocks should be normally formatted. That is an
|
||||
improvement, not a style disagreement.
|
||||
|
||||
## Comments
|
||||
|
||||
Write the comment that stops the next person from making a mistake. The existing
|
||||
codebase does this well:
|
||||
|
||||
```js
|
||||
// The cluster's nginx ingress runs with `use-forwarded-headers` off, so it
|
||||
// *overwrites* X-Forwarded-For with its own downstream peer — the VPS's
|
||||
// tailnet address — which would collapse every visitor into a single voter.
|
||||
```
|
||||
|
||||
That comment earns its place. `// set the colour` does not.
|
||||
|
||||
## Naming
|
||||
|
||||
- Components `PascalCase.astro`; everything else `kebab-case`.
|
||||
- Booleans read as assertions: `isOpen`, `hasVoted`, not `open`, `voted`.
|
||||
- No abbreviations that are not already in the codebase's vocabulary.
|
||||
|
||||
## TypeScript
|
||||
|
||||
Astro brings TS. Use it: typed props, typed collections, `strict` on. Never
|
||||
`any` — if the type is genuinely unknown, `unknown` plus a narrow.
|
||||
|
||||
## Dead code
|
||||
|
||||
Delete it. Do not comment it out, do not leave it behind a flag. Git remembers.
|
||||
|
||||
This matters here specifically: `responsive.css` is 30 KB of accumulated
|
||||
overrides, and the temptation during migration will be to port it wholesale
|
||||
"just in case". Prove each rule is needed or drop it.
|
||||
|
||||
## Commits
|
||||
|
||||
Present tense, lowercase, `type: subject`, matching the existing log
|
||||
(`feat:`, `fix:`, `docs:`). The body explains why, and states what you did not do.
|
||||
@@ -0,0 +1,53 @@
|
||||
# Rule: componentization
|
||||
|
||||
## When to make a component
|
||||
|
||||
Extract when the same markup appears **three times**, or when a block has a name
|
||||
a person would use out loud ("the eyebrow", "the route card", "the phase panel").
|
||||
|
||||
Do not extract on the second occurrence. Two similar blocks often diverge; the
|
||||
premature abstraction costs more than the duplication.
|
||||
|
||||
## Sizes
|
||||
|
||||
- A component that exceeds ~120 lines of markup is doing two jobs. Split it.
|
||||
- A page that is a bare list of components with no markup of its own has been
|
||||
over-split. Pages are allowed to contain layout.
|
||||
|
||||
## Boundaries
|
||||
|
||||
```
|
||||
src/components/
|
||||
primitives/ Eyebrow, Rule, Callout, CodeBlock — no domain knowledge
|
||||
blocks/ RouteCard, PhasePanel, HandoffTable, SkillPackage — composed, page-agnostic
|
||||
islands/ interactive only; each one justified per rules/astro.md
|
||||
```
|
||||
|
||||
- Primitives never import blocks.
|
||||
- Blocks never import page-specific data; they take props.
|
||||
- Islands are leaves. An island must not wrap static children that could have
|
||||
been server-rendered.
|
||||
|
||||
## Props
|
||||
|
||||
- Typed `interface Props`, every field. No `any`, no untyped rest spread.
|
||||
- Required by default. Optional props need a default and a reason.
|
||||
- Pass data, not markup. If you find yourself passing an HTML string, you want a
|
||||
`<slot>`.
|
||||
|
||||
## Named exports, no barrels
|
||||
|
||||
Import the file you need. Barrel `index.ts` files break tree-shaking and create
|
||||
import cycles; bulletproof-react advises against them and so do we.
|
||||
|
||||
## The catalog is data, not components
|
||||
|
||||
The 24 review-desk entries are content, not 24 components. One
|
||||
`SkillReviewCard.astro` iterating a typed collection. If you are writing the
|
||||
25th near-identical component, stop and model the data.
|
||||
|
||||
## Do not componentize
|
||||
|
||||
`hands-on/starter/` and `hands-on/rules/` are lab fixtures. Their whole value is
|
||||
being flat, dependency-free files an attendee hands to an agent. They ship from
|
||||
`public/` unchanged.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Rule: content and i18n
|
||||
|
||||
## Every user-visible string is content
|
||||
|
||||
No hard-coded copy in components. Strings live in `src/content/`, typed with
|
||||
Zod, and reach components as props or collection entries.
|
||||
|
||||
## The existing shape
|
||||
|
||||
`app.js` already stores content as `{ en: '…', pt: '…' }` objects across
|
||||
`phases`, `handsOnPrompts`, `modelGuide`, `skillSources`, and
|
||||
`skillInstallPrompts` — about 50 `en:` keys. That shape is fine and should be
|
||||
carried across, not redesigned:
|
||||
|
||||
```ts
|
||||
// src/content/config.ts
|
||||
const localized = z.object({ en: z.string(), pt: z.string() });
|
||||
```
|
||||
|
||||
Both locales are **required**. A missing `pt` must be a build error, not a
|
||||
silent English fallback — that is how bilingual sites quietly become
|
||||
monolingual.
|
||||
|
||||
## Migrating strings
|
||||
|
||||
Copy, do not retype. These are hand-written translations with specific tone
|
||||
(`'Transforme ambiguidade em trabalho'`). Retyping introduces typos and drift.
|
||||
Move the literal, then diff the extracted content against the original file to
|
||||
prove nothing changed:
|
||||
|
||||
```bash
|
||||
node .agents/scripts/extract-strings.mjs app.js > /tmp/before.json
|
||||
node .agents/scripts/extract-strings.mjs src/content/guide/ > /tmp/after.json
|
||||
diff /tmp/before.json /tmp/after.json
|
||||
```
|
||||
|
||||
## Language switching
|
||||
|
||||
Today the toggle swaps text client-side and updates `<html lang>`. Two options
|
||||
for Astro, decide in task 03:
|
||||
|
||||
- **Keep client-side swap.** Both languages ship in the payload. Zero routing
|
||||
change, matches today exactly, no URL work. Recommended — the content is small
|
||||
and the current behaviour is already what people link to.
|
||||
- **Route-based (`/en/`, `/pt/`).** Better SEO, more correct, but it changes
|
||||
every existing URL. Only do this with an explicit decision plus redirects.
|
||||
|
||||
Whichever you pick, `<html lang>` must track the active language.
|
||||
|
||||
## Rendered content
|
||||
|
||||
Markdown in `catalog.js` entries (the `improved` field) should become real
|
||||
Markdown files in the collection, rendered at build time rather than by a
|
||||
hand-rolled client-side renderer. That deletes code and improves fidelity.
|
||||
|
||||
Careful: `skill-reviews/improved/**/SKILL.md` is **generated** from those
|
||||
entries by `scripts/build-skill-review.mjs`, and the generated files are
|
||||
committed. Keep that generator working, or replace it and update every
|
||||
reference to it.
|
||||
@@ -0,0 +1,76 @@
|
||||
# Rule: quality gates
|
||||
|
||||
Three tiers. Each is scoped so that **many agents committing in parallel
|
||||
worktrees stay fast** — the whole point is that a gate you are tempted to skip
|
||||
is not a gate.
|
||||
|
||||
| Tier | Hook | Scope | Budget | Runs |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | `pre-commit` | **staged files only** (lint-staged) | < 3s | every commit |
|
||||
| 2 | `pre-push` | whole project: build + verify + audit | < 90s | every push |
|
||||
| 3 | CI (Gitea Actions) | tier 2 + visual regression | minutes | every push to `main` |
|
||||
|
||||
Tier 1 must stay under a few seconds. If it creeps, move the check to tier 2.
|
||||
An agent that waits 40s per commit will start passing `--no-verify`, and then
|
||||
you have no gate at all.
|
||||
|
||||
## Tier 1 — pre-commit (lint-staged)
|
||||
|
||||
Formats and lints **only what you staged**. Scoped by construction, so ten
|
||||
parallel worktrees do ten small jobs, not ten full-project sweeps.
|
||||
|
||||
- Prettier + ESLint on `*.{js,mjs,ts,astro}`
|
||||
- Prettier + Stylelint on `*.css`
|
||||
- `check-tokens.mjs` on changed `.astro`/`.css` — catches raw hex before it lands
|
||||
- Prettier on `*.{json,md}`
|
||||
|
||||
## Tier 2 — pre-push
|
||||
|
||||
The real gate:
|
||||
|
||||
```
|
||||
astro check types
|
||||
astro build it compiles
|
||||
verify.mjs 42 content assertions — count must not fall
|
||||
audit-ui.mjs no external runtime dependencies
|
||||
check-tokens.mjs full sweep
|
||||
```
|
||||
|
||||
## Tier 3 — CI
|
||||
|
||||
Tier 2 plus screenshot comparison against `.agents/snapshots/`. Only CI has the
|
||||
budget for it.
|
||||
|
||||
## Bypassing
|
||||
|
||||
`--no-verify` is allowed exactly once: a work-in-progress commit **on your own
|
||||
task branch that you will rebase away**. It is never allowed on a commit you
|
||||
intend to merge, and the pre-push gate has no bypass.
|
||||
|
||||
If a gate is wrong, fix the gate in its own commit. Do not route around it.
|
||||
|
||||
## Parallelism
|
||||
|
||||
- Hooks are **per-worktree**. Git's `index.lock` is per-worktree, so parallel
|
||||
commits do not contend.
|
||||
- The heavy tier-2 gate takes a **lock** (`.git/af-gate.lock`, shared across
|
||||
worktrees) so ten agents pushing at once do not run ten concurrent builds and
|
||||
thrash the machine. Waiters queue; they do not fail.
|
||||
- `npm ci` in a fresh worktree should use `--prefer-offline` to avoid registry
|
||||
contention when several spin up at once.
|
||||
|
||||
## The silent-failure mode you must know about
|
||||
|
||||
Husky sets `core.hooksPath` to `.husky/_`, and **`.husky/_` is generated by
|
||||
`npm install`, not committed**. A fresh `git worktree add` therefore has hooks
|
||||
configured but the directory missing — so **hooks silently do not run**. Every
|
||||
commit passes. Nothing is checked.
|
||||
|
||||
`.agents/scripts/worktree.sh start` runs the install and then verifies. Check
|
||||
manually any time you did not use it:
|
||||
|
||||
```bash
|
||||
.agents/scripts/verify-hooks.sh
|
||||
```
|
||||
|
||||
Run this first in any worktree you did not create with the script.
|
||||
@@ -0,0 +1,69 @@
|
||||
# Rule: git worktrees
|
||||
|
||||
This project teaches worktrees. It should use them properly.
|
||||
|
||||
## One task, one worktree, one agent
|
||||
|
||||
```bash
|
||||
# from the main checkout
|
||||
git worktree add ../af-task-07 -b refactor/task-07-route-cards
|
||||
cd ../af-task-07
|
||||
npm ci
|
||||
```
|
||||
|
||||
Naming: directory `../af-task-NN`, branch `refactor/task-NN-<slug>`. Both
|
||||
derived from the task file so the mapping is never ambiguous.
|
||||
|
||||
## Why isolation matters here
|
||||
|
||||
The migration runs many agents in parallel over the same small set of files
|
||||
(`tokens.css`, `verify.mjs`, `astro.config.mjs` are contended). Worktrees give
|
||||
each agent its own working directory over one object store — cheap, and no
|
||||
agent can see another's half-finished state.
|
||||
|
||||
The failure mode without them: two agents both "fix" `verify.mjs`, and the
|
||||
second overwrites the first's assertions.
|
||||
|
||||
## Contended files
|
||||
|
||||
These are touched by many tasks. Whoever owns them per the plan is the **only**
|
||||
writer; everyone else opens an issue in their task report instead of editing:
|
||||
|
||||
| File | Owner |
|
||||
| --- | --- |
|
||||
| `src/styles/tokens.css` | `design-system-keeper` |
|
||||
| `scripts/verify.mjs` | `verification-engineer` |
|
||||
| `astro.config.mjs`, `package.json` | `astro-architect` |
|
||||
| `src/content/config.ts` | `content-i18n-migrator` |
|
||||
|
||||
## Before you start
|
||||
|
||||
1. `git fetch origin && git rebase origin/main` — start from current `main`.
|
||||
2. Read your task file end to end before writing anything.
|
||||
3. Confirm your task's dependencies are merged. Task files list them.
|
||||
|
||||
## Before you finish
|
||||
|
||||
1. `npm run verify` green — without deleting assertions.
|
||||
2. The relevant checklist in [`../checklists/`](../checklists/) complete.
|
||||
3. `git rebase origin/main` again, resolve conflicts in your worktree.
|
||||
4. Task report: what changed, what you verified, **what you did not do**.
|
||||
|
||||
## Cleanup
|
||||
|
||||
```bash
|
||||
cd - # back to the main checkout
|
||||
git worktree remove ../af-task-07
|
||||
git branch -d refactor/task-07-route-cards
|
||||
```
|
||||
|
||||
Stale worktrees hold locks and confuse the next agent. `git worktree list`
|
||||
should be short.
|
||||
|
||||
## Never
|
||||
|
||||
- Never work directly on `main`.
|
||||
- Never force-push a shared branch. The `pages` branch is the sole exception,
|
||||
and only if CI owns it (see [`../context/publishing.md`](../context/publishing.md)).
|
||||
- Never `git add -A` from the repository root. This repo has untracked local
|
||||
scratch (`.serena/`, `scripts/inspect.py`) that must not be swept into a commit.
|
||||
@@ -0,0 +1,78 @@
|
||||
# Rule: theming
|
||||
|
||||
Binding for every colour, font size, spacing value, and breakpoint.
|
||||
|
||||
**Read [`../context/design-system.md`](../context/design-system.md) first.** The
|
||||
current CSS has three drifting palettes and a broken `@font-face`. This rule
|
||||
describes the target; that file describes what you are migrating from.
|
||||
|
||||
## One token layer
|
||||
|
||||
Every value comes from `src/styles/tokens.css`. If a component needs a value
|
||||
that is not a token, either it is a genuine one-off (justify it in a comment) or
|
||||
the token layer is missing something (add it there, not inline).
|
||||
|
||||
```css
|
||||
/* forbidden */
|
||||
color: #172f42;
|
||||
color: rgba(23,47,66,.6);
|
||||
|
||||
/* required */
|
||||
color: var(--ink);
|
||||
color: var(--muted);
|
||||
```
|
||||
|
||||
No raw hex outside `tokens.css`. `.agents/scripts/check-tokens.mjs` enforces it;
|
||||
wire it into `npm run verify`.
|
||||
|
||||
## Semantic names, not literal ones
|
||||
|
||||
`--ink`, `--paper`, `--muted`, `--line`, `--accent`, `--gold`, `--blue`,
|
||||
`--deep` are the existing vocabulary. Keep it — it is already semantic and the
|
||||
team reads it fluently. Do not rename to `--color-neutral-900`.
|
||||
|
||||
If a genuine second surface is needed, extend semantically
|
||||
(`--surface-lab`, `--ink-inverse`), never numerically.
|
||||
|
||||
## Type scale
|
||||
|
||||
Replace the 14 ad-hoc `clamp()` triples with named steps:
|
||||
|
||||
```css
|
||||
--step-display: clamp(56px, 9vw, 126px); /* h1 */
|
||||
--step-6: clamp(36px, 5vw, 65px); /* section h2 */
|
||||
--step-5: clamp(24px, 3vw, 38px); /* sub-head */
|
||||
--step-4: clamp(22px, 3vw, 36px); /* pull-quote */
|
||||
--step-1: 15px; /* body */
|
||||
--step-0: 11px; /* eyebrow / label */
|
||||
```
|
||||
|
||||
The eyebrow treatment (`10–11px` monospace, `letter-spacing:.08–.1em`,
|
||||
uppercase) is a signature of this design. Make it one class, not fifteen
|
||||
repetitions.
|
||||
|
||||
## Breakpoints
|
||||
|
||||
Five named widths replace the current sixteen:
|
||||
|
||||
```css
|
||||
--bp-sm: 560px; --bp-md: 800px; --bp-lg: 1100px;
|
||||
--bp-xl: 1600px; --bp-2xl: 2200px;
|
||||
```
|
||||
|
||||
When collapsing a component's old breakpoint onto a named one, screenshot at the
|
||||
**old** value. That is where the regression will be.
|
||||
|
||||
## Preserve the house style
|
||||
|
||||
- Flat colour blocks, hairline `1px` rules, near-zero border-radius.
|
||||
- Tight negative tracking on display type (`-.06em` … `-.08em`).
|
||||
- Grid separators built as `gap:1px` over a coloured parent background. This is
|
||||
deliberate. Do not "fix" it into `border`.
|
||||
- `Georgia, serif` for emphasis spans (`h1 em`). It renders today; keep it.
|
||||
|
||||
## Fonts
|
||||
|
||||
Do not add a webfont without an explicit decision recorded in the task. The
|
||||
intended Manrope/DM Mono has never rendered; introducing it is a visual redesign,
|
||||
not a refactor. Default: match what renders today.
|
||||
Reference in New Issue
Block a user