Style property reference
Every styling property an IWC element, layout or theme accepts, in one place. For the reasoning behind them — the four styling layers, dark mode, hooks for your CSS — read Styling & responsive design.
Where styles come from
| Layer | Where | Wins over |
|---|---|---|
| Theme tokens | app.iwc theme.tokens / theme.dark → CSS variables on :root | — |
| Your stylesheets | app.iwc styles, and styleSource on screens, layouts, components and elements | built-in CSS, always |
| Element styling | className, style, responsive, states, transition, animation, vars | — |
| Built-in CSS | thin defaults inside @layer iwc | nothing: any rule you write wins |
Layout: flexbox
layout on any container sets flexbox:
| Key | Values | CSS |
|---|---|---|
direction | row column row-reverse column-reverse | flex-direction |
align | start center end stretch baseline | align-items |
justify | start center end between around evenly | justify-content (between = space-between) |
gap | CSS length | gap |
wrap | boolean | flex-wrap: wrap |
row and column/stack already set the direction.
{
"type": "row",
"layout": { "align": "center", "justify": "between", "gap": "16px", "wrap": true },
"elements": [
{ "type": "text", "variant": "h3", "content": "Orders" },
{ "type": "button", "label": "Export", "variant": "secondary", "action": "navigate", "target": "/orders" }
]
}Layout: grid
| Property | Value | Effect |
|---|---|---|
columns | integer 1–24, or a CSS string | equal columns, or grid-template-columns ("2fr 1fr", "repeat(auto-fill, minmax(220px, 1fr))") |
responsiveColumns | {breakpoint: count} | column count at or below a breakpoint |
gap | CSS length | gap between cells (also for flex) |
{
"type": "grid",
"columns": "repeat(auto-fill, minmax(220px, 1fr))",
"gap": "20px",
"elements": [{ "type": "box", "content": "A" }, { "type": "box", "content": "B" }, { "type": "box", "content": "C" }]
}style: spacing, sizing, colour, typography, borders, shadows
style takes any CSS property, in camelCase or kebab-case, with string or number values. Unsafe CSS (expression(, javascript:, @import, </) is dropped. Each element's CSS is scoped to it with a generated class and sent under the page's CSP nonce.
| Category | Properties (examples) |
|---|---|
| Spacing | padding, paddingInline, margin, marginTop, gap |
| Sizing | width, maxWidth, minHeight, aspectRatio, flex |
| Colour | color, background, backgroundImage, opacity |
| Typography | fontSize, fontWeight, fontFamily, lineHeight, letterSpacing, textAlign, textTransform |
| Borders & radius | border, borderBottom, borderRadius, outline |
| Shadows | boxShadow, textShadow, filter |
| Position | position, top, zIndex, overflow |
{
"type": "box",
"content": "Free delivery over ₹999",
"style": {
"padding": "12px 20px",
"maxWidth": "60ch",
"background": "var(--color-surface)",
"color": "var(--color-text)",
"fontSize": "15px",
"fontWeight": 600,
"border": "1px solid var(--color-border)",
"borderRadius": "var(--radius)",
"boxShadow": "0 8px 24px rgba(0,0,0,.08)"
}
}responsive: breakpoints
Breakpoints are max-widths. Defaults: mobile = 640px, tablet = 1024px. Add or change them in app.iwc:
{ "name": "Shop", "breakpoints": { "mobile": 640, "tablet": 1024, "wide": 1440 } }responsive applies CSS at or below a breakpoint's width. Write desktop styles first, then override:
{
"type": "section",
"layout": { "direction": "row", "gap": "48px" },
"responsive": { "tablet": { "flexDirection": "column", "gap": "24px" }, "mobile": { "padding": "32px 16px" } },
"elements": [{ "type": "text", "variant": "title", "content": "Light that feels like home." }]
}states: hover, focus, active
| Key | CSS selector |
|---|---|
hover | :hover |
focus | :focus-visible |
active | :active |
{
"type": "card",
"fields": { "title": "name" },
"states": { "hover": { "transform": "translateY(-4px)", "boxShadow": "0 18px 40px rgba(0,0,0,.12)" }, "focus": { "outline": "2px solid var(--color-primary)" } },
"transition": "transform 200ms ease, box-shadow 200ms ease"
}transition and animation
transition is a CSS transition string. animation is an entrance animation:
| Key | Values | Default |
|---|---|---|
name (required) | fade fade-up fade-down slide-left slide-right zoom none | — |
trigger | visible (when scrolled into view), load | visible |
duration | duration (700ms) | — |
delay | duration (100ms) | — |
Visitors who prefer reduced motion see no movement.
{ "type": "column", "animation": { "name": "fade-up", "duration": "700ms", "delay": "100ms" }, "elements": [{ "type": "text", "content": "Appears as it scrolls into view" }] }vars: values from data
vars sets CSS custom properties per record, from data. Your stylesheet reads them:
{
"type": "grid",
"columns": 3,
"data": { "source": "STATIC", "items": [{ "name": "Design", "pct": 80, "color": "#7c3aed" }, { "name": "Build", "pct": 45, "color": "#2563eb" }] },
"item": { "type": "box", "className": "progress", "vars": { "--accent": "=color", "--progress": "=concat(pct, '%')" }, "bind": "name" }
}.progress { background: linear-gradient(90deg, var(--accent) var(--progress), #eee 0); }Theme tokens
app.iwc theme.tokens become CSS variables on :root; theme.dark overrides them under prefers-color-scheme: dark. Any names work; the built-in elements read these:
| Token | Used for |
|---|---|
color-primary, color-on-primary | primary buttons, active tabs, charts |
color-text, color-background | the page |
color-surface, color-border | cards, inputs, tables, dropdowns |
color-link | links (default: the text colour) |
color-danger | field errors |
color-rating | review stars |
radius | buttons, cards, inputs |
font-body, font-heading | body text and h1–h6 |
carousel-gap | space between carousel slides |
{
"name": "Shop",
"theme": {
"tokens": { "color-primary": "#5b4cf5", "color-on-primary": "#fff", "color-surface": "#f6f4ff", "radius": "14px", "font-body": "Inter, sans-serif", "font-heading": "'Plus Jakarta Sans', sans-serif" },
"dark": { "color-background": "#0f0f1a", "color-text": "#ececf4", "color-surface": "#1a1a2b" },
"fonts": [{ "family": "Inter", "google": true, "weights": [400, 600] }, { "family": "Plus Jakarta Sans", "google": true, "weights": [700, 800] }]
}
}fonts entries are {family, google, weights} (Google Fonts) or {family, source, weight, style} (a file under assets/, emitted as @font-face).
Layout slots
.layout.iwc structure sets per-slot behaviour:
| Option | Values | Effect |
|---|---|---|
position | normal sticky fixed | sticky keeps a header in view |
mobile | normal hidden drawer bottom | below the mobile breakpoint: hide, slide-in drawer, or pin to the bottom |
collapsible | boolean | a button that collapses the slot |
width | CSS length | fixed width |
style | CSS | extra styles |
template lays slots out as CSS grid areas (["header header", "sidebar content"]). See Layouts.
Custom CSS
Everything is also reachable from plain CSS. Hooks: your classNames; each element's type class (.iwc-text, .iwc-button, .iwc-card); variants (.iwc-text-h1, .iwc-button-secondary); parts (.iwc-card-media, .iwc-field-error, .iwc-navbar-links); slots (.iwc-slot-header); states ([aria-current="page"], details[open]); and your attributes ([data-tone=dark]). Because IWC's own CSS is in @layer iwc, any rule you write wins over it.
{ "name": "Shop", "styles": ["styles/theme.css", "styles/main.css"] }className accepts utility-framework syntax (md:grid-cols-2); IWC runs no CSS build step, so compile the framework's CSS into styles/ yourself.
Source: apps/marketing/content/docs/style-reference.md