wcygan.net Demo Migrations
Migrate one mounted article explanation at a time without changing what it teaches. Preserve the owning prose and causal invariant, then replace only the representation, lifecycle, and visual system needed to make that lesson article-native, inspectable, and accessible.
Load $wcygan-editorial-diagrams before acting. Load $agent-browser for
rendered verification and $better-ui or $better-typography when available
and relevant. Read AGENTS.md; it remains authoritative when this skill and the
repository differ.
Start From Evidence
Record
git status --short, the branch, and the current commit. Preserve unrelated work and define the files the migration may own.Read the MDX import plus the prose immediately before and after the demo.
Read the component and its complete backing model, engine, renderer, viewport, tests, and canonical
src/styles/app.csssection.Render the current route before editing. Capture the first frame, a decisive middle frame, final or reset behavior, both required viewports, and reduced motion. Treat this as behavioral evidence, not a visual target.
Run the audit helper from the repository root:
deno run --allow-read=. \ .agents/skills/wcygan-demo-migrations/scripts/audit-demo.ts \ src/components/ExampleDemo.tsxAdd
--jsonwhen a machine-readable report is useful. The helper identifies source ownership and common migration risks; it does not decide whether Canvas should be removed.
Before designing, write down:
- the one-sentence invariant;
- the input, actors, causal boundary, and state mutation;
- the exact first, decisive middle, and final states;
- the non-goals and copy/product names that must remain; and
- observable acceptance criteria and validation commands.
Stop and resolve the lesson if those states cannot be stated without a legend or several unrelated outcomes.
Choose The Representation Deliberately
Use the simplest representation that keeps the lesson crisp and testable:
- Prefer DOM/CSS for labels, records, ordered steps, lanes, counters, ledgers, state rows, and compact pipelines.
- Prefer SVG for fixed paths, connectors, and small topology.
- Retain Canvas for dense simulations, continuous geometry, particle fields, or scenes whose DOM/SVG equivalent would be materially worse.
- Preserve maps, ASCII, or static diagrams when their medium is the lesson.
Migration does not mean “replace every Canvas.” It means move semantics and domain state out of drawing code, adopt the editorial shell and tokens, remove unnecessary looping, and make important states inspectable. Read migration archetypes before choosing a Canvas-to-DOM rewrite or retaining a specialized renderer.
Build The New Causal Model First
Create or refine a deterministic model under src/demos/<name>/ before
polishing motion.
- Represent typed phases, domain values, actor states, payload positions, and the final summary.
- Derive snapshots from clamped normalized progress or explicit user input.
- Test the strict causal order and the decisive state boundary.
- Keep downstream state unchanged until the matching payload reaches its apply boundary.
- Autoplay a narrative sequence once, then use the replay policy required by the owning article. Default to a persistent final state until Replay.
- Do not use modulo revival. If an explicitly accepted shared countdown is part of the design, document that exception and test its settled interval.
- Make Replay reset the model immediately, without CSS interpolating from stale completion state.
Do not invent artificial waiting merely to make a progress display look busy. The visible phase, narration, active actor, payload, and domain state must all describe the same event.
Replace The Shell And Canonical CSS
Use a semantic <figure> with a quiet editorial header:
- Author
data-graphic-frame="bare|plate|workbench"by reader role, not renderer. - Give Bare figures no stage. Add exactly one
data-graphic-stage="flush|padded"to every Plate or Workbench. - Keep a stable
data-graphic-keyacross renderer changes and setdata-graphic-kindonly when the inferred kind would be wrong. - Never author route-generated
data-article-graphic,data-graphic-id,data-graphic-index, ordata-graphic-marker. Authordata-graphic-labelonly when the inferred accessible label needs a stable override. - Keep Replay and other controls outside the stage.
- Connect a stable title and concise static explanation with
aria-labelledbyandaria-describedby. - Hide redundant animated geometry from assistive technology and announce only the decisive final result.
- Keep controls native, visibly focused, and at least
44x44px.
Replace the old component CSS section in place. Remove superseded selectors; never append a stronger override island over retired styles. Scope one semantic token family to the component and start from:
#181817, #20201e, #292927, #3f3f3b, #696862, #f4f3ee,
#aaa9a2, #efeee9, and #20201d.
Use structure, labels, position, persistent state, and line treatment before
semantic color. Avoid dashboard cards, badge collections, legends required to
decode color, gradients, decorative infrastructure art, and ambient pulsing.
Keep the stage compact inside the 644px reading column.
Name every transitioned property. Animate transform and opacity for travel,
with restrained color or surface transitions for real state changes. Never use
transition: all, layout-property animation, or permanent will-change.
Read the migration contract for the detailed old-to-new mapping, reference implementations, lifecycle rules, and acceptance matrix.
Implement A Finite Lifecycle
For time-based playback:
- derive progress from
requestAnimationFrametimestamps, not frame counts; - stop frame work at completion;
- pause while
document.hiddenand resume without a time jump; - respond to live
prefers-reduced-motionchanges; - render the complete or representative conclusion immediately in reduced motion;
- clean up every frame, timer, observer, listener, and renderer instance; and
- restrict
will-changeto the currently moving element, returning it toautowhen settled or reduced.
The reduced-motion state must teach the same invariant. A random middle frame or a one-time mount check is not sufficient.
Validate In Layers
Run narrow checks first, then the repository gate:
deno task test src/demos/<name>/model.test.ts
deno task typecheck
deno task pre-commit
Run deno task build when MDX, routing, static diagrams, or prerender behavior
changes.
Use $agent-browser on the real article route at 1440x900 and 390x844.
Inspect:
- first, decisive middle, transport crossing, and persistent final states;
- Replay, keyboard focus, and the
44x44pxhit target; - normal → reduced → normal preference changes;
- zero page-level horizontal overflow and no browser warnings;
- the expected frame/key/label and authored stage count under
?inspect=graphics; and - computed token-to-text and text-to-border clearance, not screenshots alone.
If browser tooling cannot advance animation time or dispatch media-query changes reliably, record the limitation and use another supported rendered browser plus component tests. Never claim a state that was not observed.
Finish With Scope Proof
Review git status --short, git diff --check, the scoped diff, and deleted
renderer imports. Confirm that unrelated dirty files retain their baseline
fingerprints when overlap exists.
Report:
- owning route and invariant;
- representation and lifecycle decisions;
- files added, changed, and removed;
- first/middle/final and reduced-motion evidence;
- exact test/build/browser results;
- remaining uncertainty; and
- whether any commit, push, or deployment occurred.
Do not call the migration complete because the new stage looks polished. It is complete only when the article prose, model tests, visible causal state, accessibility text, reduced motion, and rendered desktop/mobile evidence all teach the same lesson.