CSS Variables & Utility Patterns
Custom properties, design tokens, dark mode, and reusable utility classes
Last updated on
CSS Custom Properties (variables) let you define reusable values and build a consistent design system. They're essential for mid-level developers.
CSS Variables (Custom Properties)
Defining Variables
:root {
--primary: #2563eb;
--primary-dark: #1d4ed8;
--text-color: #1a1a1a;
--text-muted: #6b7280;
--bg-color: #ffffff;
--bg-secondary: #f8f9fa;
--radius: 8px;
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--font-sans: "Inter", sans-serif;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
}Using Variables
.card {
background: var(--bg-color);
color: var(--text-color);
border-radius: var(--radius);
padding: var(--spacing-lg);
box-shadow: var(--shadow);
font-family: var(--font-sans);
}
.button {
background: var(--primary);
color: white;
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius);
}
.button:hover {
background: var(--primary-dark);
}Fallback Values
color: var(--text-color, #333); /* uses #333 if --text-color is not defined */Scoped Variables
Variables can be scoped to any selector:
.dark-section {
--text-color: #ffffff;
--bg-color: #1a1a1a;
}
/* Children of .dark-section use these overridden values */Dark Mode with CSS Variables
:root {
--bg: #ffffff;
--text: #1a1a1a;
--border: #e2e8f0;
--card-bg: #ffffff;
}
[data-theme="dark"] {
--bg: #0f172a;
--text: #e2e8f0;
--border: #334155;
--card-bg: #1e293b;
}
/* Or use the system preference */
@media (prefers-color-scheme: dark) {
:root {
--bg: #0f172a;
--text: #e2e8f0;
--border: #334155;
--card-bg: #1e293b;
}
}
body {
background: var(--bg);
color: var(--text);
}Toggle with JavaScript
const toggle = document.querySelector("#theme-toggle");
toggle.addEventListener("click", () => {
const current = document.documentElement.getAttribute("data-theme");
document.documentElement.setAttribute(
"data-theme",
current === "dark" ? "light" : "dark"
);
});Design Token System
A complete set of variables for a consistent design:
:root {
/* Colors */
--color-primary: hsl(220, 90%, 56%);
--color-success: hsl(142, 70%, 45%);
--color-warning: hsl(38, 92%, 50%);
--color-danger: hsl(0, 84%, 60%);
/* Typography */
--font-sans: "Inter", sans-serif;
--font-mono: "Fira Code", monospace;
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-6: 24px;
--space-8: 32px;
/* Borders */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-full: 9999px;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}Utility Classes
Small, single-purpose classes that can be composed:
/* Text alignment */
.text-left { text-align: left; }
.text-center { text-align: center; }
.text-right { text-align: right; }
/* Display */
.flex { display: flex; }
.grid { display: grid; }
.hidden { display: none; }
.block { display: block; }
/* Flex utilities */
.items-center { align-items: center; }
.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.gap-sm { gap: var(--space-2); }
.gap-md { gap: var(--space-4); }
.gap-lg { gap: var(--space-6); }
/* Spacing */
.mt-1 { margin-top: var(--space-1); }
.mt-2 { margin-top: var(--space-2); }
.mt-4 { margin-top: var(--space-4); }
.p-4 { padding: var(--space-4); }
.p-6 { padding: var(--space-6); }
/* Width */
.w-full { width: 100%; }
.max-w-lg { max-width: 960px; }
.mx-auto { margin-left: auto; margin-right: auto; }
/* Visually hidden (accessible) */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}Variables vs Preprocessors (Sass)
| Feature | CSS Variables | Sass Variables |
|---|---|---|
| Runtime | Yes (live in browser) | No (compiled) |
| JavaScript access | Yes | No |
| Cascade/Scope | Yes | No |
| Theming/Dark mode | Easy | Hard |
| Calculations | calc() | $var + 10 |
| Browser support | Modern browsers | All (compiled to CSS) |
Recommendation: Use CSS Variables for theming and runtime values. Sass variables are still useful for build-time constants.