5

I just came across CSS Arrow please which helps me creating CSS triangles. However, that's not enough. It only creates an outer arrow, while I would like to create inner triangles as well.

Target result

This above was just created within Photoshop. I'm able to create the first able using CSS Arrow Please, but then the hard(er) part comes along. How do I create a block that contains both an outer (right aligned) and inner (left-aligned) arrow, where the last one only contains an inner (left-aligned) arrow.

The result of this should be a clickable proces chain.

To create the first one, this is the CSS

.arrow_box:first-child {
    position: relative;
    background: #1abc9c;
    border: 5px solid #16a085;
}

.arrow_box:first-child:after, .arrow_box:first-child:before {
    left: 100%;
    top: 50%;
    border: solid transparent;
    content: " ";
    height: 0;
    width: 0;
    position: absolute;
    pointer-events: none;
}

.arrow_box:first-child:after {
    border-color: rgba(26, 188, 156, 0);
    border-left-color: #1abc9c;
    border-width: 16px;
    margin-top: -16px;
}
.arrow_box:first-child:before {
    border-color: rgba(22, 160, 133, 0);
    border-left-color: #16a085;
    border-width: 23px;
    margin-top: -23px;
}

But then my question rises: How would I create the other two?

Thanks!

user1118321
  • 25,567
  • 4
  • 55
  • 86
