VisualSite
Design system story
Dot animation of a website's typography, colors, buttons, and spacing being extracted.
$ npx context-ds add story-design-systemUsage
Use it for
A style guide or design system page hero or card on the site. Plays only on screen; shows the board with reduced motion.
Avoid
- Stories as the main content of a page
Dependencies: dot-engine, story-output, story-parts
Example
examples/story-design-system.tsx
import { Card } from "@/components/ui/card";import { DesignSystemStory } from "@/components/visuals/stories/design-system-story";export function Site() { return ( <Card variant="ring" className="min-h-[22rem] w-full max-w-xl items-center justify-center"> <div className="relative w-full max-w-[26rem]"> <DesignSystemStory /> </div> </Card> );}Source
components/visuals/stories/design-system-story.tsx
'use client';import { DESIGN_SYSTEM_RESTING, DESIGN_SYSTEM_STORY_GRID } from '@/components/visuals/stories/design-system-story-scenes';import { DotStory, type LoadDotStory } from '@/components/visuals/dot-engine/dot-story';const loadDesignSystemStory: LoadDotStory = async (stage) => { const { createDesignSystemStoryPlayer } = await import('@/components/visuals/stories/design-system-story-player'); return createDesignSystemStoryPlayer(stage);};/** * The Style guide page's picture: a live website is scanned and becomes its design system, a board of its type, * colors, buttons and spacing. With reduced motion it shows the board. */export function DesignSystemStory({ className }: { className?: string }) { return <DotStory grid={DESIGN_SYSTEM_STORY_GRID} resting={DESIGN_SYSTEM_RESTING} load={loadDesignSystemStory} className={className} />;}components/visuals/stories/design-system-story-player.ts
import { BOARD_LABELS, DESIGN_SYSTEM_STORY_GRID, SITE_URL, STYLE_BOARD, WEBSITE, paintScan } from '@/components/visuals/stories/design-system-story-scenes';import { createBeatStoryPlayer } from '@/components/visuals/dot-engine/dot-story-beats';import { clamp01 } from '@/components/visuals/dot-engine/dot-story-cells';import type { DotStoryPlayer, DotStoryStage } from '@/components/visuals/dot-engine/dot-story-player';/** * One extraction, played as a loop: a scan runs down a live website twice, and the site melts into its design * system, a board whose quarters, type, color, buttons and spacing, are labeled one after another. */const MELT_SECONDS = 1.1;const SCAN = { startsAt: 0.4, seconds: 1.1, times: 2 };const LABELING = { startsAt: 0.2, everySeconds: 0.35, seconds: 0.3 };export function createDesignSystemStoryPlayer(stage: DotStoryStage): DotStoryPlayer { return createBeatStoryPlayer(stage, { grid: DESIGN_SYSTEM_STORY_GRID, resting: { beat: 1, heldSeconds: 2.4 }, beats: [ { picture: WEBSITE, arrival: { kind: 'melts', seconds: MELT_SECONDS }, holdSeconds: SCAN.startsAt + SCAN.seconds * SCAN.times + 0.4, animate(states, heldSeconds) { const scans = (heldSeconds - SCAN.startsAt) / SCAN.seconds; if (scans >= 0 && scans < SCAN.times) paintScan(states, scans % 1); }, write: (pen) => pen.text(SITE_URL), }, { picture: STYLE_BOARD, arrival: { kind: 'melts', seconds: MELT_SECONDS }, holdSeconds: 4, write: (pen, heldSeconds) => BOARD_LABELS.forEach((text, index) => pen.text(text, clamp01((heldSeconds - LABELING.startsAt - index * LABELING.everySeconds) / LABELING.seconds))), }, ], });}components/visuals/stories/design-system-story-scenes.ts
import { OUTPUT_STORY_GRID, PAGE_ALONE, PAGE_BODY_ROWS, PAGE_URL } from '@/components/visuals/stories/output-story-scenes';import { typeSpecimen } from '@/components/visuals/stories/style-guide-parts';import { DOT_ABSENT, DOT_FILLED, DOT_HOLLOW, type DotGrid } from '@/components/visuals/dot-engine/dot-morph-dots';import { pictureOf, type StoryPicture } from '@/components/visuals/dot-engine/dot-story-beats';import { cellsOfStates, isInDotBox, rings, roundedBox, solid, solidWithSpeckle, type DotBox, type DotPainter } from '@/components/visuals/dot-engine/dot-story-cells';import type { DotStoryText } from '@/components/visuals/dot-engine/dot-story-player';/** * The Style guide page's pictures, in the brand dot grid: a live website, and the design system read out of it, a * board with its type, its colors, its buttons and its spacing scale each in their own quarter. */export const DESIGN_SYSTEM_STORY_GRID: DotGrid = OUTPUT_STORY_GRID;const { columns: COLUMNS, rows: ROWS, pitch: PITCH } = DESIGN_SYSTEM_STORY_GRID;const x = (column: number) => column * PITCH;// Doto sits a third of its size below the middle of the row it is set on.const baselineOn = (row: number, size: number) => (row + 0.5) * PITCH + size / 3;const between = (value: number, from: number, to: number) => value >= from && value <= to;// The website is the Scrape page's browser, moved down to the middle of the picture it has to itself here.const SITE_DROP = 5;const siteStates = new Uint8Array(COLUMNS * ROWS);siteStates.set(PAGE_ALONE.states.subarray(0, (ROWS - SITE_DROP) * COLUMNS), SITE_DROP * COLUMNS);export const WEBSITE: StoryPicture = { states: siteStates, cells: cellsOfStates(DESIGN_SYSTEM_STORY_GRID, siteStates) };export const SITE_URL: DotStoryText = { ...PAGE_URL, y: PAGE_URL.y + SITE_DROP * PITCH };const SITE_BODY = { top: PAGE_BODY_ROWS.top + SITE_DROP - 1, bottom: PAGE_BODY_ROWS.bottom + SITE_DROP, left: PAGE_BODY_ROWS.left - 2, right: PAGE_BODY_ROWS.right + 2 };/** The website with a scan `scanned` of the way down it: a solid bar across the page (none at 0 or 1). */export function paintScan(states: Uint8Array, scanned: number) { if (scanned <= 0 || scanned >= 1) return states; const bar = SITE_BODY.top + Math.round(scanned * (SITE_BODY.bottom - SITE_BODY.top)); for (let column = SITE_BODY.left; column <= SITE_BODY.right; column++) states[bar * COLUMNS + column] = DOT_FILLED; return states;}const BOARD = { left: 3, right: 54, top: 2, bottom: 35, column: 29, row: 19 };const SPECIMEN = typeSpecimen({ column: 7, row: 5 }, 9, 0.9);const SWATCH = { top: 5, bottom: 12, columns: 6, lefts: [33, 40, 47], tones: [solid, solidWithSpeckle, rings], radius: 1 };const BUTTONS: { box: DotBox; isPrimary: boolean }[] = [ { box: { left: 7, right: 24, top: 22, bottom: 25 }, isPrimary: true }, { box: { left: 7, right: 24, top: 27, bottom: 30 }, isPrimary: false },];const BUTTON_RADIUS = 1.5;const SPACE = { left: 33, rows: [22, 24, 27, 30], lengths: [2, 4, 8, 16] };// Each quarter of the board holds one part of the design system.const board: DotPainter = (column, row) => { if (!isInDotBox(BOARD, column, row)) return DOT_ABSENT; if (column === BOARD.left || column === BOARD.right || row === BOARD.top || row === BOARD.bottom) return DOT_FILLED; if (column === BOARD.column || row === BOARD.row) return DOT_HOLLOW; if (SPECIMEN(column, row)) return DOT_FILLED; const swatch = SWATCH.lefts.findIndex((left) => roundedBox({ left, right: left + SWATCH.columns - 1, top: SWATCH.top, bottom: SWATCH.bottom }, SWATCH.radius)(column, row)); if (swatch >= 0) { const isEdge = column === SWATCH.lefts[swatch] || column === SWATCH.lefts[swatch] + SWATCH.columns - 1 || row === SWATCH.top || row === SWATCH.bottom; return isEdge ? DOT_FILLED : SWATCH.tones[swatch](column, row); } const button = BUTTONS.find(({ box }) => roundedBox(box, BUTTON_RADIUS)(column, row)); if (button) return button.isPrimary || column === button.box.left || column === button.box.right || row === button.box.top || row === button.box.bottom ? DOT_FILLED : DOT_ABSENT; const space = SPACE.rows.indexOf(row); return space >= 0 && between(column, SPACE.left, SPACE.left + SPACE.lengths[space] - 1) ? DOT_FILLED : DOT_ABSENT;};export const STYLE_BOARD = pictureOf(DESIGN_SYSTEM_STORY_GRID, board);const label = (text: string, column: number, row: number): DotStoryText => ({ text, x: x(column), y: baselineOn(row, 13), size: 13, weight: 700, muted: true });/** What each quarter of the board says, in the order they are read. */export const BOARD_LABELS: DotStoryText[] = [label('Inter, 400 to 600', 7, 16), label('3 color roles', 33, 15.5), label('radius 8px', 7, 32.5), label('4, 8, 16, 32', 33, 32.5)];/** The board: what the picture shows before it plays, and instead of playing with reduced motion. */export const DESIGN_SYSTEM_RESTING = { ...STYLE_BOARD, texts: BOARD_LABELS };Reference: site src/components/sections/products/design-system-story.tsx.