CSS Gradients

Avatar of Chris Coyier
Chris Coyier on (Updated on )

Just as you can declare the background of an element to be a solid color in CSS, you can also declare that background to be a gradient. Using gradients declared in CSS, rather using an actual image file, is better for control and performance.

Gradients are typically one color that fades into another, but in CSS you can control every aspect of how that happens, from the direction to the colors (as many as you want) to where those color changes happen. Let’s go through it all.

Gradients are background-image

While declaring the a solid color uses background-color property in CSS, gradients use background-image. This comes in useful in a few ways which we’ll get into later. The shorthand background property will know what you mean if you declare one or the other.

.gradient {

  /* can be treated like a fallback */
  background-color: red;

  /* will be "on top", if browser supports it */
  background-image: linear-gradient(red, orange);

  /* these will reset other properties, like background-position, but it does know what you mean */
  background: red;
  background: linear-gradient(red, orange);

}

Linear Gradient

Perhaps the most common and useful type of gradient is the linear-gradient(). The gradients “axis” can go from left-to-right, top-to-bottom, or at any angle you chose.

Not declaring an angle will assume top-to-bottom:

Those comma-separated colors can type of color you normally use: Hex, named colors, rgba, hsla, etc.

To make it left-to-right, you pass an additional parameter at the beginning of the linear-gradient() function starting with the word “to”, indicating the direction, like “to right”:

This “to” syntax works for corners as well. For instance, if you wanted the axis of the gradient to start at the bottom left corner and go to the top right corner, you could say “to top right”:

If that box was square, the angle of that gradient would have been 45°, but since it’s not, it isn’t. If you wanted to make sure it was 45°, you could declare that:

.gradient {
  background-image:
    linear-gradient(
      45deg, 
      red, #f06d06
    );
}

You aren’t limited to just two colors either. In fact you can have as many comma-separated colors as you want. Here’s four:

.gradient {
  background-image:
    linear-gradient(
      to right, 
      red, 
      #f06d06, 
      rgb(255, 255, 0), 
      green
    );
}

You can also declare where you want any particular color to “start”. Those are called “color-stops”. Say you wanted yellow to take up the majority of the space, but red only a little bit in the beginning, you could make the yellow color-stop pretty early:

We tend to think of gradients as fading colors, but if you have two color stops that are the same, you can make a solid color instantly change to another solid color. This can be useful for declaring a full-height background that simulates columns.

Browser Support / Prefixes

So far we’ve only looked at the new syntax, but CSS gradients have been around for quite a while. Browser support is good. Where it gets tricky is syntax and prefixing. There are three different syntaxes that browsers have supported. This isn’t what they are officially called, but you can think of it like:

  1. Old: original WebKit-only way, with stuff like from() and color-stop()
  2. Tweener: old angle system, e.g. “left”
  3. New: new angle system, e.g. “to right”

And then prefixing as well.

Let’s try a chart:

Chrome1-9: Old, prefixed
10-25: Tweener, prefixed
26: New, unprefixed
Safari3-: No support
4-5.0: Old, prefixed
5.1-6.0: Tweener, prefixed
6.1: New, unprefixed
Firefox3.5-: No support
3.6-15: Tweener, prefixed
16: New, unprefixed
Opera11.0-: No support
11.1-11.5: Tweener, prefixed, only linear
11.6-12: Tweener, prefixed, added radial
12.1: Tweener, unprefixed
15: New, unprefixed
IE8-: No support
9: filters only
10+: New, unprefixed (also supports Tweener w/ prefix)
Android2.0-: No support
2.1-3.0: Tweener, prefixed
4.0-4.3: New, prefixed
4.4+: New, unprefixed
iOS3-: No support
3.2-4.3: Tweener, prefixed
5.0-6.1: New, prefixed
7.0: New, unprefixed

There is some overlap in there. For instance when a browser supports the New syntax they probably also support the older syntaxes as well, including the prefix. Best practice is: if it supports New, use New.

So if you wanted to absolute deepest possible browser support, a linear gradient might look like this:

