Custom Properties and calc(): Dynamic Values
10 min read
Combining Variables with Math
CSS custom properties become exponentially more powerful when combined with calc(). You can store a base value in a variable and compute derived values in your rules, keeping a single source of truth.
css
:root {
--space-base: 8px;
--space-xs: calc(var(--space-base) * 0.5); /* 4px */
--space-sm: calc(var(--space-base) * 1); /* 8px */
--space-md: calc(var(--space-base) * 2); /* 16px */
--space-lg: calc(var(--space-base) * 4); /* 32px */
--space-xl: calc(var(--space-base) * 8); /* 64px */
--sidebar-w: 260px;
--gap: 24px;
}
.layout {
display: grid;
/* Main column = viewport minus sidebar and gap */
grid-template-columns: var(--sidebar-w) calc(100% - var(--sidebar-w) - var(--gap));
gap: var(--gap);
}Mixing units inside calc()
calc() can mix incompatible units that no single value could express, such as % and px. This is invaluable for responsive layout math.
css
.full-bleed {
/* Extend to viewport edges even inside a centered container */
margin-left: calc(50% - 50vw);
margin-right: calc(50% - 50vw);
}
.sidebar {
/* 33% of the parent, but never less than 200px */
width: calc(max(33%, 200px));
}
/* Dynamic gap trick: halve the gap for children */
:root { --col-gap: 32px; }
.grid { gap: var(--col-gap); }
.grid-item { padding: calc(var(--col-gap) / 2); }Animating custom properties
css
/* Register a property for smooth animation (modern browsers) */
@property --progress {
syntax: "<number>";
initial-value: 0;
inherits: false;
}
.progress-ring {
--progress: 0;
background: conic-gradient(
#6366f1 calc(var(--progress) * 1%),
#e2e8f0 0
);
transition: --progress 600ms ease;
}
.progress-ring.loaded {
--progress: 75;
}@property lets the browser understand the type of a custom property so it can interpolate it during transitions and animations. Without @property, changing a custom property snaps instantly because the browser treats it as a string.
Ready to test yourself?
Practice CSS— quiz & coding exercises