{"slug":"design-system","title":"design-system","summary":"Mechanical implementation invariants for frontend design: token architecture, typography hierarchy, loading order, FOUT prevention, chrome stability, motion timing, color semantics. Use with design when building components, pages, or design systems. (Aesthetic direction lives in.","platform":"ChatGPT","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-08-16T13:38:25.648943Z","repo":{"url":"https://github.com/sickn33/agentic-awesome-skills","stars":46883,"forks":6831,"license":"MIT","updatedAt":"2026-09-25T05:43:16Z"},"bodyHtml":"<hr>\n<h2>name: design-system\ndescription: \"Mechanical implementation invariants for frontend design: token architecture, typography hierarchy, loading order, FOUT prevention, chrome stability, motion timing, color semantics. Use with design when building components, pages, or design systems. (Aesthetic direction lives in...\"\nrisk: critical\nsource: <a href=\"https://github.com/connerkward/ckw-design-skill/tree/main/design-system\">https://github.com/connerkward/ckw-design-skill/tree/main/design-system</a>\nsource_repo: connerkward/ckw-design-skill\nsource_type: community\ndate_added: 2026-07-01\nlicense: MIT\nlicense_source: <a href=\"https://github.com/connerkward/ckw-design-skill/blob/main/LICENSE\">https://github.com/connerkward/ckw-design-skill/blob/main/LICENSE</a>\nauthor: Conner K Ward</h2>\n<h1>Design system</h1>\n<h2>When to Use</h2>\n<p>Use this skill when you need mechanical implementation invariants for frontend design: token architecture, typography hierarchy, loading order, FOUT prevention, chrome stability, motion timing, color semantics. Use with design when building components, pages, or design systems. (Aesthetic direction lives in...</p>\n<p>Apply with <strong>design</strong> when implementing UI: components, pages, or design systems. Every color, type, and motion choice should trace back to these rules.</p>\n<h2>Token architecture</h2>\n<p>All colors map to a small set of primitives. No random hex values.</p>\n<ul>\n<li><strong>Foreground</strong>: Text hierarchy (primary, secondary, muted).</li>\n<li><strong>Background</strong>: Surface elevation (base, raised, overlay).</li>\n<li><strong>Border</strong>: Separation hierarchy (subtle, default, emphasis).</li>\n<li><strong>Brand</strong>: Identity and primary accent.</li>\n<li><strong>Semantic</strong>: Destructive, warning, success (and optional info).</li>\n</ul>\n<p>Use tokens in code (CSS variables, theme objects); never hardcode hex for UI.</p>\n<h2>Typography</h2>\n<ul>\n<li><strong>Hierarchy</strong>: Headlines — heavier weight, tighter letter-spacing for presence. Body — comfortable weight for readability. Labels/UI — medium weight, works at smaller sizes. Data — monospace, <code>tabular-nums</code> for alignment.</li>\n<li>Combine size, weight, and letter-spacing so hierarchy is clear at a glance. If you squint and can't tell headline from body, hierarchy is too weak.</li>\n<li><strong>Fonts</strong>: pair a display font with a body font; keep hierarchy legible at a glance. <em>Which</em> fonts is direction, not mechanics — pick from the domain and push off your first/default instinct (the mean); see design-spatial §2.</li>\n<li><strong>Data (functional only)</strong>: real aligned numbers, IDs, timestamps in monospace with <code>tabular-nums</code> — mono earns its place when values line up in a column. Do NOT sprinkle mono on decorative eyebrow/metadata microtext (\"35MM · DEVELOP · SCAN\", fake spec captions) for a \"technical\" look — that's the current trend-slop, not data. See design-spatial §2.</li>\n</ul>\n<h2>Loading order — first seen, first loaded</h2>\n<p>The first viewport must paint complete and correct, fast. Order every resource by whether the user sees it first; the rest waits.</p>\n<ul>\n<li><strong>Prioritize only the above-the-fold set</strong> (hero text, hero image/video, brand mark). Preloading everything is the same as preloading nothing — the true criticals lose the bandwidth race. Pick the few things in the first screenful and prioritize <em>those</em>.</li>\n<li><strong>Fonts: self-host WOFF2.</strong> Convert OTF/TTF → WOFF2 (Brotli; ~half the bytes, identical glyphs) and <code>&lt;link rel=\"preload\" as=\"font\" type=\"font/woff2\" crossorigin&gt;</code> the weights used in the first viewport. Never a render-blocking third-party font stylesheet — a Google Fonts <code>&lt;link&gt;</code> adds a CSS round-trip plus extra DNS/TLS before the font even starts downloading; self-host instead.</li>\n<li><strong>LCP image/video:</strong> <code>fetchpriority=\"high\"</code> on the hero image (or the video poster); <code>&lt;link rel=\"preload\" as=\"image\"&gt;</code> it when it's CSS-referenced (the parser can't see CSS <code>url()</code>s early). The hero box must never be empty — ship a poster/low-res placeholder so there's no blank frame.</li>\n<li><strong>Below the fold:</strong> <code>loading=\"lazy\" decoding=\"async\"</code> on images; <code>preload=\"none\"</code> (or <code>\"metadata\"</code>) on video; <code>defer</code> non-critical JS. Always reserve space (<code>aspect-ratio</code>, or <code>width</code>+<code>height</code>) so deferred media can't shift layout (CLS).</li>\n<li>Keep the render-blocking head minimal: inline critical CSS, defer the rest.</li>\n</ul>\n<h2>Never let fonts pop in (no FOUT) — ever</h2>\n<p><code>font-display: swap</code> <strong>is</strong> the pop — it paints a fallback face, then swaps to the webfont and reflows. Do not use it for any text the user watches load (titles, wordmarks, hero copy). The rule is absolute: title/display text must never flash a fallback or reflow.</p>\n<ul>\n<li><strong>Gate visibility on the real font.</strong> Synchronously in <code>&lt;head&gt;</code>, add a <code>fonts-pending</code> class to <code>&lt;html&gt;</code> that holds the display-font text at <code>opacity: 0</code>. On <code>document.fonts.ready</code> — kick it with <code>document.fonts.load('&lt;weight&gt; 1em \"Family\"')</code> for each critical face — swap to <code>fonts-ready</code> and fade the text in (~0.5s). Always include a safety timeout (~2.5s) that reveals regardless, so a font failure can never leave text permanently hidden.</li>\n<li>Pair this with preload + WOFF2 (above) so the hidden window is a few hundred ms, not seconds — the fade reads as intentional, not as a stall.</li>\n<li>For body text where a sub-perceptual swap is tolerable, at minimum kill the reflow: define a fallback <code>@font-face</code> (or <code>font-family</code> fallback) tuned with <code>size-adjust</code> / <code>ascent-override</code> / <code>descent-override</code> so the fallback occupies the same metrics as the webfont and the swap shifts nothing.</li>\n</ul>\n<p>Worked example — an AR product-research page: a head script toggles <code>fonts-pending → fonts-ready</code> (titles fade in on <code>fonts.ready</code>, 2.5s fallback), preloads the four above-the-fold WOFF2 weights, and self-hosts the brand face so there's no Google round-trip.</p>\n<h2>Slow-loading content — never show the ugly intermediate state</h2>\n<p>Anything that <em>could</em> take a noticeable moment to be ready — fonts (above), large images, video, <code>&lt;canvas&gt;</code> scenes, Three.js / WebGL, lazy-loaded React islands, anything that fetches over the network or runs heavy main-thread setup — must either <strong>arrive fast</strong> or <strong>load gracefully</strong>. The default browser behavior (blank box → partial paint → reflow → final state) is the ugly intermediate state. Catch it.</p>\n<p>Two levers; use both:</p>\n<ul>\n<li><strong>Arrive faster.</strong> Compress (WOFF2 for fonts, Draco for glTF, WebP/AVIF for images, h264/h265 for video with <code>preload=\"metadata\"</code>). Preload the <em>few</em> assets the first viewport actually needs (<code>&lt;link rel=\"preload\"&gt;</code>). Lazy-load below-the-fold so the LCP set isn't competing. Reserve the box (<code>aspect-ratio</code>, <code>width</code>+<code>height</code>) so deferred content can't trigger CLS.</li>\n<li><strong>Load gracefully.</strong> Hide the in-flight state behind a styled placeholder, then fade the real thing in. Skeleton boxes, low-res blurred posters, a single ASCII glyph, even just the container's bg color — anything coherent with the design beats the default partial-paint.</li>\n</ul>\n<p>What \"ugly\" looks like, concretely, and the fix:</p>\n<table>\n<thead>\n<tr>\n<th>Symptom</th>\n<th>Fix</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Annotation labels stack at <code>translate(0,0)</code> (top-left of container) until JS positions them</td>\n<td>Start labels at <code>opacity: 0</code> with a <code>transition: opacity ~0.35s</code>; first projection sets inline opacity → CSS fades them up.</td>\n</tr>\n<tr>\n<td>Canvas/WebGL paints empty/black for a frame on first render</td>\n<td>Show a placeholder (CSS art, low-res poster image, or paper/skeleton fill) in the same box; remove it once the first real frame has rendered.</td>\n</tr>\n<tr>\n<td>Lazy image fetches and snaps in with a layout-jump</td>\n<td><code>aspect-ratio</code> + <code>&lt;link rel=\"preload\"&gt;</code> (above-the-fold) or <code>loading=\"lazy\" decoding=\"async\"</code> (below); fade from <code>opacity:0</code> on the <code>load</code> event for the first paint.</td>\n</tr>\n<tr>\n<td>Video poster pops to first frame on play</td>\n<td><code>poster</code> matches a still you control; once <code>playing</code> event fires, you've already had a clean handoff.</td>\n</tr>\n<tr>\n<td>3D model \"appears\" mid-screen with no transition</td>\n<td>Keep the canvas visible but at <code>opacity: 0</code>; toggle a <code>.viewer-ready</code> class (or set inline opacity) inside the GLTFLoader success callback, after the first <code>tick()</code>.</td>\n</tr>\n<tr>\n<td>Lazy React island flashes a fallback that looks worse than no UI</td>\n<td>Replace <code>Suspense</code> fallback with a skeleton that traces the final layout, not a spinner.</td>\n</tr>\n</tbody>\n</table>\n<p>Rule of thumb: if a user could screenshot the page mid-load and you'd be embarrassed, you owe it a graceful state. The placeholder doesn't have to be fancy — it has to be <em>intentional</em>, sized correctly, and in the design language of what's coming.</p>\n<h2>Chrome stays still — status text never resizes layout</h2>\n<p>Persistent chrome (headers, nav, toolbars, search bars, status regions) must hold a <strong>constant height</strong> no matter what text lands in it. Transient status / loading / explanatory copy — \"loading model…\", \"N matching · M indexed\", empty-state hints — must not wrap to a second line and shove adjacent controls down. A status region that grows and shrinks as its message changes is a layout-jank bug, not dynamic content.</p>\n<ul>\n<li><strong>Constrain to one line:</strong> <code>white-space: nowrap; overflow: hidden; text-overflow: ellipsis</code> so the longest message truncates instead of wrapping.</li>\n<li><strong>Reserve the space up front:</strong> give the container a fixed <code>height</code> (or <code>min-height</code>) sized for the message, so the shortest and longest states — and the empty state — occupy the same footprint.</li>\n</ul>\n<p>Only the content area should move while chrome stays fixed; layout shift from transient text reads as broken polish. (Concrete failure this prevents: in a search app, a model-loading message wrapping to two lines and pushing the search bar downward.)</p>\n<h2>Motion</h2>\n<ul>\n<li>Keep timing consistent and purposeful; one well-orchestrated moment (staggered page load with <code>animation-delay</code>) beats scattered micro-interactions. Prefer CSS-only for HTML; Motion library for React. (Honor <code>prefers-reduced-motion</code> for public/multi-user projects.)</li>\n<li><strong>Defaults for restrained/professional UIs</strong> (a starting point, not law): micro-interactions ~150ms, larger transitions 200–250ms, ease-out. A playful/toy-like tone (design-thinking) may want spring/bounce and longer beats — match motion feel to the chosen direction rather than defaulting to these numbers.</li>\n<li><strong>Choreography</strong> — for anything beyond a single micro-interaction (route/page transitions, list reorder, reveals, shared elements), load <a href=\"references/motion-choreography.md\">references/motion-choreography.md</a>: when a transition earns its keep (it must <em>communicate</em> something or get cut), which kinds to implement and in what order, <strong>style by navigation type</strong> (directional slide only for hierarchical/ordered — a slide between peers lies about depth; laterals fade), a duration table, and craft (compositor-only props, motion-blur on morphs, never raster-scale text, persistent-chrome isolation). Framework-agnostic.</li>\n</ul>\n<h3>Scroll-driven narrative (scrollytelling)</h3>\n<p>For <strong>explanatory / editorial / data-walkthrough</strong> content, prefer <strong>scroll-driven graphics over click-interactive widgets</strong>. A reader scrolls by default; making them hunt for and click a toggle to advance an explanation adds friction and gets skipped. Use the NYT/Pudding pattern: pin one graphic (<code>position: sticky</code>) while short text \"steps\" scroll past it, and let each step drive the graphic's state.</p>\n<ul>\n<li><strong>Mechanics:</strong> one <code>IntersectionObserver</code> with <code>rootMargin: '-48% 0px -48% 0px'</code> (threshold 0) so a step goes \"active\" exactly as it crosses the viewport mid-line; the active index re-renders the pinned graphic. ~30 lines — this <em>is</em> scrollama minus the dependency; don't add a scroll library.</li>\n<li><strong>Layout:</strong> two columns — steps scroll in one, the graphic <code>sticky top-0 h-screen</code> in the other; stack on mobile with the graphic sticky on top. Give each step ~85vh so exactly one is centered at a time; dim the inactive step cards (<code>opacity:.3</code>) so the live one reads.</li>\n<li><strong>Graphic is a pure function of the active step</strong> (<code>graphic(active)</code>), holding no click state of its own — so it also screenshots/exports deterministically and degrades to a static figure. Animate <em>between</em> states (color / width / opacity, 300–700ms) so scrolling feels continuous, not steppy.</li>\n<li><strong>When NOT to:</strong> dashboards, tools, forms — anything the user <em>operates</em> rather than <em>reads</em> — stay interactive. Scrollytelling is for <strong>narration</strong>, where you own the order. (Public/multi-user builds: honor <code>prefers-reduced-motion</code> per the Motion note above; keep the state changes but drop the tweens.)</li>\n</ul>\n<h2>Spatial composition &amp; layout</h2>\n<p>Grid systems, the 8-point spacing scale, visual-weight balance, alignment, and the render-then-critique loop live in <strong>design-spatial</strong> (<a href=\"../design-spatial/SKILL.md\">../design-spatial/SKILL.md</a>) — the mechanical counterpart to this file's tokens/type/color. Load it whenever composing pages, dashboards, or components. (Direction nugget that belongs here: match composition ambition to the vision — maximalist earns elaborate/layered code; minimal/refined demands restraint and precise spacing.)</p>\n<h2>Nested radii (only when one rounded element sits inside another)</h2>\n<p>Not a push to round things — this governs the case where a rounded element is nested in\nanother (a button in a card, an inset panel in a container). When nested:</p>\n<ul>\n<li><strong>Child radius ≤ parent radius</strong>, never larger (a child corner rounder than its parent looks\nlike it's bulging out).</li>\n<li><strong>Concentric</strong> is the ideal: <code>child_radius = parent_radius − gap</code> (the padding between them),\nso the two curves run parallel and the inner corner echoes the outer. Flat/unrounded children\nin a rounded parent are fine; what reads as broken is mismatched, non-concentric curves.</li>\n</ul>\n<h2>Color</h2>\n<ul>\n<li><strong>Palette from domain</strong>: colors should feel like they came <em>from</em> the product's world, not applied on top.</li>\n<li><strong>Beyond temperature</strong>: quiet vs loud, dense vs spacious, serious vs playful, geometric vs organic — not just warm/cool.</li>\n<li><strong>Color carries meaning</strong>: gray builds structure; color communicates status, action, emphasis, identity. Unmotivated color is noise. (Restraint — one accent, not five — is a direction principle; see design-thinking → <em>reserve impact for punctuation</em>.)</li>\n<li><strong>Contrast — APCA for decisions, WCAG for the gate.</strong> For <em>perceptual</em> contrast judgments (is this text comfortably readable on this surface?) prefer <strong>APCA</strong> (<a href=\"https://apcacontrast.com/\">apcacontrast.com</a>) — it models lightness perception far better than the WCAG 2 ratio, which mis-rates light-on-dark and mid-tones. Keep <strong>WCAG 2 (4.5 / 3:1) as the compliance floor</strong> — it's what <code>design-spatial</code>'s <code>layout-audit.js</code> gates on and what accessibility standards require. Use APCA to design, WCAG to certify.</li>\n<li><strong>Interactive states gain contrast.</strong> <code>:hover</code>, <code>:active</code>, <code>:focus</code> must read as <em>more</em> prominent than rest — more contrast, not less. A hover that lowers contrast (e.g. lightens text toward the bg) reads as disabled.</li>\n</ul>\n<p>Avoiding the generic/trend look (Inter, purple-on-white, the same dark-glass card) and varying across generations is <strong>design-spatial §2</strong> — not restated here.</p>\n<h2>Backgrounds &amp; detail</h2>\n<p>Atmosphere over flat fills — but matched to the chosen aesthetic, not a default. The reflexive gradient-mesh / noise / grain \"premium\" treatment is itself the designer-trend mean (design-spatial §2); reach for it only when the direction genuinely calls for it, never as decoration for its own sake.</p>\n<h2>Limitations</h2>\n<ul>\n<li>Use this skill only when the task clearly matches its upstream source and local project context.</li>\n<li>Verify commands, generated code, dependencies, credentials, and external service behavior before applying changes.</li>\n<li>Do not treat examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.</li>\n</ul>\n","files":[{"path":"references/motion-choreography.md","sizeBytes":6541,"isText":true},{"path":"SKILL.md","sizeBytes":15385,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-08-16T13:41:00.74387Z","sha256":"D7338C15F3B3337DD37180899AFAD2182F825892E2FC497AA19AE43CC08ED4DE","sizeBytes":10492},"review":null,"source":{"repositoryUrl":"https://github.com/sickn33/agentic-awesome-skills","path":"skills/design-system","license":"MIT","commit":"f2bba339de74414b0771234cbe4f6a15258e32a3","subtreeSha":"F5BFFE725EA3CD69BDBCF8C1FDE91B3BD7C017AC921A0AD3E2345D84F6A3A0D4","lastSyncedAt":"2026-09-25T06:48:39.853703Z"},"reviewedAt":"2026-08-16T13:44:27.880714Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/sickn33/agentic-awesome-skills/tree/main/skills/design-system"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install sickn33-agentic-awesome-skills@llmmart"},{"target":"git","command":"git clone https://github.com/sickn33/agentic-awesome-skills.git"}]}