A fully functional, cross-platform app simulator built with vanilla HTML, CSS, and JavaScript. Designed as a prototyping and demonstration tool for the Continua Social AI platform, supporting guided scenario walkthroughs, dark mode, and responsive desktop/mobile layouts.
Continua is a high-fidelity interactive prototype that behaves like a real app. Users can navigate between screens, send messages, create group chats, and interact with AI agent conversations — all rendered client-side with no backend.
The app has 3 tabs (Home, Messages, Settings) and 19 detail screens including agent chat, group chat, world chat, direct messages, call UIs, and settings sub-screens. The Home tab shows dynamic action cards — weather, flight itinerary, calendar events, email summary, and news — that appear based on the active scenario.
On top of the fully functional app, a Lesson System provides guided scenario walkthroughs (onboarding, agent setup, group chat creation) with step-by-step tracking, auto-execution, and replay-based navigation.
experiments/trefoil-demo.html is a standalone exploration tool: an SVG-based emotion state visualizer with arousal, valence, dominance, and intensity sliders. It's separate from the main app.
The system is organized into three layers:
┌─────────────────────────────────┐
│ Lesson Layer │ LessonRunner, step list UI,
│ (guided walkthroughs) │ completion detection, replay
├─────────────────────────────────┤
│ Flow Engine │ FLOWS registry, FlowEngine,
│ (conversation scripts) │ declarative step definitions
├─────────────────────────────────┤
│ App Core │ Navigation, APP_STATE, UI
│ (always functional) │ components, screen management
└─────────────────────────────────┘
App Core — The app works like a real messaging app at all times. Navigation (switchTab, openScreen, closeScreen), state management (APP_STATE), and all UI components function independently of lessons or flows.
Flow Engine — Conversations are declarative definitions in a FLOWS registry. Each flow specifies an init state and a series of steps with onResponse handlers. FlowEngine.start(), .handleInput(), .reset(), and .jumpToStep() manage the lifecycle. Adding a new conversation flow means adding one entry to FLOWS.
Lesson System — A LESSONS registry defines guided scenarios with labeled steps. Each step has an execute() function (for auto-play) and a detect() function (for completion polling). LessonRunner manages the active lesson, renders a step list in the debug panel, and supports click-to-jump replay navigation.
├── index.html Main application (HTML + inline JS, all screens)
├── styles.css App component styles (references token CSS vars)
├── package.json npm scripts: build:tokens, build:icons, add-icon
├── style-dictionary.config.js Token build config (Style Dictionary 4)
├── assets/ SVG/PNG icons and avatars (brand marks, avatars, ic_* icons)
├── design-system/
│ ├── tokens/ DTCG token source files — edit these
│ │ ├── primitive.color.json
│ │ ├── semantic.color.json
│ │ ├── semantic.color.dark.json
│ │ ├── semantic.typography.json
│ │ ├── semantic.spacing.json
│ │ ├── semantic.radius.json
│ │ ├── semantic.motion.json
│ │ ├── component.json
│ │ └── component.dark.json
│ ├── tokens.css GENERATED — do not edit
│ ├── tokens-dark.css GENERATED — do not edit
│ ├── REFERENCE.md GENERATED — auto token reference for LLM context
│ ├── BRAND.md Hand-authored brand voice & design principles
│ ├── components.html Living component library (inspection + audit)
│ └── archive/ Legacy pre-token-system files
├── scripts/
│ ├── inline-icons.js Build step: replaces icon spans with inline SVGs
│ ├── add-icon.js Helper: normalises a new SVG and prints ICON_MAP entry
│ └── svg-inject.js Runtime SVG injector (legacy, not currently wired)
└── experiments/
├── trefoil-demo.html Emotion state visualization (arousal/valence/dominance)
└── character-creator.html Character/persona creator tool
- Mobile: 390×844px phone frame with native iOS chrome (status bar, home indicator, tab bar)
- Desktop: 800px content model, full-width frosted header, centered navigation, modal sign-in
- Instant transitions on desktop, slide animations on mobile
- Toggle between layouts via the debug panel
All colors, spacing, radii, typography, and motion values are CSS custom properties from a W3C DTCG–compliant token system compiled by Style Dictionary. No hardcoded values in component styles.
Three-tier hierarchy:
| Tier | Purpose | Files |
|---|---|---|
| Tier 1 — Primitives | Raw palette values, never used directly in components | primitive.color.json |
| Tier 2 — Semantic | Intent-based tokens (text, bg, border, brand, state) | semantic.*.json |
| Tier 3 — Component | Per-component tokens with dark mode overrides | component.json, component.dark.json |
CSS variable conventions:
| Prefix | Example | Purpose |
|---|---|---|
--color-text-* |
--color-text-primary |
Text hierarchy |
--color-brand-* |
--color-brand-primary |
Brand purple |
--color-bg-* |
--color-bg-surface |
Surface hierarchy |
--color-border-* |
--color-border-default |
Border colors |
--component-* |
--component-bubble-user-background |
Per-component tokens |
Build:
npm run build:tokens # token JSON → tokens.css + tokens-dark.css + REFERENCE.md
npm run build:icons # icon spans → inline SVGs in index.html and components.html
npm run build # both, in orderDark mode token overrides are defined in semantic.color.dark.json and component.dark.json. The build generates tokens-dark.css which applies all overrides under body.dark-mode. No manual body.dark-mode blocks in styles.css — all dark mode values live in token files.
Toggle in the debug panel; persisted to localStorage.
All icons are inline SVGs injected at build time by scripts/inline-icons.js. The SVG files in /assets/ use fill="var(--fill-0, #fallback)" — a CSS variable system that responds to the design token cascade automatically, including dark mode.
Icon CSS variables:
| Variable | Light | Dark | Purpose |
|---|---|---|---|
--fill-0 |
var(--tone-purple-10) dark indigo |
var(--tone-purple-80) light purple |
Icon primary color |
--fill-1 |
var(--tone-purple-60) accent purple |
var(--tone-purple-60) |
Icon accent (send arrow, agent bubble) |
Context overrides:
.action-check-24 { --fill-0: #fff; }— white checkmark on colored button.flight-widget { --fill-0: var(--widget-flight-text); }— blue flight icons
Adding a new icon:
- Export SVG from Figma → drop in
/assets/ npm run add-icon assets/ic-my-icon.svg— normalises fill colors, prints ICON_MAP entry- Add the printed entry to
scripts/inline-icons.js npm run build:icons
Scoping rule: All hover effects are double-scoped: @media (hover: hover) AND .layout-desktop. Hover states never appear on the mobile prototype.
Asymmetric timing: Hover fades in at 100ms, fades out at 50ms. The base state transition governs exit; the :hover state transition governs entry.
.action-card { transition: filter 0.05s ease, background 0.05s ease; }
.layout-desktop .action-card:hover { transition: filter 0.1s ease, background 0.1s ease; }Four built-in scenarios accessible from the debug panel:
| # | Scenario | What it covers |
|---|---|---|
| 1 | Login | Sign-in modal dismissal |
| 2 | Agent Setup | Open agent chat, name agent, set permissions |
| 3 | Group Chat | Create chat, add people, submit, open conversation |
| 4 | Next Day | Browse home cards, check messages |
LessonRunner architecture notes:
-
Race condition pattern:
_startDetectionruns arequestAnimationFrameloop pollingstep.detect().executeCurrentStepalso pollsdetect()via asetTimeoutchain. Fix: alwayscancelAnimationFrame(self._rafId)before executing a step manually, and restart RAF only after execution completes. -
_onStepClickbehavior: Clicking any step always callsexecuteCurrentStep()(the current pending step). Do not callgoToStepor_replayTofrom click handlers — replay from step 0 causes intermediateexecute()calls to fire visible side effects. -
Step
execute()should be side-effect-free for replay: Steps that open overlays must not do so during_replayTo. The group chat step selects contacts viatogglePerson()without callingtogglePeoplePicker().
design-system/components.html is a living inspection and audit library showing every UI component with:
- All states (default, hover, active, disabled, error, loading)
- Token inspector — click any component to see which tokens it uses
- Mobile (390px, 33% width) and desktop (67% width) previews side by side, both flexible
- Token audit (components using unregistered values)
The debug panel (gear icon, bottom-right) provides:
- Scenario launcher — Start any lesson with one click
- Step list — Interactive checklist with completion tracking
- Layout toggle — Switch between phone and desktop views
- Theme toggle — Light/dark mode
- Hierarchy tree — DOM structure inspector
- Layout debug — Colored overlay showing component boundaries with hover inspection
- Reset — Full state reset
Layout debug hover tool:
- Hover over any element inside the phone frame to see: component name, color tokens by property category (
bg,text,border), and current app state (dark mode, desktop, scenario, tab, screen). - Double-click copies a structured plain-text report to clipboard — identity, state, tokens, layout, typography — ready to paste into an agent conversation.
Dark mode layout debug: The overlay uses high-contrast colors distinct from the dark mode palette so region boundaries are legible regardless of theme.
- Dependencies: Style Dictionary (dev dep, token build only). App itself has zero runtime dependencies.
- Local preview: Serve with Live Server or any static server. Open
index.html. Runnpm run buildif token or icon files changed. - GitHub Pages: Deployed from the
masterbranch root. - State management:
APP_STATEtracks scenario, flow phase, and dynamic content.resetScenarioState()performs full DOM + JS cleanup on scenario switch. - Desktop overrides: CSS organized by component section (
/* ── Desktop: Site Header ── */) for maintainability. - Agent naming: Agent names are never hardcoded in component markup. Always rendered from
APP_STATEvia[data-agent-name]attributes.
This project is built with Cursor and Claude Code. Custom Claude Code skills live in .claude/. The workflow:
- Describe changes in the agent chat (Cursor or Claude Code)
- Review diffs in the editor
npm run buildbefore committing if token or icon files changed- Commit and push — GitHub Pages deploys automatically from
master
Branch naming: design-system/* for token/system work, feature/* for component work.