CSS Variables (officially called Custom Properties) allow you to define reusable values that can be used throughout your CSS code. They provide powerful ways to create more maintainable, themeable, and dynamic styles.
CSS Variables are entities defined by developers that contain specific values to be reused throughout a document. They're set using custom property notation (e.g., --main-color: blue;
) and are accessed using the var()
function.
calc()
on variables.:root {
--main-color: #3498db;
--accent-color: #e74c3c;
--font-size-base: 16px;
--spacing: 20px;
}
CSS variables are declared with a double-dash prefix (--
) followed by a name. The :root
selector is commonly used for global variables, but they can be defined on any element.
.button {
background-color: var(--main-color);
color: white;
padding: var(--spacing);
font-size: var(--font-size-base);
}
The var()
function accesses the value of a custom property. If the custom property is not defined, the browser will use the default value (if provided) or ignore the property.
/* If --accent-color is not defined, red will be used */
.button-accent {
background-color: var(--accent-color, red);
}
.theme-button {
background-color: var(--button-color);
color: var(--button-text);
padding: var(--button-padding);
border: none;
border-radius: 4px;
}
Variables defined in the :root
selector are globally available to all elements in the document:
:root {
--global-color: blue;
}
/* Available to any element */
.element {
color: var(--global-color);
}
Variables can also be defined for specific elements, limiting their scope:
.card {
--card-color: green; /* Local to .card and its children */
background-color: var(--card-color);
}
.card .title {
color: var(--card-color); /* Can access parent's variable */
}
Custom properties follow the normal rules of cascading and inheritance in CSS:
Parent element with blue variable
Child element with green variable (overrides parent)
.parent-scope {
--scope-color: blue;
}
.child-scope {
--scope-color: green; /* Overrides the parent scope */
}
.scope-element {
color: var(--scope-color);
}
// Get a variable from root
const rootStyles = getComputedStyle(document.documentElement);
const mainColor = rootStyles.getPropertyValue('--main-color');
console.log(mainColor); // " #3498db"
// Set a variable on root
document.documentElement.style.setProperty('--main-color', '#9b59b6');
// Set a variable on a specific element
const element = document.querySelector('.my-element');
element.style.setProperty('--element-color', 'green');
This is a themed content area that changes based on the selected theme.
/* Light Theme (default) */
:root {
--theme-bg: #ffffff;
--theme-text: #333333;
--theme-primary: #3498db;
--theme-accent: #e74c3c;
}
/* Dark Theme */
[data-theme="dark"] {
--theme-bg: #2c3e50;
--theme-text: #ecf0f1;
--theme-primary: #3498db;
--theme-accent: #e74c3c;
}
/* Usage */
.themed-element {
background-color: var(--theme-bg);
color: var(--theme-text);
}
Change variables based on screen size:
.responsive-container {
--columns: 1; /* Default for mobile */
display: grid;
grid-template-columns: repeat(var(--columns), 1fr);
}
@media (min-width: 576px) {
.responsive-container {
--columns: 2; /* Tablet */
}
}
@media (min-width: 768px) {
.responsive-container {
--columns: 3; /* Desktop */
}
}
.animation-demo {
--animation-duration: 2s;
--animation-timing: ease-in-out;
animation: slide var(--animation-duration) var(--animation-timing) infinite alternate;
}
@keyframes slide {
from { transform: translateX(0); }
to { transform: translateX(200px); }
}
This element adapts to your system's color scheme preference (if your browser supports it).
.dark-mode-aware {
--bg-color: #f8f9fa; /* Light mode default */
--text-color: #2c3e50;
}
@media (prefers-color-scheme: dark) {
.dark-mode-aware {
--bg-color: #2c3e50; /* Dark mode override */
--text-color: #ecf0f1;
}
}
.calculated-demo {
--base-size: 16px;
--ratio: 1.2;
--font-small: calc(var(--base-size) * 0.875);
--font-medium: var(--base-size);
--font-large: calc(var(--base-size) * var(--ratio));
--font-xlarge: calc(var(--base-size) * var(--ratio) * var(--ratio));
}
.fallback-demo {
/* Fallback first */
color: #ff6347;
/* Then the variable */
color: var(--custom-color, tomato);
/* Complex fallback chain */
color: var(--primary-color, var(--backup-color, var(--default-color, #333)));
}
:root {
--hue: 200;
--saturation: 70%;
--lightness: 50%;
}
.element {
/* HSL based on variables */
background-color: hsl(var(--hue), var(--saturation), var(--lightness));
/* Lighter variant */
color: hsl(var(--hue), var(--saturation), calc(var(--lightness) + 30%));
}
/* Component based */
.alert {
--alert-bg: #f8d7da;
--alert-text: #721c24;
--alert-border: #f5c6cb;
}
/* Semantic naming */
:root {
--color-primary: #3498db;
--color-secondary: #2ecc71;
--color-danger: #e74c3c;
--color-warning: #f39c12;
/* Instead of: */
--blue: #3498db; /* Avoid color names */
--green: #2ecc71; /* that might change */
}
/* Structured naming with BEM-like syntax */
:root {
--button-primary-bg: #3498db;
--button-primary-text: white;
--button-danger-bg: #e74c3c;
--button-danger-text: white;
}
:root {
/* Colors */
--color-primary: #3498db;
--color-secondary: #2ecc71;
--color-text: #2c3e50;
--color-background: #f5f7fa;
/* Typography */
--font-family-base: 'Segoe UI', sans-serif;
--font-size-base: 16px;
--font-size-h1: 2rem;
--font-size-h2: 1.5rem;
/* Spacing */
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
/* Effects */
--border-radius: 4px;
--box-shadow: 0 2px 4px rgba(0,0,0,0.1);
--transition: all 0.3s ease;
}
:root {
/* Primary Colors
* Use these for main UI elements
* ------------------------------- */
--color-primary: #3498db; /* Blue: Primary actions, links */
--color-secondary: #2ecc71; /* Green: Success, confirmation */
--color-accent: #e74c3c; /* Red: Errors, destructive actions */
/* Neutral Colors
* Use for text, backgrounds, borders
* ---------------------------------- */
--color-text: #2c3e50; /* Main text color */
--color-text-light: #7f8c8d; /* Secondary text, captions */
--color-border: #ecf0f1; /* Borders, dividers */
}
CSS Variables are well-supported in all modern browsers (Chrome, Firefox, Safari, Edge). Internet Explorer 11 does not support CSS Variables.
@supports (--custom-property: value) {
/* Code that uses CSS variables */
:root {
--color-primary: blue;
}
}
@supports not (--custom-property: value) {
/* Fallback code */
.button {
background-color: blue; /* Hardcoded value */
}
}
For older browsers, you can use a polyfill like css-vars-ponyfill:
// Include the polyfill
<script src="css-vars-ponyfill.min.js"></script>
// Initialize
<script>
cssVars({
// Options
onlyLegacy: true, // Only apply to browsers that don't support CSS variables
variables: {
// Define variables that don't exist in CSS
'--color-primary': '#3498db'
}
});
</script>
:root
./* Base Variables */
:root {
--color-primary: #3498db;
--color-text: #2c3e50;
--spacing-unit: 8px;
--border-radius: 4px;
}
/* Component Variables */
.card {
--card-padding: calc(var(--spacing-unit) * 3);
--card-bg: white;
--card-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: var(--card-padding);
background-color: var(--card-bg);
box-shadow: var(--card-shadow);
border-radius: var(--border-radius);
}
.button {
--button-bg: var(--color-primary);
--button-color: white;
--button-padding-y: calc(var(--spacing-unit) * 1.5);
--button-padding-x: calc(var(--spacing-unit) * 3);
background-color: var(--button-bg);
color: var(--button-color);
padding: var(--button-padding-y) var(--button-padding-x);
border-radius: var(--border-radius);
}
/* Theme Definitions */
:root {
/* Default (Light) Theme */
--theme-bg: #ffffff;
--theme-surface: #f5f7fa;
--theme-text: #2c3e50;
--theme-primary: #3498db;
--theme-secondary: #2ecc71;
--theme-error: #e74c3c;
}
/* Dark Theme */
body.dark-theme {
--theme-bg: #1a1a2e;
--theme-surface: #16213e;
--theme-text: #e0e0e0;
--theme-primary: #4fc3f7;
--theme-secondary: #67d88e;
--theme-error: #ff7675;
}
/* High Contrast Theme */
body.high-contrast-theme {
--theme-bg: #000000;
--theme-surface: #121212;
--theme-text: #ffffff;
--theme-primary: #fcff4d;
--theme-secondary: #61ff61;
--theme-error: #ff5252;
}
:root {
--font-size-base: 16px;
--line-height: 1.5;
--scale-ratio: 1.25;
/* Type Scale */
--font-size-sm: calc(var(--font-size-base) / var(--scale-ratio));
--font-size-md: var(--font-size-base);
--font-size-lg: calc(var(--font-size-base) * var(--scale-ratio));
--font-size-xl: calc(var(--font-size-base) * var(--scale-ratio) * var(--scale-ratio));
--font-size-xxl: calc(var(--font-size-base) * var(--scale-ratio) * var(--scale-ratio) * var(--scale-ratio));
}
/* Adjust base size at different breakpoints */
@media (min-width: 768px) {
:root {
--font-size-base: 18px;
}
}
@media (min-width: 1200px) {
:root {
--font-size-base: 20px;
}
}