agentsclimarketplace

Design system

Skill connerkward/ckw-design-skill/design-system

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 design-thinking; anti-trend/anti-slop in design-spatial.)From its SKILL.md

Install
npx -y skills add connerkward/ckw-design-skill --skill design-system

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

14.1 KB, ~3.3k tokens by cl100k_base, as published. Nobody here has run it

Design system

Apply with design when implementing UI: components, pages, or design systems. Every color, type, and motion choice should trace back to these rules.

Token architecture

All colors map to a small set of primitives. No random hex values.

  • Foreground: Text hierarchy (primary, secondary, muted).
  • Background: Surface elevation (base, raised, overlay).
  • Border: Separation hierarchy (subtle, default, emphasis).
  • Brand: Identity and primary accent.
  • Semantic: Destructive, warning, success (and optional info).

Use tokens in code (CSS variables, theme objects); never hardcode hex for UI.

Typography

  • Hierarchy: Headlines — heavier weight, tighter letter-spacing for presence. Body — comfortable weight for readability. Labels/UI — medium weight, works at smaller sizes. Data — monospace, tabular-nums for alignment.
  • 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.
  • Fonts: pair a display font with a body font; keep hierarchy legible at a glance. Which fonts is direction, not mechanics — pick from the domain and push off your first/default instinct (the mean); see design-spatial §2.
  • Data (functional only): real aligned numbers, IDs, timestamps in monospace with tabular-nums — 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.

Loading order — first seen, first loaded

The first viewport must paint complete and correct, fast. Order every resource by whether the user sees it first; the rest waits.

  • Prioritize only the above-the-fold set (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 those.
  • Fonts: self-host WOFF2. Convert OTF/TTF → WOFF2 (Brotli; ~half the bytes, identical glyphs) and <link rel="preload" as="font" type="font/woff2" crossorigin> the weights used in the first viewport. Never a render-blocking third-party font stylesheet — a Google Fonts <link> adds a CSS round-trip plus extra DNS/TLS before the font even starts downloading; self-host instead.
  • LCP image/video: fetchpriority="high" on the hero image (or the video poster); <link rel="preload" as="image"> it when it's CSS-referenced (the parser can't see CSS url()s early). The hero box must never be empty — ship a poster/low-res placeholder so there's no blank frame.
  • Below the fold: loading="lazy" decoding="async" on images; preload="none" (or "metadata") on video; defer non-critical JS. Always reserve space (aspect-ratio, or width+height) so deferred media can't shift layout (CLS).
  • Keep the render-blocking head minimal: inline critical CSS, defer the rest.

Never let fonts pop in (no FOUT) — ever

font-display: swap is 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.

  • Gate visibility on the real font. Synchronously in <head>, add a fonts-pending class to <html> that holds the display-font text at opacity: 0. On document.fonts.ready — kick it with document.fonts.load('<weight> 1em "Family"') for each critical face — swap to fonts-ready 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.
  • 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.
  • For body text where a sub-perceptual swap is tolerable, at minimum kill the reflow: define a fallback @font-face (or font-family fallback) tuned with size-adjust / ascent-override / descent-override so the fallback occupies the same metrics as the webfont and the swap shifts nothing.

Worked example — an AR product-research page: a head script toggles fonts-pending → fonts-ready (titles fade in on fonts.ready, 2.5s fallback), preloads the four above-the-fold WOFF2 weights, and self-hosts the brand face so there's no Google round-trip.

Slow-loading content — never show the ugly intermediate state

Anything that could take a noticeable moment to be ready — fonts (above), large images, video, <canvas> scenes, Three.js / WebGL, lazy-loaded React islands, anything that fetches over the network or runs heavy main-thread setup — must either arrive fast or load gracefully. The default browser behavior (blank box → partial paint → reflow → final state) is the ugly intermediate state. Catch it.

Two levers; use both:

  • Arrive faster. Compress (WOFF2 for fonts, Draco for glTF, WebP/AVIF for images, h264/h265 for video with preload="metadata"). Preload the few assets the first viewport actually needs (<link rel="preload">). Lazy-load below-the-fold so the LCP set isn't competing. Reserve the box (aspect-ratio, width+height) so deferred content can't trigger CLS.
  • Load gracefully. 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.

What "ugly" looks like, concretely, and the fix:

