Skip to content

CreatorTrack Studio , Phase 1: Lock & Document the Foundation

URL: https://mkdocs.justinsforge.com/memory/handoffs/creatortrack-studio-phase1-foundation-2026-06-18/

Spawned by: home-base session, 2026-06-18, at Justin's request. Model: Opus 4.8, medium thinking (set thinking to medium; this is audit + convention work, not deep architecture, so don't burn max-think). Repo: /home/justinwieb/forge-suite (dev at dev.creatortrack.ai, local Next dev server).

Why this exists

Justin is about to fan out MULTIPLE parallel workers to build the component library (menus, popovers, scrollbars, pages, etc.) and wire them into dev.creatortrack. Parallel component work only stays collision-free if the foundation is frozen first. That is this job. You are NOT building feature components. You are locking and documenting the ground they stand on.

Required reading (scope)

  1. forge/memory/general/reference_suite_ui_kit.md , THE canonical spec for the UI kit, Studio, token store, Showcase, and the existing component inventory. Most of the foundation is ALREADY BUILT (theme.css tokens, /studio harness with Components/Preview/Inspect/Pages tabs, TokenStudioProvider token store, Showcase preview+controls frame, components/ui primitives). Treat it as ground truth; verify against the actual repo, fix the doc if reality differs.
  2. The repo itself: app/theme.css, components/ui/, components/studio/, components/kit/, app/studio/page.tsx.

Phase 1 deliverables (in order)

  1. Audit & freeze the design tokens. Read app/theme.css. Confirm the token set is complete enough that the Phase 2 components (menus, popovers/modals, scrollbars, pages/layout) can be built using ONLY var(--token) with zero hardcoded hex/px-that-should-be-a-token. Add any missing tokens (sourced from the Flask measured values in the reference doc). This file is the single retheme knob , get it right, then declare it frozen.

  2. Verify & document the component-registration path. Trace exactly how a new component gets into Studio today: folder convention under components/, how it exports, how it appears in ComponentsGallery.tsx, how Showcase + Playground wire its live preview + controls, and how a control becomes token-bound (token: "--name") vs local. Write it down precisely with file paths.

  3. Write the authoring convention at repo root: forge-suite/COMPONENT_AUTHORING.md. This is the contract every Phase 2 worker reads before touching anything. It MUST specify:

  4. One component = one folder under components/<Name>/. One worker owns one folder. Never two workers in the same folder.
  5. Token rules: read var(--token), never hardcode; if a token is missing, it was supposed to be added in Phase 1 , flag it, don't invent inline values.
  6. How to register the component in Studio (exact steps + the files touched).
  7. The shared-file rule: workers do NOT edit the shared barrel/index, routing, or theme.css. Each worker exports from its own folder; the integrator does the final wiring as one serialized step. If a worker thinks it needs to touch a shared file, it stops and notes it instead.
  8. Verification each worker must run: tsc clean + its component renders in /studio Components tab.

  9. Verify the harness end-to-end. tsc clean; dev server up; /studio 200, /kit 307→/studio; the token Save round-trip still works (edit a token → Save → theme.css rewritten in place → HMR). Report actual results, not assumptions.

  10. Update the reference doc. Fold any corrections from the audit back into reference_suite_ui_kit.md and sign [Phase1 Worker].

Done =

COMPONENT_AUTHORING.md exists and is accurate; theme.css token set is complete & declared frozen; harness verified (tsc + /studio + save round-trip, with real output pasted); reference doc reconciled. Then write a short completion note back to this handoff file and stop , do NOT start building Phase 2 components.

Hard rules (Forge doctrine)

  • No em dashes. Robust over quick, no band-aids. Fail loud. Test the path before claiming done , paste real command output. Append the URL: line to any new/ modified indexed .md.

PHASE 1 COMPLETE (2026-06-18, [Phase1 Worker])

All five deliverables done. No Phase 2 components started (per the stop rule).

