Skills/guided-demo/SKILL.md
A pattern for adding self-narrating (typed out) guided walkthrough to any HTML/web application. Use this skill whenever the user wants to add a guided demo, auto-narration, typewriter walkthrough, self-presenting mode, automated tour, auto-play demo, self-running presentation, presentation mode, or step-by-step tour to an HTML page, web app, prototype, slide deck, dashboard, or data story.
npx skillsauth add sammcj/agentic-coding guided-demoInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Add a self-narrating walkthrough to any HTML/web application. A declarative step array drives a setTimeout loop that toggles CSS classes on DOM elements and writes text character-by-character into a fixed panel. The engine overlays the existing page without modifying its code. When the demo stops, all state resets.
About 100 lines of JS and 30 lines of CSS for the core. Interstitials, speed controls, keyboard shortcuts, and progress bar are optional layering.
Ask the user these questions (skip any already answered in conversation):
querySelector must run after render.Read references/implementation.md for all code snippets (vanilla JS / static HTML). If the target application uses React with React Router, also read references/react-integration.md for React-specific patterns covering stale closures, route navigation timing, and component architecture. For other SPA frameworks (Vue, Svelte, etc.), the same concepts apply (poll for elements after route change, store timer-relevant state outside the reactivity system) but the implementation details differ.
The four required pieces are:
outline (not border) with animated glow, no layout shiftsetTimeout, narration text set via textContent (never set user-authored narration via innerHTML), blinking cursor via CSS. The innerHTML = '' clearing before each tick is intentional and safe (no user content involved)playStep(idx) that switches sections, runs actions, highlights, types, and auto-advancesThe single source of truth. Each step is a plain object:
const DEMO_SCRIPT = [
{ section: 0, target: '.card', text: "Narration text." },
{ section: 1, target: null, text: "Transition.", transition: true },
{ section: 1, target: '#el', text: "Detail.", action: 'open', actionTarget: 'panel-id' },
];
Properties: section (which view), target (CSS selector or null), text (narrator copy), transition (show interstitial), action/actionTarget (trigger UI change), delay (optional per-step pause override in ms, defaults to PAUSE_MS).
| Constant | Default | When to adjust | | --- | --- | --- | | TYPE_SPEED | 5ms/char | 3ms for long text, 15ms for dramatic short statements | | PAUSE_MS | 3000ms | 4-5s if audience reads rather than listens | | Speed range | 0.5x-2x | Both constants divided by multiplier |
The typewriter effect means each word lands individually, so writing style matters:
Gate all keyboard capture behind an isActive flag so it does not interfere with normal page interaction. Space = play/pause, arrows = step, M = toggle TTS narration, Escape = exit and reset.
Browser-native TTS using the speechSynthesis Web Speech API. Reads each step's narration aloud alongside the typewriter effect. Include in all guided demos but must be off by default - never start speaking without explicit user action. The user toggles TTS via a speaker icon in the control bar (dimmed when off, full opacity when on).
Key requirements:
speechSynthesis.addEventListener('voiceschanged', ...) and cache the selected voice. Implement a preference cascade: filter speechSynthesis.getVoices() by predicate functions in priority order, returning the first match. Adjust the locale cascade to suit the project's target audience (e.g. en-AU, en-GB, en-US).speakText() at the start of every step, before both the typewriter branch (auto-play) and the instant-text branch (paused/manual stepping). One call site, not two. Set utterance.rate to track the demo's speed setting.speechSynthesis.cancel() must be called in: clearAllTimers(), stopDemo(), component unmount cleanup, and inside the mute toggle when muting. speakText() itself should cancel before speaking so stepping to a new step cuts off the previous utterance. Guard every speechSynthesis call with if ('speechSynthesis' in window) for SSR/test environments.isActive flag and skip when focus is in form inputs, same as existing keyboard controls.A subtle progress bar that fills left-to-right during the pause after the typewriter text finishes, showing how much time remains before auto-advancing. Gives the viewer a sense of pacing without being distracting. Include in all guided demos.
Key design decisions:
@keyframes animation with animation-duration set dynamically from the actual pause duration. No setInterval, no requestAnimationFrame. The browser handles smooth rendering.clearAllTimers(). Every action that interrupts the current step (manual step, pause, stop) calls clearAllTimers() first. Resetting the countdown there means you never need to clear it elsewhere.onDone callback only starts the countdown when stepIdx < script.length - 1. There is nothing to count down to on the final step.rgba(255, 255, 255, 0.06) reads as a subtle track when empty. Fill colour should be the application's accent colour at 0.6-0.8 opacity. Use linear timing, not ease - the viewer reads it as a countdown and easing makes the remaining time harder to judge.Full-screen overlay with cycling status messages between sections. Simulates processing time. Define messages per section in a 2D array. Fade each message, then dismiss overlay via callback.
String-matched in executeStep(). Actions run before highlighting because elements inside collapsed panels can't be found by querySelector until the panel is open. Adding a new action is one if block. Keep it simple. Common patterns beyond expand/collapse: expandOne (open one panel, close all siblings - accordion style), call (trigger a named function like requestBriefing()), addClass/removeClass (toggle a CSS class on document.body for global state changes).
These are the failure points that come up repeatedly:
outline not border for highlighting. Outline does not affect box model..css-1a2b3c) break between builds. Use data-* attributes or IDs.scrollIntoView({ block: 'center' }) conflicts with fixed headers/panels. Set scroll-padding-bottom on the scroll container to account for the narrator panel height.e.target.tagName and bail out for INPUT, TEXTAREA, SELECT.<script type="module">, all demo functions called from onclick must be on window.*.@media print.role="status" and aria-live="polite". Highlight outlines must meet contrast requirements.speechSynthesis.cancel() is missing from any cleanup path (stop, step, mute, unmount), the previous utterance plays over the new one. Every function that clears timers must also cancel speech. Guard all speechSynthesis calls with if ('speechSynthesis' in window).speechSynthesis.getVoices() returns an empty array on first call in some browsers. Always listen for the voiceschanged event and cache the result.Works for: prototypes, PoCs, HTML slide decks, data storytelling dashboards, product demos, workshop facilitation, investor pitches, onboarding walkthroughs.
Does not replace: user testing tools, screen recorders, production onboarding tours (use a tour library with persistence and analytics for those).
For framework apps, mount the demo engine as a conditional overlay component and pass the script array as a prop. For single-file demos, inline everything for offline/USB-stick distribution.
development
Use when answering questions from this machine-learning knowledge base. Triggers: questions about transformers, attention cost and efficiency, and long-context scaling; 'what do we know about attention', 'check the ML wiki'. Read-only querying of compiled knowledge; to add, update, supersede, lint, or audit, use the llm-wiki skill instead.
development
Use when building or maintaining a self-contained personal knowledge base (an LLM wiki) as plain markdown, optionally opened as an Obsidian vault. Triggers: ingesting sources into a wiki, querying wiki knowledge, linting wiki health, auditing article claims against their sources, superseding stale knowledge, 'add to wiki', or any mention of 'LLM wiki' or 'Karpathy wiki'.
tools
Provides guidance and tools for hardware design. Activate when using KiCAD, looking up electronic parts or designing PCBs.
testing
Grilling session that challenges your plan against the existing domain model, sharpens terminology, and updates documentation (CONTEXT.md, ADRs) inline as decisions crystallise.