Module
[SCAFFOLD - Baki to revise] Every tile this site can show, all in one place
Cards
- header
Title + meta row (archetype, audience, domain, type). Page identity - always renders, always first in the text column. Height: 256px. Title size: clamp(20px, 2.5vw, 44px).
- pullquote
Italic accent-bordered callout for the single line you want to echo. Pulls from frontmatter pullquote or (project only) promise. Height: 224px. Accent: node color.
- metrics
Oversized monospace value + letter-spaced label, one tile per entry. Click-to-expand detail panel below the strip. Value size: clamp(17px, 2.8vw, 36px). Height: 192px.
- cards
Expandable detail cards stacked vertically. Click a row to unfurl the body, ▸ arrow rotates 0 → 90° when open. Icon circle inverts on hover. Each card's accent can override the node color. Height: 480px.
- longform
The MDX body - headings, paragraphs, code blocks, Callouts, LinkCards. Wiki-links ([[slug]]) resolve to node-local anchors. Spans 2u wide (narrower column, taller wrap). Height: 1280px.
- demo
Iframe embed of a live URL with a fullscreen affordance. Rendered when frontmatter demo_url is set. Replaces 'media' when both are present. Height: 480px.
- media
Image or video embed - 16:9 at 512px width + caption padding. Honors media_type ('image' | 'video') and media_src. Gradient and scene types route elsewhere. Height: 384px.
- process
Numbered step list, vertical. Sits in the right column under scene (or gallery). Each step = 1 action line + optional detail sentence. Height: 352px.
- echoes
Related-entries strip. Auto-populates from frontmatter echoes + semantic edges in getRelatedIds(). Click one to fly to that node. Height: 256px.
- scene
Registered scene component in the right column. Keys: void-isometric (1280h, square), orbit-constellation (1280h), sumi-ripple (640h). CursorRipples overlays pointer-reactive rings.
- gallery
Horizontal scroll strip of concept art / screenshots. Title + caption per frame. Sits above scene in the right column. Height: 480px.
- launch
Live-deck iframe for talks. 16:9 at 1280px wide, 720px tall. Top of the right column, above gallery/scene/process. Only enabled for node.type === 'talk' with a deck_url.
- corpus
Pages-in-tag grid. Only renders for tag nodes. Lists every content entry that references the tag, as promotable node cards. Height: 640px (grows via MeasuredFrame).
- widget:terminal-glow
Ambient widget - flickering CRT-style scanline field in the node's accent color. Opt-in via frontmatter widgets: ['terminal-glow']. Height: 320px.
- widget:type-physics
Ambient widget - letterforms that sag, drift, and recover as if lightly gravity-bound. Opt-in via widgets: ['type-physics']. Height: 288px.
- widget:orbit-field
Ambient widget - points tracing slow elliptical paths, accent-tinted. Opt-in via widgets: ['orbit-field']. Height: 320px.
- beats (Wave 3 D1)
Beat-row renderer. Splits body at ## H2 boundaries; each beat renders as a 2:5 grid row (summary prose left, detail modules right) aligned by meaning. Span 8 (full width, 2048px). Opt-in via beat_layout frontmatter. Claims listed detail modules from top-level scatter. Height: 2400px initial - MeasuredFrame grows.
- experts (Wave 3 A4)
Expert-panel grid for research projects where the Stack beat is a panel of seats (not tech). Each seat: name + discipline + C/X/D trait marker + optional position. Card shell matches CardsModule language (left-border accent, hover glow). Height: 640px.
- debates
Three-way trilemma triangles. SVG with 3 domain-colored vertices + connecting lines + position list + optional resolution line. Each debate is a labeled container (left-border accent, no click state - vertices are the interaction). Height: 720px.
- widget:signal-shape
Live tuner for the signal-drop expanded-panel shape. Three sliders (vertex jitter, squircle exponent min/max) + an 8-shape preview grid that updates as you drag. Values persist via prefs and every mounted SignalDropBubble repaints in lockstep. Height: 480px.
- widget:presence-hub
Presence visualization hub - 5-tab strip across orb state machine, particle sandbox, page heat slider, rail halo timeline, substrate cadence picker. Tunes motion.presence_orb / heat_halo / substrate_tone live before persisting via GlobalsEditor. Height: 1200px.
- widget:click-feedback
Click + cursor + signal feedback orchestrator - stacks CursorRippleSandbox, ClickRippleReplay, VectorRippleGravityEditor, SignalPulsePropagation. Each sub-view tunes its own motion sub-tree (cursor_ripple / ripple / vector_ripple / signal_pulse) against a private LocalKnobStore. Height: 1400px.
- widget:rail-composition
Rail-side animation hub - tabs across LampSlide, HaloBloom, CelestialShadow demos. Closes the rail-composition coverage gap (3 effects, 0% covered). Each demo unmounts when its tab is inactive so off-screen rAF loops tear down. Height: 1024px.
- widget:button-states
Chrome transition reference - 4 canonical button patterns (color · background · border · transform) rendered three times each across atelier/void/edit-mode registers. Single Tuner scrubs motion.duration site-wide; force-reduced-motion A/B toggle reveals the calm fallback. Height: 1024px.
- widget:title-states
Title typography lab - 5 audited animations (TitleBreath · TitleGravity halo · PhysicsTitle drift · hover glow · caret blink) across 3 presence states (new · read · returned). Stacks Tuners for motion.title_breath / physics_title / character_drift. Height: 1024px.
- widget:frame-interactions
Canvas-frame lifecycle hub - hover lift, bracketMerge (select), bracketUnmerge (deselect), pageDismiss (replay). Tuner scrubs motion.canvas_frame against a private LocalKnobStore so admins feel the duration / easing knobs without polluting documentElement. Height: 1024px.
Module enablement rules
enabledModules(node) reads the frontmatter and returns the ordered list of modules
that should render. Rules, in order:
header- always first.launch- talks withdeck_url.gallery- whengalleryhas entries.scene- whensceneis set.pullquote- whenpullquoteorpromiseis set.metrics- whenmetricshas entries.process- whenprocesshas entries.widget:<name>- one per entry inwidgets.cards- whencardshas entries.experts- whenexpertshas entries (Wave 3 A4).debates- whendebateshas entries (Wave 3 Amendment).corpus- tag nodes with a corpus.beats- whenbeat_layoutfrontmatter is present AND body has ≥1 H2 (Wave 3 D1).longform- when the MDX body is non-empty AND nobeat_layout(falls back).demo- whendemo_urlis set (suppressesmedia).media- otherwise, whenmedia_type+media_srcare set.echoes- when explicit echoes exist or semantic edges resolve.
Order here is the declaration order. Column placement is decided by span:
span 5(right/detail lane): scene, gallery, process, launch, experts, debatesspan 8(full width, both lanes): beatsspan 1/2/3(left/summary lane): everything else
Modules claimed by a beat via beat_layout are REMOVED from top-level scatter and
nested inside BeatsModule instead. Header stays top-level always.
Beat-row layout (Wave 3 D1)
Project pages can opt into a beat-row layout via beat_layout frontmatter. When
present, the MDX body’s ## Heading sections become beat rows - each row is a 2:5
CSS grid with the beat’s prose on the left (summary lane, 512px) and its assigned
detail modules on the right (detail lane, 1280px), with a 64px gutter between.
beat_layout:
Frame: [scene, pullquote] # prose + scene + pullquote
Mechanism: [process] # prose + process steps
The Panel: [experts, metrics] # prose + expert grid + metrics
The Debates:[debates, cards] # prose + trilemma triangles + cards
Trace: [gallery] # prose + concept art
Echoes: [echoes] # prose + related content
Keys are H2 heading text (case-insensitive slug match); values are detail-module
names. Supported modules: scene, pullquote, metrics, process, cards,
gallery, experts, debates, media, demo, launch, echoes. Modules listed here are CLAIMED - removed from top-level scatter.
Modules not listed render top-level per the legacy layout.
Beat row order = body H2 order. The outer beats module is span 8 (2048px) with canvas-frame brackets wrapping the entire row stack; each beat section inside is separated by its H2 border-bottom + a 32px vertical gap.
Inline editing (D3)
Append ?edit=1 to any file-backed page URL and - if your owner token is in
localStorage under baki.owner.v1 - the canvas flips into edit mode. Click any
module to select it; drag it anywhere on the canvas to reposition; edit content fields
in the top-right panel. Save writes directly to the MDX file via the dev-server
middleware (scripts/save-layout-middleware.mjs). No build step, no database -
frontmatter is the source of truth, and visitors see the same layout and content
editors do.
Two axes of edit
- Position - drag a module and the new
(x, y)world coords land in the frontmatter’smodule_positions[]. Coordinates are absolute, not deltas. - Content - every editable module has a schema (
src/lib/module-schemas.ts) that declares field specs. The panel renders entirely from the schema; saving patches the frontmatter keys those fields name.
The ten design rules
Full details live in the D3 inline-edit UX conventions rule in the Builder
registry. Summary:
- Schema-driven, not UI-duplicated - register in
module-schemas.ts, zero UI changes. - Two storage modes - top-level frontmatter keys OR nested-array entries. A schema picks one; no mixing.
- Fixed widget vocabulary -
text·textarea·select·color·tag-list·number. New widgets are a lib change, not a schema-local one. - Amber brackets = edit signal at canvas level. Modules don’t repeat it. Only selected/dragging modules add a 1px cyan outline.
- Gestures - click selects, drag moves, panel-row-click selects. Double-click and long-press reserved for future use.
- Save is atomic per page - one POST commits
modulePositions+nodeFieldsnestedArrays+ optionalmdxBody. No autosave. Amber dirty badge until success.
- Sizing modes -
content-free(frame grows padding; text, metrics) orcontent-anchored(content stretches to frame; gallery, scene, iframe). Resize handles respect the mode. - Visitor = editor rendering - saved frontmatter drives both. No preview mode. What you see during edit is what visitors see on next reload.
- Field names can’t shadow - two schemas declaring the same top-level frontmatter key on the same node type fails registry validation.
- Array items are positional - nested-array writer patches by index. Add appends; delete collapses. Reorder is a dedicated future phase.
Module editability matrix
| Module | Storage | Sizing | Status |
|---|---|---|---|
header | node-top-level (title, tagline, domain, color, archetype, audience, tech) | content-free | shipped (Phase 3a canary) |
pullquote | node-top-level (pullquote, promise) | content-free | task 3b.2 |
metrics | nested-array of {value, label, detail} | content-free | task 3b.3 |
cards | nested-array of {title, body, icon, accent} | content-free | task 3b.4 |
process | nested-array of {step, detail} | content-free | task 3b.5 |
experts | nested-array of {name, discipline, marker, position} | content-free | task 3b.6 |
debates | nested-array of {title, top/left/right vertex, resolution} | content-free | task 3b.7 |
echoes | node-top-level (tag-list of slugs) | content-free | task 3b.8 |
gallery | nested-array of {src, caption, beat} | content-anchored | task 3b.9 |
scene | node-top-level (select from scene registry) | content-anchored | task 3b.10 |
launch | node-top-level (deck_url, event, venue, date, status) | content-anchored | task 3b.11 |
widget:* | node-top-level (widgets tag-list) | content-free | task 3b.12 |
demo / media | node-top-level (demo_url OR media_type + media_src) | content-anchored | task 3b.13 |
longform | mdx-body (the whole body after ---) | content-free | task 3b.14 (special) |
beat-prose:<slug> | mdx-body-section (per-H2 fragment) | content-free | task 3b.15 (special) |
signal-drop-compose | view-only (visitor writes, not author) | - | not editable |
corpus | view-only (tag-derived aggregation) | - | not editable |
preferences / presence-dashboard | system modules | - | not editable |
Phases beyond 3b:
- 3c - content-anchored vs content-free sizing enforcement.
- 3d - resize handles with padding control.
- 3e - add-module palette (drag a new module onto the canvas from a sidebar).
Tasks 3b.1 (nested-array infrastructure) and 3b.14 (mdx-body editor) are
prerequisites for the remaining nested-array schemas and the beat-prose editor
respectively. The umbrella D3 Phase 3b - Inline-edit schemas for remaining modules tracks the full fan-out.