39

The code below will create an arrow right below an <a> element:

JSFiddle

.btn {
    position: relative;
    display: inline-block;
    width: 100px;
    height: 50px;
    text-align: center;
    color: white;
    background: gray;
    line-height: 50px;
    text-decoration: none;
}
.btn:after {
    content: "";
    position: absolute;
    bottom: -10px;
    left: 0;
    width: 0;
    height: 0;
    border-width: 10px 50px 0 50px;
    border-style: solid;
    border-color: gray transparent transparent transparent;   
}
<a href="#" class="btn">Hello!</a>

The problem is that we have to indicate the link width to get an arrow of a proper size because we cannot indicate the border width in pixels.

How to make a responsive triangle percent based?

Gleb Kemarsky
  • 10,160
  • 7
  • 43
  • 68
sdvnksv
  • 9,350
  • 18
  • 56
  • 108

7 Answers7

55

You could use a skewed and rotated pseudo element to create a responsive triangle under the link :

DEMO (resize the result window to see how it reacts)

The triangle maintains it's aspect ratio with the padding-bottom property.

If you want the shape to adapt it's size according to it's content, you can remove the width on the .btn class

.btn {
  position: relative;
  display: inline-block;
  height: 50px; width: 50%;
  text-align: center;
  color: white;
  background: gray;
  line-height: 50px;
  text-decoration: none;
  padding-bottom: 15%;
  background-clip: content-box;
  overflow: hidden;
}
.btn:after {
  content: "";
  position: absolute;
  top:50px;  left: 0;
  background-color: inherit;
  padding-bottom: 50%;
  width: 57.7%;
  z-index: -1;
  transform-origin: 0 0;
  transform: rotate(-30deg) skewX(30deg);
}
/** FOR THE DEMO **/

body {
  background: url('http://i.imgur.com/qi5FGET.jpg');
  background-size: cover;
}
<a href="#" class="btn">Hello!</a>

For more info on responsive triangles and how to make them, you can have a look at Triangles with transform rotate (simple and fancy responsive triangles)

web-tiki
  • 99,765
  • 32
  • 217
  • 249
  • 2
    While this solution works in this case, I just want to point out to others that it will only work on a white (or solid color) background because of your border-top:300px solid #fff; being used to hide the rest of the :after rotated square. – GreatBlakes Mar 23 '15 at 16:24
  • 3
    @GreatBlakes thx for pointing it out, I have sovled the issue and edited the answer. The top border isn't necessary anymore, the rest of the `:after` pseudo element is hidden with `overflow:hidden;`. – web-tiki Mar 24 '15 at 09:02
25

Another solution to this would be to use a CSS clip-path to clip a triangle out of a coloured block. No IE support however, but could be used for internal tools etc.

DEMO

Written with SCSS for ease.

.outer {
  background: orange;
  width: 25%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 1em;

  p {
    margin: 0;
    text-align: center;
    color: #fff;
  }

  &:after {
    content: '';
    position: absolute;
    top: 100%;
    left: 0; 
    right: 0;
    padding-bottom: 10%;
    background: orange;
    -webkit-clip-path: polygon(0% 0%, 100% 0%, 50% 100%);
    clip-path: polygon(0% 0%, 100% 0%, 50% 100%);
  }

}
Probocop
  • 10,346
  • 28
  • 85
  • 115
8

I found solution that works with any width/height. You can use two pseudo-elements with linear-gradient background, like this, (fiddle):

.btn {
    position: relative;
    display: inline-block;
    width: 100px;
    height: 50px;
    text-align: center;
    color: white;
    background: gray;
    line-height: 50px;
    text-decoration: none;
}
.btn:before {
    content: "";
    position: absolute;
    top: 100%;
    right: 0;
    width: 50%;
    height: 10px;
    background: linear-gradient(to right bottom, gray 50%, transparent 50%)
}

.btn:after {
    content: "";
    position: absolute;
    top: 100%;
    left: 0;
    width: 50%;
    height: 10px;
    background: linear-gradient(to left bottom, gray 50%, transparent 50%)
}
Alexandr Subbotin
  • 1,714
  • 2
  • 17
  • 16
