The following is a guest post by Ana Tudor. She is passionate about experimenting and learning new things. Also she loves maths and enjoys playing with code.
I had no idea how powerful CSS gradients could be until late 2011, when I found the CSS3 Patterns Gallery made by Lea Verou. The idea that you can obtain many shapes using just gradients was a starting point for many CSS experiments I would later do.
Recently, while browsing through the demos on CodePen, I came across a CSS3 Color Wheel and thought hey, I could do it with just one element and gradients. So I did and the result can be seen here. And now I’m going to explain the reasoning behind it.
Breaking it down
The wheel - or you can think of it as a pie - is first split horizontally into two halves and then each half is split into five slices, so there are ten slices in total. Which means that the central angle for each slice is 360°
/10 = 36°
.
The pen below shows graphically how to layer the multiple backgrounds. It also has a pause button so that the infinite animation doesn’t turn into a performance problem.
Check out this Pen!
For both the original pen and this helper demo, the interesting part is this one:
background: linear-gradient(36deg, #272b66 42.34%, transparent 42.34%),
linear-gradient(72deg, #2d559f 75.48%, transparent 75.48%),
linear-gradient(-36deg, #9ac147 42.34%, transparent 42.34%) 100% 0, linear-gradient(
-72deg,
#639b47 75.48%,
transparent 75.48%
) 100% 0,
linear-gradient(36deg, transparent 57.66%, #e1e23b 57.66%) 100% 100%, linear-gradient(
72deg,
transparent 24.52%,
#f7941e 24.52%
) 100% 100%,
linear-gradient(-36deg, transparent 57.66%, #662a6c 57.66%) 0 100%, linear-gradient(
-72deg,
transparent 24.52%,
#9a1d34 24.52%
) 0 100%, #43a1cd linear-gradient(#ba3e2e, #ba3e2e) 50% 100%;
background-repeat: no-repeat;
background-size: 50% 50%;
We first specify the nine gradient backgrounds, their positioning and the background-color
using the shorthand background
syntax.
The background shorthand
For anyone who doesn’t remember, the background layers are listed from the top one to the bottom one and the background-color
is specified together with the bottom layer. A background layer includes the following:
<background-image>
<background-position>
/<background-size>
<background-repeat>
<background-attachment>
<background-origin>
<background-clip>
If the background-position
is not specified, then the background-size
isn’t specified either. Also, since background-origin
and background-clip
both need the same kind of value (that is, a box value like border-box
or content-box
), then, if there is only one such value, that value is given to both background-origin
and background-clip
. Other than that, any value except the one for background-image
can be missing and then it is assumed to be the default.
Since we have nine background layers and we want to have the same non-default values for background-repeat
and background-size
for all of them, we specify these outside the shorthand so that we don’t have to write the same thing nine times.
In the case of background-size
, there is also another reason to do that: Safari doesn’t support background-size
inside the shorthand and, until recently (up to and including version 17), Firefox didn’t support that either. Also, two values should be always given when the background-image
is a gradient, because giving it just one value is going to produce different results in different browsers (unless that one value is 100%, in which case it might as well be missing as that is the default).
The background-color
is set to be a light blue (#43a1cd
) and then, on top of it, there are layered nine non-repeating (background-repeat: no-repeat
for all) background images created using CSS gradients. All nine of them are half the width
and the height
of the element (background-size: 50% 50%
).
The bottom one - horizontally centred (50%
) and at the bottom (100%
) - is really simple. It’s just a gradient from a firebrick red to the same color (linear-gradient(#ba3e2e, #ba3e2e)
), so the result is simply a solid color square.
The other eight are gradients from transparent
to a solid color or from a solid color to transparent
. Four of them look like double slices, having a central angle of 2*36° = 72°
, but half of each such double slice gets covered by another single slice (having a central angle of 36°
).
A few things about linear gradients
In order to better understand gradient angles and how the %
values for color stops are computed, let’s see how a linear gradient is defined. Hopefully, this demo that lets you change the gradient angle helps with that - just click the dots.
Check out this Pen!
The gradient angle is the angle - measured clockwise - between the vertical axis and the gradient line (the blue line in the demo). This is for the new syntax, which is not yet supported by WebKit browsers (however, this is going to change). The old syntax measured angles just like on the trigonometric unit circle (counter-clockwise and starting from the horizontal axis).
Note: coming from a mathematical background, I have to say the old way feels more natural to me. However, the new way feels consistent with other CSS features, like rotate transforms, for which the angle values are also clockwise.
What this means is that we (almost always) have different angle values in the standard syntax and in the current WebKit syntax. So, if we are not using something like -prefix-free (which I do almost all the time), then we should to be able to compute one when knowing the other. That is actually pretty simple. They are going in opposite directions, so the formula for one includes the other with a minus sign. Also, there is a 90°
difference between them so this is how we get them:
newSyntax = 90° - oldSyntax;
oldSyntax = 90° - newSyntax;
Note: if no gradient angle or destination side is specified (for example, linear-gradient(lime, yellow)
), then the resulting gradient is going to have a gradient angle of 180°
, not 0°
.
All the points on a line that is perpendicular on the gradient line have the same color. The perpendicular from the corner in the quadrant that’s opposite to the quadrant of the angle is the 0%
line (the crimson line in the demo) and its intersection with the gradient line is the starting point of the gradient (let’s call it S
). The perpendicular from the opposite corner (the one in the same quadrant as the gradient angle) is the 100%
line (the black line in the demo) and its intersection with the gradient line is the ending point of the gradient (let’s call it E
).
In order to compute the %
value of any point P
, we first draw a perpendicular on the gradient line starting from that point. The intersection between the gradient line and this perpendicular is going to be a point we’ll name I
. We now compute the ratio between the lengths of SI
and SE
and the %
value for that point is going to be 100%
times that ratio.
Putting it all to work
Now let’s see how we apply this for the particular case of the rainbow wheel.
Let’s first consider a gradient that creates a single slice (one with a central angle of 36°
). This is a square image (see below), with a blue slice having an angle of 36°
in the lower part. We draw the horizontal and vertical axes through the point O
at which the diagonals intersect. We draw a perpendicular from that point to the line that separates the dark blue part from the transparent part. This is going to be the gradient line. As it can be seen, there is a 36°
angle between the vertical axis and the gradient line, so the angle of the gradient is 36°
.
We now draw a perpendicular from the corner of the square in the quadrant that is opposite to the one in which the gradient angle is found. This is the 0%
line. Then we draw a perpendicular from the corner of the square in the same quadrant (Q I
) as the gradient angle - this is the 100%
line.
The intersection of the diagonals of a square splits each one of them into two, so AO
and BO
are equal. The BOE
and AOS
angles are equal, as they are vertical angles. Moreover, the BOE
and AOS
triangles are right triangles. All these three mean that the two triangles are also congruent. Which in turn means that SO
and EO
are equal, so the length of SE
is going to be twice the length of EO
or twice the length of SO
.
Note: before moving further, let’s go through a couple of trigonometry concepts first. The longest side of a right-angled triangle is the one opposing that right angle and it’s called the hypotenuse. The other two sides (the ones forming the right angle) are called the catheti of the right triangle. The sine of an acute angle in a right triangle is the ratio between the cathetus opposing that angle and the hypotenuse. The cosine of the same angle is the ratio between the adjacent cathetus and the hypothenuse.
Computing the length of EO
in the right triangle BOE
is really simple. If we take the length of the side of the square to be a
, then the length of the half diagonal BO
is going to be a*sqrt(2)/2
. The BOE
angle is equal to the difference between the BOM
angle, which is 45°
, and the EOM
angle, which is 36°
. This makes BOE
have 9°
. Since BO
is also the hypotenuse in the right triangle BOE
, the length of EO
is going to be (a*sqrt(2)/2)*cos9°
. Which makes the length of SE
be a*sqrt(2)*cos9°
.
We now draw a perpendicular from A
to the PI
line. ASID
is a rectangle, which means that the length of SI
equals the length of AD
. We now consider the rectangular triangle APD
. In this triangle, AP
is the hypotenuse and has a length of a
. This means that AD
is going to have a length of a*sin36°
. But SI
is equal to AD
, so it also has a length of a*sin36°
.
Since we now know both SI
and SE
, we can compute their ratio. It is sin36°/(sqrt(2)*cos9°) = 0.4234
. So the %
value for the color stop is 42.34%
.
In this way, we’ve arrived at: linear-gradient(36deg, #272b66 42.34%, transparent 42.34%)
Computing the %
values for the other background layers is done in the exact same manner.
Automating all this
By now, you’re probably thinking it sucks to do so many computations. And it must be even worse when there are more gradients with different angles…
Even though for creating the rainbow wheel experiment I did compute everything on paper… I can only agree with that! This is why I made a really basic little tool that computes the %
for any point inside the gradient box. You just need to click inside it and the %
value appears in a box at the bottom center.
Check out this Pen!
You can change the dimensions of the gradient box and you can also change the gradient itself. It accepts the newest syntax for linear gradients, with angle values in degrees, to <side>
values or no value at all for describing the direction of the gradient.
Final words
CSS gradients are really powerful and understanding how they work can be really useful for creating all sorts of imageless textures or shapes that would be difficult to obtain otherwise.