openfreebooks-bookgenerator/.agents/skills/ofb/SKILL.md

189 lines
11 KiB
Markdown

---
name: ofb
description: >-
Develop Open Free Books (OFB): Zola static site, catalog/curriculum JSON,
HTML chapters, Solid.js, Mermaid catalog maps, Sass. Use when editing openfreebooks,
adding subjects/chapters/curriculum, catalog, math content, or deploying to
Cloudflare.
---
# Open Free Books (OFB)
Free, static, open-source textbooks. **Zola** + theme `openfreebooks` + **Solid.js** + **Bun/Vite**. Deploy: `public/` via Wrangler.
Read [spec.md](../../../spec.md) for product rules. Pair with [zola](../zola/SKILL.md) for template/Sass details.
## Architecture (mental model)
```text
data/catalog.json → subject list + global curriculum filter chips
data/{subject}-curriculum.json → strands, chapters, curriculums[], graph.edges
content/{subject}/… → Zola sections (chapter pages at /{subject}/{slug}/)
themes/…/partials/{subject}/ → chapter HTML bodies (not Markdown today)
frontend/ → catalog-app, Mermaid map view, site chrome
/catalog/ → browse UI (list + Mermaid map)
```
**Catalog is the subject index.** `/math/` redirects to `/catalog/?subject=math`. Do not revive a separate book index unless asked.
**Curriculum labels** appear only in catalog data (badges + filters), **never** in chapter page prose ([spec.md](../../../spec.md)).
## Commands
```bash
bun install
bun run dev # instant local serve (zola only)
bun run build:chapter math/measures-dispersion math/loci
bun run build:chapter math/*
bun run build:chapter # auto-detect changed chapters (interactive confirm)
bun run build:site # full rebuild (sync external data + all chapters + site)
bun run build # alias of build:site
bun test
```
## Non-negotiables
| Rule | Detail |
|------|--------|
| Styling | Sass + `class=""` in templates/JSX — **no Tailwind** |
| React | Functional **Solid** only; mount via `main.tsx` |
| Theme static | **Only** commit `themes/openfreebooks/static/js/bundle.js` (+ fonts if any). **Never** commit `public/` or stale HTML under theme `static/` |
| Tera | Never name a loaded JSON variable `math` (reserved). Use `math_catalog`, etc. |
| Chapter URLs | `/{subjectId}/{chapter-slug}/` (e.g. `/math/quadratic-equations/`) |
| Map | Mermaid DAG view (`?view=tree`) driven by `graph.edges` |
| Graph | `graph.edges` must be **acyclic**; builder throws on cycles |
| Chart axes (all widgets) | Every coordinate chart must render **x/y axis labels, positive-direction arrowheads, and visible graduations (ticks + scale labels)**. Apply this consistently across all subjects/widgets. |
| Accessibility | Avoid decorative characters that may render as emoji on mobile. Prefer broadly supported symbols/text so desktop and mobile stay visually consistent. |
| Demo responsiveness (all widgets) | Mobile-first: keep demo viewport short, reserve stage/canvas height up front to avoid CLS, and never let status/controls overlap or block the interactive area. Use collapsible/toggle controls on small screens when needed. |
## Common tasks → where to edit
| Task | Files |
|------|--------|
| New global curriculum chip (e.g. GCSE) | `data/catalog.json` → `curriculums[]`; `_catalog.scss` badge; `frontend/src/lib/catalog-badge.ts`; rebuild |
| New subject (sidebar) | `data/catalog.json` → `subjects[]`; `data/{id}-curriculum.json` (catalog template auto-loads by `id`) |
| New chapter (planned) | `{subject}-curriculum.json` strand entry; optional `graph.edges` |
| New chapter (live) | Above + `content/{subject}/{slug}/_index.md` + HTML partial + `chapter.html` wiring + `main.tsx` widget mount if interactive |
| Sitemap/feed coverage | `scripts/generate-site-metadata.ts`, `static/sitemap.xml`, `static/feed.xml`, `package.json` build script |
| Homepage subject card | `themes/.../templates/index.html` (not yet data-driven) |
| Browse / nav URL | `zola.toml` `[extra] browse_url` + `base.html` site-config + `main.tsx` fallback |
| Search UI / index | `base.html` (Pagefind assets + modal), `content/search.md`, `_search.scss`; chapter/site builds refresh Pagefind |
## Sitemap / feed auto-generation
- `bun run generate:site-metadata` generates:
- `static/sitemap.xml` (all user-facing built pages + live chapters)
- `static/feed.xml` (RSS 2.0 for pages + live chapters)
- URL timestamps come from `git log -1` on content-source paths. If a page/chapter has no commit yet, generator falls back to filesystem mtime.
- Build pipeline runs this automatically before `zola build`.
- Include only resolvable pages; planned chapters without live content are excluded.
## Search (Pagefind)
| Piece | Path |
|-------|------|
| Global modal + assets | `themes/.../templates/base.html` |
| Dedicated page | `/search/` — `content/search.md`, `templates/search.html` |
| Header trigger | `frontend/src/components/site-header.tsx` (`pagefind-modal-trigger`) |
| Nav link | `main.tsx` + `zola.toml` `search_url` |
| Theming | `themes/.../sass/_search.scss` (`--pf-*` → site tokens) |
- **Component UI** only (`pagefind-component-ui.js`), not legacy `pagefind-ui.js`.
- Index runs **after** `zola build` in build commands (`build:chapter`/`build:site`).
- v1 indexes **live chapter pages only** (`data-pagefind-body` on `templates/chapter.html`); home, about, catalog, credits, and other site pages are excluded.
Detailed checklists: [curriculum-data.md](curriculum-data.md). Human-facing guide: [CONTRIBUTING.md](../../../CONTRIBUTING.md).
## Catalog / map
| Piece | Path |
|-------|------|
| Shell UI (subject, filters, list/map toggle) | `frontend/src/components/catalog-app.tsx` |
| Shared chapter card (list + map markup) | `frontend/src/components/catalog-chapter-card.tsx` |
| Map viewer (Mermaid) | `frontend/src/components/catalog-mermaid-view.tsx` (lazy chunk) |
| Mermaid source builder | `frontend/src/lib/catalog-to-mermaid.ts` |
| Mermaid theme (light/dark hex colors) | `frontend/src/lib/catalog-mermaid-theme.ts` |
| Theme-mode helpers used by map | `frontend/src/lib/catalog-canvas-theme.ts` |
| Curriculum badge CSS class map | `frontend/src/lib/catalog-badge.ts` |
| Types | `data/catalog.types.ts` |
| Styles | `themes/openfreebooks/sass/_catalog.scss` |
- Embedded data: `#catalog-data` in `templates/catalog.html` (merged at build time)
- URL: `/catalog/?subject=math` (list), `&view=tree` (map), `&view=compare` (math curricula doc — HTML partial `partials/math-curricula-compare.html`)
**List view:** full-card link for live chapters; number + title row on wide screens.
**Map view:** `CatalogMermaidView` renders a Mermaid DAG from `graph.edges`. Only live chapter titles become links; planned chapters are non-clickable.
**Map layout:** generated in `catalog-to-mermaid.ts` with strand subgraphs + edges from curriculum DAG data.
**Mermaid quirks:** use `theme: "base"` + hex `themeVariables`; Mermaid applies inline fills, so cluster fills are controlled in Mermaid config, not plain Sass overrides.
Prerequisites: **only** `graph.edges` (required, global DAG, cross-strand OK). Longest-path levels. No implicit strand-list chains. List order ≠ map order.
## Chapter content (colocated folder)
Live chapters live under `content/{subject}/{slug}/`:
1. `data/{subject}-curriculum.json` — catalog metadata (`status`, `curriculums`, `graph.edges`)
2. **Subject shared** (when a subject has multiple chapters): `content/{subject}/subject.scss` → `/css/subjects/{subject}.css`; `content/{subject}/subject.ts` → `initSubject()` via `frontend/src/generated/subject-modules.ts`. Math HTML conventions: [math-chapter-patterns.md](math-chapter-patterns.md).
3. `_index.md` — front matter only (`template = "chapter.html"`, `[extra] subject`, `chapter_id`, `strand`)
4. `core.html` (required), optional `supplement.html`, `assets/`, `widgets/*.tsx`, optional `chapter.scss` (chapter-only styles — do not duplicate subject.scss)
5. `bun run sync:chapters` merges HTML into `_index.md`, copies assets to `static/chapters/…`, compiles subject + chapter CSS, generates `chapter-widgets.ts` and `subject-modules.ts`, writes `data/_generated/subjects-meta.json`
6. `templates/chapter.html` loads subject CSS then chapter CSS; `main.tsx` runs subject init then mounts widgets
**Checkpoints (all subjects):** use `book-question`, `book-question__prompt`, `book-question__solution` (styled in theme `_book.scss`). Never use `book-question__answer`.
**Charts (all subjects):** coordinate charts in chapter widgets must include axis labels, arrowheads, and graduation/tick scale by default. Do not ship unlabeled axes.
**Math checkpoints / dots:** follow [math-chapter-patterns.md](math-chapter-patterns.md); shared dot classes in `content/math/subject.scss`.
**Math equation layout (all subjects):** for chained calculations, **always prefer multi-line KaTeX** and **always align at the equal sign** using `aligned` (e.g. `\begin{aligned} ... &= ... \\ ... \end{aligned}`). Do not write long one-line chains; aligned multi-line steps are the default for readability and mobile layout.
**Dialog/modals in widgets:** when adding a `<dialog>` in chapter widgets, use the shared modal pattern and classes from `themes/openfreebooks/sass/_contributors.scss` (`contributor-dialog`, `contributor-dialog-panel`, `contributor-dialog-close`) as the base style, keep the dialog centered in modal mode, and only layer minimal widget-specific overrides on top.
**Widget layout standards (all subjects):**
- Keep the primary demo stage visible without long pre-scroll on mobile.
- Reserve stage space from first render (`min-height`/fixed aspect) so hydration or assets do not shift nearby content.
- Keep feedback UI (step badges, status, toolbars) outside the canvas interaction area, or anchored so they never occlude controls/content.
### Interactive demo technology choices
Interactive demos can use different technologies based on the learning goal and interaction type:
- **Vanilla JavaScript / Solid widget**: default for lightweight chapter interactions and custom canvas/SVG behavior.
- **JSXGraph**: geometric constructions (points, lines, labeled angles, right-angle markers, draggable Euclidean figures).
- **Plotly.js**: data-heavy charts, coordinate plots, and quick interactive graphing.
- **D3**: custom data visualizations where fine-grained control over rendering/animation is needed.
- **three.js**: 3D scenes, spatial intuition demos, and rotation/perspective interactions.
Choose the simplest tool that satisfies the pedagogy, performance, and accessibility needs of the chapter.
Reference: `content/math/quadratic-equations/`.
## Verify before PR
```bash
bun run build
```
- `public/catalog/index.html` includes your JSON changes in `#catalog-data`
- No stale `themes/openfreebooks/static/**/*.html` (delete if present — they override Zola)
- `public/main.css` contains new Sass selectors
- Catalog: subject appears, filters work, map loads without cycle error; map cards not clipped (check long titles e.g. permutations); live map titles link and show underline on hover
- Live chapter: breadcrumb → catalog, no curriculum mentions in body
## Planned / not implemented (do not assume)
- Defuddle “copy as Markdown” on non-chapter pages
- Data-driven homepage cards
- Single source for subject metadata (`catalog.json` + `*-curriculum.json`)
- Subject scaffold command (`scaffold:subject`)
See README **Contributor experience** task list for tracked fixes.
## Additional resources
- [curriculum-data.md](curriculum-data.md) — JSON schemas, examples, graph rules
- [zola/SKILL.md](../zola/SKILL.md) — Tera, Sass, Solid mount points
- [zola/zola-solid.md](../zola/zola-solid.md) — `main.tsx`, bundle, dialogs