CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this repo is
An interactive, self-contained HTML artifact teaching the mental model of how LLMs work. Plain HTML/CSS/vanilla JS — no build step, no server, no npm. Opens by double-clicking index.html.
The README.md is the user-facing description. The plan/spec lives at /Users/apple/.claude/plans/optimized-greeting-dusk.md — read it before making any non-trivial changes; it captures every design decision the user has approved.
Preview / “run”
Open /Users/apple/LLM learning/index.html in any modern browser. Everything works offline. There is no dev server, no test suite, no linter, no CI. Verification is manual: open the page, click through, verify the change.
If you need to vendor an external library (as was done with gpt-tokenizer), use curl from unpkg to download the UMD bundle into assets/ — do not add a build step.
Architectural baseline (locked)
The teaching spine of the artifact is fixed. Do not deviate without user approval:
- Tokens + position → 2. Attention → 3. Transformer blocks → 4. Inference loop → 5. Four lenses (weights / context / scaffolding / decoding) → 6. Training → 7. Eval → 8. Levers → 9. Diagnosis (capstone).
The canonical architecture is a vanilla decoder-only transformer (GPT family). Variants (MoE, GQA/MQA, encoder-decoder, multimodal, RAG, reasoning scaffolds) appear only as 🌿 Variant note callouts on the deep pages where they naturally attach.
Locked component grammar (do not invent new patterns)
Five reusable components are defined once in assets/styles.css and decorated by assets/shared.js. Every page must reuse them:
callout-analogy— 🔍 Think of it like… (analogy before the precise explanation)callout-break— ⚠️ Where this breaks… (when the analogy is imperfect)callout-invariant— 📐 What this teaches (and what it doesn’t) (on every interactive widget)callout-variant— 🌿 Variant note (deviations from the decoder-only baseline)lens-tag/lens-box— 🔭 Which lens? (tags concepts with one of the four lenses)
If a piece of content doesn’t fit one of these, it’s probably in the wrong place.
Widget system
Every interactive widget lives in assets/widgets.js inside the IIFE. Contract:
- Mount point:
<div data-widget="<name>"></div>in HTML. - Registry: the
REGISTRYmap near the bottom ofwidgets.jsmaps widget names → mount functions. - Each mount function must:
- Declare an invariant via
callout-invariant(what the widget teaches AND what it doesn’t). - Look obviously illustrative — never pretend to be live model behavior. Probabilities / attention weights / positions are canned.
- Follow the enhancement template:
makePresetRow(),makeToggleRow(),makeInspectPanel(),makeStatsRow()+updateStatsRow(),makeTieBack()(all defined at the top ofwidgets.js). - Tie-back footer uses `` placeholder →
deep/on the hub,""on deep pages.
- Declare an invariant via
- Cross-page widgets (e.g. tokenizer appears on hub and tokens.html) should accept config via
data-*attributes. - Use
var(--…)for all colors — never hardcoded hex. This ensures dark mode works.
Current widgets: tokenizer, inference-stepper, attention-hover, layer-stack, lens-sorter, training-stages, sampling, lora-rank, quantization, context-length, prefill-decode, rank-outputs, embed-sim, rope-rotation.
File layout
index.html — the hub (the whole mental model, ~30 min read, standalone)
deep/*.html — one deep page per teaching-spine topic
README.md — user-facing description
CLAUDE.md — this file
assets/
styles.css — design tokens + all component styles (~2000 lines)
widgets.js — all 14 widgets + helpers (~2500 lines)
shared.js — el() / svg() helpers; callout decoration
glossary.js — hover-tooltip auto-wrap for glossary terms
progress.js — reading progress bar + heading anchors + back-to-top
shortcuts.js — keyboard shortcuts (← →, ?, Esc) + help modal
theme.js — dark/light toggle with localStorage
nav.js — top-bar hamburger menu (all pages)
toc.js — "On this page" side index (scroll-synced)
search.js — heading search palette (Cmd-K / /)
search-index.js — hand-maintained manifest of all H2s
gpt-tokenizer.js — vendored cl100k_base UMD bundle (~2 MB, DO NOT EDIT)
Script-load order on every page (order matters — some depend on DOM already containing glossary terms, etc.):
<script src="assets/gpt-tokenizer.js"></script> <!-- only on pages with the tokenizer widget -->
<script src="assets/shared.js"></script>
<script src="assets/widgets.js"></script>
<script src="assets/glossary.js"></script>
<script src="assets/progress.js"></script>
<script src="assets/shortcuts.js"></script>
<script src="assets/theme.js"></script>
<script src="assets/nav.js"></script>
<script src="assets/search-index.js"></script>
<script src="assets/search.js"></script>
<script src="assets/toc.js"></script> <!-- only if multiple H2s -->
Deep pages prefix ../assets/. Glossary page skips glossary.js (would self-loop).
Design tokens (top of styles.css)
All spacing, font sizes, radii, shadows, and colors go through CSS variables declared in :root and overridden in html[data-theme="dark"]. Never add a hardcoded color — pick the closest token or propose a new one.
Key token prefixes: --space-N (4–64px), --fs-{xs,sm,md,base,lg,xl,2xl}, --radius-{sm,md,lg}, --shadow-{sm,md,lg}, --lens-{weights,context,scaffold,decoding}(-brd), plus the palette (--bg, --text, --accent, --analogy, --rule, etc.).
Utility classes for widgets: .w-row, .w-col, .w-caption, .w-panel, .w-stage-card, .w-prompt-display, .w-bar-row, .w-formula-out, .sorter-card, .rank-card. Prefer these over inline style: {…} in widgets.js — dynamic values (computed widths, state colors) are the only legitimate reason to use inline styles.
Teaching principles (treat as non-negotiable)
From durable user guidance:
- The hub is the product, not a preface.
index.htmlmust stand alone; deep pages are optional bonus. Don’t make deep pages load-bearing for the mental model. - Decoder-only baseline. Variants are footnotes, never part of the main line.
- Four lenses are an active diagnostic frame, not decorative labels. Tag concepts with them; use them to debug failures on the diagnosis page.
- Runtime state (KV cache, activations) is NOT a fifth lens. Keep it distinct from weights/context/scaffolding/decoding whenever the distinction could blur.
- Diagnosis is the acceptance test. If a real failure mode can’t be reasoned about using this artifact, the artifact — not the diagnosis page — needs work.
- Analogy first, precision second, “where it breaks” third when a concept gets mechanical.
- Every interactive must declare its invariant. Obvious-illustrative > pretend-realistic.
- Clarity, mechanism, transfer > polish, cleverness, extra detail. Cut when in doubt.
Common change patterns
- Adding a new glossary term: update
TERMSinassets/glossary.jsand add an entry todeep/glossary.html. Auto-wrap picks up the first 1–2 occurrences per page. - Adding a new heading-search entry: update
assets/search-index.jsby hand (the index is static; there’s no scanner). - Changing a widget: edit
assets/widgets.js. If the change affects multiple widgets’ shared look, change the utility class instyles.css, not the widget code. Re-verify the widget appears correctly on every page that mounts it. - Adding a page: add it to
deep/, include the same<script>block at the bottom, add an entry tonav.jsPAGESlist, and add its H2s tosearch-index.js. - Adjusting visuals: change the token in
:root(and the dark-mode override). Widespread visual changes should almost never require editing multiple selectors.
Non-goals (user has rejected these)
- No frameworks (React, Vue, htmx, Tailwind, anything with a build step).
- No mock/stub tokenizers — use the real
gpt-tokenizer(cl100k_base). - No CDN loading at runtime — vendor bundles locally.
- No body-text search (heading search only).
- No click-to-pin tooltips in this generation (hover only).
- No print stylesheet.
- No real-model inference (widgets stay illustrative).
Do not skip
- Ask before pre-deciding. User has explicitly requested this twice — surface every non-trivial decision (heading wording, example choices, palette shifts, scope inclusions, tone) as an AskUserQuestion rather than making the call silently. “Deferred decisions” sections in plans are a red flag.
- Stepwise approval. User prefers to review each step before moving on. Complete one step, mark the task, report, then continue.
- Keep the component grammar locked. Do not introduce new callout types, new lens variants, new widget template patterns without discussion.
When in doubt, read the plan file at /Users/apple/.claude/plans/optimized-greeting-dusk.md.