Design Guide — TypeScript Survival Guide System
This is the system that keeps the course from becoming a picture book.
The problem we solved
Table of contents was a slew of shit, then a bunch of imagery. TypeScript is first, pictures are supporting. Two TOCs were rendering the same Day 1–5 bullets (static syllabus + side itinerary), and deck headers had no reserved real estate for image, use case, and hands-on — so each surface invented its own. Inception.
Single source, single TOC, reserved zones everywhere else.
The 4-zone standard
Every deck — whether in the syllabus list or at the top of its lesson section — reserves the same four zones, in the same order. If data is missing, the zone still holds space (skeleton), so layout never shifts.
Media — 16:9 reserved. Syllabus list uses a 48×48 supporting thumb (not a hero) with
aspect-ratio:16/9andbackground: sunk. Deck header uses a 320-wide figure floated inline, not full-bleed. Troop roster uses 56×56 and 40×40 avatars, not giant portraits. All<img>have explicitwidth/height,loading=lazy,decoding=async.Use case — one line, always boxed left-accent
border-left:2px solid accentwith<strong>Use case:</strong>label,Surface-overlay 92%wash. Never italic muted. Appears directly under media, before bullets.Bullets — exhaustive 5 per deck from
DECK_BULLETS(single sourceassets/typescript/src/ds/course-doc.ts:161). No high-level placeholders. Grouped Day 1–5: Foundations & Type System (Day 1), Functions/Classes/Generics (Day 2), Advanced Types (Day 3), Async & Architecture (Day 4), Testing/Best Practices/Capstone (Day 5).Hands-on — footer row with two pills:
Try It → Lab: <name>(inlinedatacouch-tryit) andVS Code → Code: <path>(open lab folder,npm install,npx tsc --noEmit, Run & Debug to inspect shapes). Separated byborder-top:1px dashedfrom bullets, pill buttonsborder-radius:999px.
Where each zone lives
Syllabus (static Hugo,
layouts/page/course-decks.html:ts-syllabus) — the single table of contents. Compact gridminmax(280px,1fr),48×48thumbs left of title, boxed use case, 5 bullets, pill footer. Printable on one letter page at 6.5pt. The sidets-itineraryis hidden on this page to kill duplication (body:has(.ts-syllabus) .ts-side {display:none}); the syllabus is the TOC.Deck header (JS
datacouch-course-docshadow DOMdoc-deck) — afterdoc-scout(180px avatar, why-line), insertsdoc-figure(320-wide,16/9, lazy,max-width:320px, floated),doc-usecasebox,doc-bulletslist,doc-codelinkspill row +— open lab folder in VS Code…hint. Shadow styles use inlinestyle+aspect-ratioso the figure reserves space before the image loads.Lesson slides (
slide-with-figure) — per-slide figures are 200pxslide-figure(max 240, 16:9 cap), not full-page. Only used when a slide’s frontmatter declaresimage: {src:'/img/troop/...', alt}. Otherwise no zone, no shift.Troop rails (
ts-troopbelow the outline, not above it) — three guides as56×56avatars withwhyline, then two<details>collapsed by default: 40 scouts as40×40mini-cards and 23 illustrations as40×40thumbs. Grid verifies/img/troopparity without making the course look like a look-at-our-pictures course.
Image placement rules
- Never full-bleed in TOC. Syllabus and side nav never use
640×360. Deck headers use320×180at most, floated, text wraps. - Reserved before load. Every image has
width+height+ CSSaspect-ratio:16/9+ sunk background so CLS stays 0. - Alt is the use case. e.g.,
Aaliyah checking the safety circle paper plate — Primer manners use case where SafetyCircle must be valid before any Match is struck. No PII in pixels. - Thumbs are supporting, not banners. If you remove the image, the card still reads as TypeScript — title + use case + 5 bullets + hands-on remains.
TypeScript first
Order is fixed: title (deck name + lesson count) → use case → 5 bullets (TypeScript concepts) → hands-on. Imagery sits beside title, not above bullets. The syllabus ts-syllabus__lede now states the contract: Image · use case · 5 bullets · hands-on Try It + VS Code lab per deck — reserved real estate, every zone utilized — but image gets 48px, TypeScript gets 5 lines.
Inception fixed
Before: ts-syllabus cards duplicated ts-itinerary side nav (same Day 1–5, same bullets, same 75 lines) plus doc-deck repeated them again. Now ts-side is hidden when ts-syllabus is present, ts-body collapses to single column, and ts-syllabus is the sole TOC. Deep links still work via history.replaceState scroll into datacouch-course-doc shadow.
Verifying
Headless Chromium (playwright) asserts in one pass: 15 syllabus cards, 15 ts-syllabus__thumb at 48×48 16/9, 15 boxed 2px solid use cases, 30 pills (15 labs + 15 code), and 15 shadow deck headers each with doc-figure img at 320×180 16/9, doc-usecase, doc-bullets, doc-codelinks, doc-drill; then screenshots at 375/1024/1920 + full.
Footer link
This style guide is linked from every course footer as Design Guide so future edits have a hold on where each zone lives and what size it reserves.
Ark / Park UI + Panda CSS — token → component → object
This course is Panda CSS at the token layer, Ark UI headless at the component layer, and Park UI recipes at the object layer — all build-time, no runtime.
Tokens (primitive → semantic):
assets/design-system/src/tokens/primitive/{color,scale}.tsandsemantic/{elevation,index}.tsdefine the source. Primitiveink #1c2230 / paper #ffffff / plum #5e41a2 / ember #ff9900become semanticpage / prose / link / wash / surface-* / border / focus-ringvia{colors.paper} _dark:{colors.ink}. Panda codegen (panda.config.ts→styled-system/, pinned) emits them as CSS variables — like a lockfile. Nostyleprops with raw hex; every color/spacing/radius flows from tokens.Components (Ark headless → Park recipes): Ark provides the accessible behavior (Dialog
aria-modal, Tabsaria-controls, Slideraria-valuenow, Dialog focus trap,Escto close) — we mirror that indatacouch-course-docshadow (role="main",aria-labelledby, slide-deck prev/nextaria-label). Park UI recipes would becva({ base, variants })forcard(syllabus),avatar(56/40),button(Try It pillmin-height:44px),dialog(mobile.ts-side.is-open). Even where we hand-writets-*classes for Hugo, the values arevar(--datacouch-*)tokens, and the structure matches Park’scard/avataranatomy so a futureparkPresetswap is mechanical.Objects (Panda patterns + cva):
hstack/vstack/gridpatterns andcvarecipes are the object layer (e.g., syllabusgrid: repeat(auto-fit, minmax(280px,1fr)), deckfloat:right 320pxwithaspect-ratio 16/9reserved,ts-text-scalegroup). One token change (e.g.,--ts-scale) cascades through all objects without touching markup — scalable, repeatable, styled in full.
Check panda.config.ts pinned styled-system/ and assets/design-system/src/tokens/ as the source of truth — no ad-hoc hex in layouts/.