02
Framer

Radial background

Summary
  1. Create animated CSS gradient effects powered by Framer Motion.

  2. Animate gradients by altering background-size and background-position.

  3. Utilize keyframes to choreograph the animation.

01 / 05

Background

A while back, I explored the concept of animating the CSS repeating-radial-gradient property, and the results were quite impressive, considering that it only involved CSS within a single <div>. This inspired me to develop a Framer component to recreate the same effect.

This effect combines:

  • Multiple repeating-radial-gradient layers.

  • Adjusting color stops.

  • Experimenting with background-position.

  • Incorporating background-size.

  • Animations using Framer Motion.

The outcome can be a series of fascinating visual effects!

Directly manipulating the code to achieve the desired effect can be challenging, as it's often difficult to anticipate the outcome with numerous options affecting each other. Therefore, I transformed it into a Framer component with a wide range of customizable properties, making it simpler to obtain the desired results.

This component is designed to accommodate various use cases:

✅ 100% responsiveness.
✅ Static pattern, animating on start or on hover.
✅ Complete customization (Color stops, transition, keyframes).
✅ Seamless integration with Framer's native features.

02 / 05

Radial gradient composition

The CSS repeating-radial-gradient() function enables creating a radial gradient pattern that repeats endlessly. I won't delve into its mechanics, as numerous developer resources already explain how it works: repeating-radial-gradient()

However, I will provide an overview of how this component is constructed.

For this Framer component, I opted for two repeating radial gradients, as more layers would make the component difficult to configure.

Regarding color stops, I chose three distinct stops where users can define their placements.

React props are passed through propertyControls.

const radialStyle = {
  backgroundImage: 
  `repeating-radial-gradient( 
      ${props.radialA.color0} ${props.radialA.stop0}%, 
      ${props.radialA.color50} ${props.radialA.stop50}%, 
      ${props.radialA.color100} ${props.radialA.stop100}%),      
  repeating-radial-gradient( 
      ${props.radialB.color0} ${props.radialB.stop0}%, 
      ${props.radialB.color50} ${props.radialB.stop50}%, 
      ${props.radialB.color100} ${props.radialB.stop100}%)`,
  width: "100%",
  height: "100%",
}
03 / 05

Size and position manipulation

While the repeating-radial-gradient() function includes syntax for determining the radial pattern's size and position, I find it more convenient to control the size and position separately using background-size and background-position.

By adjusting the size and position, unique effects can be created.

04 / 05

Keyframe animations

Animations bring the pattern to life. This Framer component employs a duration-based animation type: "tween" and offers the flexibility to specify various keyframes for the animation:

  • 0 → 100%

  • 0 → 50% → 100%

  • 0 → 25% → 50% → 75% → 100%

Each keyframe allows customization of background-size and background-position, enabling the choreography of the pattern's movement within the container.

By default, each keyframe occupies an equal portion of the overall duration. However, using a times array, users can designate when each keyframe in the sequence should be reached. Values between 0 and 1 represent progress values in the sequence. The times values will always start at 0 and end at 1, and the number of values in times should match the number of keyframes.

The keyframe at 0% reflects what users see on the design canvas and is set as the value for the initial prop within the motion component.

The function below generates the appropriate background properties for background-size and background-position based on the number of selected keyframes through the Framer UI. These values populate the variants.

Using if…else conditions, the correct properties are generated depending on the number of chosen keyframes.

const generateBackgroundProps = (keyframes, sizeOrPosX, sizeOrPosY, initial) => {
  let properties = [];

  // 0% (Initial)
  properties.push(
    `${props.radialA[sizeOrPosX]}% ${props.radialA[sizeOrPosY]}%, 
    ${props.radialB[sizeOrPosX]}% ${props.radialB[sizeOrPosY]}%`
  );

  // 25%
  if (keyframes.keyframeNumber === 4 && !initial) {
    properties.push(
      `${keyframes.keyframe25A[sizeOrPosX]}% ${keyframes.keyframe25A[sizeOrPosY]}%, 
      ${keyframes.keyframe25B[sizeOrPosX]}% ${keyframes.keyframe25B[sizeOrPosY]}%`
    );
  }

  // 50%
  if (keyframes.keyframeNumber >= 2 && !initial) {
    properties.push(
      `${keyframes.keyframe50A[sizeOrPosX]}% ${keyframes.keyframe50A[sizeOrPosY]}%, 
      ${keyframes.keyframe50B[sizeOrPosX]}% ${keyframes.keyframe50B[sizeOrPosY]}%`
    );
  }

  // 75%
  if (keyframes.keyframeNumber === 4 && !initial) {
    properties.push(
      `${keyframes.keyframe75A[sizeOrPosX]}% ${keyframes.keyframe75A[sizeOrPosY]}%, 
      ${keyframes.keyframe75B[sizeOrPosX]}% ${keyframes.keyframe75B[sizeOrPosY]}%`
    );
  }

  // 100%
  if (!initial) {
    properties.push(
      `${keyframes.keyframe100A[sizeOrPosX]}% ${keyframes.keyframe100A[sizeOrPosY]}%, 
      ${keyframes.keyframe100B[sizeOrPosX]}% ${keyframes.keyframe100B[sizeOrPosY]}%`
    );
  }

  return properties;
}
const radialVariants = {
  initial: {
    backgroundSize: generateBackgroundProps(
      props.keyframes,
      "size",
      "size",
      true
    ),
    backgroundPosition: generateBackgroundProps(
      props.keyframes,
      "posX",
      "posY",
      true
    ),
  },
  hover: {
    backgroundSize: generateBackgroundProps(
      props.keyframes,
      "size",
      "size",
      false
    ),
    backgroundPosition: generateBackgroundProps(
      props.keyframes,
      "posX",
      "posY",
      false
    ),
  },
}
05 / 05

Final thoughts

To wrap up, this component is available for free (Or pay what you want) where I'll be actively maintaining and updating it. Feel free to visit the demo at radial-background.framer.website to explore the various effects that can be achieved with this component.

If you think it might be a valuable addition to your project, go get it!

⬑ Like this? Show your support

⬑ Like this? Show your support

⬑ Like this? Show your support