4

A modified version of the below code can help you to achieve this

HTML

<div class="triangle-down"></div>

CSS

.triangle-down {
    width: 10%;
    height: 0;
    padding-left:10%;
    padding-top: 10%;
    overflow: hidden;
}
.triangle-down:after {
    content: "";
    display: block;
    width: 0;
    height: 0;
    margin-left:-500px;
    margin-top:-500px;
    
    border-left: 500px solid transparent;
    border-right: 500px solid transparent;
    border-top: 500px solid #4679BD;
}

For further reading on responsive triangles: CSS triangles made responsive (archived link)

natevw
  • 16,807
  • 8
  • 66
  • 90
lessismore
  • 100
  • 2
3

I tried the other answers and found them to be either too complex and/or unwieldy to manipulate the shape of the triangle. I decided instead to create a simple triangle shape as an svg.

The triangle height can be set to an absolute value, or as a percentage of the rectangle so it can be responsive in both directions if necessary.

html, body{
  height:100%;
  width:100%;
}
.outer{
  width:20%;
  height:25%;
  background:red;
  position:relative;
  
}
.inner{
  height:100%;
  width:100%;
  background-color:red;
}
.triangle-down{
  height:25%;
  width:100%;
  position:relative;
}
.triangle-down svg{
  height:100%;
  width:100%;
  position:absolute;
  top:0;
}
svg .triangle-path{
  fill:red;
}
<div class="outer">
<div class="inner"></div>
  <div class="triangle-down">
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 2 1">
 <g>
  <path class="triangle-path" d="M0,0 l2,0 l-1,1 z" />
 </g>
</svg>
</div>

Tested FF, Chrome, IE, Edge, mob Safari and mob Chrome

Will Jenkins
  • 9,507
  • 1
  • 27
  • 46
3

Another option would be to use background liner gradients, and flex positioning to make sure that the triangle always scales to its parent container. No matter how wide or narrow you make that container, the triangle always scales with it. Here is the fiddle:

https://jsfiddle.net/29k4ngzr/

<div class="triangle-wrapper-100">
  <div class="triangle-left"></div>
  <div class="triangle-right"></div>
</div>

.triangle-wrapper-100 {
    width: 100%;
    height: 100px;
    display:flex;
    flex-direction: column;
    flex-wrap: wrap;
}
.triangle-right {
    right: 0px;
    background: linear-gradient(to right bottom, #6940B5 50%, transparent 50%);
    width: 50%;
    height: 100px;
}
.triangle-left {
    left: 0px;
    background: linear-gradient(to right bottom, #6940B5 50%, transparent 50%);
    width: 50%;
    height: 100px;
    transform: scaleX(-1);
}
Metro Smurf
  • 37,266
  • 20
  • 108
  • 140
brett m
  • 91
  • 1
  • 10
  • I see that clip-path has some problems in Edge. That's my understanding. So, +1 for your answer. – Andrei May 05 '22 at 16:25
0

I took @Probocop's answer and come up with the following:

<style>
    .btn {
        background-color: orange;
        color: white;
        margin-bottom: 50px;
        padding: 15px;
        position: relative;
        text-align: center;
        text-decoration: none;
    }

    .btn:after {
        background-color: inherit;
        clip-path: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg"%3E%3Cdefs%3E%3CclipPath id="p" clipPathUnits="objectBoundingBox"%3E%3Cpolygon points="0 0, 1 0, 0.5 1" /%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E#p'); /* fix for firefox (tested in version 52) */
        clip-path: polygon(0% 0%, 100% 0%, 50% 100%);
        content: '';
        height: 50px;
        left: 0;
        position: absolute;
        right: 0;
        top: 100%;
    }
</style>

<a href="#" class="btn">Hello!</a>

This works in Chrome and I've added a fix for Firefox. It doesn't work in Edge, however if you decrease the height of the down arrow then it doesn't look so bad.

Please note that if you are using bootstrap you will need to either change the name or override some of the styles it applies. If you decide to rename it then you also need to add the following to the .btn style:

box-sizing: content-box;
nfplee
  • 7,643
  • 12
  • 63
  • 124