Sander Schaeffer
  • 2,757
  • 7
  • 28
  • 58
  • 1
    You are going to have real trouble using pseudo-elements to create that particular style. This is one case where a bg image would be the way to go. – Paulie_D May 06 '14 at 15:29
  • What you seem to want is a [ribbon](http://css-tricks.com/snippets/css/ribbon/) effect. I'm not sure if you can make that particular one have blocky tips instead of pointed tips. An image, as Paulie_D suggests, would probably be better. – ajp15243 May 06 '14 at 15:30
  • 2
    You can do it using [this](http://stackoverflow.com/a/8867645/2065702) [approach](http://stackoverflow.com/a/8898048/2065702) but each end uses two pseudo elements, so you'd have to have an inner element in addition – Zach Saucier May 06 '14 at 15:35
  • Yeah,,,with an internal span to play with it would be easier but the code would get pretty complex, pretty fast. Good idea though. – Paulie_D May 06 '14 at 15:38
  • Let me clarify that the above CSS code and a div with the .arrow_box class is enough for the first one in the image. No internal spans at all. Just Text here - That's all. Perhaps the second block (and therefor also the third one) might be harder, but I don't expect them to have an altered HTML, as the only requirement I vision is to change the CSS.. – Sander Schaeffer May 06 '14 at 15:42
  • I agree with Paulie. use images – MilkyTech May 06 '14 at 15:43
  • Are these going to be clickable? As in will there be `a`s inside? – Kelderic May 06 '14 at 15:52
  • @Sander Schaeffer You might think that but I think you will find it harder than you imagine. However, you seem to be confident...have at it. – Paulie_D May 06 '14 at 15:54
  • Why use images? There is a pure CSS way to do it. See my answer. – disinfor May 06 '14 at 17:13
  • Sander, you brought up a few concerns about my answer. Did you see that I responded and edited? – Kelderic May 12 '14 at 12:26
  • Hey Andy, thanks for reminding me! I'm currently off-office. I'll put a reminder in my To-Do list and report back tomorrow. Based upon the changes I'm currently looking at, I think we/you've got it all sorted right now. Thanks very much mate! – Sander Schaeffer May 12 '14 at 12:39
  • Great! If there's anything else you need, please let me know. This was an interesting question, I'm glad I was able to help with it. – Kelderic May 13 '14 at 12:36

3 Answers3

6

You need 4 pseudo elements to create this properly, because triangles are created using borders, so they can't have a border and different background-color. We therefore need an inner and outer triangle for both sides. We can use both ::before and ::after, but since that only gives us two, we need at least two "real" elements.

Since this is a navigation panel, I used a series of lis and placed an a inside each one. This is a complicated setup though, so I'm going to break it down into several Fiddles showing the progression.

Edit: I've updated this so that the navigation elements are fluid, not fixed-width, per OP's comments.


Step 1 - Navigation Setup:

First we set up the navigation boxes according to your mockup.

HTML:

<nav>
    <li><a href="">Office</a></li>
    <li><a href="">Office</a></li>
    <li><a href="">Office Two</a></li>
    <li><a href="">Short</a></li>
</nav>

CSS:

nav {
    background: #1abc9c;
}
nav li {
    display:inline-block;
    position:relative;
    margin:10px;
    margin-right:0;
    border: 5px solid #16a085;
    padding:15px 30px;
}
nav li a {
    color:white;
    font-weight:bold;
    display:block;
    height:100%;
    width:100%;
    text-decoration:none;
    font-size:24px;
    font-family:Arial;
}

enter image description here


Step 2 - Set Up Pseudo-elements:

We are going to be using :before and :after, based on the code you provided in your question. Note, I've compressed this CSS based on rules that are shared across elements. Both inner triangles have the same color, both left arrows have the same position, etc.

CSS:

/* Arrows */
nav li:after, nav li a:after, nav li:before, nav li a:before {
    left: 100%;
    top: 50%;
    border: solid transparent;
    content: " ";
    height: 0;
    width: 0;
    position: absolute;
    pointer-events:none; /* So that the mouse will ignore this on top of the clickable area.*/
}
nav li:before, nav li a:before {
    left: -5px;   
}
nav li a:before, nav li a:after {
    border-left-color: #1abc9c;
    border-width: 16px;
    margin-top: -16px;
}
nav li:before, nav li:after {
    border-left-color: #16a085;
    border-width: 23px;
    margin-top: -23px;
}

We've got an issue with the wrong triangles overriding others though. We can fix this with some judicial z-index;

enter image description here


Step 3 - Fix Display Order:

nav li:before {
    z-index:0;
}
nav li a:before {
    z-index:1;
}
nav li:after {
    z-index:2;
}
nav li a:after {
    z-index:3;
}

Now we have working arrows that look right, on all elements. Success! ....almost. We have the arrows appearing on all elements, and we need to hide certain arrows for the beginning and end of the navigation panel.

enter image description here


Step 4 - Fix First and Last Navigation Children:

In this final step we want to remove the two arrows before the first header element, and the two arrows after the last child. The code is surprisingly simple with the structure we've set up so far. We need two selectors, and a display:none;.

/* First & Last Arrow Fix */
nav li:first-child:before, nav li:first-child a:before {
    display:none;
}
nav li:last-child:after, nav li:last-child a:after {
    display:none;
}

And we're done!

Edit: Props to disinfo for idea of using nav instead of header.

enter image description here


Summary

As requested by OP in comments, final code is:

HTML:

<nav>
    <li><a href="">Office</a></li>
    <li><a href="">Office</a></li>
    <li><a href="">Office Two</a></li>
    <li><a href="">Short</a></li>
</nav>

CSS:

nav {
    background: #1abc9c;
}
nav li {
    display:inline-block;
    position:relative;
    margin:10px;
    margin-right:0;
    border: 5px solid #16a085;
    padding:15px 30px;
}
nav li a {
    color:white;
    font-weight:bold;
    display:block;
    height:100%;
    width:100%;
    text-decoration:none;
    font-size:24px;
    font-family:Arial;
}
/* Arrows */
nav li:after, nav li a:after, nav li:before, nav li a:before {
    left: 100%;
    top: 50%;
    border: solid transparent;
    content: " ";
    height: 0;
    width: 0;
    position: absolute;
    pointer-events:none; /* So that the mouse will ignore this on top of the clickable area.*/
}
nav li:before, nav li a:before {
    left: -5px;   
}
nav li a:before, nav li a:after {
    border-left-color: #1abc9c;
    border-width: 16px;
    margin-top: -16px;
}
nav li:before, nav li:after {
    border-left-color: #16a085;
    border-width: 23px;
    margin-top: -23px;
}
/* Overlapping Fix */
nav li:before {
    z-index:0;
}
nav li a:before {
    z-index:1;
}
nav li:after {
    z-index:2;
}
nav li a:after {
    z-index:3;
}
/* First & Last Arrow Fix */
nav li:first-child:before, nav li:first-child a:before {
    display:none;
}
nav li:last-child:after, nav li:last-child a:after {
    display:none;
}
Kelderic
  • 6,502
  • 8
  • 46
  • 85
  • Thanks for your great solution and extended explanation. Just small feedback: include a roundup at the end, so one could copy all parts at once. :) - Just a few minor details though.. I've removed the `width` and `height` from the `li` item to make them dynamic. However, i'm having a little problem, as you might be able to see in the screenshot -> http://checkspot.nl/appendo/website/problem.png // I've changed the section BG to red to make it more clear. I've checked the code and also all other CSS code, but I don't see anything messing it up.. – Sander Schaeffer May 07 '14 at 12:55
  • It looks like the issue you are having is that the outer triangle on the right side is missing. I've taken my final step, removed the fixed `height` and `width`, replaced with `padding:20px`, and changed the background like you did. All triangles are appearing for me. See: http://jsfiddle.net/2jasG/7/ http://i.stack.imgur.com/XXvey.png. – Kelderic May 07 '14 at 14:57
  • Also if you don't want your arrows to overlap (looks that way from your problem.png screenshot), then we can clean up the CSS further, and change the 4 separate `z-index`s to 2. – Kelderic May 07 '14 at 14:58
  • Went ahead and updated the answer to use flexible navigation items. When you have a change to take a look at my initial comment response and jsfiddle.net/2jasG/7, please let me know if you are able to figure out what's causing the issue on your end or if you need help. We should be able to figure it out. – Kelderic May 07 '14 at 15:25
  • I forgot to report back to you! Thank you very much! Had to adjust a few things to make it perfect, but they were completely working. Thanks a big bunch =D – Sander Schaeffer May 19 '14 at 17:07
2

Here's a pure CSS way:

HTML

 <div id="nav">
    <li><a href="#">Test</a></li>
    <li><a href="#">Test 2</a></li>
    <li><a href="#">Test 3</a></li>
</div><!--nav-->

CSS

      #nav {
    background:#4fd34e;
    height:100px;
    width:100%;
    position:relative;
}

