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.
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.
The 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.
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!
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.
A really great improvement! Thanks ;)
This is just epic.
You could also use this to detect the angle of approach in css.
Very Cool!
Bout time!
WOOT HOT DOG!
Really nice tutorial, thanks to share!
Well! Heidi-ho! +1000
Great example of CSS power! Very cool!
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.
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.
Absolutely amazing. Thanks for the trick guys.
Arun
Wow, very good! :)
Very clever trick, I like that.
You may want to slightly change your Sass loop to make it easier to read though:
Great work nevertheless!
Yes… so would be better. Thank you, Hugo!
Looks amazing.
Good job, Shankar!!
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.
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.
Finally, an elegant solution for image-free clown nipples.
Honk honk! DB
The demo doesn’t work right on Safari.
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.
Nice to see conical gradients getting some traction, although the amount of code this results in probably makes it unfit for production use. Still, we need experiments like this to get going with conical gradients.
Btw, here’s a simpler way to do it, but a lot more limited (you can have 4 segments, not 8): http://dabblet.com/gist/6915653
@Lea Verou…
that is ruddy awesome… nice one
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!
Well Done! But need to wait couple of years to have it supported in IE:)
A tribute to the Mac OS Finder implementation :-)
CSS Spinning Wheel
Any idea how to put text in the tiles ?
This was the first case that I used the conical gradient ;)
http://codepen.io/shankarcabus/details/hBbDi