CPCODELAB
CSS

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