Did you know you can make triangles with pure CSS? It’s pretty easy. You just make a block level element with zero width and height, a colored border on one side, and transparent borders on the two adjacent sides. They are fun for all kinds of things, like little arrow sticking out from speech bubbles, navigation pointers, and more. Often times these are just visual flourishes, undeserving of dedicated markup. Fortunately, pseduo elements are often a perfect fit. That is, using ::before
, ::after
, or both to create these block level elements and place the triangle. One neat use that came to mind in this vein: breadcrumb navigation.

The HTML Markup
Let’s keep things as absolutely clean as possible, and go with a simple unordered list with a “breadcrumb” class:
<ul class="breadcrumb">
<li><a href="#">Home</a></li>
<li><a href="#">Vehicles</a></li>
<li><a href="#">Vans</a></li>
<li><a href="#">Camper Vans</a></li>
<li><a href="#">1989 VW Westfalia Vanagon</a></li>
</ul>
The CSS
First, we’ll ensure our list doesn’t look like a typical list. We’ll remove the default list styles, float the items to the left and set up very basic styling for each link. Note the hidden overflow on the list itself, this will be useful for two reasons. One, it ensures the menu has a height. Containers that contain only floated elements collapse, which is often not ideal. Two, when we make our triangles, we’re going to make them big.
.breadcrumb {
list-style: none;
overflow: hidden;
font: 18px Sans-Serif;
}
.breadcrumb li {
float: left;
}
.breadcrumb li a {
color: white;
text-decoration: none;
padding: 10px 0 10px 65px;
background: brown; /* fallback color */
background: hsla(34,85%,35%,1);
position: relative;
display: block;
float: left;
}
To create the triangle, we’ll use the ::after
selector to make a pseudo element. It will be block-level, of zero height and width, and absolutely positioned 100% to the left, which means it will begin at the right edge of its parent. We’ll position it 50% from the top, and pull it back up -50px with margin, which will ensure it is absolutely centered. This is a classic trick. A few other things to note. The borders we’ll use are 50px on the top, 50px on the bottom, and on the left (making a right facing arrow) only 30px. This means a more flat-faced arrow. If we went higher than 50px it would be more sharp-tipped. Equal to 50px would make it a perfect 90 degrees. Because the top and bottom borders are 50px each, that makes the height of the arrow 100px. 100px is far taller than our menu is likely to be with its 18px font size and 10px of top and bottom padding. This is a good thing. It means we have plenty of room to play with tweaking the font size without worrying the triangle will show its limit.
.breadcrumb li a::after {
content: " ";
display: block;
width: 0;
height: 0;
border-top: 50px solid transparent; /* Go big on the size, and let overflow hide */
border-bottom: 50px solid transparent;
border-left: 30px solid hsla(34,85%,35%,1);
position: absolute;
top: 50%;
margin-top: -50px;
left: 100%;
z-index: 2;
}
Notice the little 1px white line of separation? That’s another little trick. We can’t add a border directly to the triangle because it’s already made of a border! Instead, we’ll make another triangle, and set it behind our original triangle, and color it white. This one uses the ::before
selector, but is otherwise exactly the same. Note that the z-index is what is important here. You could switch around which triangle uses ::after
and which uses ::before
, it really doesn’t matter.
.breadcrumb li a::before {
content: " ";
display: block;
width: 0;
height: 0;
border-top: 50px solid transparent;
border-bottom: 50px solid transparent;
border-left: 30px solid white;
position: absolute;
top: 50%;
margin-top: -50px;
margin-left: 1px;
left: 100%;
z-index: 1;
}
Now onto the coloration. Since we are going a touch progressive here, there are two bits of CSS I think are perfect matches for this idea: nth-child and HSLa.
- The cool part about nth-child: we can color the different levels of the breadcrumb with no additional markup
- The cool part about HSLa: we can color the different levels of the breadcrumbs based on a single hue with different shades very easily
In addition to the colorization, we’ll make the first link have less left padding (less needed single a triangle isn’t up in its grill) and we’ll make the last link not have any coloring at all as well as not respond to clicks or show a pointer cursor. We can do these also with no additional markup needed through :first-child
and :last-child
.
.breadcrumb li:first-child a {
padding-left: 10px;
}
.breadcrumb li:nth-child(2) a { background: hsla(34,85%,45%,1); }
.breadcrumb li:nth-child(2) a:after { border-left-color: hsla(34,85%,45%,1); }
.breadcrumb li:nth-child(3) a { background: hsla(34,85%,55%,1); }
.breadcrumb li:nth-child(3) a:after { border-left-color: hsla(34,85%,55%,1); }
.breadcrumb li:nth-child(4) a { background: hsla(34,85%,65%,1); }
.breadcrumb li:nth-child(4) a:after { border-left-color: hsla(34,85%,65%,1); }
.breadcrumb li:nth-child(5) a { background: hsla(34,85%,75%,1); }
.breadcrumb li:nth-child(5) a:after { border-left-color: hsla(34,85%,75%,1); }
.breadcrumb li:last-child a {
background: transparent !important;
color: black;
pointer-events: none;
cursor: default;
}
.breadcrumb li:last-child a::after {
border: 0;
}
And finally, hover states. The only trick here is that we need to color the triangle as well as the link. No big deal.
.breadcrumb li a:hover {
background: hsla(34, 85%, 25%, 1);
}
.breadcrumb li a:hover:after {
border-left-color: hsla(34, 85%, 25%, 1) !important;
}
Deeper Browser Compatibility
Call me a lazy sack, but I didn’t bother to deal with very deep browser compatibility. I was more excited about the idea than thinking about production. If you want to use this idea, but are concerned about older browsers, here are some things to think about:
- Don’t use
hsla()
for coloring, use hex codes - Don’t use
:nth-child
for coloring, give each list item a class name - For browsers that don’t support
::after
/::before
, use an image based system. This is my favorite image based breadcrumb tutorial. - Use Modernizr for detection of features (like HSLa)
- Use conditional stylesheets for IE
Enjoy
Here’s the example we covered, and a couple of variations:
See the Pen CSS Triangle Breadcrumbs by CSS-Tricks (@css-tricks) on CodePen.
Sweet. Very impressive what you can do with slightly modern CSS. I still don’t get how the border makes the triangle though!
Ok, I get it now :) http://jsfiddle.net/JvUW4/
Thank you for the link :)
yeah, that explained it.
Multicoloured! http://jsfiddle.net/FPUzF/
Very nice. Thanks for the link :)
Shouldn’t that be wrapped inside a tag? I would think it be smarter to do then to put it inside the ul.
Nice Navigation Chris! But it doesn’t work on IE. I thought it was Cross-Browser Compatible
But anyways, this is a great article! Thanks!
Read the section “Deeper Browser Compatibility” in the article above for some tips on how you can start working on that yourself.
Wow! Just tried to see the demo in IE6, but I started by trying to load this article in it, and that was as far as I could get! Is CSS-tricks purposefully that broken for IE6? I mean, it seems actively, _playfully_ non-functional in that browser…
I don’t intend to support IE 6 on this site, sorry. I also don’t want this to turn into an IE 6 conversation, so subsequent replies will be buried. (No hard feelings, I appreciate the feedback!)
If you drag the link away (in Firefox at least) you can see what the css is actually doing.
Otherwise, this is more or less what I had implemented for the site I am currently working on, until I needed to use a gradient background that is..
I’ve began to use a greater than symbol for the triangles in breadcrumbs lately, they’re a lot easier to work with!
Good new way to change breadcrumb.. Thanx Chris
Nice technique. The bit I didn’t see in the article was the style used to turn off the triangle after the last breadcrumb. From the code it’s a nice.
.breadcrumb li:last-child a:after { border: 0; }
I guess a similar before selector could be used if the overall background wasn’t white.
Wow! I’m an avid reader of your blog, and don’t usually comment, but this one really caught my eye! Great idea and beautiful implementation
Why are you setting both display block and float left, surely you just need float left as the element would behave like display inline block ?
Very cool technique. This could very easily be made more functional in IE 8 with about ten more minutes of work. I was impressed with the technique, but definitely rough around the edges.
If you got those 10 minutes, I got an article update with your name on it.
Instead of using nth-child, you could apply class names such as
level-1
to each<li>
. Since you are already calling nth-child so much in the CSS for the lightening colors, you might as well.Doing this, along with adding z-index starting with 1 at the lowest level and ending at (for this example) 5 at the highest level, and using rgb instead of hsla, gives you IE8 support.
Test case on Snipplr
Looks terrible in Chrome (6.0.472.63) OSX SL… Just sayin’
Looks fine to me in Chrome 8. http://cl.ly/34Ee
It doesn’t seem like there is anything in here that Chrome 6 wasn’t fully capable of. If you find a fix, please let us know so we can update this. I feel like there should be some clever saying like “if you got time to lean you got time to clean”, only like “if you have time to comment about problems, you have time to code a fix.”
I was referring to the edge of the triangles – you can see in your screenshot how jagged they are… :)
http://cl.ly/808614bd38384d2d8f66
…this does seem to be related to the sharpness of the triangle border though…at 90 degree angles chrome renders them beautifully.
Ah yes, definitely the “squatter” triangles render way worse. Not sure what’s up with that. 90-degree triangles are typically much cleaner. The white triangle really emphasizes the crappy rendering, so if you just remove the :before triangle, that looks much cleaner too.
Chrome is rubbish at anti-aliasing certain effects, so it leaves jaggies on anything that doesn’t easily fit into straight pixels (a 45 degree line is fine because it just puts the diagonal pixels on, but anything other than 45 degrees will alias). Having said that, on Chrome 7 Windows 7 the demo is perfectly smooth :)
Really a great article Chris! I really love the design of this breadcrumb…
I can’t wait to see full CSS3 support on every browser.
not working for me in IE7,
Al
same with me. not working in IE7
I created pretty much the same thing a year ago (though not for breadcrumbs):
http://cl.ly/1f8177a6147a07371650
Works in:
PC IE6/7/8, Firefox 3+, Safari 4+, Chrome 4+, Opera 10.5+
Mac Firefox 3+, Safari 4+ (untested in Chrome, only have Tiger on my MacBook)
Markup is an unordered list, uses sprites and had to use JavaScript to get it to work in IE6 only, I think. Without JS in IE6, I think the boxes just render with a squared off right side (been a while since I built it).
The site is still in development (internal dev failure which was then outsourced, which has slipped dates a couple times!) but is scheduled to launch by end of year. We’ll see if it still works come launch, which I don’t have a lot of faith in as the vendor has said they haven’t even done any cross browser testing yet (yikes).
If I remember, I’ll come back here and post up a link when it’s live.
images looks better and smooth
Nice stuff until it works in damn IE. Thanks for sharing :)
sweet… dint now we can make triangles from css… will apply this trick to my next project :)
good idea chris
Also, here are some CSS triangle links from my bookmarks:
http://www.pvmgarage.com/2010/01/how-to-create-depth-and-nice-3d-ribbons-only-using-css3/
http://hedgerwow.appspot.com/demo/arrows
http://www.dinnermint.org/blog/css/creating-triangles-in-css (I think this is the first place I read about the technique)
there’s no reason to use hsla when it’s opaque, use hsl
hsl(8%, 100%, 50%)
Alternatively, there’s no reason to alter the Lightness property of HLSA when on a white background, make it more transparent.
hsl(8%, 100%, 50%, 1)
hsl(8%, 100%, 50%, .8)
hsl(8%, 100%, 50%, .6)
I really like this one :) great!
very creative use of CSS!
the color scheme reminds me of the RV from Breaking Bad
Proof of concept CSS3 only, with gradient backgrounds and borders.
http://jsfiddle.net/qXCed/5/
(Webkit and FF4 only though)
In IE9 beta it looks a bit weird…
The same idea is used in http://zemz-ua.com/en/about language switcher/ Just for fun.
Love it! Simply love it! Thanks so much for sharing. By the way @Jeremy, yours is great too.
Probably it wont work in IE… Anybody knows?
Great tutorial. Breadcrumbs have never looked so beautiful earlier.
What an innovative solution. Thanks for taking the time to put this together.
lol – i don’t intend to support ie6, i have a good laugh. apart from that ninja article Chris, thanks.
Sorry, but this post starts off wrong. The css trick itself is interesting, but the HTML is not semantic. Coding a breadcrumb as an unordered list makes it seem like the items are on the same hierarchical level, like it’s a set of items in a row. The items in a breadcrumb are not, they represent a path, with each link being a “child” of the last.
So a p-tag as wrapper, containing simple a-tags would already be better. There’s multiple options for coding a breadcrumb, but many are convulated or semantically incorrect. Thought I’d point that out.
Without wanting to start a semantics war here, I don’t think there is anything wrong with the markup. The problem with semantics is that it is subjective.
My own view on breadcrumbs is that they are an ordered list, as they appear in a defined logical order. It doesn’t make me right and you and Chris wrong.
Great article Chris. I love the new tings that are becoming possible with CSS, I just wish that more of the other developers I work with shared that opinion.
I think Geert has a good point about an unordered list not being the most perfectly semantic thing. I also don’t think a paragraph tag is the right answer. This is where my mind goes first, but it seems WAY overkill:
I think an ordered list would be fine semantically (because the order of the breadcrumbs matter), and is simple.
The difference between using an
<ol>
and a bunch of nested<ul>
s is how you perceive the way breadcrumbs work.For the
<ol>
case:Begin at “Top Level”
Continue to “Second Level”
Continue to “Third Level”
Finish at “Current Item”
For the nested
<ul>
case:Begin at the list of links in the “Top Level”
Choose “Second Level” from the next list of choices
Choose “Third Level” from the next list of choices
Choose “Current Item” from the next list of choices
I suppose it depends on whether you want to view it as a direct path/causal relationship (
<ol>
) or as a series of choices showing only the chosen items (<ul>
).@Thomas: Semantics are subjective to an extend, I agree, but some things are just wrong and this is one of them. On the other hand, in the specific case of a breadcrumb, there isn’t a definitive right solution either. Using an unordered isn’t horrible, but it’s not correct. And I don’t think that’s subjective. What anyone ends up using is their own choice of course.
@Chris: The example you posted is maybe a little better in terms of semantics, but obviously it’s horribly convulated as you point out. As said, I think there is no definitive semantically correct solution for a breadcrumb, which is why I decided to keep it simple (p-tag containing a-tags). If every solution has its downsides than I’ll choose the simplest in this case. But to each his own.
An interesting discussion either way, and something I think could have been addressed better during the development of HTML5.
Geert, my take on this is that the breadcrumbs are an aid to navigation, not a description of the site hierarchy. From the point of view of the HTML document the breadcrumb trail is a list of links that can be used to navigate back up the site tree. To me this supplemental to the topic of the document so I wouldn’t go into too much detail for it.
However, if the topic of the document is describing the site hierarchy then I would say a list just wouldn’t cut the mustard.
But on the other hand breadcrumbs also show you where in the hierarchy you are so it can easily work both ways!
Hope this makes sense.
I agree with Thomas Heaney, an ordered list makes perfect sense here.
I like james use of the greater or less than symbol.
Cool.. Love it!
Excellent article, love the design of the breadcrumb, will try and use this maybe for a navigation pointer. LT
This is a great technique as it is really cool, you can use it as a pointer on a tooltip too. Too bad it isn’t compatible with older browsers though.
This is what i’ve been looking for :D Thank you so much :)
This is a great tutorial. Very helpful. Thanks!
Simply awesome, I can work around for old browser only if there’s a really motive to do so, other than that it will just gratefully degrade with no nice tricks, I had it with IE.
It’s awesome! I really like it, thank you so much!
Any suggestions how to implement css arrows with div only breadcrumbs? Thanks.
Nice idea but not really browsercompatible. Although i am going to use it :)
as usual awesome work :) thanks
It is very nice making triangles with out using images. Still I dont understand How he did it. I have to go through it.
thanks
vara
Where was this tutorial months ago when I needed to code exactly this kind of breadcrumb??? :P AWESOME technique Chris.
nice. realy helpfull
hi sir i can have small doubt. which property we can use the mouse over after click the menu item color will fixed and go to click next item thats can be change in fixed . how is it possible.
using the property can tell me sir…