How the site consumes the shared @yesid/* design system, the app's styling responsibilities, and the rules that keep styles consistent.

Reference Value
Subject Design packages, CSS composition, theme boot, motion policy, reading scale, design gates; for anyone changing visuals
Verified source apps/web/package.json, app.css, tools/ on main at f40e8704; checked 2026-11-07. package.json re-checked at 0461e045 on 2026-11-10; app.css, tools/, hooks.server.ts, article-body.css and morphGate.ts unchanged since 7a71e081; first-visit sections behavior from tests/remembered-sections.spec.ts at 0461e045
Support state Implemented from main f40e8704. Main 7a71e081 (#671) pinned v0.39.1; main 0461e045 (#692) pins all @yesid/* packages to v0.40.0 from yesid-dev/yesid.design (installed from the organization since #683). Public requests on November 7, 2026 returned the f40e8704 revision's project redirect and robots tag
flowchart LR
  R["yesid.design<br/>GitHub release"] --> P["package.json URLs<br/>bun.lock integrity"]
  P --> T["@yesid/tokens<br/>tokens.css, fonts"]
  P --> U["@yesid/ui<br/>base.css, components, head"]
  P --> M["@yesid/motion"]
  P --> G["@yesid/gates"]
  T & U --> A["src/app.css"]
  M --> MO["src/lib/motion"]
  G --> LD["lint:design"]

Packages

Package Used for
@yesid/tokens tokens.css (also injected into PostCSS as global custom media), self-hosted Inter and JetBrains Mono, font preload helper
@yesid/ui base.css, shared components and blueprint drawings, head theme boot injection, boot.json CSP hash
@yesid/motion Motion primitives and the reduced-motion policy
@yesid/seo-kit Sitemap and robots builders, JSON-LD helpers, code highlighting, OG palette and fonts; the app owns its share-card layout in src/lib/og/card.ts
@yesid/analytics, @yesid/i18n-core Plausible client sending through the same-origin relay route src/routes/relay/e/+server.ts (endpoint: '/relay/e' in src/lib/analytics/client.ts), locale helpers
@yesid/gates, @yesid/config Style regression gates; shared Svelte, Playwright and tooling config (root package.json)

All packages install by URL from release v0.40.0 (https://github.com/yesid-dev/yesid.design/releases/download/v0.40.0/...) and are pinned by integrity in bun.lock. Never patch them in node_modules. Change yesid.design, publish a release, then bump every @yesid/* URL together in one consumer PR.

App-owned styling

Concern Contract
Composition app.css imports tokens, Tailwind, @yesid/ui/base.css and @yesid/ui/station-tabs.css, then app rules. Sheet loads its own animation stylesheet. Keyframes used by two or more components live here
Values Every color and spacing value references a token; no arbitrary Tailwind values such as text-[11px]. A value that recurs becomes a token upstream
Breakpoints Canonical Tailwind breakpoints and token custom media only
Viewport units dvh, svh, lvh; never vh. Safe areas through env(safe-area-inset-*)
Sticky layouts Use overflow-x: clip, not hidden, on ancestors of sticky elements
Cascade No !important, z-index: 9999 or transition: all; use z-index and duration tokens
Theme before paint hooks.server.ts calls injectThemeBoot; the script hash comes from @yesid/ui/boot.json for the hash-only CSP policy

Motion

Motion responds to interaction: tap, hover and scroll. The policy lives in @yesid/motion (shouldAnimate(tier) and the reducedMotion store) and has two tiers: safe-always effects (opacity, color, moves of 4px or less, brief feedback) run under prefers-reduced-motion; motion-gated effects (pins, scrubs, parallax, ambient loops, smooth scrolling) do nothing. The reduced-motion Playwright project checks this. In the app, every SVG entrance and path morph runs through src/lib/motion/utils/morphGate.ts: a running animation always finishes, and input during it only changes the end state, which plays afterwards (tests/svg-morph.spec.ts).

Reading scale

src/lib/styles/article-body.css maps the installed @yesid/tokens reading roles (--text-reading*) onto a .reading-detail scale for Blog, Project and Service detail pages, legal copy and the home biography:

Element Phone / desktop
Running text 1.1875rem / 1.25rem, line-height 1.7, measure at most 70ch
Code blocks 1rem / 1.0625rem, line-height 1.6, horizontal overflow inside the block
Supporting prose 1.125rem
Collapsible section headings 1.25rem / 1.375rem

Change these sizes upstream in the tokens, not in the app. Verify resized text and spacing in the rendered page rather than trusting pixel values.

Gates

Gate Command Checks
Design lint bun run --cwd apps/web lint:design @yesid/gates style regressions, raw brand-hex checks with three named illustration/Mermaid exceptions, plus the site's forbidden token-utility patterns in tools/design-gates.ts. Contrast-pair and color-mix floor engines ship in @yesid/gates but are not invoked by this repository's gates
Breakpoints Vitest and Playwright responsive-breakpoint-contract.test.ts, responsive-breakpoint-boundaries.spec.ts
Visual themes Playwright light, dark, phone, tablet, desktop projects

Shared interaction contracts

With JavaScript disabled, server-rendered section bodies are open and readable. If storage access throws, interactive pages use declared defaults and bulk actions are usable.

Subject Current contract
Pointer effects @yesid/motion shares an any-hover/any-pointer fine policy, including hybrid devices; Lenis keeps its own capability test
Station tabs Shared stylesheet owns inactive weight 400, active weight 700, inactive primary-foreground ink, active signage colors and a 3px hover rule. Native links use aria-current="page"; site distance opacity has a 0.9 floor
Disclosure state On eligible pages with at least two collapsible content sections, a first visit (no stored choice, or a malformed one) starts with every section expanded and the bulk control in its neutral state, labelled Collapse all (since design v0.37.0; tests/remembered-sections.spec.ts). A stored Collapse all collapses content across pages and visits. Bulk actions store collapsed or expanded in origin-wide localStorage['sections-choice']; that choice applies across pages and visits. Individual toggles, TOC/fragment jumps and restoration do not write it. Pages below the threshold and independent panels use their declared defaults.
Consumer copy Header, language switch, dialog Close and relevant TOC labels belong to the app's EN/FR/ES locale files
Contract-changing release When API reports or migration notes change, each of the three consumers (Transit, yesid.dev, yesid.login) rehearses the rc.N candidate inside a draft adoption PR. After stable promotion, the same PR switches to stable URLs, reruns CI and merges