.gradient {
  
  /* Fallback (could use .jpg/.png alternatively) */
  background-color: red;

  /* SVG fallback for IE 9 (could be data URI, or could use filter) */
  background-image: url(fallback-gradient.svg); 

  /* Safari 4, Chrome 1-9, iOS 3.2-4.3, Android 2.1-3.0 */
  background-image:
    -webkit-gradient(linear, left top, right top, from(red), to(#f06d06));
  
  /* Safari 5.1, iOS 5.0-6.1, Chrome 10-25, Android 4.0-4.3 */
  background-image:
    -webkit-linear-gradient(left, red, #f06d06);

  /* Firefox 3.6 - 15 */
  background-image:
    -moz-linear-gradient(left, red, #f06d06);

  /* Opera 11.1 - 12 */
  background-image:
    -o-linear-gradient(left, red, #f06d06);

  /* Opera 15+, Chrome 25+, IE 10+, Firefox 16+, Safari 6.1+, iOS 7+, Android 4.4+ */
  background-image:
    linear-gradient(to right, red, #f06d06);

}

That’s an awful lot of code there. Doing it by hand would be error-prone and a lot of work. Autoprefixer does a good job with it, allowing you to trim that amount of code back as you decide what browsers to support.

The Compass mixin can do SVG data URI’s for IE 9 if that’s important to you.

To complicate things just a little more, the way degrees work in the OLD vs NEW syntax is a bit different. The OLD (and TWEENER – usually prefixed) way defines 0deg and left-to-right and proceeds counter-clockwise, while the NEW (usually unprefixed) way defines 0deg as bottom-to-top and proceeds clockwise.

OLD (or TWEENER) = (450 – new) % 360

or even simpler:

NEW = 90 – OLD
OLD = 90 – NEW

like:

OLD linear-gradient(135deg, red, blue)
NEW linear-gradient(315deg, red, blue)

IE filters

Internet Explorer (IE) 6-9, while they don’t support the CSS gradient syntax, do offer a programmatic way to do background gradients

/* "Invalid", but works in 6-8 */
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr=#1471da, endColorstr=#1C85FB);

/* Valid, works in 8-9 */
-ms-filter: "progid:DXImageTransform.Microsoft.gradient (GradientType=0, startColorstr=#1471da, endColorstr=#1C85FB)";

There are some considerations here on deciding to use this or not:

  1. filter is generally considered a bad practice for performance,
  2. background-image overrides filter, so if you need to use that for a fallback, filters are out. If a solid color is an acceptable fallback (background-color), filter is a possibility

Even though filters only work with hex values, you can still get alpha transparency by prefacing the hex value with the amount of transparency from 00 (0%) to FF (100%). Example:

rgba(92,47,90,1) == #FF5C2F5A
rgba(92,47,90,0) == #005C2F5A

Radial Gradients

Radial gradient differ from linear in that they start at a single point and emanate outwards. Gradients are often used to simulate a lighting, which as we know isn’t always straight, so they can be useful to make a gradient seem even more natural.

The default is for the first color to start in the (center center) of the element and fade to the end color toward the edge of the element. The fade happens at an equal rate no matter which direction.

You can see how that gradient makes an elliptical shape, since the element is not a square. That is the default (ellipse, as the first parameter), but if we say we want a circle we can force it to be so:

.gradient {
  background-image:
    radial-gradient(
      circle,
      yellow,
      #f06d06
    );
}

Notice the gradient is circular, but only fades all the way to the ending color along the farthest edge. If we needed that circle to be entirely within the element, we could ensure that by specifying we want the fade to end by the “closest-side” as a space-separated value from the shape, like:

The possible values there are: closest-corner, closest-side, farthest-corner, farthest-side. You can think of it like: “I want this radial gradient to fade from the center point to the __________, and everywhere else fills in to accommodate that.”

A radial gradient doesn’t have to start at the default center either, you can specify a certain point by using “at ______” as part of the first parameter, like:

I’ll make it more obvious here by making the example a square and adjusting a color-stop:

Browser support for radial gradients is largely the same as linear-gradient(), except a very old version of Opera, right when they started supporting gradients, only did linear and not radial.

But similar to linear, radial-gradient() has gone through some syntax changes. There is, again: “Old”, “Tweener”, and “New”.

/* Example of Old */
background-image: 
  -webkit-gradient(radial, center center, 0, center center, 141, from(black), to(white), color-stop(25%, blue), color-stop(40%, green), color-stop(60%, red), color-stop(80%, purple));

/* Example of Tweener */
background-image: 
  -webkit-radial-gradient(45px 45px, farthest-corner, #F00 0%, #00F 100%) repeat scroll 0% 0% rgba(0, 0, 0, 0);

/* Example of New */
background-image: 
  radial-gradient(circle farthest-side at right, #00F, #FFF);

The hallmarks being:

  • Old: Prefixed with -webkit-, stuff like from() and color-stop()
  • Tweener: First param was location of center. That will completely break now in browsers that support new syntax unprefixed, so make sure any tweener syntax is prefixed.
  • New: Verbose first param, like “circle closest-corner at top right”

Again, I’d let Autoprefixer handle this. You write in the newest syntax, it does fallbacks. Radial gradients are more mind-bending than linear, so I’d recommend attempting to just get comfortable with the newest syntax and going with that (and if necessary, forget what you know about older syntaxes).

Conic Gradients

A conic gradient is similar to a radial gradient. Both are circular and use the center of the element as the source point for color stops. However, where the color stops of a radial gradient emerge from the center of the circle, a conic gradient places them around the circle.

Illustrating the difference betweenConic (left) and Radial (right) gradients.

They’re called “conic” because they tend to look like the shape of a cone that is being viewed from above. Well, at least when there is a distinct angle provided and the contrast between the color values is great enough to tell a difference.

The conic gradient syntax is easier to understand in plain English:

Make a conic-gradient that is located at [some point] that starts with [one color] at some angle and ends with [another color] at [some angle]

At it’s most basic level, it looks like this:

.conic-gradient {
  background: conic-gradient(#fff, #000);
}

…where it is assumed that the location of the gradient starts at the very center of the element (50% 50%) and is evenly distributed between white and black color values.

We could have written this in several other ways, all of which are valid:

.conic-gradient {
  /* Original example */
  background-image: conic-gradient(#fff, #000);
  /* Explicitly state the location center point */
  background: conic-gradient(at 50% 50%, #fff, #000);
  /* Explicitly state the angle of the start color */
  background: conic-gradient(from 0deg, #fff, #000);
  /* Explicitly state the angle of the start color and center point location */
  background: conic-gradient(from 0deg at center, #fff, #000);
  /* Explicitly state the angles of both colors as percentages instead of degrees */
  background: conic-gradient(#fff 0%, #000 100%);
  /* Explicitly state the angle of the starting color in degrees and the ending color by a full turn of the circle */
  background: conic-gradient(#fff 0deg, #000 1turn);
}

If we do not specify an angle for the colors, then it is assumed that the gradient is evenly divided between the colors, starting at 0deg and ending at 360deg. That kind of creates a hard stop where the colors bump right up to one another at 0deg and 360deg. If our starting color would begin somewhere else on the circle, say one quarter of the way at 90deg, then that creates a smoother gradient and we start to get that cool cone-looking perspective.

.conic-gradient {
  background: conic-gradient(from 90deg, #fff, #000);
}

We can have fun with conic gradients. For exmaple, we can use it to create the same sort of pattern you might see in a color picker or the infamous Mac spinning beach ball indicator:

.conic-gradient {
  background: conic-gradient(red, yellow, lime, aqua, blue, magenta, red);
}
A mockup illustrating a conic gradient emulating a color wheel pattern

Or, let’s try a simple pie chart by adding hard stops between three color values:

.conic-gradient {
  background: conic-gradient(lime 40%, yellow 0 70%, red 0);
}
A mockup illustrating a conic gradient emulating a simple three-color pie chart

Unfortunately, conic-gradient has no browser support at the time of this writing. It’s currently part of the CSS Image and Replaced Content Module Level 4 specification, which is in working draft. In the meantime, Lea Verou (who has contributed to the spec) provides a polyfill that makes them possible.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

ChromeFirefoxIEEdgeSafari
6983No7912.1

Mobile / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
12312412312.2-12.5

Repeating Gradients

With ever-so-slightly less browser support are repeating gradients. They come in both linear and radial varieties.

There is a trick, with non-repeating gradients, to create the gradient in such a way that if it was a little tiny rectangle, it would line up with other little tiny rectangle versions of itself to create a repeating pattern. So essentially create that gradient and set the background-size to make that little tiny rectangle. That made it easy to make stripes, which you could then rotate or whatever.

With repeating-linear-gradient(), you don’t have to resort to that trickery. The size of the gradient is determined by the final color stop. If that’s at 20px, the size of the gradient (which then repeats) is a 20px by 20px square.

Same with radial:

Improper Fallback Loading

As we’ve covered, some really old browsers don’t support any CSS gradient syntax at all. If you need a fallback that is still a gradient, an image (.jpg / .png) could do the trick. The scary part with that is that some slightly-less-old browsers, that were just starting to support CSS gradients, would load the fallback image. As in, make the HTTP request for the image even though it would render the CSS gradient.

Firefox 3.5.8 did this (see screenshot), as well as Chrome 5- and Safari 5.0.1. See:

Safari 5.0.1 loading fallbacks improperly

The good news is this isn’t really any issue anymore. The only offending browsers were Chrome and Safari and Chrome hasn’t done it since 6 and Safari hasn’t done it as of 5.1, going on three years ago.

Additional Resources