Grow your CSS skills. Land your dream job.

Conical Gradients in CSS

Published by Guest Author

The following is a guest post by Shankar Cabus. Shankar made a really rad color wheel on CodePen and showed it to me. I thought it was an image underneath, because gradients like this aren't something CSS can natively do. Or so I thought. Shankar found a way to do it with newfangled tech.

When I create filters, shadows, transformations or gradient backgrounds in CSS, it feels amazing. Who would have thought CSS would come this far. No need to use images for any of that stuff. I can almost hear the browser telling me "don't worry, I do it for you."

Despite all this evolution, CSS still has limitations. For instance, background gradients can only be created in linear or radial styles, but not a conical shape.

In 2011, Lea Verou started a thread about implementing a native CSS conical gradient, creating a draft specification which has already been introduced to W3C's official draft. However, we still have to wait for the W3C to formalize the feature and for the browsers to implement it, which might still take quite some time. In the meantime, I will show you how to simulate a conical gradient using CSS3 only.


Example of a conical gradient.

Beautiful, right? Let's look at the code!

The Beginning

To reduce code duplication, I'm using Sass. One of the most interesting features of CSS preprocessors is the @mixin. A @mixin is a blend of function and include which, when called, returns its contents.

@mixin circle($size) {
  content: "";
  position: absolute;
  border-radius: 50%;
  width: $size;
  height: $size;
  left: calc(50% - #{$size/2});
  top: calc(50% - #{$size/2});
}

This @mixin is only used to set properties of shape and placement, creating a circle of absolute position and centered horizontally and vertically in relation to its parent.

The Magic!

By combining a @mixin circle with the clip property, we can get a semi-circle. Let's start by creating a full circle by placing two semi-circles (with different colors) together. Can you imagine what is going to happen if we rotate one of the semi-circles? Magic!

$wheel: 15em;
.color {
  @include circle($wheel);
  background: red;
  clip: rect(0, $wheel, $wheel, #{$wheel/2});
  &:after {
    @include circle($wheel);
    background: blue;
    clip: rect(0, #{$wheel/2}, $wheel, 0);
    transform: rotate(45deg);
  }
}

The clip: rect (top, right, bottom, left) property restricts the visible area to a rectangular element, which causes only half of the red circle to be seen in the example above. The same principle is applied to the blue circle, the .color:after element. At this point we would have a full circle which is half red and half blue. However, the transform property causes the blue circle's visible area to invade the red circle's. See example.


Semi-circles with clip property

The Colorful Umbrella


Colorful umbrella

We already know how to perform this magic trick. Let's create a 12 color umbrella:

<div class="wheel">
  <ul class="umbrella">
    <li class="color"></li>
    <li class="color"></li>
    <li class="color"></li>
    <li class="color"></li>
    <li class="color"></li>
    <li class="color"></li>
    <li class="color"></li>
    <li class="color"></li>
    <li class="color"></li>
    <li class="color"></li>
    <li class="color"></li>
    <li class="color"></li>
  </ul>
</div>

Since we want to create a full circle by combining semi-circles, from the seventh item (first item in the second half), we must reverse the clip:

.color, .color:nth-child(n+7):after {
  clip: rect(0, $wheel, $wheel, #{$wheel/2});
}
.color:after, .color:nth-child(n+7) {
  @include circle($wheel);
  clip: rect(0, #{$wheel/2}, $wheel, 0);
}

From the seventh item, the .color becomes a semi-circle with the left half of the circle and the .color:after elements pass to the right half of the circle.

We are almost done!! We need to change the colors and the angle of each element. Again, let's abuse the power of Sass to generate 26193^42 lines of code in just over 10 ;)

$colors: (#9ED110, #50B517, #179067, #476EAF, #9f49ac, #CC42A2, #FF3BA7, #FF5800, #FF8100, #FEAC00, #FFCC00, #EDE604);
@for $i from 0 to length($colors) {
  .color:nth-child(#{1+$i}):after {
    background-color: nth($colors, $i+1);
    @if $i < 6 {
      transform: rotate(#{30*(1+$i)}deg);
      z-index: #{length($colors)-$i};
    } @else {
      transform: rotate(#{-30+(30*(1+$i))}deg);
    }
  }
}

First, we define the $colors array with the "12 colors of the rainbow" and then iterate over the list creating .color:nth-child(n):after selector with the background-color, rotate and z-index properties.

The rotate property has some important points. Its angle is defined according to the number of colors in the circle. In our case, there are 12 colors, so 360/12 = 30 is the rotation of each color. But from the seventh item on, the other half of the circle starts, remember? Thus, the process we just described stops at the seventh item. We will then start the very same process again, but this time, the rotation will take place on another direction.. That's why there is an @else with rotate(#{-30+(30*($i+1))}deg) which subtracts 30 degrees from the elements of the second half of the circle.

If you are a good observer (and understood everything so far), you should have noticed that our our umbrella is actually a fan! Bazinga! So for the last color of the first half of the circle does not get on top of other colors, we need to reverse the index of these elements. For example, the z-index (6) = 1 and z-index (1) = 6.

A Little More Magic

Finally, we need to ease the transition between colors, after all, we don't want an umbrella-range-rainbow, we want a conical gradient!

.umbrella {
  -webkit-filter: blur(1.7em);
}

.wheel {
  overflow: hidden;
  box-shadow: inset 0 0 0 3em rgba(0, 0, 0, 0.3);
}

The blur filter is responsible for mixing the colors. But, by applying the blur, the colors strain the limits of the circle. That's why the overflow: hidden property was also added to the .wheel element. The inner box-shadow is used to darken the edges that were "washed out". This is the end result:

See the Pen Conical gradient in pure CSS by Shankar Cabus (@shankarcabus) on CodePen

Demo

The conical gradient can be used in different ways to create different effects. But one of the most interesting applications is the Color Picker, as in the example below:

See the Pen Color Picker in Pure CSS by Shankar Cabus (@shankarcabus) on CodePen

Other Demos

Editor's note: I've seen many other approaches to this over time. I'll start dropping them here as a reference.

See the Pen css conical gradient by Kai Waddington (@waddington) on CodePen.

Another demo by Michael Wehner.

Comments

  1. WOW! Just WOW.

    This is actually one of the most intelligent CSS tricks I’ve seen in a while.

    Thanks and keep up the good work!

  2. Joel Birch
    Permalink to comment#

    Mind blown! Very clever stuff.

    A nice alternative to darkening the outer edge with an inset shadow is to scale up the “umbrellas” inside the “wheel” cropping element. Looks a bit more natural, I think. Here’s a fork of the final pen showing this.

  3. This is just epic.

  4. Chris Reed
    Permalink to comment#

    You could also use this to detect the angle of approach in css.
    Very Cool!

  5. Johnny Betts
    Permalink to comment#

    Bout time!

    WOOT HOT DOG!

  6. giacomo upCreative
    Permalink to comment#

    Really nice tutorial, thanks to share!

  7. Mark Johnson
    Permalink to comment#

    Well! Heidi-ho! +1000

  8. Wilson Mendes
    Permalink to comment#

    Great example of CSS power! Very cool!

  9. DarrenM
    Permalink to comment#

    Pretty neat. I have had to do this very thing as a background for a radar plot and ended up using SVG because I couldn’t think of any other way to do it without images.

    I don’t know if it’s my eyes, my monitor, or Chrome, but I can see an artefact of a faint white circle within the main one, a bit over half the diameter.

    • Joel Birch
      Permalink to comment#

      I can see that artifact via my ipad. I think it’s caused by the inset shadow used to darken the outer edge. See my comment (second from the top) for an alternative to this.

  10. Arun Mahendrakar
    Permalink to comment#

    Absolutely amazing. Thanks for the trick guys.

    Arun

  11. Randson
    Permalink to comment#

    Wow, very good! :)

  12. Very clever trick, I like that.

    You may want to slightly change your Sass loop to make it easier to read though:

    @for $i from 1 through length($colors) {
        .color:nth-child(#{$i}):after {
            /* ... */
        }
    }
    

    Great work nevertheless!

  13. Permalink to comment#

    Looks amazing.
    Good job, Shankar!!

  14. IndiCss
    Permalink to comment#

    This might explode if you touch it, but here‘s a little experiment with another way to fake a conical gradient with pure CSS. It’s not as nice as it’s an actual conical gradient, so the center doesn’t look too nice. You could place something in the middle to cover it though.

    (Disclaimer: it’s probably not an accurate gradient.)

    I can explain what you’re looking at if it’s not obvious.

    • Reagan
      Permalink to comment#

      You could clean it up like the example above by just adding

      -webkit-filter: blur(1.7em);

      to your umbrella class.
      Still well done to both you and
      Shankar. I love examples like these.

  15. Finally, an elegant solution for image-free clown nipples.

    Honk honk! DB

  16. m
    Permalink to comment#

    The demo doesn’t work right on Safari.

  17. Permalink to comment#

    Conical gradients can also be emulated with linear gradients (this also means using just one element). The idea behind is the one I’ve explained in this article.

  18. Rayeed
    Permalink to comment#

    Very cool stuff- thanks for posting this! I hope conical gradients will be possible in the very near future without using multiple elements, kinad like how you can use linear or radial gradients. That’s also something that SVG should really have as a standard.
    Thanks again for posting this!

  19. Greg
    Permalink to comment#

    Well Done! But need to wait couple of years to have it supported in IE:)

  20. Tom Bille
    Permalink to comment#

    A tribute to the Mac OS Finder implementation :-)

    CSS Spinning Wheel

    Any idea how to put text in the tiles ?

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".