feat(15d): complete localization of full-guide page to PT
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: motion-designer
|
||||
description: Adds and audits animation — transitions, state changes, optional view transitions. Use for task 17 and any change involving movement. Do not use for static layout or styling work.
|
||||
description:
|
||||
Adds and audits animation — transitions, state changes, optional view
|
||||
transitions. Use for task 17 and any change involving movement. Do not use for
|
||||
static layout or styling work.
|
||||
tools: Read, Write, Edit, Bash, Grep, Glob
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: reviewer
|
||||
description: Merge gate. Reviews a task branch diff against its brief and the project rules. Use before merging any refactor task. Never writes features or fixes findings itself.
|
||||
description:
|
||||
Merge gate. Reviews a task branch diff against its brief and the project
|
||||
rules. Use before merging any refactor task. Never writes features or fixes
|
||||
findings itself.
|
||||
tools: Read, Grep, Glob, Bash
|
||||
---
|
||||
|
||||
@@ -8,7 +11,8 @@ You are the merge gate. You read diffs and report. **You do not write features
|
||||
and you do not fix what you find** — you name it precisely enough that the
|
||||
owning agent can.
|
||||
|
||||
**Read**: the task file, then every rule in `.agents/rules/` relevant to the diff.
|
||||
**Read**: the task file, then every rule in `.agents/rules/` relevant to the
|
||||
diff.
|
||||
|
||||
## Order of checks — highest-value first
|
||||
|
||||
|
||||
@@ -4,18 +4,18 @@
|
||||
|
||||
Ten hand-written HTML pages, each linking its own CSS and one ES module:
|
||||
|
||||
| Route | Page | Script | Stylesheets |
|
||||
| --- | --- | --- | --- |
|
||||
| `/` | `index.html` | — | `chapters.css`, `landing.css` |
|
||||
| `/full-guide/` | `full-guide/index.html` | `app.js` (50 KB) | `styles.css`, `responsive.css`, `audit.css` |
|
||||
| `/summary/` | `summary/index.html` | — | `chapters.css` |
|
||||
| `/models/` | `models/index.html` | — | `chapters.css` |
|
||||
| `/agents/` | `agents/index.html` | — | `chapters.css` |
|
||||
| `/skills/` | `skills/index.html` | `skills/app.js` | `skills/styles.css` |
|
||||
| `/rules/` | `rules/index.html` | `rules/app.js` | `rules/styles.css` |
|
||||
| `/skills-review/` | `skills-review/index.html` | `skills-review/app.js` | `skills-review/styles.css`, `change-lens.css` |
|
||||
| `/hands-on/starter/` | lab fixture | own | own |
|
||||
| `/hands-on/rules/` | lab fixture | own | own |
|
||||
| Route | Page | Script | Stylesheets |
|
||||
| -------------------- | -------------------------- | ---------------------- | --------------------------------------------- |
|
||||
| `/` | `index.html` | — | `chapters.css`, `landing.css` |
|
||||
| `/full-guide/` | `full-guide/index.html` | `app.js` (50 KB) | `styles.css`, `responsive.css`, `audit.css` |
|
||||
| `/summary/` | `summary/index.html` | — | `chapters.css` |
|
||||
| `/models/` | `models/index.html` | — | `chapters.css` |
|
||||
| `/agents/` | `agents/index.html` | — | `chapters.css` |
|
||||
| `/skills/` | `skills/index.html` | `skills/app.js` | `skills/styles.css` |
|
||||
| `/rules/` | `rules/index.html` | `rules/app.js` | `rules/styles.css` |
|
||||
| `/skills-review/` | `skills-review/index.html` | `skills-review/app.js` | `skills-review/styles.css`, `change-lens.css` |
|
||||
| `/hands-on/starter/` | lab fixture | own | own |
|
||||
| `/hands-on/rules/` | lab fixture | own | own |
|
||||
|
||||
Weight is concentrated: `app.js` 50 KB, `responsive.css` 30 KB,
|
||||
`skills-review/catalog.js` 27 KB, `skills-review/submitted-catalog.js` 18 KB.
|
||||
@@ -35,7 +35,8 @@ Weight is concentrated: `app.js` 50 KB, `responsive.css` 30 KB,
|
||||
`wins[]`, `improve[]`, `extras`, `improved` (full markdown). 24 entries across
|
||||
`catalog.js` + `submitted-catalog.js`. This is already a content collection in
|
||||
all but name.
|
||||
- **`skills-review/files.js` / `submitted-files.js`** — generated file manifests.
|
||||
- **`skills-review/files.js` / `submitted-files.js`** — generated file
|
||||
manifests.
|
||||
- **`vote.js`** — the vote widget island; talks to `vote-service/`.
|
||||
|
||||
## Target (Astro)
|
||||
@@ -53,12 +54,12 @@ public/
|
||||
|
||||
### Non-negotiables for the target
|
||||
|
||||
- **URLs do not change.** `/full-guide/`, `/skills-review/`, `/hands-on/starter/`
|
||||
and the rest must resolve exactly as they do now, trailing slash included.
|
||||
Existing links (including `docs/`, SilverBullet, and shared URLs with
|
||||
`?author=…&skill=…&view=…` query params) must keep working.
|
||||
- **Zero JS by default.** Seven of the ten pages ship no JavaScript today.
|
||||
They must still ship none. Islands are opt-in, per component, and justified.
|
||||
- **URLs do not change.** `/full-guide/`, `/skills-review/`,
|
||||
`/hands-on/starter/` and the rest must resolve exactly as they do now,
|
||||
trailing slash included. Existing links (including `docs/`, SilverBullet, and
|
||||
shared URLs with `?author=…&skill=…&view=…` query params) must keep working.
|
||||
- **Zero JS by default.** Seven of the ten pages ship no JavaScript today. They
|
||||
must still ship none. Islands are opt-in, per component, and justified.
|
||||
- **`hands-on/` stays vanilla.** It goes in `public/` untouched. It is a lab
|
||||
fixture, not a component.
|
||||
- **No external runtime requests.** `audit-ui.mjs` enforces this and it is part
|
||||
|
||||
@@ -49,4 +49,5 @@ 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.
|
||||
readers pick pronunciation from it. This already works today — do not regress
|
||||
it.
|
||||
|
||||
@@ -20,12 +20,16 @@ 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; }
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reduced motion means *reduced*, not *broken*: the end state must still be
|
||||
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`.
|
||||
|
||||
@@ -35,7 +39,7 @@ Emulate `prefers-reduced-motion`.
|
||||
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.
|
||||
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.
|
||||
|
||||
+14
-11
@@ -15,12 +15,12 @@ this site's ten pages ship no JS today and must continue to.
|
||||
|
||||
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` |
|
||||
| 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
|
||||
|
||||
@@ -31,8 +31,11 @@ Only these need interactivity. Anything else claiming island status is wrong:
|
||||
// 3. destructure Astro.props
|
||||
// 4. derived values — no side effects, no fetch in components
|
||||
---
|
||||
|
||||
<!-- markup -->
|
||||
<style>/* component-scoped */</style>
|
||||
<style>
|
||||
/* component-scoped */
|
||||
</style>
|
||||
```
|
||||
|
||||
- Typed props always: `interface Props { … }`, then `const { … } = Astro.props`.
|
||||
@@ -60,13 +63,13 @@ almost one-to-one — do that rather than importing a 27 KB JS file.
|
||||
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.
|
||||
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 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.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## 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
|
||||
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
|
||||
@@ -44,5 +44,5 @@ overrides, and the temptation during migration will be to port it wholesale
|
||||
|
||||
## Commits
|
||||
|
||||
Present tense, lowercase, `type: subject`, matching the existing log
|
||||
(`feat:`, `fix:`, `docs:`). The body explains why, and states what you did not do.
|
||||
Present tense, lowercase, `type: subject`, matching the existing log (`feat:`,
|
||||
`fix:`, `docs:`). The body explains why, and states what you did not do.
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
## 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").
|
||||
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.
|
||||
|
||||
@@ -55,5 +55,5 @@ 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.
|
||||
committed. Keep that generator working, or replace it and update every reference
|
||||
to it.
|
||||
|
||||
@@ -27,8 +27,12 @@ const text = html
|
||||
.replace(/<style[\s\S]*?<\/style>/gi, '')
|
||||
.replace(/<!--[\s\S]*?-->/g, '')
|
||||
.replace(/<[^>]+>/g, '\n')
|
||||
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
.replace(/"/g, '"').replace(/�?39;/g, "'").replace(/ /g, ' ')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/�?39;/g, "'")
|
||||
.replace(/ /g, ' ')
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: content-migration
|
||||
description: Move bilingual copy out of app.js and catalog.js into typed Astro content collections without losing or altering a single string. Use for any task that relocates user-visible text.
|
||||
description:
|
||||
Move bilingual copy out of app.js and catalog.js into typed Astro content
|
||||
collections without losing or altering a single string. Use for any task that
|
||||
relocates user-visible text.
|
||||
---
|
||||
|
||||
# Content migration
|
||||
@@ -9,8 +12,8 @@ description: Move bilingual copy out of app.js and catalog.js into typed Astro c
|
||||
|
||||
- `app.js` — ~50 `{ en, pt }` keys across `phases`, `handsOnPrompts`,
|
||||
`modelGuide`, `skillSources`, `skillInstallPrompts`
|
||||
- `skills-review/catalog.js` + `submitted-catalog.js` — 24 entries with
|
||||
`id`, `author`, `title`, `status`, `focus`, `wins[]`, `improve[]`, `extras`,
|
||||
- `skills-review/catalog.js` + `submitted-catalog.js` — 24 entries with `id`,
|
||||
`author`, `title`, `status`, `focus`, `wins[]`, `improve[]`, `extras`,
|
||||
`improved` (full markdown)
|
||||
|
||||
These are hand-written translations with deliberate tone. **Copy them. Never
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: motion
|
||||
description: Add or review animation on the ai-for-dummies site — transitions, state changes, view transitions. Use when any element moves, fades, or transforms, or when auditing existing motion for performance and reduced-motion support.
|
||||
description:
|
||||
Add or review animation on the ai-for-dummies site — transitions, state
|
||||
changes, view transitions. Use when any element moves, fades, or transforms,
|
||||
or when auditing existing motion for performance and reduced-motion support.
|
||||
---
|
||||
|
||||
# Motion
|
||||
@@ -22,10 +25,14 @@ If there is no answer, ship it static. That is a legitimate, common outcome.
|
||||
|
||||
```css
|
||||
.panel {
|
||||
transition: opacity 180ms cubic-bezier(.2,0,0,1),
|
||||
transform 180ms cubic-bezier(.2,0,0,1);
|
||||
transition:
|
||||
opacity 180ms cubic-bezier(0.2, 0, 0, 1),
|
||||
transform 180ms cubic-bezier(0.2, 0, 0, 1);
|
||||
}
|
||||
.panel[data-state='entering'] {
|
||||
opacity: 0;
|
||||
transform: translateY(6px);
|
||||
}
|
||||
.panel[data-state='entering'] { opacity: 0; transform: translateY(6px); }
|
||||
```
|
||||
|
||||
- **`transform` and `opacity` only.** Animating `width`/`height`/`top`/`left`
|
||||
@@ -39,14 +46,18 @@ If there is no answer, ship it static. That is a legitimate, common outcome.
|
||||
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* { animation-duration: .01ms !important; animation-iteration-count: 1 !important;
|
||||
transition-duration: .01ms !important; scroll-behavior: auto !important; }
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then **test it**: DevTools → Rendering → Emulate `prefers-reduced-motion:
|
||||
reduce`. The end state must still be correct and the UI still usable. Reduced,
|
||||
not broken.
|
||||
Then **test it**: DevTools → Rendering → Emulate
|
||||
`prefers-reduced-motion: reduce`. The end state must still be correct and the UI
|
||||
still usable. Reduced, not broken.
|
||||
|
||||
## Page transitions
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
name: verify-contract
|
||||
description: Evolve scripts/verify.mjs across the Astro migration without losing coverage. Use whenever a verify assertion fails because of a refactor, or when adding checks for new architecture.
|
||||
description:
|
||||
Evolve scripts/verify.mjs across the Astro migration without losing coverage.
|
||||
Use whenever a verify assertion fails because of a refactor, or when adding
|
||||
checks for new architecture.
|
||||
---
|
||||
|
||||
# The verification contract
|
||||
@@ -29,13 +32,13 @@ report. Nobody else may reduce coverage.
|
||||
|
||||
## Translating assertions
|
||||
|
||||
| Kind | Old | New |
|
||||
| --- | --- | --- |
|
||||
| Content presence | `html.includes('data-phase="plan"')` | same token, read from `dist/full-guide/index.html` |
|
||||
| Implementation detail | `js.includes('renderTree')` | assert the rendered output has the tree UI, not that a function is named that |
|
||||
| Asset version | `'app.js?v=20260904-vote-widget'` | assert the built HTML references a hashed asset |
|
||||
| Kind | Old | New |
|
||||
| --------------------- | ------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
| Content presence | `html.includes('data-phase="plan"')` | same token, read from `dist/full-guide/index.html` |
|
||||
| Implementation detail | `js.includes('renderTree')` | assert the rendered output has the tree UI, not that a function is named that |
|
||||
| Asset version | `'app.js?v=20260904-vote-widget'` | assert the built HTML references a hashed asset |
|
||||
|
||||
Implementation-detail assertions are the dangerous ones: they *look* deletable.
|
||||
Implementation-detail assertions are the dangerous ones: they _look_ deletable.
|
||||
They are pinning a feature. Replace with an output-level assertion of the same
|
||||
feature; never drop.
|
||||
|
||||
@@ -54,9 +57,9 @@ Commit the snapshots. They are the migration's regression net.
|
||||
|
||||
## Extend audit-ui.mjs
|
||||
|
||||
It rejects external `<script>`/`<link>` but **misses external URLs inside CSS** —
|
||||
which is exactly how the broken Google Fonts `@font-face` in `styles.css:1` got
|
||||
into a "dependency-free" site. Add:
|
||||
It rejects external `<script>`/`<link>` but **misses external URLs inside CSS**
|
||||
— which is exactly how the broken Google Fonts `@font-face` in `styles.css:1`
|
||||
got into a "dependency-free" site. Add:
|
||||
|
||||
```js
|
||||
if (/@import|src:\s*url\(['"]?https?:|url\(['"]?https?:/i.test(css))
|
||||
|
||||
@@ -24,7 +24,7 @@ const { label, columns = 3 } = Astro.props;
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--columns), 1fr);
|
||||
gap: 1px; /* hairline separators, drawn by the parent background */
|
||||
gap: 1px; /* hairline separators, drawn by the parent background */
|
||||
background: var(--line);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ const { label, columns = 3 } = Astro.props;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.grid { grid-template-columns: 1fr; }
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,25 +20,33 @@ const { items, initialId = items[0]?.id } = Astro.props;
|
||||
|
||||
<div class="island" data-initial={initialId}>
|
||||
<div class="tabs" role="tablist" aria-label="Sections">
|
||||
{items.map((item) => (
|
||||
<button
|
||||
role="tab"
|
||||
id={`tab-${item.id}`}
|
||||
aria-controls={`panel-${item.id}`}
|
||||
aria-selected={item.id === initialId}
|
||||
data-tab={item.id}
|
||||
>{item.label}</button>
|
||||
))}
|
||||
{
|
||||
items.map((item) => (
|
||||
<button
|
||||
role="tab"
|
||||
id={`tab-${item.id}`}
|
||||
aria-controls={`panel-${item.id}`}
|
||||
aria-selected={item.id === initialId}
|
||||
data-tab={item.id}
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
{items.map((item) => (
|
||||
<div
|
||||
role="tabpanel"
|
||||
id={`panel-${item.id}`}
|
||||
aria-labelledby={`tab-${item.id}`}
|
||||
data-panel={item.id}
|
||||
hidden={item.id !== initialId}
|
||||
>{item.body}</div>
|
||||
))}
|
||||
{
|
||||
items.map((item) => (
|
||||
<div
|
||||
role="tabpanel"
|
||||
id={`panel-${item.id}`}
|
||||
aria-labelledby={`tab-${item.id}`}
|
||||
data-panel={item.id}
|
||||
hidden={item.id !== initialId}
|
||||
>
|
||||
{item.body}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -68,7 +76,10 @@ const { items, initialId = items[0]?.id } = Astro.props;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.tabs { display: grid; gap: 8px; }
|
||||
.tabs {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 12px;
|
||||
@@ -78,7 +89,7 @@ const { items, initialId = items[0]?.id } = Astro.props;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
/* transform/opacity only — never animate layout properties */
|
||||
transition: background 180ms cubic-bezier(.2, 0, 0, 1);
|
||||
transition: background 180ms cubic-bezier(0.2, 0, 0, 1);
|
||||
}
|
||||
|
||||
button[aria-selected='true'] {
|
||||
@@ -92,6 +103,8 @@ const { items, initialId = items[0]?.id } = Astro.props;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
button { transition-duration: .01ms; }
|
||||
button {
|
||||
transition-duration: 0.01ms;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -40,7 +40,7 @@ const { eyebrow, title, body, href } = Astro.props;
|
||||
.eyebrow {
|
||||
color: var(--accent);
|
||||
font: var(--font-eyebrow);
|
||||
letter-spacing: .1em;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ const { eyebrow, title, body, href } = Astro.props;
|
||||
margin: 0;
|
||||
font-size: var(--step-5);
|
||||
line-height: 1.05;
|
||||
letter-spacing: -.06em; /* tight display tracking is a signature of this design */
|
||||
letter-spacing: -0.06em; /* tight display tracking is a signature of this design */
|
||||
}
|
||||
|
||||
p {
|
||||
@@ -69,6 +69,8 @@ const { eyebrow, title, body, href } = Astro.props;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.block { padding: 18px; }
|
||||
.block {
|
||||
padding: 18px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
{
|
||||
"extends": ["stylelint-config-standard"],
|
||||
"ignoreFiles": [
|
||||
"dist/**",
|
||||
"public/hands-on/**",
|
||||
"submitted-skills/**"
|
||||
],
|
||||
"ignoreFiles": ["dist/**", "public/hands-on/**", "submitted-skills/**"],
|
||||
"rules": {
|
||||
"custom-property-pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$",
|
||||
"declaration-property-value-disallowed-list": {
|
||||
|
||||
@@ -8,11 +8,11 @@ export default [
|
||||
{
|
||||
ignores: [
|
||||
'dist/**',
|
||||
'public/hands-on/**', // lab fixtures ship verbatim — linting them would
|
||||
// invite "fixes" that break the exercise
|
||||
'submitted-skills/**', // other people's work, reproduced as submitted
|
||||
'skill-reviews/**', // generated from skills-review/catalog.js
|
||||
'vote-service/**', // Go service, separate lifecycle
|
||||
'public/hands-on/**', // lab fixtures ship verbatim — linting them would
|
||||
// invite "fixes" that break the exercise
|
||||
'submitted-skills/**', // other people's work, reproduced as submitted
|
||||
'skill-reviews/**', // generated from skills-review/catalog.js
|
||||
'vote-service/**', // Go service, separate lifecycle
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # gate.sh compares assertion counts against origin/main
|
||||
fetch-depth: 0 # gate.sh compares assertion counts against origin/main
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
|
||||
@@ -28,25 +28,30 @@ const lang = 'en'; // TODO: wire to the language toggle decision (task 03)
|
||||
</section>
|
||||
|
||||
<GridGroup label="Chapter sections" columns={3}>
|
||||
{chapter.data.sections.map((section) => (
|
||||
<StaticBlock
|
||||
eyebrow={section.eyebrow[lang]}
|
||||
title={section.title[lang]}
|
||||
body={section.body[lang]}
|
||||
/>
|
||||
))}
|
||||
{
|
||||
chapter.data.sections.map((section) => (
|
||||
<StaticBlock
|
||||
eyebrow={section.eyebrow[lang]}
|
||||
title={section.title[lang]}
|
||||
body={section.body[lang]}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</GridGroup>
|
||||
</ChapterLayout>
|
||||
|
||||
<style>
|
||||
/* Page-level layout only. Anything reusable belongs in a component. */
|
||||
.hero { max-width: 780px; padding: clamp(75px, 12vh, 145px) 0 85px; }
|
||||
.hero {
|
||||
max-width: 780px;
|
||||
padding: clamp(75px, 12vh, 145px) 0 85px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 16px 0 24px;
|
||||
font-size: var(--step-display);
|
||||
line-height: .86;
|
||||
letter-spacing: -.08em;
|
||||
line-height: 0.86;
|
||||
letter-spacing: -0.08em;
|
||||
}
|
||||
|
||||
/* Georgia is a real system font and DOES render — unlike Manrope/DM Mono.
|
||||
@@ -57,5 +62,9 @@ const lang = 'en'; // TODO: wire to the language toggle decision (task 03)
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.lede { max-width: 570px; color: var(--muted); line-height: 1.65; }
|
||||
.lede {
|
||||
max-width: 570px;
|
||||
color: var(--muted);
|
||||
line-height: 1.65;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -39,7 +39,18 @@ const items = entries.map((entry) => ({
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
.hero { max-width: 780px; padding: clamp(75px, 12vh, 145px) 0 85px; }
|
||||
h1 { font-size: var(--step-display); line-height: .86; letter-spacing: -.08em; }
|
||||
h1 em { color: var(--blue); font-family: Georgia, serif; font-weight: 400; }
|
||||
.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>
|
||||
|
||||
Reference in New Issue
Block a user