5

I am trying to style a div with a bottom-border that has a downward-pointing arrow. The div will have an image in it, and should not have a top, right or left border. The fill of the downward-pointing arrow should be either the same as the div or transparent.

I have been able to get it to work for the most part using the code below:

.hero {
  position: relative;
  background-color: yellow;
  height: 320px !important;
  width: 100% !important;
  border-bottom: 1px solid red;
}
.hero:after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -50px;
  width: 0;
  height: 0;
  border-top: solid 50px #e15915;
  border-left: solid 50px transparent;
  border-right: solid 50px transparent;
}
<div class="hero"></div>

See this fiddle: http://jsfiddle.net/alisamii/tjep3h8t/

Whatever I try to do to "hollow-out" the arrow either results in a borderless div (so it has a fill of yellow but no border on any side) or in a border that goes around the whole div.

Any help?

web-tiki
  • 99,765
  • 32
  • 217
  • 249
Ali Samii
  • 1,672
  • 4
  • 28
  • 49
  • This should help : [transparent arrow/triangle](http://stackoverflow.com/questions/23758922/transparent-arrow-triangle/23759602#23759602) – web-tiki Dec 13 '14 at 18:39
  • _"The fill of the downward-pointing arrow should be either the same as the div or transparent."_ , If the arrow is transparent, what is the point of having it? – Weafs.py Dec 13 '14 at 18:49
  • The border-bottom isn't transparent. The idea is to create this display, which is done using an image: http://d.pr/i/1bT5J As you can see, there is a 1px line with a downward-pointing arrow. So, I want to create the div that has the border-bottom and no border-left, border-right or border-top. – Ali Samii Dec 13 '14 at 20:14

3 Answers3

6

There are two methods to achieve this using CSS3. One is using skewX on pseudo-elements while the other is using rotate on pseudo-elements. Both the methods are also responsive.

Using Skew:

This method is adapted from web-tiki's answer in this thread. It basically uses two pseudo-elements (with roughly 50% width of the container) that are skewed in opposite directions and positioned appropriately to arrive at the arrow shape. The shapes have borders where as the background is set to transparent meaning the pseudo-elements would produce a bottom border + downward arrow effect. The arrow fill would always be transparent (or body color) in this sample.

body {
  background: rgb(245, 242, 242);
}
.bordered {
  position: relative;
  height: 200px;
  width: 200px;
  margin: 10px;
  line-height: 200px;
}
.bordered:after,
.bordered:before {
  position: absolute;
  content: ' ';
  height: 8px;
  width: 50%;
  bottom: 0px;
}
.bordered:before {
  left: 0px;
  border-top: 1px solid gray;
  border-right: 1px solid gray;
  transform-origin: left bottom;
  transform: skewX(45deg);
}
.bordered:after {
  right: 0px;
  border-top: 1px solid gray;
  border-left: 1px solid gray;
  transform-origin: right bottom;
  transform: skewX(-45deg);
}
.bordered img {
  width: 150px;
  padding: 25px;
  vertical-align: middle;
}
/* Just for demo */

.bordered {
  transition: all 1s;
  text-align: center;
}
.bordered:hover {
  height: 250px;
  width: 250px;
  line-height: 250px;
}
<!-- library included only to avoid browser prefixes -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>

<div class="bordered">
  <img src="http://i.imgur.com/0Xqum3A.png" />
</div>

enter image description here

The below method would produce a transparent triangle such that the page's background is seen through the triangular cut.

body {
  background: rgb(245, 242, 242);
}
.bordered {
  position: relative;
  height: 200px;
  width: 200px;
  margin: 10px;
  background: rgb(200, 200, 200);
  background-clip: content-box;
  line-height: 200px;
  overflow: hidden;
}
.bordered.top {
  padding-top: 8px;
}
.bordered.bottom {
  padding-bottom: 8px;
}
.bordered:after,
.bordered:before {
  position: absolute;
  content: ' ';
  height: 8px;
  width: 50%;
  background: inherit;
}
.bordered.top:after,
.bordered.top:before {
  top: 0px;
}
.bordered.bottom:after,
.bordered.bottom:before {
  bottom: 0px;
}
.bordered:before {
  left: 0px;
  border-right: 1px solid gray;
}
.bordered.top:before {
  border-top: 1px solid gray;
  transform-origin: left bottom;
  transform: skewX(45deg);
}
.bordered.bottom:before {
  border-bottom: 1px solid gray;
  transform-origin: left top;
  transform: skewX(-45deg);
}
.bordered:after {
  right: 0px;
  border-left: 1px solid gray;
}
.bordered.top:after {
  border-top: 1px solid gray;
  transform-origin: right bottom;
  transform: skewX(-45deg);
}
.bordered.bottom:after {
  border-bottom: 1px solid gray;
  transform-origin: right top;
  transform: skewX(45deg);
}
.bordered img {
  width: 150px;
  padding: 25px;
  vertical-align: middle;
}

/* Just for demo */

div{
  display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="bordered top">
  <img src="http://i.imgur.com/0Xqum3A.png" />
</div>

<div class="bordered bottom">
  <img src="http://i.imgur.com/0Xqum3A.png" />
</div>

Using Rotate:

This method uses a rotated pseudo-element (rotated by 45 degrees) to achieve the downward arrow effect and is then placed on top of the bottom border of the div. In this method, the background of the pseudo-element is set to the same color as the div containing it. (Note: In this sample the image and div have different fill colors.)

body {
  background: lightgray;
}
.colored {
  height: 200px;
  width: 200px;
  position: relative;
  margin: 10px;
  line-height: 200px;
}
.colored img {
  vertical-align: middle;
  width: 150px;
  padding: 25px;
}
.colored {
  background: yellow;
  border-bottom: 1px solid gray;
}
.colored:after {
  height: 10px;
  width: 10px;
  position: absolute;
  content: '';
  background: yellow;
  border: 1px solid gray;
  border-top-width: 0px;
  border-left-width: 0px;
  transform: rotate(45deg);
  bottom: -6px;
  left: calc(50% - 4px);
}

/* Just for demo */

.colored{
  text-align: center;
  transition: all 1s;
}
.colored:hover{
  height: 250px;
  width: 250px;
  line-height: 250px;
}
<!-- library included only to avoid prefixes -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>

<div class="colored">
  <img src="http://i.imgur.com/0Xqum3A.png" />
</div>

enter image description here

Community
  • 1
  • 1
Harry
  • 87,580
  • 25
  • 202
  • 214
  • 1
    Great answer. Thank you. It provides me with two options, both of which have flexibility and the ability to provide a more accurate and creative solution. – Ali Samii Jan 02 '15 at 12:58
  • Unrealted, but you seem to have a strong grasp of edge-layout capabilities (not that the above is very "edge, but what the heck). Maybe you could look at this other question I posted about using flexbox with twbs3 and provide some insight there? http://stackoverflow.com/questions/27743257/using-flexbox-with-bootstrap-3-and-laravel-4-blade-templates-and-associated-less – Ali Samii Jan 02 '15 at 13:53
  • @alisamii: Was just reading that question mate. Pure CSS and Less I would have definitely tried. Laravel and Bootstrap, I am not really that comfortable as I have never used Laravel and very rarely used bootstrap. But let me give it a try :) – Harry Jan 02 '15 at 13:55
5

A simple solution where the background of the arrow will be transparent, allowing you to use it on changing backgrounds:

HTML:

<div class="line-separator">
    <div class="side-line"> </div>
    <div class="triangle"> </div>
    <div class="side-line"> </div>
</div>

CSS:

.side-line {
    display: inline-block;
    border-top: 1px solid black;
    width: 20%;
}

.triangle {
    display: inline-block;
    height: 7px;
    width: 7px;
    transform: rotate(45deg);
    transform-origin: center center;
    border-top: 1px solid black;
    border-left: 1px solid black;
    margin: 0 -3px -3px;
}

Live demo: http://jsfiddle.net/85saaphw/

itaya
  • 325
  • 4
  • 7
1

I'm not sure if this is the best solution, but one option could be to use a :before to render the orange triangle and a :after to render a slightly smaller triangle with the color of your background. The :after triangle mostly covers the :before triangle leaving only a small orange triangle border. Unfortunately it can't be transparent if you solve it in this way.

.hero {
  position: relative;
  background-color: yellow;
  height: 320px !important;
  width: 100% !important;
  border-bottom: 1px solid red;
}
.hero:before {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -50px;
  width: 0;
  height: 0;
  border-top: solid 50px #e15915;
  border-left: solid 50px transparent;
  border-right: solid 50px transparent;
}
.hero:after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -48px;
  width: 0;
  height: 0;
  border-top: solid 48px yellow;
  border-left: solid 48px transparent;
  border-right: solid 48px transparent;
}
<div class="hero"></div>
ckuijjer
  • 13,293
  • 3
  • 40
  • 45