1. Tokens audited + frozen. app/theme.css already covered menus/popovers/modals/ scrollbars. Gaps for pages/layout + modals were measured-but-untokenized values: added --fs-h1: 32px / --fw-h1: 700 / --lh-h1: 1.2 (Notion page title), --topbar-h: 44px (Notion topbar), --modal-w: 380px (Flask .uidlg). All five also added to lib/kit-tokens.ts as a new "Page frame" group (allowlisted + tunable). Restored --slider-h/--slider-w to spec (#c0c0c1/10px) after a live Save-test drift. theme.css declared frozen (the single retheme knob).

2. Registration path traced. Flow: a <Section> in components/studio/ComponentsGallery.tsx renders <Playground title controls render> (components/kit/Playground.tsx), which renders through Showcase (components/studio/Showcase.tsx, registers its preview pane as a token scope). Controls are LOCAL (preview prop) or TOKEN-BOUND (token: "--name" -> token store components/kit/token-store.tsx, tracked + saveable + broadcast). Save POSTs dirty tokens to app/api/kit/tokens/route.ts (dev-only, allowlist from lib/kit-tokens.ts, in-place --name: value; rewrite preserving comments). Studio shell app/studio/page.tsx; /kit 307 -> /studio via app/kit/page.tsx.

3. forge-suite/COMPONENT_AUTHORING.md written. One component = one folder components/<Name>/; var(--token) only (missing token -> flag, don't inline); register via a studioEntry exported from the folder (typed by new components/studio/types.ts StudioEntry), integrator does the gallery wiring; shared-file rule (no worker edits to barrel/gallery/shell/ theme.css/manifest/routing); per-worker verification = tsc clean + renders in /studio.

4. Harness verified end-to-end (real output): - npx tsc --noEmit: clean except one ignorable .next/types/validator.ts error ("Cannot find module '../../app/page.js'") , a Next 16 type-gen quirk for the route-group index (/ served by app/(suite)/page.tsx), not source code, not introduced here. - Routes: /studio -> 200, /kit -> 307. - Save round-trip: {"--topbar-h":"45px"} -> 200 {"ok":true,"written":["--topbar-h"]}, file rewritten in place, comment preserved; restored to 44px -> 200. Non-allowlisted --evil -> 400; injection value -> 400. (New token round-trips through the allowlist.)

5. Reference doc reconciled. reference_suite_ui_kit.md updated with the Phase 1 freeze section, signed [Phase1 Worker].

Files touched (all in /home/justinwieb/forge-suite): app/theme.css (tokens), lib/kit-tokens.ts (Page frame group), NEW components/studio/types.ts, NEW COMPONENT_AUTHORING.md. Forge tree: reference_suite_ui_kit.md + this handoff.

Note for the integrator: the Components tab is still a hand-edited monolith (ComponentsGallery.tsx). The StudioEntry contract lets workers export their entry from their own folder; wiring each entry into the gallery (and re-exporting true primitives from the ui barrel) is the serialized integrator step. A future refactor could make the gallery map over a registry of studioEntry imports to remove even that single shared-file edit.


SECTION 0 (foundational primitives) , IN PROGRESS (2026-06-18, [Phase1 Worker])

Justin's call: build Section 0 of COMPONENT_BACKLOG.md + fix the flagged gallery shared-file collision, BEFORE any Tier 1 fan-out. High thinking for the primitive architecture.

Done + verified: 1. Tech-choice locks installed (package.json): @floating-ui/react (positioning), @dnd-kit/core+sortable+utilities (DnD), recharts (charts), date-fns (dates), plus @tanstack/react-virtual (virtualization, picked since the tech table left it unnamed). Token decision (Justin approved): dark-only, KEEP names, no rename , the tokens are already intent-named (--bg/--card/--txt/--accent), so a future light mode is an override block reusing the same names. Zero token churn. 2. Behavioral primitives built in components/primitives/ (barrel @/components/primitives), all tsc-clean: floating.tsx (useAnchoredFloating over Floating UI), Portal.tsx + z-layers.ts (single overlay root + Z stacking scale), useFocusTrap.ts, useDismiss.ts, Transition.tsx (usePresence + Transition), shortcuts.tsx (ShortcutProvider + useShortcut), Field.tsx (Field/Form/useForm), dnd.tsx (SortableList over dnd-kit), virtual.ts (useVirtualList over react-virtual). 4. Gallery auto-discovery refactor (the flagged shared-file collision): the Components tab now MAPS over STUDIO_ENTRIES in components/studio/registry.tsx. Existing kit primitives are grandfathered entries there; a new component appends one import + one array line. Workers no longer touch ComponentsGallery.tsx. Verified in-browser: all 15 sections render, 27 controls, zero console errors. 5. Docs: COMPONENT_AUTHORING.md gained a "Foundational primitives" section (how a Tier 1 worker uses floating/portal/dismiss/focus-trap/transition/shortcut/form/DnD/virtualization) + the tech-locks + the token decision; registration section updated to the registry.

Verification (real output): tsc --noEmit clean (only the known .next/types route-group artifact); /studio 200; /kit 307; token Save round-trip 200 (write + restore).

  1. Overlay refit DONE + browser-verified. Menu/DropdownMenu/ContextMenu/Tooltip/Popover/Modal in components/ui/index.tsx now build on the primitives: floating ones use useAnchoredFloating
  2. FloatingPortal + Z + usePresence (a shared overlayAnim animates the INNER wrapper so it never fights Floating UI's transform positioning); Modal uses Portal + Z.modal + useFocusTrap
  3. useDismiss + presence. They are the reference impl Tier 1 copies. Verified in Justin's Chrome: Modal opens visible + Escape/scrim/Cancel all close + focus trap finds its 3 focusables; Dropdown + Popover open visible; ContextMenu opens at the cursor on real right-click.

Two real bugs found + fixed during verification (both load-bearing): - usePresence flipped to the visible "open" state in the SAME effect that triggered mount, so the single requestAnimationFrame got lost and every overlay mounted stuck at opacity:0 (invisible). Fix: split into two effects (mount, then flip on a frame keyed on mounted) + a setTimeout(60) fallback so it can never get stuck. components/primitives/Transition.tsx. - useFocusTrap used offsetParent !== null to find focusables, but offsetParent is ALWAYS null inside a position:fixed container (every modal), so it found none and never trapped focus. Fix: use getClientRects().length > 0. components/primitives/useFocusTrap.ts. (Verification note: dispatched/synthetic keyboard + contextmenu events do NOT reach React handlers in the automation harness; use real clicks/keys. Also confirm a normal-size browser window.)

SECTION 0 COMPLETE. Foundation is frozen: tokens + primitives + refit reference overlays + gallery auto-discovery + docs, all tsc-clean and browser-verified (/studio 200, /kit 307, Save 200). Tier 1 fan-out (one worktree per folder, append studioEntry to components/studio/registry.tsx) can begin.