CSS Custom Properties: Basics
Introduction to CSS Custom Properties
CSS Custom Properties (also known as CSS Variables) allow you to define reusable values that can be referenced throughout your stylesheet. They provide a way to create more maintainable, flexible, and dynamic CSS.
Key Benefits
- Reusability: Define values once and reuse them throughout your CSS
- Maintainability: Update values in one place instead of throughout the stylesheet
- Scoping: Variables can be scoped to specific elements
- Dynamic Updates: Values can be modified with JavaScript at runtime
- Contextual Changes: Values can change based on media queries or other contexts
- Native CSS: No preprocessor required
Basic Syntax and Usage
Defining Custom Properties
Custom properties are defined with a double hyphen (--) prefix and accessed using the var() function:
:root {
--primary-color: #3498db;
--secondary-color: #2ecc71;
--text-color: #333333;
--font-size-base: 16px;
--spacing-unit: 8px;
}
/* Using custom properties */
.button {
background-color: var(--primary-color);
color: white;
padding: calc(var(--spacing-unit) * 2);
font-size: var(--font-size-base);
}
.button.secondary {
background-color: var(--secondary-color);
}
The :root Selector
The :root
selector is commonly used to define global custom properties. It represents the <html>
element but has higher specificity.
Using var() Function
The var()
function retrieves the value of a custom property:
/* Basic usage */
.element {
color: var(--primary-color);
}
/* With fallback value */
.element {
color: var(--accent-color, #f39c12);
}
/* With nested fallbacks */
.element {
color: var(--theme-color, var(--primary-color, #3498db));
}
Scope and Inheritance
Cascading Variables
Custom properties follow the CSS cascade and can be overridden in more specific selectors:
:root {
--text-color: black;
}
.dark-theme {
--text-color: white;
}
/* Elements within .dark-theme will use white text */
/* Elements outside will use black text */
p {
color: var(--text-color);
}
Inheritance
Custom properties are inherited by default, meaning child elements will use the value from their parent unless overridden:
.parent {
--padding: 20px;
}
.child {
/* Inherits --padding from parent */
padding: var(--padding);
}
.child.override {
/* Overrides the inherited value */
--padding: 10px;
}
Local Scoping
You can define custom properties that only apply to specific components:
.card {
--card-padding: 16px;
--card-border-radius: 4px;
padding: var(--card-padding);
border-radius: var(--card-border-radius);
}
.card-header {
padding: var(--card-padding);
/* These variables are only available within .card and its children */
}
Calculations with Custom Properties
Using calc() with Variables
The calc()
function can perform calculations with custom properties:
:root {
--spacing: 8px;
--container-width: 1200px;
--columns: 12;
}
.container {
max-width: var(--container-width);
padding: var(--spacing);
}
.column {
/* Calculate column width based on container width and number of columns */
width: calc(var(--container-width) / var(--columns) - var(--spacing) * 2);
margin: var(--spacing);
}
.button {
/* Double the base spacing for padding */
padding: calc(var(--spacing) * 2);
}
.button.large {
/* Triple the base spacing for large buttons */
padding: calc(var(--spacing) * 3);
}
Complex Calculations
You can create more complex calculations by combining multiple operations:
:root {
--header-height: 60px;
--footer-height: 80px;
--sidebar-width: 250px;
}
.main-content {
/* Calculate available height by subtracting header and footer */
min-height: calc(100vh - var(--header-height) - var(--footer-height));
/* Calculate available width by subtracting sidebar */
width: calc(100% - var(--sidebar-width));
margin-left: var(--sidebar-width);
}
.grid-item {
/* Calculate width for a 3-column grid with gutters */
--gutter: 20px;
width: calc((100% - var(--gutter) * 2) / 3);
margin-right: var(--gutter);
}
.grid-item:nth-child(3n) {
margin-right: 0;
}
Media Queries and Responsive Design
Changing Variables with Media Queries
Custom properties can be updated within media queries to create responsive designs:
:root {
--container-padding: 15px;
--heading-size: 24px;
--body-font-size: 16px;
}
@media (min-width: 768px) {
:root {
--container-padding: 30px;
--heading-size: 32px;
--body-font-size: 18px;
}
}
@media (min-width: 1200px) {
:root {
--container-padding: 60px;
--heading-size: 48px;
--body-font-size: 20px;
}
}
.container {
padding: var(--container-padding);
}
h1 {
font-size: var(--heading-size);
}
body {
font-size: var(--body-font-size);
}
Responsive Layouts
Create responsive layouts by changing layout variables at different breakpoints:
:root {
--columns: 1;
--gutter: 16px;
}
@media (min-width: 576px) {
:root {
--columns: 2;
}
}
@media (min-width: 992px) {
:root {
--columns: 3;
--gutter: 24px;
}
}
@media (min-width: 1200px) {
:root {
--columns: 4;
--gutter: 30px;
}
}
.grid {
display: grid;
grid-template-columns: repeat(var(--columns), 1fr);
gap: var(--gutter);
}
Fallbacks and Browser Support
Providing Fallbacks
There are several ways to handle fallbacks for browsers that don't support custom properties:
/* Method 1: Fallback before var() */
.element {
color: #3498db; /* Fallback for older browsers */
color: var(--primary-color);
}
/* Method 2: Fallback within var() */
.element {
color: var(--primary-color, #3498db);
}
/* Method 3: Feature detection with @supports */
.element {
color: #3498db;
}
@supports (--css: variables) {
.element {
color: var(--primary-color);
}
}
Using a Polyfill
For broader support, you can use a polyfill like css-vars-ponyfill:
<script src="https://cdn.jsdelivr.net/npm/css-vars-ponyfill@2"></script>
<script>
cssVars({
// Options
onlyLegacy: true, // Only apply to browsers that don't support CSS variables
watch: true // Watch for changes
});
</script>
Common Use Cases
Color Management
Centralize your color palette for easy updates:
:root {
/* Primary palette */
--color-primary: #3498db;
--color-primary-light: #5dade2;
--color-primary-dark: #2980b9;
/* Secondary palette */
--color-secondary: #2ecc71;
--color-secondary-light: #58d68d;
--color-secondary-dark: #27ae60;
/* Neutral colors */
--color-text: #333333;
--color-text-light: #666666;
--color-background: #ffffff;
--color-border: #dddddd;
/* Semantic colors */
--color-success: var(--color-secondary);
--color-error: #e74c3c;
--color-warning: #f39c12;
--color-info: var(--color-primary);
}
Typography System
Create a consistent typography system:
:root {
/* Font families */
--font-primary: 'Roboto', sans-serif;
--font-secondary: 'Playfair Display', serif;
--font-monospace: 'Roboto Mono', monospace;
/* Font sizes */
--font-size-xs: 12px;
--font-size-sm: 14px;
--font-size-md: 16px;
--font-size-lg: 18px;
--font-size-xl: 24px;
--font-size-xxl: 32px;
/* Line heights */
--line-height-tight: 1.2;
--line-height-normal: 1.5;
--line-height-loose: 1.8;
/* Font weights */
--font-weight-light: 300;
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-bold: 700;
}
body {
font-family: var(--font-primary);
font-size: var(--font-size-md);
line-height: var(--line-height-normal);
color: var(--color-text);
}
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-secondary);
font-weight: var(--font-weight-bold);
}
h1 { font-size: var(--font-size-xxl); }
h2 { font-size: var(--font-size-xl); }
h3 { font-size: var(--font-size-lg); }
code {
font-family: var(--font-monospace);
font-size: var(--font-size-sm);
}
Spacing System
Create a consistent spacing system:
:root {
--spacing-unit: 8px;
--spacing-xs: calc(var(--spacing-unit) * 0.5); /* 4px */
--spacing-sm: var(--spacing-unit); /* 8px */
--spacing-md: calc(var(--spacing-unit) * 2); /* 16px */
--spacing-lg: calc(var(--spacing-unit) * 3); /* 24px */
--spacing-xl: calc(var(--spacing-unit) * 4); /* 32px */
--spacing-xxl: calc(var(--spacing-unit) * 6); /* 48px */
}
.card {
padding: var(--spacing-md);
margin-bottom: var(--spacing-lg);
}
.card-header {
margin-bottom: var(--spacing-sm);
}
.button {
padding: var(--spacing-sm) var(--spacing-md);
margin-right: var(--spacing-sm);
}
Next Steps
Now that you understand the basics of CSS Custom Properties, you can explore more advanced topics in our CSS Custom Properties: Advanced guide, including:
- Manipulating custom properties with JavaScript
- Creating dynamic themes and color schemes
- Advanced techniques for responsive design
- Integration with CSS preprocessors
- Performance considerations and best practices