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%;
}
I would add
transform: scale(1.01)
too:https://brettstrikesback.com/de-pixelating-the-css-triangle/
I think the size (border-width) should be dynamic. sometimes you will need two values, for example:
border-width: 5px 8px;
Also, you’ve mentioned in the mixing the words “opposite-position”. this means nothing to SASS… it breaks the compiler
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
orleft
.”;}
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;
}
}
/*
* 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;
}
}
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.
For those trying to use the code, you need to include the function
opposite-direction
and you need that function from the linked article, except that it is opposite-direction, not opposite-position (which does not exist, position mixin is another thing).