mindfullsilence
9/28/2017 - 5:46 PM

SVG sass triangle

Creates a triangle with fill and stroke on any side(s) you want.

@function -svg-triangle(
  $width: 10,
  $height: 10,
  $border-color: transparent,
  $borders: left bottom,
  $border-width: 0,
  $fill: #000000
) {
  $canvas: '';
  $triangle: '';
  $stroke: '';
  $width: strip-unit($width);
  $height: strip-unit($height);

  $canvas: $canvas + '<svg width="#{$width}" height="#{$height}" viewBox="-#{$border-width} -#{$border-width} #{$width + ($border-width * 2)} #{$height + ($border-width * 2)}" xmlns="http://www.w3.org/2000/svg">';

  $top-point: "M #{$width / 2} 0";
  $bottom-left: "L 0 #{$height}";
  $bottom-right: "L #{$width} #{$height} z";

  $triangle: $triangle + '<path d="#{$top-point} #{$bottom-left} #{$bottom-right}" fill="#{$fill}" />';

  @if $borders != false {
    $stroke: $stroke + '<path fill="none" stroke="#{$border-color}" stroke-width="#{$border-width}" d="';
    $stroke-top: '#{$width / 2} 0';
    $stroke-right: '#{$width} #{$height}';
    $stroke-left: '0 #{$height}';

    @if -contains($borders, right) and -contains($borders, bottom) and -contains($borders, left) {
      $stroke: $stroke + 'M#{$stroke-top} L #{$stroke-right} L #{$stroke-left} Z';
    } @else if -contains($borders, right) and -contains($borders, bottom) {
      $stroke: $stroke + 'M#{$stroke-top} L #{$stroke-right} L #{$stroke-left}';
    } @else if -contains($borders, bottom) and -contains($borders, left) {
      $stroke: $stroke + 'M#{$stroke-right} L #{$stroke-left} L #{$stroke-top}';
    } @else if -contains($borders, left) and -contains($borders, right) {
      $stroke: $stroke + 'M#{$stroke-left} L #{$stroke-top} L #{$stroke-right}';
    }

    $stroke: $stroke + '" />';
  }

  $canvas: $canvas + $stroke + $triangle + '</svg>';

  @return $canvas;
}

@mixin svg-triangle(
  $width: 10px,
  $height: 10px,
  $borders: left bottom right,
  $border-color: transparent,
  $border-width: 0,
  $fill: #000000,
  $direction: up
) {
  width: $width;
  height: $height;
  content: '';
  display: block;

  $triangle: -svg-triangle(
          $width,
          $height,
          $border-color,
          $borders,
          $border-width,
          $fill);
  background-image: url('data:image/svg+xml,' + $triangle);
  background-size: 100% 100%;
  background-repeat: no-repeat;
  background-position: center center;

  @if $direction == left {
    transform: rotate(-90deg);
  } @else if $direction == right {
    transform: rotate(90deg);
  } @else if $direction == down {
    transform: rotate(180deg);
  }
}

Use the mixin inside of a ::before or ::after pseudo element.

E.g.:

div::before {
  @include svg-triangle(
    $width: 30px, // any unit
    $height: 20px, // any unit
    $borders: left right, // left, right, bottom, or any combination of the 3
    $border-color: #cccccc, // hex
    $border-width: 2, // no unit
    $fill: #000000, // hex
    $direction: left // left, right, up, or down
  );
}

Note that the starting direction is up, and the borders will be applied to the sides corresponding to that orientation before $direction is applied.