CSS Framework
what.css is a utility-first CSS framework with semantic components, dark mode, and cascade layers. Your CSS always wins — no !important needed.
Overview
Every What project includes what.css automatically. It uses four cascade layers in this order:
@layer base, utilities, components, interactions;
| Layer | Contains |
|---|---|
base | Reset, CSS variables, theme tokens |
utilities | Spacing, layout, typography, colors |
components | Buttons, cards, forms, tables, alerts |
interactions | Modals, drawers, tooltips, transitions |
Because all framework styles live inside @layer, any CSS you write without a layer declaration automatically takes precedence. Override anything without fighting specificity.
Choose How Much You Ship
The whole framework is about 14 KB gzipped, but you don't have to take all of it. Set css in the [server] section of what.toml:
[server]
css = "minimal" # "full" (default) | "minimal" | "none"
full— everything: all four layers.minimal—base+utilitiesonly. Component and interaction styles are cut — roughly half the bytes. Your markup, your component styles.none— nothing is injected. Pure bring-your-own-CSS;/static/what.cssstays available if you change your mind.
This applies everywhere the stylesheet is served: the dev server, production, and static builds via run-what build.
Utility Classes
Spacing
Margin and padding use a numeric scale: 0, 1 (0.25rem), 2 (0.5rem), 3 (0.75rem), 4 (1rem), 6 (1.5rem), 8 (2rem).
| Pattern | Example | Result |
|---|---|---|
p-{n} | p-4 | padding: 1rem |
px-{n} / py-{n} | px-6 | padding-left: 1.5rem; padding-right: 1.5rem |
m-{n} | mt-8 | margin-top: 2rem |
gap-{n} | gap-4 | gap: 1rem |
space-y-{n} | space-y-4 | margin-top: 1rem between children |
Display & Layout
<div class="flex items-center justify-between gap-4">
<span>Left</span>
<span>Right</span>
</div>
<div class="flex-col space-y-2">
<p>Stacked vertically</p>
<p>With spacing between</p>
</div>
Available: flex, grid, block, inline-flex, hidden, flex-col, flex-wrap, flex-1, items-center, justify-between, justify-center, self-center.
Typography
| Class | Effect |
|---|---|
text-sm, text-lg, text-xl, text-2xl | Font size |
font-bold, font-semibold, font-medium | Font weight |
text-center, text-right | Alignment |
truncate | Overflow ellipsis |
uppercase, capitalize | Transform |
text-muted | Muted color (gray-500) |
Colors
Text, background, and border colors follow the same naming pattern:
<p class="text-primary">Primary text</p>
<p class="text-danger">Error text</p>
<div class="bg-gray-100 border border-gray-200 rounded-lg p-4">
Subtle background with border
</div>
Available color groups: gray-{50-900}, primary-{50-900}, success, warning, danger, red, green, yellow, blue, indigo.
Component Classes
Buttons
<button class="btn btn-primary">Save</button>
<button class="btn btn-outline">Cancel</button>
<button class="btn btn-danger btn-sm">Delete</button>
<button class="btn btn-success btn-lg btn-block">Submit</button>
Variants: btn-primary, btn-secondary, btn-success, btn-warning, btn-danger, btn-outline, btn-ghost, btn-link. Sizes: btn-sm, btn-lg, btn-xl. Modifiers: btn-block, btn-icon, btn-loading.
Cards
<div class="card">
<div class="card-header">
<h3 class="card-title">Title</h3>
</div>
<div class="card-body">Content here</div>
<div class="card-footer">Footer</div>
</div>
Variants: card-elevated, card-flat, card-bordered, card-interactive, card-horizontal. Sizes: card-sm, card-lg. Colors: card-primary, card-success, card-warning, card-danger.
Forms
<div class="form-group">
<label class="form-label">Email</label>
<input type="email" class="form-input" placeholder="[email protected]">
<span class="form-hint">We'll never share your email.</span>
</div>
Input classes: form-input, form-textarea, form-select, form-control (universal alias), form-checkbox, form-radio, form-switch. Helpers: form-label, form-group, form-error, form-hint. Sizes: form-control-sm, form-control-lg. Validation: is-valid, is-invalid, invalid-feedback.
Tables
<div class="table-container">
<table class="table table-striped table-hover">
<thead><tr><th>Name</th><th>Role</th></tr></thead>
<tbody><tr><td>Alice</td><td>Admin</td></tr></tbody>
</table>
</div>
Alerts
<div class="alert alert-info">
<div class="alert-content">
<div class="alert-title">Heads up</div>
<p class="alert-description">This is an informational message.</p>
</div>
</div>
Variants: alert-info, alert-success, alert-warning, alert-danger. Sizes: alert-sm, alert-lg.
Grid System
CSS Grid utilities for column layouts:
<div class="grid grid-cols-3 gap-4">
<div class="card card-body">One</div>
<div class="card card-body">Two</div>
<div class="card card-body">Three</div>
</div>
<!-- Spanning columns -->
<div class="grid grid-cols-4 gap-4">
<div class="col-span-2">Wide column</div>
<div>Normal</div>
<div>Normal</div>
</div>
<!-- Responsive: 1 col on mobile, 3 on desktop -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div>Card</div>
<div>Card</div>
<div>Card</div>
</div>
Available: grid-cols-1 through grid-cols-6, col-span-1 through col-span-3, col-span-full.
CSS Variables
The entire framework is built on CSS custom properties. Override them to customize your theme:
:root {
/* Brand colors */
--w-primary-500: #8b5cf6;
--w-primary-600: #7c3aed;
--w-primary-700: #6d28d9;
/* Typography */
--w-font-sans: "Inter", sans-serif;
/* Spacing scale */
--w-spacing-4: 1.25rem;
/* Component-specific */
--w-btn-radius: 9999px;
--w-card-radius: 1rem;
}
Key Variable Groups
| Group | Variables |
|---|---|
| Colors | --w-primary-{50-900}, --w-gray-{50-900}, --w-success-*, --w-danger-*, --w-warning-* |
| Semantic | --w-bg, --w-text, --w-border, --w-surface, --w-muted |
| Typography | --w-font-sans, --w-font-mono, --w-text-sm through --w-text-5xl |
| Spacing | --w-spacing-{0-24} |
| Components | --w-btn-*, --w-card-*, --w-alert-*, --w-modal-*, --w-input-*, --w-table-* |
Dark Mode
Dark mode works two ways:
Automatic — Follows the OS/browser prefers-color-scheme preference. No setup required.
Manual — Add class="dark" to the <html> element. Use class="light" to force light mode and override OS preference.
<!-- Force dark mode -->
<html class="dark">
<!-- Force light mode (even if OS is dark) -->
<html class="light">
Theme Toggle
User-controlled dark mode is one tag — it flips the class, saves the choice to localStorage['w-theme'], and the framework restores it before first paint on every page load (no flash, nothing to put in <head>):
<what-theme-toggle/>
<!-- or with your own label / classes -->
<what-theme-toggle class="nav-btn">Switch theme</what-theme-toggle>
The raw attribute form is w-theme-toggle on any element. Live:
Dark mode automatically adjusts semantic tokens: --w-bg, --w-text, --w-border, --w-surface, and --w-muted all change to dark-appropriate values.
Responsive
Responsive variants follow a {breakpoint}:{class} pattern:
| Prefix | Breakpoint |
|---|---|
sm: | 640px |
md: | 768px |
lg: | 1024px |
xl: | 1280px |
<!-- Stack on mobile, row on desktop -->
<div class="flex flex-col md:flex-row gap-4">...</div>
<!-- 1 column on mobile, 2 on tablet, 3 on desktop -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">...</div>
<!-- Hide on mobile -->
<nav class="hidden-mobile">Desktop navigation</nav>
Convenience classes: hidden-mobile (below 640px) and hidden-desktop (640px and above).
Customization
Add your own stylesheet after what.css. Thanks to cascade layers, your rules take priority without !important:
<link rel="stylesheet" href="/static/what.css">
<link rel="stylesheet" href="/static/styles.css">
/* These override framework styles automatically */
.btn {
border-radius: 9999px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.card {
border: 2px solid var(--w-primary-200);
}
/* Add new component styles */
.pricing-card {
background: linear-gradient(135deg, var(--w-primary-50), var(--w-white));
border: 1px solid var(--w-primary-200);
border-radius: var(--w-rounded-xl);
padding: 2rem;
}
.btn { --w-btn-radius: 9999px; }. This changes the variable the framework uses internally, keeping all other button styles intact.