Theme: Material
Material Design 3 aesthetic — elevated surfaces, indigo accent, Roboto type. Requires config/ → 'sans' => 'roboto':
/* WireKit Theme: Material — Material Design 3 aesthetic.
Inherits from Default: neutral palette, dark-mode colors, sizing,
WCAG-verified text/bg contrasts.
Prerequisite: config/wirekit.php → 'sans' => 'roboto'
Accent: indigo-600, 16 degrees of hue from Soft's violet-600 — close
enough that the two are told apart by their surfaces and type, not by
their accents alone.
WCAG: All indigo pairs ≥ AA — see inline ratios.
IMPORTANT: .dark block includes shadow overrides to prevent
Default's dark shadows from overriding Material's M3 values.
border-wk-width 0px → borderless: surfaces defined by elevation. */
/* `:root` and `.dark`, NOT `@theme` / `@layer theme`, and the difference decides whether
this block does anything at all.
WireKit's own defaults sit inside `:where(:root)` — specificity 0 — precisely so a plain
`:root` override wins whichever stylesheet the browser reads last. `@theme` compiles into
`@layer theme`, and UNLAYERED CSS beats LAYERED CSS whatever the specificity: the defaults
are unlayered, so a preset in `@theme` loses to them and silently does nothing except for
the handful of tokens WireKit never declares.
Nothing reports that. The CSS is valid, the custom properties resolve, and the page renders
in the default theme while the developer reads their own preset in their own stylesheet. */
:root {
/* Font — Roboto: Google's system typeface, slightly wider than Inter.
letter-spacing 0.01em — M3 spec uses positive tracking for body.
heading-weight 500 (Medium) — M3's Display/Headline weight. */
--font-wk-sans: 'Roboto', ui-sans-serif, system-ui, sans-serif;
--font-wk-heading-weight: 500;
--font-wk-letter-spacing: 0.01em;
/* Radius — 0.75rem base → sm: 0.5625rem, md: 0.75rem,
lg: 0.9375rem, xl: 1.5rem. M3 "medium" shape scale. */
--radius-wk: 0.75rem;
/* Borders — borderless. M3 defines surfaces by shadow/elevation,
not strokes. Inputs rely on bg-input tint for boundary. */
--border-wk-width: 0px;
/* Shadows — M3-style dual-layer with higher opacity than Default.
Sharper umbra + softer penumbra. Dark shadows in .dark below. */
--shadow-wk-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.12),
0 1px 2px 0 rgba(0, 0, 0, 0.24);
--shadow-wk-md: 0 3px 6px 0 rgba(0, 0, 0, 0.16),
0 3px 6px 0 rgba(0, 0, 0, 0.23);
--shadow-wk-lg: 0 10px 20px 0 rgba(0, 0, 0, 0.19),
0 6px 6px 0 rgba(0, 0, 0, 0.23);
/* Motion — M3 standard decelerate: fast entrance, soft settle.
Previous cubic-bezier(0.4,0,0.2,1) was M2 standard easing.
M3 uses (0.2,0,0,1) for most interactive transitions. */
--transition-wk-duration: 200ms;
--transition-wk-easing: cubic-bezier(0.2, 0, 0, 1);
/* Accent — Indigo (distinct from Soft's violet).
white on indigo-600: 6.44:1 (AA ✓)
white on indigo-700 (hover): 8.07:1 (AAA ✓)
indigo-600 ring on white: 6.44:1 (UI ✓) */
--color-wk-accent: var(--color-indigo-600);
--color-wk-accent-hover: var(--color-indigo-700);
--color-wk-accent-content: var(--color-indigo-700);
--color-wk-accent-fg: var(--color-white);
--color-wk-ring: var(--color-indigo-600);
/* Input background — borderless inputs need a visible tint.
neutral-50 on white: subtle but M3's "surface variant" approach.
neutral-500 placeholder on neutral-50: 4.54:1 (AA ✓) */
--color-wk-bg-input: var(--color-neutral-50);
}
.dark {
/* Accent dark — accent-fg (neutral-900) inherited from Default.
neutral-900 on indigo-400: 5.74:1 (AA ✓)
neutral-900 on indigo-300 (hover): 8.99:1 (AAA ✓) */
/* See the note in soft.md: the light block's white label wins over the base
stylesheet's dark value and lands on indigo-400 at 2.98:1. Near-black is 5.74:1. */
--color-wk-accent-fg: var(--color-neutral-900);
--color-wk-accent: var(--color-indigo-400);
--color-wk-accent-hover: var(--color-indigo-300);
--color-wk-accent-content: var(--color-indigo-300);
--color-wk-ring: var(--color-indigo-400);
/* Shadows dark — MUST override to keep Material's M3 shadow shape.
Without this, Default's .dark shadows (12/32px blur) win the
cascade and the M3 feel disappears. */
--shadow-wk-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.40),
0 1px 2px 0 rgba(0, 0, 0, 0.35);
--shadow-wk-md: 0 3px 6px 0 rgba(0, 0, 0, 0.45),
0 3px 6px 0 rgba(0, 0, 0, 0.40);
--shadow-wk-lg: 0 10px 20px 0 rgba(0, 0, 0, 0.50),
0 6px 6px 0 rgba(0, 0, 0, 0.45);
/* Input background — one step lighter than page for visibility.
neutral-400 placeholder on neutral-800: 6.00:1 (AA ✓) */
--color-wk-bg-input: var(--color-neutral-800);
}
Design notes
Material is a Material Design 3 interpretation: tonal, elevated surfaces, an indigo accent, and Roboto type. It feels immediately familiar to anyone coming from Android or the wider Google ecosystem, and it leans on elevation and state layers rather than borders to express hierarchy.
Best for: products that want MD3 familiarity or sit alongside Google-ecosystem apps. Avoid it when you want a distinctly non-Material visual identity.
Accessibility
The indigo accent is AA or better across the board: white on indigo-600 is 6.44:1, the indigo-700 hover 8.07:1 (AAA), and the focus ring 6.44:1 for UI contrast. The neutral-500 placeholder on the neutral-50 input surface lands at 4.54:1 (AA). Dark mode is contrast-checked independently.
Form-control boundaries are below 3:1 — this is the trade-off you are choosing
--border-wk-width: 0px is what makes Material read as Material: surfaces separate by elevation and state layers rather than by lines. It also means a text field has no drawn boundary, so the only thing marking where it begins is its fill against the page — 1.04:1 in light mode (neutral-50 on white) and 1.04:1 in dark (neutral-800 on the dark page background). WCAG 1.4.11 asks for 3:1 on a boundary that identifies an interactive element, and neither figure comes close.
The controls whose edge is the control fare worse than a field. An unchecked checkbox or radio is its fill alone, at the same 1.04:1, the off state of a toggle or the theme switch is a pale track, and the radio rows of a dropdown lose the rings that mark them as one set. The wk-choice-frame line in the rule below brings those edges back.
That is inherent to the borderless look and not a defect in the token values — Material Design 3 makes the same choice, and the same is true of the other borderless preset here. What matters is that it is your choice rather than a surprise: the placeholder contrast quoted above is AA, so an audit of the text alone reports clean while the field itself has no visible edge.
Two ways out, and the first is the one to reach for:
/* After WireKit's stylesheet. Every text field, the frame around a field that holds pills,
and every checkbox, radio and switch take back a 1px edge; the flat surfaces around them
stay flat. */
.wk-field,
.wk-field-frame,
.wk-choice-frame {
--border-wk-width: 1px;
}
The fields already draw their edge in --color-wk-border-strong, the token bound to 3:1 against the input fill, in both modes, precisely because it carries the boundary of an interactive element; at 0px there is only no width to draw it with. The rule gives the width back on the fields alone, so cards and panels keep the borderless look, and it reaches every field that carries the wk-field marker (the combobox, the multi-select, the date and number fields), not only the three plain ones.
Do not do this by personalizing the input's base block with a string. A string replaces the whole block, and the input's base block holds its fill, its text colors, its width, its focus ring and its disabled state, so a string holding only a border takes all of that with it, the focus ring included.
Or deepen the fill instead — --color-wk-bg-input: var(--color-neutral-200) reaches 1.26:1, which is more visible but still short of 3:1. The border is what actually satisfies 1.4.11.
Cupertino takes the third route for the same problem: a 0.5px hairline instead of no border at all.