SymptomFix
Annotation labels stack at translate(0,0) (top-left of container) until JS positions themStart labels at opacity: 0 with a transition: opacity ~0.35s; first projection sets inline opacity → CSS fades them up.
Canvas/WebGL paints empty/black for a frame on first renderShow 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.
Lazy image fetches and snaps in with a layout-jumpaspect-ratio + <link rel="preload"> (above-the-fold) or loading="lazy" decoding="async" (below); fade from opacity:0 on the load event for the first paint.
Video poster pops to first frame on playposter matches a still you control; once playing event fires, you've already had a clean handoff.
3D model "appears" mid-screen with no transitionKeep the canvas visible but at opacity: 0; toggle a .viewer-ready class (or set inline opacity) inside the GLTFLoader success callback, after the first tick().
Lazy React island flashes a fallback that looks worse than no UIReplace Suspense fallback with a skeleton that traces the final layout, not a spinner.

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 intentional, sized correctly, and in the design language of what's coming.

Chrome stays still — status text never resizes layout

Persistent chrome (headers, nav, toolbars, search bars, status regions) must hold a constant height 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.

  • Constrain to one line: white-space: nowrap; overflow: hidden; text-overflow: ellipsis so the longest message truncates instead of wrapping.
  • Reserve the space up front: give the container a fixed height (or min-height) sized for the message, so the shortest and longest states — and the empty state — occupy the same footprint.

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.)

Motion

  • Keep timing consistent and purposeful; one well-orchestrated moment (staggered page load with animation-delay) beats scattered micro-interactions. Prefer CSS-only for HTML; Motion library for React. (Honor prefers-reduced-motion for public/multi-user projects.)
  • Defaults for restrained/professional UIs (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.
  • Choreography — for anything beyond a single micro-interaction (route/page transitions, list reorder, reveals, shared elements), load references/motion-choreography.md: when a transition earns its keep (it must communicate something or get cut), which kinds to implement and in what order, style by navigation type (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.

Scroll-driven narrative (scrollytelling)

For explanatory / editorial / data-walkthrough content, prefer scroll-driven graphics over click-interactive widgets. 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 (position: sticky) while short text "steps" scroll past it, and let each step drive the graphic's state.

  • Mechanics: one IntersectionObserver with rootMargin: '-48% 0px -48% 0px' (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 is scrollama minus the dependency; don't add a scroll library.
  • Layout: two columns — steps scroll in one, the graphic sticky top-0 h-screen 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 (opacity:.3) so the live one reads.
  • Graphic is a pure function of the active step (graphic(active)), holding no click state of its own — so it also screenshots/exports deterministically and degrades to a static figure. Animate between states (color / width / opacity, 300–700ms) so scrolling feels continuous, not steppy.
  • When NOT to: dashboards, tools, forms — anything the user operates rather than reads — stay interactive. Scrollytelling is for narration, where you own the order. (Public/multi-user builds: honor prefers-reduced-motion per the Motion note above; keep the state changes but drop the tweens.)

Spatial composition & layout

Grid systems, the 8-point spacing scale, visual-weight balance, alignment, and the render-then-critique loop live in design-spatial (../design-spatial/SKILL.md) — 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.)

Nested radii (only when one rounded element sits inside another)

Not a push to round things — this governs the case where a rounded element is nested in another (a button in a card, an inset panel in a container). When nested:

  • Child radius ≤ parent radius, never larger (a child corner rounder than its parent looks like it's bulging out).
  • Concentric is the ideal: child_radius = parent_radius − gap (the padding between them), so the two curves run parallel and the inner corner echoes the outer. Flat/unrounded children in a rounded parent are fine; what reads as broken is mismatched, non-concentric curves.

Color

  • Palette from domain: colors should feel like they came from the product's world, not applied on top.
  • Beyond temperature: quiet vs loud, dense vs spacious, serious vs playful, geometric vs organic — not just warm/cool.
  • Color carries meaning: 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 → reserve impact for punctuation.)
  • Contrast — APCA for decisions, WCAG for the gate. For perceptual contrast judgments (is this text comfortably readable on this surface?) prefer APCA (apcacontrast.com) — it models lightness perception far better than the WCAG 2 ratio, which mis-rates light-on-dark and mid-tones. Keep WCAG 2 (4.5 / 3:1) as the compliance floor — it's what design-spatial's layout-audit.js gates on and what accessibility standards require. Use APCA to design, WCAG to certify.
  • Interactive states gain contrast. :hover, :active, :focus must read as more prominent than rest — more contrast, not less. A hover that lowers contrast (e.g. lightens text toward the bg) reads as disabled.

Avoiding the generic/trend look (Inter, purple-on-white, the same dark-glass card) and varying across generations is design-spatial §2 — not restated here.

Backgrounds & detail

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.

What ships with it: 1 file

6.4 KB alongside SKILL.md

references/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.