← Back to gallery
CSS

Typed Gradient Property Interpolation

A CSS gradient animation pattern that registers angle, hue, and stop-position custom properties so internal gradient values interpolate continuously instead of swapping whole strings.

@propertylinear-gradienthslangleinterpolationcustom-propertyreduced-motion

Background / @property / gradient internals

Typed Gradient Property Interpolation

Three gradient backgrounds animate registered internal values — angle, hue, and stop position — instead of swapping a full gradient string.

Registered angle

Angle Spin

Register the angle and feed it into linear-gradient().

  • @property
  • <angle>
  • linear-gradient

Registered hue

Hue Slide

Animate hue as a typed number, not as a replaced color string.

  • hsl
  • hue
  • number

Registered percentage

Stop Breathe

Animate the stop position while preserving the same gradient function.

  • stop position
  • percentage
  • smooth

Typed gradient inspector

Angle Spin

  • @property
  • <angle>
  • linear-gradient

The gradient angle is a typed <angle>, so the background rotates continuously without a discrete string jump.

Helped you ship something? 🐟 Send my cat a churu

/* Angle Spin: Register the angle and feed it into linear-gradient(). */
@property --rc-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 90deg;
}

.motion-gradient-angle-spin {
  --motion-duration: 5.0s;
  --rc-angle: 90deg;
  position: relative;
  width: min(100%, 18rem);
  min-height: 9rem;
  display: grid;
  place-items: center;
  overflow: hidden;
  border-radius: 1rem;
  background: #08111f;
}

.motion-gradient-angle-spin__surface {
  position: absolute;
  inset: 1rem;
  border-radius: .9rem;
  background:
    linear-gradient(var(--rc-angle), #67e8f9, #a78bfa, #08111f 76%);
  animation: gradient-angle-spin var(--motion-duration) ease-in-out infinite;
}

@keyframes gradient-angle-spin {
  50% { --rc-angle: 250deg; }
}

@media (prefers-reduced-motion: reduce) {
  .motion-gradient-angle-spin__surface { animation: none; }
}

How to make this

Typed gradient interpolation registers angle, hue, and stop variables so CSS animates the internal gradient values instead of swapping whole gradients.

html
<div class="typed-gradient">Typed gradient</div> <style>4@property --angle {  syntax: '<angle>';  inherits: false;  initial-value: 120deg;}9@property --hue {  syntax: '<number>';  inherits: false;  initial-value: 185;}14@property --stop {  syntax: '<percentage>';  inherits: false;  initial-value: 46%;}.typed-gradient {  --angle: 120deg;  --hue: 185;  --stop: 46%;  width: 18rem;  aspect-ratio: 16 / 9;  display: grid;  place-items: center;  border-radius: 1rem;  color: white;  background:    linear-gradient(      var(--angle),32      hsl(var(--hue) 86% 62%) 0%,      hsl(calc(var(--hue) + 74) 78% 68%) var(--stop),      hsl(calc(var(--hue) + 128) 80% 64%) 100%    );  animation: typed-gradient-shift 4.5s ease-in-out infinite;}@keyframes typed-gradient-shift {39  50% { --angle: 250deg; --hue: 284; --stop: 68%; }}41@media (prefers-reduced-motion: reduce) {  .typed-gradient { animation: none; }}</style>

Annotated snippet

  1. Line 4The angle is typed as an angle, so it can tween through intermediate values.

    Replacing a whole gradient string snaps between frames. A registered angle gives the browser real intermediate degrees.

    PitfallWhy register an angle custom property?

    Unregistered custom properties are parsed as tokens. Registering <angle> lets the browser interpolate through intermediate degrees.

  2. Line 9Hue is registered as a number so hsl() can interpolate color families without replacing the full gradient.

    Swapping named color stops jumps palettes. A numeric hue variable keeps the gradient structure stable while the color family moves.

    PitfallCan hue be registered as a color?

    Use a number when it feeds hsl(). Registering the full color is useful for different cases, but hsl hue math needs numeric values.

  3. Line 14Stop position is registered as a percentage, so the bright band can travel without replacing the gradient string.

    A hardcoded stop swap jumps the highlight. A registered percentage lets the stop position breathe through intermediate frames.

    PitfallWhy register a stop position?

    Percentage stops are numeric values. Registering them lets the highlight move through intermediate positions instead of jumping between gradient strings.

  4. Line 32The gradient declaration stays stable; only its typed variables change.
    PitfallWhy keep the gradient declaration stable?

    Stable declarations make interpolation predictable. Swapping whole gradient strings often falls back to a discrete jump.

  5. Line 39Keyframes target numeric controls. This is the core difference from background-position sweeps.
    PitfallHow is this different from a moving gradient background?

    Background-position moves an oversized image. This pattern changes the gradient values themselves.

  6. Line 41Reduced motion keeps a readable gradient frame and removes the continuous color sweep.
    PitfallWhat frame should reduced motion use?

    Choose the safest contrast frame. Do not pause on a bright or low-contrast midpoint just because it is visually dramatic.

Other pitfalls

What should I verify before shipping this pattern?
Check that the preview card and showcase communicate the same start and end state, every inspector control visibly changes the animation, compare demos stay fixed-height and centered, and reduced motion preserves the information without running a substitute loop.

Notes

Overview

Typed gradient interpolation keeps the gradient string stable and animates its numeric internals. CSS custom properties are normally string-like unless the browser knows their syntax. Registering angle, hue, stop, or percentage values with @property lets the browser interpolate the underlying numbers continuously.

When to use it

Use it when the animation needs to teach or expose how gradients change internally: angle turns, hue rotation, stop travel, or a controlled color sweep inside a stable surface. It is a good fit for technical demos, premium cards, and small branded surfaces. For a simple moving stripe, background-position is usually simpler and more predictable.

How it works

@property defines syntax such as <angle>, <number>, or <percentage>, plus inheritance and an initial value. The gradient references those variables, and keyframes change only the registered values so the browser can tween them rather than discrete-step the declaration.

Production gotchas

Always include a static fallback before the registered-property version. Browser support, cascade order, and initial values all matter here; a missing registration can turn a smooth interpolation into a jump. If the inspector exposes hue, angle, or stop controls, every control must visibly change the gradient; unused sliders should not ship.

Accessibility

Animated gradients can pass through low-contrast frames. Test text at the worst frame, not just the starting frame, and keep the animated area bounded so it does not compete with page reading. Reduced motion should freeze the safest readable frame rather than swapping to a different effect.

References

Implementation depth

This pattern is about animating gradient internals, not moving a gradient bitmap. Register angle, hue, and stop-position variables with @property so the browser can interpolate numeric pieces of the gradient continuously.

Unregistered custom properties often step discretely because the browser sees them as strings. Include a static fallback gradient, keep the animated area bounded, and verify the reduced-motion frame has enough contrast without depending on the sweep.

The implementation should make the typed value visible. Angle turns, hue shifts, and stop travel are different examples; changing only endpoint colors does not teach why @property matters.

Be conservative with inspector controls. Every exposed control needs to map to a registered variable or a real animation parameter, otherwise the snippet becomes misleading for users trying to copy the technique.