#nav li {
    display:inline-block;
    line-height:2;
    border:3px solid #FFF;
    box-sizing:border-box;
    -moz-box-sizing:border-box;
    position:relative;
    margin-top:20px;
    margin-left:20px;
}

#nav li:before {
    content:"";
    display:block;
    position:absolute;
    width:20px;
    height:20px;
    background:#4fd34e;
    border:3px solid #FFF;
    border-width:3px 3px 0 0;
    margin-left:-12px;
    margin-top:10px;
    -ms-transform: rotate(45deg); /* IE 9 */
    -webkit-transform: rotate(45deg); /* Chrome, Safari, Opera */
    -moz-transform: rotate(45deg); /* FF */
    transform: rotate(45deg);
 }

#nav li:after {
    content:"";
    display:block;
    position:absolute;
    width:20px;
    height:20px;
    background:#4fd34e;
    border:3px solid #FFF;
    border-width:3px 3px 0 0;
    right:-14px;
    top:8px;
    -ms-transform: rotate(45deg); /* IE 9 */
    -webkit-transform: rotate(45deg); /* Chrome, Safari, Opera */
    -moz-transform: rotate(45deg); /* FF */
    transform: rotate(45deg);
    z-index:100;
 }

#nav li a {
    display:block;
    text-decoration:none;
    padding:5px 20px;

}

#nav li:first-child:before,
#nav li:last-child:after {
    display:none;

}

Here's the fiddle: http://jsfiddle.net/disinfor/8KvRp/3

