new-lesson
Scaffold a new bilingual (en/es) lesson or topic for this Lessons site — creates the MDX files in the correct locale folders with valid frontmatter, wires in the reusable interactive components, and r
SKILL.md.claude/skills/new-lesson/SKILL.md
GitHubAuthoring a Lesson or Topic
This project teaches the subject defined in TOPIC.md across multiple
slug-based lesson pages, in English and Spanish. Its mission: take a
learner with no prior knowledge of the subject to complete expert.
Read TOPIC.md first — it is the source of truth for subject, mission,
scope, and voice. Follow these steps to add content consistently.
Mission fit (REQUIRED before scaffolding)
Every topic sits on the zero-to-expert ladder that TOPIC.md defines:
- Assume no background in the subject at the
beginnertier — define every domain term on first use; never lean on jargon the lesson hasn't taught. - Set the
difficultyfrontmatter field (beginner|intermediate|advanced|expert) — required, same value in the en/es twins. It renders as a badge on the catalog graph. - Wire
dependenciesso every concept a non-beginnercourse assumes is taught by one of its prerequisites already on the platform. expertmeans expert — go all the way to quantitative, real-practitioner depth; don't stop at "intro".
0. Research first — then write with voice, animation, exercises
Before scaffolding, use the research-topic skill to build an accurate,
sourced brief and outline (concepts, definitions, misconceptions, analogies,
animation opportunities). Never write non-trivial lesson prose from memory.
As you write, apply the companion skills:
lesson-copy— voice/tone: witty, lightly sarcastic, analogy-driven copy that is genuinely fun to study, without sacrificing accuracy.lesson-animations— include at least one animation that teaches the core idea (a process/relationship shown moving, not decoration).exercise-components— include at least one exercise (multi-answerMCQ,Quiz, or aconcept → definitionmatcher), reusing/extending the component library rather than inlining one-off JSX.
Interaction density (REQUIRED — not just at the end)
Lessons must be interactive throughout, not a text wall with one quiz at the
bottom. Retention comes from testing right after learning. Aim for at least
one exercise per ## section, ideally two (a prequestion to open, a check to
close). Err on the side of more exercises.
- Open each
##with a prequestion (MCQ pretest): make the learner guess before reading. Pretesting boosts encoding even when the guess is wrong, and pretests never count toward aQuizscore. Seeexercise-components. - Interleave checks: after each explanation, add an exercise or animation that checks or shows what was just taught. Don't pool questions at the end.
- Rotate types every time — never the same format twice running: single
MCQ, multi-answerMCQ(allowMultiple),MCQ pretest, concept→definitionMatchConcepts,Categorize(sort into buckets),FillBlank(pick-one-of-three cloze), scoredQuiz,Revealself-checks. - For must-remember terms reach for
FillBlank— each blank is the right term flanked by two plausible distractors, so the learner discriminates rather than just recognises. Keep options equal-length; the answer must never be the longest. (NoStepThrough— it was removed; use a numbered Markdown list.) - Spaced recall: write some questions that reference earlier sections so the user recalls prior material; the end quiz mixes the whole lesson.
- Animations per concept: an animation at each major
transformation/process/relationship, beside its explanation — not one hero
animation per lesson. See
lesson-animations. - Close with a chunking recap: end every lesson with a learner-built
MindMap(Mermaid "big picture" linking the section concepts) plus a mixedQuiz— not a passive bullet-list summary. Seelesson-animations.
Apply the same density to the Spanish twin (parity includes exercises and animations, with Spanish label props).
Final exam (REQUIRED — every course ends with one)
Every course (topic) MUST conclude with a final exam as its LAST lesson. This is mandatory, not optional — Claude: whenever you finish a topic, or add the last teaching lesson to one, that topic is not "done" until a final-exam lesson sits at the end of it. When building a brand-new topic, plan the exam as the final lesson from the start.
- Slug & position:
final-examin both locales (src/content/lessons/<lang>/<topic>/final-exam.mdx), withorderset to one higher than every other lesson so it sorts last. Title it as an exam (e.g. "Final Exam: …" / "Examen final: …"). - It must be genuinely hard. The point is to verify the learner truly understood everything — not a victory-lap recap. Use scenario-based, multi-step reasoning with plausible distractors and subtle traps drawn from the misconceptions each lesson warned about.
- Use the
FinalExamisland — NOTQuizand NOT themed recap sections. The final exam is a graded, irreversible run: one MCQ at a time, the learner submits, the answer locks for good (no Back, no Try again, no Restart), the result reveals instantly, and the pass/fail score appears only after the last question. Don't rebuild the old elaborate flow (pretest →Callout→Categorize/FillBlank/MatchConceptsper theme → a separate scoredQuiz); that is exactly the over-complex structureFinalExamreplaces. Present the questions directly. - Keep the page flat and short: a one-paragraph intro → a
Callout variant="warning""How this exam works" box (states: answers are final, no retries, score shown at the end, pass mark70%) → the<FinalExam client:visible passPercent={70} questions={[…]} />island →## Course RecapMindMap→## Key TakeawaysCallout variant="success". - Question pool: 20–30 fresh MCQs, covering ALL prior lessons roughly
proportionally. Mostly single-answer; include 2–4 multi-answer questions
(
allowMultiple: true, multiple options markedcorrect: true). Nopretesthere — an exam tests, it doesn't pre-teach. - VARIETY IS MANDATORY — repetition is the #1 failure mode. Vary every axis:
question type (numeric/calculation with the arithmetic shown in the
explanation, scenario/application, "which statement is TRUE", spot-the-trap,
definition, side-by-side comparison, cause→effect, ranking-as-single-answer),
stem wording (never reuse the same sentence frame twice in a row; don't open
five questions with "Which of the following…"), and difficulty. Every
question carries a concise
explanationteaching the why. - Same rules as any lesson: keep numbers/LaTeX in MDX prose and tables, never baked into island option/question text (put numeric scenarios in a table right above the island and phrase the question conceptually); ship a full es-ES twin with Spanish label props.
1. Decide: new topic or new lesson in an existing topic?
- List existing topics: look under
src/content/topics/en/. - A topic needs
src/content/topics/en/<topic>.mdx+ theestwin. - A lesson goes in
src/content/lessons/en/<topic>/<lesson>.mdx+ theestwin.
Slugs must be kebab-case and SEO-friendly (e.g. getting-started).
2. Topic frontmatter (src/content/topics/<lang>/<topic>.mdx)
---
title: Getting Started
description: A one-line summary used on cards and as the default meta description.
tagline: From the basics to genuine mastery. # optional, shown under the hero
icon: 🚀 # emoji chip
order: 1 # sort order in the catalog
accent: brand # 'brand' (blue) | 'accent' (sky)
---
Short intro prose for the topic landing page (rendered in `.prose-lesson`).
The Spanish file is identical structure under .../es/<topic>.mdx, translated.
3. Lesson frontmatter (src/content/lessons/<lang>/<topic>/<lesson>.mdx)
---
title: Your First Lesson
description: SEO meta description (≤155 chars), specific and compelling.
topic: "en/getting-started" # MUST be the owning topic's id: "<lang>/<topic>"
order: 1 # position within the topic
minutes: 6 # estimated time
updated: 2026-05-30 # ISO date
---
- For the Spanish twin, set
topic: "es/getting-started". - Use
##and###headings — they generate the on-page Table of Contents.
4. Use the reusable components (import via the @ alias)
At the top of the MDX body:
import { Callout, MCQ, Quiz, FinalExam, Reveal,
MatchConcepts, Categorize, FillBlank, MindMap } from '@/components/react';
Exercise islands: MCQ (add pretest for prequestions, allowMultiple for
multi-answer), Quiz, MatchConcepts, Categorize, FillBlank. FinalExam is
the graded, lock-on-submit island reserved for a course's final-exam lesson (see
above). Recap visual: MindMap. See exercise-components and lesson-animations
for each.
Interactive islands need client:visible; Callout is presentational (no directive).
These are the only islands the template ships in @/components/react: MCQ,
Quiz, FinalExam, Reveal, Callout, FillBlank, MatchConcepts,
Categorize, MindMap, CourseGraph, LessonComplete, CourseComplete,
ProgressTransfer. No subject-specific chart or animation islands exist —
those you build for your topic (see lesson-animations and exercise-components
for how to author one the right way).
<Callout variant="tip" title="Key idea">State the one thing the learner must remember.</Callout>
<MCQ
client:visible
question="What is the core point of this section?"
options={[
{ text: 'The correct, precise answer', correct: true },
{ text: 'A plausible near-miss' },
{ text: 'A common misconception' },
]}
explanation="Explain why the right answer holds and the distractors don't."
/>
<Quiz client:visible questions={[ /* array of MCQ prop objects */ ]} />
<FillBlank
client:visible
text="The {{correct-term|sibling-term|another-sibling}} is what makes this idea {{work|fail|repeat}}."
/>
<Reveal client:visible><Callout>Animates in on scroll.</Callout></Reveal>
Spanish lessons must pass Spanish label props so the UI is fully localized, e.g.
<MCQ ... checkLabel="Comprobar" retryLabel="Reintentar" />,
<Quiz ... questionLabel="Pregunta" ofLabel="de" scoreLabel="Tu puntuación" restartLabel="Reiniciar" nextLabel="Siguiente" backLabel="Atrás" />,
<FillBlank ... checkLabel="Comprobar" retryLabel="Reintentar" instructions="Elige la opción correcta para cada hueco y comprueba." />.
The FinalExam island needs its own Spanish props:
<FinalExam ... questionLabel="Pregunta" ofLabel="de" submitLabel="Enviar respuesta" lockWarningLabel="Atención: una vez que envíes, esta respuesta es definitiva. No podrás cambiarla." selectHintLabel="Selecciona una respuesta para continuar." nextLabel="Siguiente pregunta" seeResultsLabel="Ver resultados" correctLabel="Correcto" incorrectLabel="Incorrecto" correctAnswerLabel="Respuesta correcta" explanationLabel="Explicación" completeTitleLabel="Examen completado" scoreLabel="Has acertado" passLabel="Aprobado" passMessage="Has superado el listón: buen trabajo." failLabel="No superado" failMessage="Esta vez por debajo del aprobado: repasa las lecciones y vuelve a intentarlo." reviewLabel="Tus respuestas" />.
See DESIGN.md for the full prop reference of every component.
5. Always create BOTH locales
Every English file needs a Spanish twin at the mirrored es path. This keeps hreflang,
the sitemap, and the header language switcher correct. If a translation is genuinely
unavailable, the alternates helper falls back to the locale home — but prefer real twins.
6. Verify
bun run check # check:latex + astro check + tsc — must be 0 errors
bun run check:latex # standalone: renders every $...$/$$...$$ through KaTeX, fails on parse errors
bun run build # routes for the new pages should appear
LaTeX gotcha — \$ inside a $...$ span breaks rendering. rehype-katex
renders bad math as silent red error text (build stays green), so it's easy to
miss. The classic trap: writing a currency amount inside inline math, e.g.
$0.08 \times 1000 = \$80$. The math-delimiter scanner closes the span at that
$, leaving a stray \ → KaTeX parse error. Fix: keep currency OUT of the
math span — $0.08 \times 1000$, i.e. \$80. bun run check:latex (also run
inside bun run check and pre-commit) catches this class of bug across en+es.
Routes appear automatically (no route file edits needed) because pages use
getStaticPaths over the content collections.
7. Regenerate OG images
bun run og:build # build, then Playwright screenshots new /og cards into public/og/
(bun run pre-commit also does this; run it before committing, then git add public/og.)
Conventions recap
@import alias only — never relative../.- Tailwind design tokens only (no raw hex) — see
DESIGN.md. - Keep content accurate, concise, and genuinely interactive — at least one exercise per lesson.