How to make this arrow in CSS only?

2019-01-01 04:08发布

问题:

I\'m building a wizard-like ordering process where I have this menu: \"menu\"

The active page is colored green (in this case, Model).

How does one make this arrow using only CSS?:

\"arrow\"

At the moment i\'m achieving my goal by using several divs and images:

<div class=\"menuItem\">
    <div></div> <!-- The left image -->
    <div>Varianten</div>
    <div></div> <!-- The right image -->
</div>

The left image: \"enter

The right image:\"enter

I found a SO answer which does part of this: Arrow Box with CSS, however i\'m having trouble with the indent at the left.

If you have a better idea about how to do this, please let me know!

回答1:

If the space between the arrows does not need to be transparent (it is solid color) you can use the :before and :after to create the edges (without new elements in DOM)

Basically, it creates rotated squares with the borders we want and places them accordingly

#flowBoxes {
    margin:auto;
    padding:20px;
    min-width:700px;

}
#flowBoxes div {
    display:inline-block;
    position:relative;
    height:25px;
    line-height:25px;
    padding:0 20px;
    border:1px solid #ccc;
    margin-right:2px;
    background-color:white;
}

#flowBoxes div.right:after{
    content:\'\';
    border-top:1px solid #ccc;
    border-right:1px solid #ccc;
    width:18px;
    height:18px;
    position:absolute;
    right:0;
    top:-1px;
    background-color:white;
    z-index:150;
    
    -webkit-transform: translate(10px,4px) rotate(45deg);
       -moz-transform: translate(10px,4px) rotate(45deg);
        -ms-transform: translate(10px,4px) rotate(45deg);
         -o-transform: translate(10px,4px) rotate(20deg); 
            transform: translate(10px,4px) rotate(45deg);
}

#flowBoxes div.left:before{
    content:\'\';
    border-top:1px solid #ccc;
    border-right:1px solid #ccc;
    width:18px;
    height:18px;
    position:absolute;
    left:0;
    top:-1px;
    background-color:white;
    z-index:50;
    
    -webkit-transform: translate(-10px,4px) rotate(45deg);
       -moz-transform: translate(-10px,4px) rotate(45deg);
        -ms-transform: translate(-10px,4px) rotate(45deg);
         -o-transform: translate(-10px,4px) rotate(20deg);
            transform: translate(-10px,4px) rotate(45deg);
}
#flowBoxes .active{
    background-color:green;
    color:white;
}
#flowBoxes div.active:after{
    background-color:green;
}
<div id=\"flowBoxes\">
        <div class=\"right\">Diersoort / I&amp;R</div>
        <div class=\"left right active\">Model</div>
        <div class=\"left right\">Varianten</div>
        <div class=\"left right\">Bedrukkingen</div>
        <div class=\"left\">Bevestiging</div>
</div>



回答2:

Here is an alternate approach to the whole thing using CSS3 features. One advantage of using this method (and one of the main reasons for adding a separate answer) is that the space in between the arrows can be transparent.

Basically the implementation is as follows:

  1. There is one div for each step/item and it contains the text that needs to be displayed. Let us say the height of this div is x (50px in this example).
  2. Two pseudo-elements (:before and :after) are created with their width the same as the parent div and height as half (x/2) of the parent. The :before element has no border-bottom whereas the :after element has no border-top to avoid a line appearing in the middle of the shape (parallel to x-axis).
  3. These two pseudo-elements are then skew transformed in opposite directions and are positioned in such a way that they are directly below each other and thus ends up forming the required shape.
  4. The pseudo-elements are assigned a negative z-index to push them to be behind the parent div (and therefore its text).
  5. The first-child and the last-child elements are modified slightly (left position, border of pseudo-elements, background and border of parent div) to achieve the straight sides.
  6. We can add an active class for active elements and hover effects also to the shapes like in the below sample.

