This is episode #22 in a series examining modern CSS solutions to problems I've been solving over the last 13+ years of being a frontend developer.
Modern CSS - and modern browser support - provides us three excellent methods to create pure, basic CSS shapes. In this tutorial, we will examine how to create CSS triangles using:
- linear gradients
Method 1: Borders#
This is the first trick I learned to create CSS triangles, and it's still a solid standby.
Given a zero width and zero height element, any values provided
border directly intersect and are the only visible indication of an element. This intersection is what we can take advantage of to create a triangle shape.
To illustrate how this works, we'll supply a different border color to each side:
border: 10px solid blue;
Which produces the following, in which you can see we've essentially already achieved 4 triangle shapes:
In order to create this as a single triangle instead, we first decide which direction we want the triangle pointing. So, if we want it pointing to the right, similar to a "play" icon, we want to keep the left border visible. Then, we set the colors of the other borders to
transparent, like so:
border: 10px solid transparent;
In the demo image below, I've added a red
outline to see the bounding box so we can discuss some improvements.
One improvement we can make is to remove width of the right border to prevent it being included in the total width of the element. We can also set unique values for top and bottom to elongate the triangle visual. Here's a compact way to achieve these results:
/* top | right | bottom | left */
border-width: 7px 0 7px 10px;
As seen in the updated image below, we first assign a solid, transparent border. Then we define widths such that the top and bottom are a smaller value than the left to adjust the aspect ratio and render an elongated triangle.
So to point the triangle a different direction, such as up, we just shuffle the values so that the bottom border gains the color value and the top border is set to zero:
/* top | right | bottom | left */
border-width: 0 7px 10px 7px;
Borders are very effective for triangles, but not very extendible beyond that shape without getting more elements involved. This is where our next two methods come to the rescue.
CSS gradients are created as
First let's set our stage, if you will, by defining box dimensions and preventing
/* Optional - helping us see the bounding box */
outline: 1px solid red;
Following that, we'll add our first gradient. This will create the appearance of coloring half of our element blue because we are creating a hard-stop at 50% between blue and a transparent value.
background-image: linear-gradient(45deg, blue 50%, rgba(255,255,255,0) 50%);
Now, if our element was square, this would appear to cut corner to corner, but we ultimately want a slightly different aspect ratio like we did before.
Our goal is to create a triangle with the same appearance as when using our border method. To do this, we will have to adjust the
The first adjustment is to change the
background-size. In shorthand, the first value is width and the second height. We want our triangle to be allowed 100% of the width, but only 50% of the height, so add the following:
background-size: 100% 50%;
With our previous
linear-gradient unchanged, this is the result:
Due to the
45deg angle of the gradient, the shape appears a bit strange. We need to adjust the angle so that the top side of the triangle appears to cut from the top-left corner to the middle of the right side of the bounding box.
I'm not a math wizard, so this took a bit of experimentation using DevTools to find the right value ?
linear-gradient value to the following:
linear-gradient(32deg, blue 50%, rgba(255,255,255,0) 50%);
And here's our progress - which, while technically a triangle, isn't quite the full look we want:
While for the border trick we had to rely on the intersection to create the shapes, for
linear-gradient we have to take advantage of the ability to add multiple backgrounds to layer the effects and achieve our full triangle.
So, we'll duplicate our
linear-gradient and update it's degrees value to become a mirror-shape of the first, since it will be positioned below it. This results in the following for the full
linear-gradient(32deg, blue 50%, rgba(255,255,255,0) 50%),
linear-gradient(148deg, blue 50%, rgba(255,255,255,0) 50%);
But - we still haven't quite completed the effect, as can be seen in the progress image:
The reason for the overlap is because the default position of both gradients is
0 0 - otherwise known as
top left. This is fine for our original gradient, but we need to adjust the second.
To do this, we need to set multiple values on
background-position. These go in the same order as
background-position: top left, bottom left;
And now we have our desired result:
The downside of this method is that it's rather inflexible to changing aspect ratio without also re-calculating the degrees.
However, CSS gradients can be used to create more shapes especially due to their ability to be layered to create effects.
For a master class in CSS gradients for shapes and full illustrations, check out A Single Div by Lynn Fisher
This final method is the slimmest and most scalable. It is currently slightly lagging in support so be sure to check our own analytics to determine if this is an acceptable solution.
Here's our starting point for our element, which is box dimensions and a
The concept of
clip-path is that you use it to draw a polygon shape (or circle, or ellipse) and position it within the element. Any areas outside of the
clip-path are effectively not drawn by the browser, thus "clipping" the appearance to just the bounds of the
To illustrate this more, and to generate your desired
clip-pathdefinition, check out the online generator: Clippy
The syntax can be a bit more difficult to get used to, so I definitely suggest using the generator noted above to create your path.
For our purposes, here's a triangle pointing to the right:
clip-path: polygon(0 0, 0% 100%, 100% 50%);
clip-path, you are defining coordinates for every point you place along the path. So in this case, we have a point at the top-left (
0 0), bottom-left (
0% 100%), and right-center (
And here is our result:
clip-path is very flexible for many shapes, and also the most scalable due to adapting to any bounding box or aspect ratio, there are some caveats.
When I mentioned the browser doesn't draw anything outside of the bounding box, this includes borders,
outline. Those things are not re-drawn to fit the clipped shape. This can be a gotcha, and may require additional elements or moving of effects to a parent to replace the lost effects.
Here's an egghead video by Colby Fayock to better understand
clip-pathand how to bring back effects like
This demo shows our three ways to create a CSS triangle, which is added to each element using
::after and makes use of viewport units to be responsive.