disinfor
  • 10,865
  • 2
  • 33
  • 44
  • I like this because it only uses one pseudo-element for both sides. The problem is that because it's rotated, the positioning is very manual and not precisely related to anything. Plus the rendering of the corners is going to vary across browsers. Take a look in Chrome and IE11. – Kelderic May 06 '14 at 22:00
  • I've got to compliment your solution, as it's just plain easy and working. I'm currently testing @AndyM 's answer, because of (as he notes) the Chrome and IE11 differences. You've got my +1 already, thanks! – Sander Schaeffer May 07 '14 at 12:57
  • @SanderSchaeffer Yeah, I wasn't able to cross test it across every browser, but I figured it would give you a really good starting point. AndyM, yours is really nice, too! You got a +1 from me. – disinfor May 07 '14 at 23:36
1

Not a "Pure CSS" solution but perhaps much less complicated with images:
You can see it in action in this fiddle

<div class="processChain">
    <div id="start"><img src="images/home.png" height="18" /> Offerte</div>
    <a><img src="images/home.png" height="18" />Offerte</a>
    <a><img src="images/home.png" height="18" />Offerte</a>
    <a><img src="images/home.png" height="18" />Offerte</a>
    <span><img src="images/home.png" height="18" />Offerte</span>
</div>

.processChain img {
border: none;
vertical-align: text-bottom;
margin-right: 3px;
}
.processChain #start, .processChain a, .processChain span {
background:url("images/bg-chain-start-middle.png") no-repeat scroll 100% 0 transparent;
color:#663300;
display:block;
float:left;
height:22px;
line-height:22px;
padding:0 20px 0 10px;
text-decoration:none;
position:relative;
}
.processChain a:hover{text-decoration:underline;cursor: default;}
.processChain span {
background:url("images/bg-chain-end.png") no-repeat scroll 100% 0 transparent;
padding:0 13px 0 10px;
}
MilkyTech
  • 1,919
  • 2
  • 15
  • 39
  • This provides no explanation, and doesn't answer the OP's question of "how does one created triangles with **pure CSS**". – Kelderic May 06 '14 at 23:01
  • the OP already knows how to create triangles. I am simply providing a better solution to the issue and as Paulie said in the first comment, this is a time for images but thank you Andy for your super answer! – MilkyTech May 07 '14 at 00:55
  • Fair enough, to the idea of an alternative method. I'd still recommend adding some explanation though. Something like: I know you were asking for pure CSS, but it's going to be super complicated that way. An easier way to do it is with images, so here's how to do that instead. That'll also help people reading the question in the future. Do so and you'll get a +1 from me. – Kelderic May 07 '14 at 03:29
  • To be fair, I do compliment for thinking and writing out an alternative solution. However, I've never said anything about breadcrumbs, only a process chain (which basically is "Step 1, step 2, step 3" but w/ other titles, doesn't matters though!). Secondly, the problem with images is that the width is not dynamic. Since I don't see a start, middle and end image in your code, I think I can safely each block has a static width. Although I didn't mentioned a variable width of each block as a requirement, it's some disfunctionality of your solution. However, I still thank you for your time! – Sander Schaeffer May 07 '14 at 13:01
  • not sure what code you are looking at, but you can clearly see in the css a Start, Middle, and End image. I am aware you didn't say "breadcrumbs", but the concept is identical and the word "breadcrumb" is simply the name of a class and can be substituted with "processChain" or anything that makes your heart go pitter pat – MilkyTech May 07 '14 at 13:11
  • Oh wait, really overlooked those parts. I'm seeing them now! Whether or not though, I prefer CSS only as it's quicker and easier to adapt. Still, thanks mate! – Sander Schaeffer May 07 '14 at 13:48
  • "Oh wait, really overlooked those parts."?? SERIOUSLY?? and your jumpin on Andy's back leaving negative comments when you didnt' even read my answer? – MilkyTech May 07 '14 at 14:10
  • One way to use this background-image method with variable widths would be to create two images, a left arrow and right arrow, and then with an element inside another, (`li` and `a` possibly), set `background-image`s, with `background-position`, and set the wrapper element to have the `background-color` also. – Kelderic May 07 '14 at 15:03
  • the originally question says nothing about fluid, dynamic, or variable widths. that would be another question but can most certainly be done with images. adding that requirement just to make my answer wrong is silly. – MilkyTech May 07 '14 at 15:12