.steps {
  height: 50px;
  width: 150px;
  text-align: center;
  line-height: 50px;
  position: relative;
  margin: 10px 0px 10px 20px;
  display: inline-block;
}
.steps:before,
.steps:after {
  content: \'\';
  position: absolute;
  left: 0px;
  width: 150px;
  height: 25px;
  z-index: -1;
}
.steps:before {
  top: -2px;
  border-top: 2px solid blue;
  border-right: 2px solid blue;
  border-left: 2px solid blue;
  background: lightblue;
  -moz-transform: skew(30deg);
  -webkit-transform: skew(30deg);
  transform: skew(30deg);
}
.steps:after {
  bottom: -2px;
  border-left: 2px solid blue;
  border-right: 2px solid blue;
  border-bottom: 2px solid blue;
  background: lightblue;
  -moz-transform: skew(-30deg);
  -webkit-transform: skew(-30deg);
  transform: skew(-30deg);
}
.steps:last-child {
  background: lightblue;
  border-right: 2px solid blue;
  border-top: 2px solid blue;
  border-bottom: 2px solid blue;
  margin-left: 38px;
}
.steps:first-child {
  background: lightblue;
  border-left: 2px solid blue;
  border-top: 2px solid blue;
  border-bottom: 2px solid blue;
  margin-right: 18px;
}
.steps:first-child:before,
.steps:first-child:after {
  left: 18px;
}
.steps:last-child:before,
.steps:last-child:after {
  left: -18px;
}
/* Hover Styles */

.steps:first-child:hover,
.steps:last-child:hover,
.steps:hover:before,
.steps:hover:after {
  background: lightgreen;
}
.steps:first-child:hover {
  border-left: 2px solid green;
}
.steps:last-child:hover {
  border-right: 2px solid green;
}
.steps:hover:before {
  border-top: 2px solid green;
  border-right: 2px solid green;
  border-left: 2px solid green;
}
.steps:hover:after {
  border-left: 2px solid green;
  border-right: 2px solid green;
  border-bottom: 2px solid green;
}
.steps:first-child:hover,
.steps:last-child:hover {
  border-top: 2px solid green;
  border-bottom: 2px solid green;
}

/* Active Styles */

.active:first-child,
.active:last-child,
.active:before, 
.active:after{
  background: bisque;
}
.active:first-child{
  border-left: 2px solid red;
}
.active:last-child{
  border-right: 2px solid red;
}
.active:before{
  border-top: 2px solid red;
  border-right: 2px solid red;
  border-left: 2px solid red;
}
.active:after{
  border-left: 2px solid red;
  border-right: 2px solid red;
  border-bottom: 2px solid red;
}
.active:first-child, .active:last-child{
  border-top: 2px solid red;
  border-bottom: 2px solid red;
}

