CSS Triangle Mixin

There is a fairly popular CSS hack using transparent borders on a 0-width / 0-height element to mimic triangles. There is a CSS snippet here on CSS-Tricks that depicts it.

If, like me, you never quite remember how it works, be sure we can use Sass to help us.

/// Triangle helper mixin
/// @param {Direction} $direction - Triangle direction, either `top`, `right`, `bottom` or `left`
/// @param {Color} $color [currentcolor] - Triangle color 
/// @param {Length} $size [1em] - Triangle size
@mixin triangle($direction, $color: currentcolor, $size: 1em) {
  @if not index(top right bottom left, $direction) {
    @error "Direction must be either `top`, `right`, `bottom` or `left`.";
  }

  width: 0;
  height: 0;
  content: '';
  z-index: 2;
  border-#{opposite-position($direction)}: ($size * 1.5) solid $color;
  
  $perpendicular-borders: $size solid transparent;
  
  @if $direction == top or $direction == bottom {
    border-left:   $perpendicular-borders;
    border-right:  $perpendicular-borders;
  } @else if $direction == right or $direction == left {
    border-bottom: $perpendicular-borders;
    border-top:    $perpendicular-borders;
  }
}

Additional notes:

* The opposite-position function comes from Compass; if you do not use Compass, you might need to have your own;
* The mixin does not deal with positioning the triangle; it is perfectly fine to combine it with a positioning mixin though;
* The content directive is meant to allow it to be used on pseudo-elements, which actually ends up being most cases.

Usage

.foo::before {
  @include triangle(bottom);
  position: absolute;
  left: 50%;
  bottom: 100%;
}
.foo::before {
  width: 0;
  height: 0;
  content: '';
  z-index: 2;
  border-top: 1.5em solid currentColor;
  border-left: 1em solid transparent;
  border-right: 1em solid transparent;
  position: absolute;
  left: 50%;
  bottom: 100%;
}

Comments

  1. User Avatar
    Lorenzo
    Permalink to comment#
  2. User Avatar
    vsync

    I think the size (border-width) should be dynamic. sometimes you will need two values, for example: border-width: 5px 8px;

  3. User Avatar
    vsync

    Also, you’ve mentioned in the mixing the words “opposite-position”. this means nothing to SASS… it breaks the compiler

  4. User Avatar
    xray
    Permalink to comment#

    Hi,
    Awesome find. I debug it a little bit.Works like a charm now with SCSS. I’m not sure what I was doing wrong with your code, but I couldnt display color parameter. Below mixin alteration serves to use i.e.

    @include triangle(top,#CCC,8px);

    ……………………………………………………………..

    @mixin triangle($direction, $color: currentcolor, $size: 1em) {
    @if not index(top right bottom left, $direction) {
    @error “Direction must be either top, right, bottom or left.”;
    }

    width: 0;
    height: 0;
    content: ”;
    z-index: 2;

    $perpendicular-borders: $size solid transparent;

    @if $direction == top {
    border-left: $perpendicular-borders;
    border-right: $perpendicular-borders;
    border-bottom: $size solid $color;
    } @else if $direction == bottom {
    border-left: $perpendicular-borders;
    border-right: $perpendicular-borders;
    border-top: $size solid $color;
    } @else if $direction == right {
    border-bottom: $perpendicular-borders;
    border-top: $perpendicular-borders;
    border-left: $size solid $color;
    } @else if $direction == left {
    border-bottom: $perpendicular-borders;
    border-top: $perpendicular-borders;
    border-right: $size solid $color;
    }
    }

  5. User Avatar
    Dennish Karki
    Permalink to comment#

    /*
    * Mixin for basic CSS triangles
    * @include triangle(up, #000, 50px);
    * @include triangle(bottomleft, #000, 50px);
    */
    @mixin triangle($direction:up, $color:#000, $size:100px) {
    @if ($direction == up){
    border-color: transparent transparent $color;
    border-style: solid;
    border-width: 0 $size $size;
    height: 0;
    width: 0;
    }
    @if ($direction == down) {
    border-color: $color transparent transparent transparent;
    border-style: solid;
    border-width: $size;
    height: 0;
    width: 0;
    }
    @if ($direction == left) {
    border-color: transparent $color transparent transparent;
    border-style: solid;
    border-width: $size $size $size 0;
    height: 0;
    width: 0;
    }
    @if ($direction == right) {
    border-color: transparent transparent transparent $color;
    border-style: solid;
    border-width: $size 0 $size $size;
    height: 0;
    width: 0;
    }
    @if ($direction == bottomright) {
    border-color: transparent transparent $color transparent;
    border-style: solid;
    border-width: 0 0 $size $size;
    height: 0;
    width: 0;
    }
    @if ($direction == bottomleft) {
    border-color: transparent transparent transparent $color;
    border-style: solid;
    border-width: $size 0 0 $size;
    height: 0;
    width: 0;
    }
    @if ($direction == topleft) {
    border-color: $color transparent transparent transparent;
    border-style: solid;
    border-width: $size $size 0 0;
    height: 0;
    width: 0;
    }
    @if ($direction == topright) {
    border-color: transparent $color transparent transparent;
    border-style: solid;
    border-width: 0 $size $size 0;
    height: 0;
    width: 0;
    }
    }

  6. User Avatar
    Jack Kalish
    Permalink to comment#

    Thanks for comments and fixes. Also, I think that size parameter should actually be divided by 2, for left and right arrows, to get an accurate height for 1em. 1em arrows top and bottom and actually .5em arrows left and right, if you are going by text height.

Submit a Comment

Posting Code

You may write comments in Markdown. This makes code easy to post, as you can write inline code like `<div>this</div>` or multiline blocks of code in triple backtick fences (```) with double new lines before and after.

Code of Conduct

Absolutely anyone is welcome to submit a comment here. But not all comments will be posted. Think of it like writing a letter to the editor. All submitted comments will be read, but not all published. Published comments will be on-topic, helpful, and further the discussion or debate.

Want to tell us something privately?

Feel free to use our contact form. That's a great place to let us know about typos or anything off-topic.

icon-anchoricon-closeicon-emailicon-linkicon-logo-staricon-menuicon-nav-guideicon-searchicon-staricon-tag