Saw-tooth banners with CSS

Taking advantage of CSS background gradients

The idea of exploiting the background property of CSS to create dazzling patterns is not new. Accomplished designer and coder, Lea Verou, curates a mind-boggling collection of background patterns that are crafted solely by manipulating the very property itself, and rendered with the help of standards compliant modern browsers.

I was browsing through iFixit’s manual yesterday (had to replace an old iPhone 4 battery, but that’s another story itself), and came across the following banner:

It may look fine on a regular, non-retina display, but it surely looks fuzzy on my MBP’s retina display. The reason is because the designer have chosen to use an image banner instead of a CSS and text-based one. However, I realized that it is not exceptionally hard to achieve the same effect with the help of CSS backgrounds alone:

The advantage of using a CSS-only method is that it is pixel density agnostic — you do not have to create multiple versions of the same image to cater to different display pixel densities. Also, by using CSS you are saving yourself the hassle of making additional HTTP requests for images, and downsizing the amount of bandwidth consumed for the same visual appearance.

Plus, CSS offers immense amount of flexibility over static images, allowing one to swap background colours, apply transformations, manipulate the DOM and etc.

Do note that browser support for multiple backgrounds as well as the modern syntax of background linear gradients is an absolute must. Of course, there are plenty of services out there that generates the necessary CSS code to ensure that background gradients display appropriately on majority of browsers, but for the sake of brevity I have chosen to leave that out.

The actual implementation

TL;DR — the actual demo hosted on CodePen.

My first intuition is to rely on pseudo-elements positioned absolutely at the top and the bottom, and then I realised that I do not have to do so since we can specify where each background image is positioned by declaring the background-position property for each individual image/gradient.

We have to use several gradients in order to accomplish this task. The top and bottom jagged edges require two gradients each — imagine that you need two mirrored triangles per jagged edge—while the two grey bars on the extreme left and right can be easily done with a horizontal gradient.

The choice of the word ‘gradient’ might be a misnomer here, since there is no gradual transition of colours. By specifying two colour stops sharing the same position along the gradient line, we tell the browser to render the transition as a sharp border.

Also, the start and end positions (0% and 100%) are implicitly implied by the renderer, so we can leave them out.

The top and bottom jagged edges

As mentioned before, you can imagine the jagged edges are being assembled from a mirrored set of triangles — one positioned on the top/bottom-left, the other positioned on the top/bottom-right.

background-image:
/* Top jagged */
linear-gradient(
135deg,
rgba(255,255,255,1) 50%, rgba(255,255,255,0) 50%
),
linear-gradient(
-135deg,
rgba(255,255,255,1) 50%, rgba(255,255,255,0) 50%
),
    /* Bottom jagged */
linear-gradient(
45deg,
rgba(255,255,255,1) 50%, rgba(255,255,255,0) 50%
),
linear-gradient(
-45deg,
rgba(255,255,255,1) 50%, rgba(255,255,255,0) 50%
);

Perhaps an easier way to visualise this issue is with a gif animation below. You can see that the building block of the jagged edges is made up from two horizontally-mirrored triangles. They are created by specifying a linear gradient starting from the top left and right respectively, with two colour stops at the 50% mark.

A gif animation showing the building block of the top jagged edges.

After the basic building block has been made, we declare it’s background size and allow it to repeat along the x-axis. Also, we want to make sure that the position of the jagged edges are horizontally centered. This is done by manipulating the background-size, background-repeat and background-position properties respectively, as enumerated below:

background-position:
/* Top jagged */
top center, top center,
/* Bottom jagged */
bottom center, bottom center;
background-size:
/* Top + bottom jagged */
0.75rem 0.75rem, 0.75rem 0.75rem,
0.75rem 0.75rem, 0.75rem 0.75rem;
background-repeat: repeat-x;

The reason why I chose not to declare a universal background-size is because we still have to add one more gradient later, which will have a different size. In the case of non-uniform CSS properties like this, you will have to specify the individual values for each, comma-separated of course.


Other gradients: Drop shadow

If you want to add a drop shadow to the jagged edges, you can do it by generating a top and bottom vertical gradient. This should be appended to the background-image property. If you don’t and declare a new backgrond-image property, CSS will work as it is intended (cascading, that is), which will overwrite all your previous background image declaration and only honouring the last occuring instance.

/* Top and bottom fade */
linear-gradient(
180deg,
rgba(0,0,0,0.5) 0%, rgba(0,0,0,0) 100%
),
linear-gradient(
0deg,
rgba(0,0,0,0.5) 0%,
rgba(0,0,0,0) 100%
);

For other background properties, you should do the following:

  • For background-position, it should be “top center, bottom center”
  • For background-repeat, it does not matter. The “repeat-x” value will be inherited by all background images.
  • For background-size, you should specify full width with a height you desire, which determines how far the shadow will stretch, e.g. “100% 1.5rem”.

Other gradients: Vertical grey bars

The vertical grey bars can be created by simply specifying a horizontal linear gradient, as seen below:

/* Vertical grey bars */
linear-gradient(
90deg,
rgba(200,200,200,1) 2%, rgba(200,200,200,0) 2%,
rgba(200,200,200,0) 97%, rgba(200,200,200,1) 97%
)

Again, you should append this to the background-image property. Also, it’s position and size will be different:

  • For background-position, it should be “top center” or “center center” (does not matter since you will set its size to “cover” later)
  • For background-repeat, it does not matter. The “repeat-x” value will be inherited by all background images.
  • For background-size, it should be “cover”

And you’re done!


You can refer to the demo hosted on CodePen to check out the versatility of this technique.

Have you come across other good examples of CSS gradients being used? Do leave a note here.