/* Just for creating a non solid color background */
body{
  height: 200px;
  background: -webkit-radial-gradient(center, ellipse, #400, #100);
  background: -moz-radial-gradient(center, ellipse, #400, #100);
  background: radial-gradient(center, ellipse, #400, #100);
}
<div class=\'steps-container\'>
  <div class=\'steps\'>1. Step 1</div>
  <div class=\'steps active\'>2. Step 2</div>
  <div class=\'steps\'>3. Step 3</div>
</div>


Note: The hover in the above snippet doesn\'t work when hovering on the right tip of the first-child or the left tip of the last-child because of z-index issues. If you need seamless hover functionality then using a span inside the .steps element like in the below snippet would solve it. (Thanks to The Pragmatick for pointing this out).

.steps {
  height: 50px;
  width: 150px;
  text-align: center;
  line-height: 50px;
  position: relative;
  margin: 10px 0px 10px 20px;
  display: inline-block;
}
.steps span {
  position: relative;
  z-index: 2;
}
.steps:before,
.steps:after {
  content: \'\';
  position: absolute;
  left: 0px;
  width: 150px;
  height: 25px;
}
.steps:before {
  top: -2px;
  border-top: 2px solid blue;
  border-right: 2px solid blue;
  border-left: 2px solid blue;
  background: lightblue;
  -moz-transform: skew(30deg);
  -webkit-transform: skew(30deg);
  transform: skew(30deg);
}
.steps:after {
  bottom: -2px;
  border-left: 2px solid blue;
  border-right: 2px solid blue;
  border-bottom: 2px solid blue;
  background: lightblue;
  -moz-transform: skew(-30deg);
  -webkit-transform: skew(-30deg);
  transform: skew(-30deg);
}
.steps:first-child:before,
.steps:first-child:after {
  border-left: none;
}
.steps:last-child:before,
.steps:last-child:after {
  border-right: none;
}
.steps:last-child {
  background: lightblue;
  border-right: 2px solid blue;
  border-top: 2px solid blue;
  border-bottom: 2px solid blue;
  margin-left: 38px;
}
.steps:first-child {
  background: lightblue;
  border-left: 2px solid blue;
  border-top: 2px solid blue;
  border-bottom: 2px solid blue;
  margin-right: 18px;
}
.steps:first-child:before,
.steps:first-child:after {
  left: 18px;
}
.steps:last-child:before,
.steps:last-child:after {
  left: -18px;
}
/* Hover Styles */

.steps:first-child:hover,
.steps:last-child:hover,
.steps:hover:before,
.steps:hover:after {
  background: lightgreen;
}
.steps:first-child:hover {
  border-left: 2px solid green;
}
.steps:last-child:hover {
  border-right: 2px solid green;
}
.steps:hover:before {
  border-top: 2px solid green;
  border-right: 2px solid green;
  border-left: 2px solid green;
}
.steps:hover:after {
  border-left: 2px solid green;
  border-right: 2px solid green;
  border-bottom: 2px solid green;
}
.steps:first-child:hover,
.steps:last-child:hover {
  border-top: 2px solid green;
  border-bottom: 2px solid green;
}
.steps:first-child:hover:before,
.steps:first-child:hover:after {
  border-left: none;
}
.steps:last-child:hover:before,
.steps:last-child:hover:after {
  border-right: none;
}
/* Active Styles */

.active:first-child,
.active:last-child,
.active:before,
.active:after {
  background: bisque;
}
.active:first-child {
  border-left: 2px solid red;
}
.active:last-child {
  border-right: 2px solid red;
}
.active:before {
  border-top: 2px solid red;
  border-right: 2px solid red;
  border-left: 2px solid red;
}
.active:after {
  border-left: 2px solid red;
  border-right: 2px solid red;
  border-bottom: 2px solid red;
}
.active:first-child,
.active:last-child {
  border-top: 2px solid red;
  border-bottom: 2px solid red;
}
/* Just for creating a non solid color background */

body {
  height: 200px;
  background: -webkit-radial-gradient(center, ellipse, #400, #100);
  background: -moz-radial-gradient(center, ellipse, #400, #100);
  background: radial-gradient(center, ellipse, #400, #100);
}
<div class=\'steps-container\'>
  <div class=\'steps\'>
    <span>1. Step 1</span>
  </div>
  <div class=\'steps active\'>
    <span>2. Step 2</span>
  </div>
  <div class=\'steps\'>
    <span>3. Step 3</span>
  </div>
</div>

Screenshot: (with the hover on second item)

\"enter


Responsive Implementation with Transparent Background:

For a responsive version of the progress tracker bar with semi-transparent boxes, visit this pen. The width of each step/item is assigned in such a way that their sum is always 100% of the available width and each itemis always of the same size as the others.

JavaScript is used for the following features: (All these features are value-add and can be removed depending on the needs. Note that when the JS is removed, the corresponding CSS properties should be put into the CSS file.)

  • Automatically adjust the width of each item depending on the no. of items that are present in the bar
  • Automatically adjust the width of each item when the window is resized
  • Automatically adjust the appearance of the items when the height of the bar is increased or decreased by using the slider.

\"enter



回答3:

Here\'s some great arrows for you

html{
  background-color:red;
  }
div#page {
    padding-bottom: 40px;
    padding-top: 40px;
    text-align: center;
    z-index: 1;
    position: relative;
}
div.diamond, div.ribbon, div.right-arrow, div.left-arrow {
    display: inline-block;
    color: #FFFFFF;
    font-size: 22px;
    line-height: 38px;
    margin: 15px 0;
    position: relative;
    width: 200px;
}
div.diamond:before, div.diamond:after, div.ribbon:before, div.ribbon:after, div.right-arrow:before, div.right-arrow:after, div.left-arrow:before, div.left-arrow:after {
    content:\"\";
    border-style: solid;
    border-width: 0;
    height: 0;
    position: absolute;
    width: 0;
}
div.diamond {
    background-color: #CCCCCC;
}
div.diamond:after, div.diamond:before {
    border-color: transparent #CCCCCC;
}
div.diamond:before {
    left: -19px;
    border-width: 19px 19px 19px 0;
}
div.diamond:after {
    right: -19px;
    border-width: 19px 0 19px 19px;
}
div.ribbon {
    background-color: #CCCCCC;
}
div.ribbon:before, div.ribbon:after {
    top: 6px;
    z-index: -15;
}
div.ribbon:before {
    border-color: #B2B2B2 #B2B2B2 #B2B2B2 transparent;
    border-width: 19px;
    left: -25px;
}
div.ribbon:after {
    border-color: #B2B2B2 transparent #B2B2B2 #B2B2B2;
    border-width: 19px;
    right: -25px;
}
div.right-arrow {
    background-color: #CCCCCC;
}
div.right-arrow:after, div.right-arrow:before {
    border-width: 19px 0 19px 19px;
}
div.right-arrow:before {
    border-color: #CCCCCC transparent;
    left: -19px;
}
div.right-arrow:after {
    border-color: transparent #CCCCCC;
    right: -19px;
}
div.left-arrow {
    background-color: #CCCCCC;
}
div.left-arrow:after, div.left-arrow:before {
    border-width: 19px 19px 19px 0;
}
div.left-arrow:before {
    border-color: transparent #CCCCCC;
    left: -19px;
}
div.left-arrow:after {
    border-color: #CCCCCC transparent;
    right: -19px;
}
<div id=\"page\">
    <div class=\"diamond\">Diamond</div>
    <br>
    <div class=\"ribbon\">Ribbon</div>
    <br>
    <div class=\"right-arrow\">Right arrow</div>
    <br>
    <div class=\"left-arrow\">Left arrow</div>
</div>

SOURCE

Note

this also allows gradient backgrounds/etc


For other shapes, I saw this codepen the other day, too



回答4:

If you want transparent spaces between tabs, Harry\'s current answer is they way to go.

But if you want to remove hover issues, you can try the following. It uses box-shadow for pseudo-elements instead of background with solid colour.
The same effect is achievable using border: _px inset #___ ;

.li {
    height: 50px;
    width: 120px;
    background: #F5FBF1;
    display: inline-block;
    position: relative;
    margin-left: 30px;
    line-height: 50px;
    color: black;
    font-family: sans-serif;
    text-align: center;
}
.li:before, .li:after {
    content: \'\';
    left: -15px;
    position: absolute;
    height: 23px;
    width: 132px;
    border-left: 2px solid black;
    border-right: 2px solid black;
}
.li:before {
    border-top: 2px solid black;
    -webkit-transform-origin: 0% 0%;
    -moz-transform-origin: 0% 0%;
    -ms-transform-origin: 0% 0%;
    transform-origin: 0% 0%;
    -webkit-transform: skewX(30deg);
    -moz-transform: skewX(30deg);
    -ms-transform: skewX(30deg);
    transform: skewX(30deg);
    top: 0;
    box-shadow: inset 0 8px 0 8px #F5FBF1, inset -6px 8px 0 8px #F5FBF1;
}
.li:after {
    border-bottom: 2px solid black;
    -webkit-transform-origin: 0% 100%;
    -moz-transform-origin: 0% 100%;
    -ms-transform-origin: 0% 100%;
    transform-origin: 0% 100%;
    -webkit-transform: skewX(-30deg);
    -moz-transform: skewX(-30deg);
    -ms-transform: skewX(-30deg);
    transform: skewX(-30deg);
    bottom: 0;
    box-shadow: inset 0 -8px 0 8px #F5FBF1, inset -6px -8px 0 8px #F5FBF1;
}
.li:hover {
    background: #C0EBA4;
}
.li:hover:before {
    box-shadow: inset 0 8px 0 8px #C0EBA4, inset -6px 8px 0 8px #C0EBA4;
}
.li:hover:after {
    box-shadow: inset 0 -8px 0 8px #C0EBA4, inset -6px -8px 0 8px #C0EBA4;
}
<div class=\"li\">ONE</div>
<div class=\"li\">TWO</div>
<div class=\"li\">THREE</div>
<div class=\"li\">FOUR</div>
<div class=\"li\">FIVE</div>

FIDDLE


Final version

You can hover it seamlessly. It includes flat edges of first and last tabs.

.li {
    height: 50px;
    width: 100px;
    padding-left: 20px;
    background: #F5FBF1;
    display: inline-block;
    position: relative;
    margin-left: 20px;
    line-height: 50px;
    font-family: sans-serif;
    font-size: 15px;
}
.li:before, .li:after {
    content: \'\';
    left: -15px;
    position: absolute;
    height: 23px;
    width: 132px;
    border-left: 2px solid black;
    border-right: 2px solid black;
}
.li:before {
    border-top: 2px solid black;
    -webkit-transform-origin: 0% 0%;
    -moz-transform-origin: 0% 0%;
    -ms-transform-origin: 0% 0%;
    transform-origin: 0% 0%;
    -webkit-transform: skewX(30deg);
    -moz-transform: skewX(30deg);
    -ms-transform: skewX(30deg);
    transform: skewX(30deg);
    top: 0;
    box-shadow: inset 0 8px 0 8px #F5FBF1, inset -6px 8px 0 8px #F5FBF1;
}
.li:after {
    border-bottom: 2px solid black;
    -webkit-transform-origin: 0% 100%;
    -moz-transform-origin: 0% 100%;
    -ms-transform-origin: 0% 100%;
    transform-origin: 0% 100%;
    -webkit-transform: skewX(-30deg);
    -moz-transform: skewX(-30deg);
    -ms-transform: skewX(-30deg);
    transform: skewX(-30deg);
    bottom: 0;
    box-shadow: inset 0 -8px 0 8px #F5FBF1, inset -6px -8px 0 8px #F5FBF1;
}
.li:hover {
    background: #C0EBA4;
}
.li:hover:before { box-shadow: inset 0 8px 0 8px #C0EBA4, inset -6px 8px 0 8px #C0EBA4;}
.li:hover:after { box-shadow: inset 0 -8px 0 8px #C0EBA4, inset -6px -8px 0 8px #C0EBA4;}

/*First and Last styles*/
.li:first-of-type {
    left: -15px;
    box-shadow: 15px 0 0 0 #F5FBF1;
    border-left: 2px solid black;
}
.li:first-of-type:before, .li:first-of-type:after {
    left: -1px;
    width: 135px;
    border-left: 0;
}
.li:first-of-type:hover {box-shadow: 15px 0 0 0 #C0EBA4;}
.li:last-of-type {
    left: 0px;
    width: 115px;
    box-shadow: inset -2px 0 0 0 black, inset 0 -2px 0 0 black, inset 0 2px 0 0 black;
    border: 0;
}
.li:last-of-type:before, .li:last-of-type:after {
    left: -15px;
    border-right: 0;
}
.li:last-of-type:hover {background: #C0EBA4;}
<div class=\"li\">Tab one</div>
<div class=\"li\">Tab two</div>
<div class=\"li\">Tab three</div>
<div class=\"li\">Tab four</div>
<div class=\"li\">Tab five</div>

FIDDLE (final)

Output:

\"enter