Developer guide
WCKD UI is a starter kit, not a complete design system. This guide explains how the system is layered: tokens, layout, utilities, then JS-backed components when you need them. Use component pages for markup contracts and demos.
Overview
Start here whether you are evaluating WCKD UI or onboarding a team. Three steps, then a short checklist you can reuse in pull requests.
1) Understand the core
Read Mindset, System logic, and Utility rules so parent vs child utilities and theme.css ownership are clear.
2) Ship one section
Copy a block from Patterns, then adapt text/media only. Avoid custom CSS until structure is correct.
3) Theme and expand
Set tokens in Design tokens and theme.css, then add Components only where interaction is required.
Fast checklist
- Use .columns for structure.
- Use parent utilities for shared child styling.
- Use child utilities only for intentional exceptions.
- Set brand in theme.css tokens before custom selectors.
- Add component roots only when interaction is needed.
Page assembly workflow
Use this sequence when building a new page. It keeps structure clear, minimizes CSS drift, and makes code review predictable across teams.
1) Define regions
Draft the page shell first: header, main, optional aside, and footer.
2) Compose sections
Use patterns and .columns layout before writing custom selectors.
3) Apply tokens
Set brand and rhythm in theme.css variables, then verify utilities inherit correctly.
4) Add interaction
Add wckd-* roots only where state/behavior is required, then copy exact component markup.
5) QA and handoff
Check mobile/desktop, keyboard focus, and scroll/runtime hooks before merge.
Code example
<!-- Step 1: shell and regions -->
<main class="wckd-page">
<header>...</header>
<main id="main">
<!-- Step 2: section composition with utilities -->
<section class="columns count-2 gap-l padded filled-light rounded">...</section>
<!-- Step 3: tokens — set once in theme.css; utilities inherit (no extra markup here) -->
<!-- Step 4: add one interactive root only where needed -->
<section class="wckd-accordion columns count-1">...</section>
</main>
<footer>...</footer>
</main>
Pattern vs component vs theme override
Choose the smallest tool that solves the problem. Start with structure and tokens, then escalate only when behavior is needed.
Use a pattern
For full page sections: heroes, card rails, comparisons, and story blocks.
Goal: compose content quickly with known utility contracts.
Use a component
When the UI needs state and interaction: tabs, overlays, accordion, carousel, menu.
Goal: rely on documented behavior markup instead of custom JS.
Use theme.css
For brand decisions: color, type rhythm, spacing scale, motion and small site overrides.
Goal: keep core files vendored and updates pullable.
Escalation rule
1) Semantic HTML
2) Utility classes
3) Token override in theme.css
4) Component root (wckd-*) for behavior
5) Custom JS only when no documented component fits
Site-level contracts and governance
Teams building multiple pages should agree on a small set of shared contracts. This prevents drift between marketing, docs, and product templates.
Template contracts
- One shell model (
.wckd-page+ semantic regions) across templates. - Shared nav/footer markup conventions and focus order.
- Parent-first utility usage for repeated card/list treatment.
- Known runtime hooks (scroll classes,
in-view, theme attr) documented per template.
Ownership contracts
- Choose one source of truth per project: root bundle files or starter kit bundle, not a mix.
- Treat core CSS/JS as vendored; avoid brand edits inside those files.
- Put token and project rules in
theme.cssand local templates. - Trim via package selection or bundler inputs, not by deleting random core blocks.
Definition of done
Before merge:
- Shell and sections render correctly on mobile and desktop
- Interactive roots match component docs exactly
- Keyboard and focus states verified
- No brand overrides were added to core kit files
- theme.css contains new token decisions
Mindset: starter kit → your design system
You are meant to outgrow the defaults in a controlled way. The shipped CSS and JS stay a thin foundation: layout primitives, tokens, and interactive roots. Your design system is what you maintain day to day: token choices, spacing rhythm, which utilities are allowed in your app, and any components you add or wrap.
Treat core files as vendored
Keep wckd-ui.css, wckd-ui-components.css, and wckd-ui.js close to upstream when you want fixes and new patterns.
Avoid one-off edits inside them; merge updates from releases instead of drifting forever.
Own everything after core
theme.css (loaded last) is your contract: brand colors, type scale tweaks, spacing, motion, and small site-only rules. Templates, content models, and team conventions live next to that, not inside the kit files.
HTML-first: semantic regions and headings first, then utility classes for layout and surface. Add wckd-* hooks and wckd-ui.js only when the UX is genuinely interactive (menus, tabs, overlays, carousels). If CSS and tokens can do it, stop there. Fewer moving parts, easier ownership.
Documentation map
Match the page to the task so this guide stays scannable and deep detail stays on the right URL.
First load: WCKD UI CSS + JS, a theme.css, and a minimal page shell you can copy.
Per-pattern pages: expected markup, demos, accessibility notes, and kit slices when you trim bundles.
Larger compositions (heroes, rails) you paste and edit, not abstract APIs.
This style guide (sections below)
System logic, utility rules, tokens, typography, colour, media, controls. Treat this page as a reference spine while you build.
1) System logic: how WCKD UI works
The kit is opinionated so your app does not have to invent structure every sprint: .columns for regions, parent utilities for repeated treatment, child utilities for intentional exceptions, then typography and surfaces driven by tokens (see Mindset). That stack is what you teach once and reuse everywhere you own the design system.
1) Layout contract
.columns defines layout structure. Parent classes set count, gap, and repeated child treatments.
2) Child intent
Children declare span, order, size, and alignment with utilities, not one-off selectors.
3) Brand control
Tokens in theme.css control typography, color, spacing, and depth across the system.
4) Progressive behavior
Add JS-backed components only where interaction is needed; keep base layout utility-first.
Code example
<!-- Parent sets count, gap, and shared surface on all columns -->
<section class="columns count-3 gap padded filled-light rounded">
<article class="column span-2 desktop-span-1">Content</article>
<aside class="column">Meta</aside>
<aside class="column">CTA</aside>
</section>
2) Utility architecture: structure and rules
This is the operating spec for wckd-ui.css. Layers are intentional: tokens, then structure, then surfaces, then optional JS-backed behaviour. When your team extends the system, add rules in theme.css or a product stylesheet. Do not fork the core layer for brand tweaks.
Composition order (always)
- Token layer: set design decisions in
theme.cssvariables. - Structure layer: define the Grid with
.columns,.count-*, and gaps. - Visual layer: add surfaces (
fill,round,border,shadow). - Behavior layer: add one
wckd-*root when interaction is required.
Parent vs child utility decision
Parent (shared treatment)
.padded, .filled, .rounded, .borders, .shadows
Child (intentional difference)
.pad, .fill, .round, .border, .align-*, .span-*
Code example
<!-- Preferred: parent controls repeated child treatment -->
<div class="columns count-3 gap padded filled-light rounded borders">
<div class="column">A</div>
<div class="column">B</div>
<div class="column">C</div>
</div>
<!-- Override only where a child must differ -->
<div class="column pad-s fill-dark round white-text">Intentional exception</div>
Reference map
Structure: .columns .count-* .locked .span-* .desktop-span-* .order-* .desktop-order-*
Spacing: .gap .pad .padded .margin
Surfaces: .fill .filled .round .rounded .border .borders .shadow .shadows
Positioning: .relative .fixed .sticky .layer-* .align-*
Visibility/runtime: .desktop-only .mobile-only .animate .image-zoom
Utility patterns
Start from the inspiration gallery for remix ideas, then apply this guide's composition order: layout, spacing, surface, type, then behavior.
Hero shell
Responsive cards
Feature A
Stacked on small screens.
Feature B
Feature C
Form + content
Use utilities for spacing and frame, then component classes for behavior.
Code example
<!-- Pattern: hero slot + card row; swap media and copy only -->
<div class="columns count-2 gap filled-light rounded borders padded">
<article class="column span-2 mobile-span-1">Primary</article>
<article class="column">Secondary</article>
<article class="column">Tertiary</article>
</div>
<div class="height-30 relative hide-overflow round">
<div class="background-image"><img src="..." alt=""></div>
<div class="overlay tint-dark align-left align-bottom pad">...</div>
</div>
Class reference
Composition starter set:
.columns .count-* .span-* .desktop-span-* .gap-*
.fill-* .round* .border* .shadow
.pad-* .margin-* .text-* .white-text
.relative .hide-overflow .overlay .tint-light/.tint-dark
.button .button.outline .wckd-form
3) Adoption path (existing or new builds)
Onboard in this order so ownership stays clear: brand and tokens first, then the Grid and page shell regions, then interactive components. That sequence limits CSS drift and gives design and engineering one contract. The kit stays thin; your theme.css and templates absorb product specifics.
1) Tokens first
Set brand variables in theme.css (--primary-color, text scale, spacing) before touching component styles.
2) Structure with utilities
Lay out sections on the Grid: .columns, .count-*, .gap-*, .pad-*, .fill-*, .round*. App-wide header, main, aside, and footer use the page shell (.wckd-page), not the Grid alone.
3) Add behavior components
Only then add JS-backed components (menu, carousel, tabs, tooltips, youtube, etc.) where interaction is required.
4) Harden and trim
Check responsive spans, focus states, and media overlays; trim unused components for leaner custom bundles.
Code example
/* theme.css: brand contract (load after wckd-ui.css + wckd-ui-components.css) */
:root {
--primary-color: #0b6ea8;
--text-color: #1d1f24;
--size-m: 1rem;
}
<!-- Page shell: utility-first main + one section -->
<main class="max-width pad-xl">
<section class="columns count-2 gap-l">...</section>
</main>
4) Design tokens (theme source of truth)
Tokens are where your design system commits to numbers and colours. Set overrides in theme.css (or equivalent) so utilities and components inherit one source of truth. That file is the main lever when the brand evolves.
--background-color-light
--background-color-dark
--background-color-fade
Brand blue
Brand orange
Semantic green
Colour variables
--primary-color
--link-color
--link-hover-color
--background-color
--background-color-light
--background-color-dark
--background-color-fade
--text-color
--text-color-header
--text-color-secondary
--line-color
--yellow --pink --blue --orange --green
--purple --white --red --beige --brown --black
Scale variables
--size-xxs .. --size-xxl
--text-xxs .. --text-9xl
--width-xxs .. --width-xxl
--image-xxs .. --image-xxl
--border-thin, --border-medium, --border-thick
--shadow
Code example
/* Example: hero reads colours from tokens, not hard-coded in every template */
:root {
--primary-color: #0077aa;
--background-color-light: #f5f5f5;
}
.hero {
background: var(--background-color-fade);
color: var(--text-color);
}
Class reference
Background utilities (map to tokens):
.fill .fill-light .fill-dark .fill-fade
.background-color .background-color-light
.background-color-dark .background-color-fade
5) Typography foundations
Typography is built for hierarchy and readability. Use real h1–h6 and p first, then add text-* utilities only when the visual scale should differ from the default for that tag.
Headings
Heading one
Heading two
Heading three
Heading four
Large body for intros and hero support.
Default body for pages and documentation.
Small body for metadata and captions.
Fine print and legal.
Code example
<!-- Semantic heading levels first; add text-* only when scale must differ from default -->
<h1>Heading one</h1>
<h2>Heading two</h2>
<h3>Heading three</h3>
<h4>Heading four</h4>
<p class="text-l">Large body</p>
<p>Default body</p>
<p class="text-s">Small body</p>
<p class="fine-print">Fine print</p>
6) Type utilities: scale and treatments
Type utilities keep scale and weight consistent. Pair them on purpose so dense dashboards and simple marketing pages both stay readable.
Scale
text-xxs
text-xs
text-s
text-m
text-l
text-xl
text-xxl
text-3xl
text-4xl
text-5xl
text-6xl
text-7xl
text-8xl
text-9xl
Weight
text-light (200)
text-regular (300)
text-normal (300)
text-medium (500)
text-semibold (600)
text-bold (700)
text-extra-bold (900)
Treatment
uppercase
fade
text-shadow
Code example
<!-- Pair scale, weight, and tone on running copy -->
<p class="text-xl text-medium">Display line</p>
<p class="text-s fade">Supporting line</p>
Class reference
Scale: .text-xxs … .text-xxl / .text-3xl … .text-9xl
Weight: .text-light .text-regular .text-normal .text-medium .text-semibold
.text-bold (alias .text-strong, <b>) .text-extra-bold (alias .text-extra-strong)
Treatment: .uppercase .fade .text-shadow
Token reference (fragment)
--text-xxs: 0.625rem;
--text-xs: 0.75rem;
--text-s: 0.875rem;
--text-m: 1rem;
--text-l: clamp(1.125rem, 1.05vw, 1.25rem);
--text-xl: clamp(1.25rem, 1.35vw, 1.5rem);
--text-xxl: clamp(1.5rem, 1.8vw, 1.875rem);
--text-3xl … --text-9xl (see wckd-ui.css :root)
Font weight tokens (map to utility classes of the same name, except
--text-bold matches .text-bold / b / .text-strong; --text-extra-bold matches
.text-extra-bold / .text-extra-strong):
--text-light: 200;
--text-regular: 300;
--text-normal: 300;
--text-medium: 500;
--text-semibold: 600;
--text-strong: 700;
--text-bold: 700;
--text-extra-strong: 900;
--text-extra-bold: 900;
--text-shadow: (tuned in :root and [data-theme="light"] for .text-shadow)
7) Colour and surfaces
Use semantic surface tokens for UI states and raw palette utilities for accents. This keeps interfaces balanced while preserving expressive brand color moments.
.yellow
.pink
.blue
.orange
.green
.purple
.white
.red
.beige
.brown
.black
Text colour
blue-text · orange-text · green-text · red-text
Highlight
yellow-highlight · blue-highlight · green-highlight
surface-info
surface-success
surface-warning
surface-danger
Code example
<!-- Palette surfaces + text-colour helpers in body copy -->
<div class="blue white-text round pad">Callout</div>
<p><span class="orange-text">Emphasis</span> in body copy.</p>
Class reference
Surfaces: .yellow .pink .blue .orange .green .purple
.white .red .beige .brown .black
Semantic surfaces: .surface-info .surface-success .surface-warning .surface-danger
.text helpers: .black-text .white-text
Text: .*-text (palette name + -text)
Highlight: .*-highlight
Token reference
--yellow:#F2C94C; --pink:#E91E63; --blue:#0077AA;
--orange:#F2994A; --green:#27AE60; --purple:#9B51E0;
--white:#FFFFFF; --red:#EB5757; --beige:#A1887F;
--brown:#6D4C41; --black:#24292E;
--surface-info / --surface-success / --surface-warning / --surface-danger
8) Spacing system: padding, margin, and gap
Spacing uses one shared scale across the system. Apply element spacing with pad*/margin* and layout gutters with gap* for predictable rhythm.
Padding
pad-s
pad / pad-m
pad-l
Child A
Child B
Child C
Margin
Gap
Uniform: gap-s, gap-l. Axis-only: gap-column-s + gap-row-l (two rows so row-gap shows).
A
B
C
A
B
C
A
B
C
D
E
F
Code example
<!-- Padding (self) -->
<div class="columns count-3 gap rounded filled-light">
<div class="column pad-s">pad-s</div>
<div class="column pad">pad / pad-m</div>
<div class="column pad-l">pad-l</div>
</div>
<!-- Padding (children) -->
<div class="columns count-3 gap padded-l rounded filled-light">
<div class="column">Child A</div>
<div class="column">Child B</div>
<div class="column">Child C</div>
</div>
<!-- Margin -->
<div class="columns count-1 padded rounded filled-light">
<div class="column margin-bottom-s">margin-bottom-s</div>
<div class="column margin-bottom">margin-bottom</div>
<div class="column margin-bottom-l">margin-bottom-l</div>
</div>
<!-- Gap -->
<div class="columns count-3 gap-s padded rounded filled-light">…</div>
<div class="columns count-3 gap-l padded rounded filled-light">…</div>
<div class="columns count-3 gap-column-s gap-row-l padded rounded filled-light">
<!-- six cells: row-gap between rows, column-gap between columns -->
</div>
Class reference
Padding (self):
.pad-0 .pad-xxs .pad-xs .pad-s .pad/.pad-m .pad-l .pad-xl .pad-xxl
.pad-top-* .pad-right-* .pad-bottom-* .pad-left-*
Padding (children): .padded-* (same scale)
Margin: .margin-0 … .margin-xxl
.margin-top-* .margin-right-* .margin-bottom-* .margin-left-*
Gap: .gap-* (both axes); .gap-row-* (row-gap); .gap-column-* (column-gap) — combine for mixed gutters
9) Layout engine: columns and child intent
This is the center of WCKD UI. Treat .columns as your layout API: parent classes define structure, child classes define intent, and layouts remain legible during iteration and redesign.
Parent controls
.columns .count-* .locked .gap-* .gap-row-* .gap-column-* .padded-* .filled-* .rounded-* .borders* .shadows .match-height .fit-content
Child controls
.span-* .mobile-span-* .desktop-span-* .order-* .desktop-order-* .width-* .height-* .align-* .text-left/center/right
Code example
<!-- Parent first -->
<div class="columns count-3 gap-l padded-s filled-light rounded">
<!-- Then child controls -->
<div class="column span-2 desktop-span-1 order-2 desktop-order-1">A</div>
<div class="column order-1 desktop-order-2">B</div>
<div class="column">C</div>
</div>
<!-- Locked counts: demo/testing only -->
<div class="columns count-3 locked gap">
<div class="column">A</div>
<div class="column span-2 desktop-span-1">B spans two on mobile, one on desktop</div>
<div class="column">C</div>
</div>
Class reference
The Grid — .columns .count-1 … .count-6 .locked
.span-1 .span-2 .span-3 .span-4 .span-5 .span-6
.mobile-span-1 … .mobile-span-6 (≤590px only)
.desktop-span-1 … .desktop-span-6
.order-1 .order-2 .desktop-order-1 .desktop-order-2
.fit-content .match-height
Display / flex: .flow .grid .flex .flex-wrap .min-w-0 .flex-column
.space-between .space-around
.aligned-left .aligned-center .aligned-right .aligned-top .aligned-middle .aligned-bottom
.aligned-start .aligned-end .flex-grow .flex-shrink-1 .flex-basis-0
.relative .fixed .sticky .hide-overflow
Layout edges
These are not WCKD UI bugs: they are normal CSS grid and flex interactions authors hit in any utility-first layout. The kit already sets min-width: 0 on direct .columns children so tracks can shrink; problems usually appear inside a cell (for example a horizontal flex row that refuses to wrap or clip).
When flex content overflows a narrow column
Flex items default to min-width: auto, so a row of checkbox + label + meta can overflow instead of wrapping. Combine .flex-wrap on the flex container with .min-w-0 on the text child that should shrink and wrap. Use .flex-grow for the usual flex: 1 shorthand, or add .flex-shrink-1 and .flex-basis-0 when you want those longhands explicitly (for example next to a custom flex-grow in your CSS).
<label class="column flex flex-wrap gap-xs aligned-top">
<input type="checkbox" />
<span class="text-s min-w-0 flex-grow">Long label v1.0.0</span>
</label>
When nested grids feel “broken”
A .columns.count-2 inside a half-width parent gives each track roughly a quarter of the page. Previews, file lists, and long tokens then break awkwardly. Prefer a single column for that region, stack sections vertically, or reserve multi-column grids for full-width parents.
Keep deep layout gotchas here in the developer guide. Use support for project help, not for documenting every CSS sharp corner.
Page shell: app-level layout regions
When a page needs persistent structure beyond a single content block, use the page shell classes. They give teams stable regions for header, main content, optional sidebar, and footer while preserving utility-first composition inside each region.
Shell contract
.wckd-page wraps the viewport. Inside it, use semantic header, main, optional aside, and footer.
When main includes an aside, the shell automatically becomes a two-column layout on desktop and stacks on narrow screens.
Token defaults
--header-height and --sidebar-width (defined next to it in the theme) set the shell. Put main content first and <aside> second for a right sidebar, or add class="wckd-sidebar-left" on <main> and place <aside> first for a left sidebar. The footer has no min-height; it sizes from its content.
Open the Page shell pattern for a copy/paste implementation.
Code example
<!-- Full viewport shell: header, main (optional aside), footer -->
<div class="wckd-page">
<header>...</header>
<main>
<section>...</section>
<aside>...</aside>
</main>
<footer>...</footer>
</div>
10) Surface depth and borders
Surfaces set visual hierarchy quickly. Use self-targeting utilities for single blocks and child-targeting variants for repeated grids to keep pages cohesive.
Self
Children
A
B
C
D
Code example
<!-- Self surface on one block -->
<div class="fill-light round-s border-medium pad-s">Card</div>
<!-- Parent applies the same surface to every child in the grid -->
<div class="columns count-2 gap padded filled-light rounded borders shadows">
<div class="column">A</div>
<div class="column">B</div>
</div>
Class reference
Self: .fill .fill-light .fill-dark .fill-fade
.round .round-m .round-xxs … .round-xxl
.border .border-* .border-0 .border-thin .border-medium .border-thick
.shadow
Children: .filled .filled-light .filled-dark .filled-fade
.rounded-* .borders .borders-* .borders-thin .borders-medium .borders-thick
.shadows
11) Size controls
Use size utilities to set explicit bounds before alignment. Width and height helpers make placement predictable across breakpoints.
Width utilities
Percent widths
Height utilities
Growth inside a flex row
Code example
<!-- Width / height utilities inside a two-column demo grid -->
<div class="columns count-2 gap-l">
<div class="column">
<div class="columns count-1 gap-s rounded padded filled-light">
<div class="column width-s">width-s</div>
<div class="column width-m">width-m</div>
<div class="column width-l">width-l</div>
</div>
</div>
<div class="column">
<div class="columns count-3 gap rounded padded filled-light">
<div class="column height-s">height-s</div>
<div class="column height-m">height-m</div>
<div class="column height-l">height-l</div>
</div>
<div class="flex gap-s rounded padded filled-light">
<div>fixed</div>
<div class="flex-grow">flex-grow</div>
<div>fixed</div>
</div>
</div>
</div>
Class reference
Width: .max-width .width-xxs … .width-xxl (set --max-width in your theme.css)
.width-10 … .width-100
Height: .height-xxs … .height-xxl
.height-10 … .height-100
Flex sizing: .flex-grow .flex-shrink-1 .flex-basis-0
12) Alignment controls
Parent: aligned-*, space-between, and space-around on a flex or grid container move or space its children. One grid cell: align-* pairs use justify-self / align-self (great on grid; flex rows usually ignore justify-self, so do not use align-right to pin a flex item—see below). Copy: text-left / text-center / text-right.
Basics — flex row
Basics — grid child + text
.text-left
.text-center
.text-right
Common content patterns
Toolbar row (trailing action), vertical stack in a column, image overlay.
List / toolbar row. .align-right on a flex item does not flush it right (that helper is justify-self). Grow the middle or use space-between.
Column stack. On flex-column, main axis is vertical: aligned-left|center|right = top/middle/bottom. aligned-bottom = cross-axis (often right in LTR), not the bottom of the box—use aligned-right to pack to the lower edge.
Overlay on media. Default overlay is a flex row. One wrapper + align-bottom pins that block to the lower edge; or use flex-column + aligned-right on the overlay.
Markup snippets
<div class="flex aligned-center">…</div>
<div class="flex space-between">…</div>
<!-- Row: trailing control -->
<div class="flex aligned-middle gap">
<img class="thumbnail" src="…" alt="" />
<div class="flex-grow min-w-0">…</div>
<span class="round">icon</span>
</div>
<!-- Column: content to lower edge -->
<div class="flex flex-column aligned-right gap">…</div>
<!-- Grid cell -->
<div class="grid height-20">
<span class="align-bottom align-right">…</span>
</div>
Class reference
Parent (flex row): .aligned-left .aligned-center .aligned-right .aligned-top .aligned-middle .aligned-bottom .space-between .space-around
Parent (flex-column): .aligned-left|center|right → vertical pack; .aligned-top|middle|bottom → horizontal pack (“bottom” of column = .aligned-right)
Grid child: .align-top .align-right .align-bottom .align-left .align-middle .align-center
Flex row body: .flex-grow .min-w-0 .flex-wrap .flex-shrink-1 .flex-basis-0 (see Layout edges)
Text: .text-left .text-center .text-right
13) Visibility controls
Use breakpoint and opacity helpers for state and emphasis changes without custom selectors.
Breakpoint visibility
desktop-only show only on desktop widths.
mobile-only show only on narrow viewports.
mobile-width-full force full width on mobile.
Opacity helpers
Code example
<!-- Breakpoint visibility + opacity on children -->
<div class="desktop-only">Desktop content</div>
<div class="mobile-only">Mobile content</div>
<div class="columns count-3 gap-s">
<div class="column opacity-3">Muted</div>
<div class="column opacity-6">Medium</div>
<div class="column opacity-9">Strong</div>
</div>
Class reference
.desktop-only .mobile-only .mobile-width-full
.opacity-1 … .opacity-10 .opacity-85
14) Overlay and media readability effects
Overlay utilities keep text readable on media without custom wrapper CSS. Tints and effects are lightweight so teams can ship consistent hero and card treatments quickly.
Code example
<!-- Media slot + overlay for readable text on imagery -->
<div class="column hide-overflow round height-30">
<div class="background-image">
<img class="round" src="photo.jpg" alt="" />
</div>
<div class="overlay tint-light aligned-center aligned-middle">…</div>
</div>
Class reference
Overlay: .overlay
Tints: .tint-light .tint-dark
Surface: .blur .fade
Alignment: .aligned-center .aligned-middle (and other .aligned-*)
15) Layering and z-index system
Layer helpers provide a controlled z-index scale. Use them to avoid ad-hoc stacking values and keep interactive surfaces predictable.
Pin and floor helpers: .layer-top and .layer-bottom.
Code example
<!-- Fixed z-index scale: layer-1 (low) through layer-4 (high) -->
<div class="fill-fade round relative height-20 padded-s">
<div class="layer-1 yellow round-xs width-75" style="position:absolute;top:12px;left:12px;">layer-1</div>
<div class="layer-2 blue round-xs width-66" style="position:absolute;top:44px;left:44px;">layer-2</div>
<div class="layer-3 purple round-xs width-50" style="position:absolute;top:76px;left:76px;">layer-3</div>
</div>
Class reference
.layer-1 .layer-2 .layer-3 .layer-4
.layer-top .layer-bottom
16) Media system: still, video, and embed slots
Component pages: Images & media slots · Facepile (overlapping portraits / avatar stack).
Media uses one slot model for stills, video, and embeds. Templates stay stable while content changes. Use <ws-youtube> when you need managed, performance-friendly YouTube behavior.
Still images
img.thumbnail|medium|large, .image-left|center|right, .aspect* for ratio locks.
Background loops
Use .relative height-* hide-overflow + .background-image and a video child (slot CSS applies object-fit: cover).
Embeds
Use an iframe in the same .background-image slot, or ws-youtube for managed/lazy player behavior.
Background image
Parent is relative hide-overflow round height-30; slot uses background-image + img (no .cover needed).
Element sizes (thumbnail · medium · large)
img.thumbnail
img.medium
img.large
Alignment on media
For still images, prefer image wrappers: .image-left, .image-center, .image-right. Keep one image per bounded slot.
image-left
image-center
image-right
Aspect ratio
16:9
1:1
Video backgrounds
Direct file and raw YouTube both use the same background-image slot (object-fit: cover on img, video, or iframe inside a sized parent). For a managed embed (posters, click-to-load, in-view autoplay), use ws-youtube.
Direct file · media/web-development.mp4
YouTube · same frame (autoplay, controls hidden)
Embed URL uses controls=0, autoplay=1, mute=1, playsinline=1, and loop + playlist. More patterns (chromeless pointer policy, ws-youtube, IFrame API): YouTube Embed (Optimized) component.
YouTube · ws-youtube (click to load)
Poster-first; optional in-view autoplay muted; see the component page.
Code example
<!-- One media slot API for still/video/embed -->
<div class="relative hide-overflow round height-30">
<div class="background-image">
<img class="round" src="photo.jpg" alt="" />
</div>
</div>
<img class="thumbnail round" src="thumb.jpg" alt="" />
<img class="medium round" src="photo.jpg" alt="" />
<img class="large round" src="photo.jpg" alt="" />
<div class="column height-10 fill-fade round pad-s image-center">
<img class="thumbnail round" src="photo.jpg" alt="" />
</div>
<div class="aspect aspect-16-9 round hide-overflow">
<img src="hero.jpg" alt="" />
</div>
<!-- Direct MP4 in the same slot as a background image -->
<div class="relative height-30">
<div class="background-image">
<video src="media/your-loop.mp4" muted playsinline loop autoplay preload="metadata"></video>
</div>
</div>
<!-- YouTube: same slot as MP4; mute + playsinline for autoplay; loop needs playlist=id -->
<div class="relative hide-overflow round height-30 width-m">
<div class="background-image">
<iframe class="round" src="https://www.youtube-nocookie.com/embed/VIDEO_ID?autoplay=1&mute=1&controls=0&playsinline=1&loop=1&playlist=VIDEO_ID" title="…" allow="autoplay; encrypted-media; picture-in-picture" allowfullscreen></iframe>
</div>
</div>
<!-- Lite embed (requires wckd-ui.js) -->
<ws-youtube videoid="VIDEO_ID" title="Accessible title"></ws-youtube>
Class reference
.cover .object-cover
.aspect .aspect-1 .aspect-3-4 .aspect-16-9
.background-video .background-image (slot applies object-fit: cover to img/video/iframe; .cover optional elsewhere)
ws-youtube (custom element; see components/youtube)
img.thumbnail img.medium img.large
.images-xxs … .images-full .image-xxs … .image-full
.image-left .image-right .image-center
.media-left .media-right
.ws-embed .ws-embed-google-maps
.bg-fadeout .bg-fadeout-dark .bg-fadeout-light .bg-fadeout-fade
.to-light-* .to-dark-* .to-background-*
(* = right left top bottom; empty div; stack: .background-image/.background-video z0, fades z1, then .overlay z2 or .layer-* on foreground blocks)
17) Scroll and viewport runtime hooks
With wckd-ui.js, scroll work is coalesced to one requestAnimationFrame per frame (passive scroll / resize). document.body publishes --scroll-progress, data-scroll-progress, edge flags, and direction classes. Style or script against them. A separate idle init toggles in-view on .animate for entrance patterns; pair .animate with .image-zoom for a documented still (background image) zoom when in-view applies.
Component reference: Animation and scroll effects (API, bootstrap notes, smaller live panel).
Body flags (live)
Pills highlight when the matching body class is on. Direction pills follow motion; both clear at at-top. Scroll the page to see them change.
scrolling-up / scrolling-down; neutral tilt at at-top.
At the document foot: body.at-bottom is active. Typical hook for infinite scroll, sticky CTAs, or “release” animations.
Scroll progress
Bar: fill uses transform: scaleX(var(--scroll-progress)) (GPU-friendly). Line: gradient stop at calc(var(--scroll-progress) * 100%). Text: mirrors data-scroll-progress and computed --scroll-progress (tiny script, style-guide only).
in-view entrances
Scroll until the dashed region crosses the viewport. .animate for fade + lift (demo CSS here only). For a scale-on-reveal card, use the same .animate hook with your own transform CSS, or .animate.image-zoom on a .background-image block (see wckd-ui.css). Threshold defaults to 0.4; set data-threshold="0.15" on the node to fire earlier.
.animate
Lift + shadow when .in-view is applied (demo CSS in this section only).
.animate · scale · data-threshold
Same .animate / in-view hook; lower threshold so this card wakes slightly before the left one.
Code example
<!-- Progress: transform keeps layout stable -->
.scroll-fill {
transform: scaleX(var(--scroll-progress, 0));
transform-origin: left center;
}
<!-- Gradient “read” line -->
.read-line {
background: linear-gradient(90deg, var(--primary-color) 0%,
var(--primary-color) calc(var(--scroll-progress, 0) * 100%),
var(--line-color) 0);
}
<!-- Body flags in CSS -->
body.scrolling-down .site-header { box-shadow: 0 4px 20px rgb(0 0 0 / 12%); }
body.at-bottom .load-more { opacity: 1; pointer-events: auto; }
<!-- Entrance (theme.css) -->
.reveal { opacity: 0; transform: translateY(12px); transition: 0.45s ease; }
.reveal.in-view { opacity: 1; transform: none; }
<article class="animate reveal">…</article>
Class / API reference
Body (wckd-ui.js, always on):
--scroll-progress (0–1, inline on body)
data-scroll-progress (string, two decimals)
.at-top .at-bottom .scrolling-down .scrolling-up
Elements (idle: wckdToggleInViewClass):
.animate → toggles .in-view
.animate.image-zoom → still zoom on .background-image > img (see wckd-ui.css)
data-threshold on node or .wckd-carousel (default 0.4)
.wckd-carousel: hide-arrows / hide-dots for one control; hide-nav for both (on by default)
Components: .hide-and-seek uses body.scrolling-down / scrolling-up
18) Controls: buttons and forms
Controls are intentionally simple and consistent. Use utilities for surrounding layout, and component classes for interaction and form behavior.
Code example
<!-- Link-as-button chrome + wckd-form floating-label layout -->
<a class="button outline size-s" href="#">Outline</a>
<form class="wckd-form" method="post" action="#">
<div>
<label for="email">Email</label>
<input id="email" name="email" type="email" placeholder=" " />
</div>
</form>
Class reference
Buttons / links: .button .outline .text .white-text
.size-xxs .size-xs .size-s .size-l .size-xl .size-xxl
.icon-right .disabled (and :disabled)
.icon-button .icon-button.size-s .icon-button.size-l
.icon-label
Forms: .wckd-form (layout + floating labels)
19) Icons and directional helpers
Icon helpers normalize size, direction, and spacing so actions remain legible across dense or sparse interfaces.
Code example
<!-- Icon wrapper: size + optional rotate-* for arrows -->
<span class="wckd-icon size-m rotate-north-east" aria-hidden="true">
<svg>…</svg>
</span>
Class reference
.wckd-icon .ws-icon
.wckd-icon.size-xs .size-s .size-m .size-l .size-xl
.rotate-north .rotate-north-east .rotate-east .rotate-south
.rotate-south-east .rotate-west .rotate-south-west .rotate-north-west
.solid
20) Focus and accessibility defaults
Accessibility is a baseline. Core includes skip-link and focus-visible defaults you can extend in theme.css while preserving usability standards.
Try keyboard focus on the sample buttons in Controls. The first focusable element on each template is the skip link (off-screen until focused).
Code example
<!-- First focusable: skip link (off-screen until Tab) -->
<body>
<a href="#main" class="skip-link">Skip to main content</a>
…
</body>
Class reference
.skip-link .skip-link:focus
Selectors (core): input:focus-visible textarea:focus-visible
select:focus-visible input[type="checkbox"]:focus-visible
input[type="radio"]:focus-visible
Using this guide day to day
Keep this page open for architecture, tokens, and utility inventories. For exact markup and JS options for a control, use the Components catalog. For wiring assets or trimming the kit ZIP, use Get Started. Mindset here, procedures there, contracts on each component page.
The kit files are the starter; theme.css and your conventions are what you ship as the design system. Need a website or landing pages? Contact Us for a quote.