How do CSS triangles work?

2018-12-30 23:45发布

There're plenty of different CSS shapes over at CSS Tricks - Shapes of CSS and I'm particularly puzzled with a triangle:

CSS Triangle

#triangle-up {
  width: 0;
  height: 0;
  border-left: 50px solid transparent;
  border-right: 50px solid transparent;
  border-bottom: 100px solid red;
}
<div id="triangle-up"></div>

How and why does it work?

17条回答
倾城一夜雪
2楼-- · 2018-12-31 00:01

Lets say we have the following div:

<div id="triangle" />

Now Edit the CSS step-by-step, so you will get clear idea what is happening around

STEP 1: JSfiddle Link:

 #triangle {
        background: purple;
        width :150px;
        height:150PX;
        border-left: 50px solid black ;
        border-right: 50px solid black;
        border-bottom: 50px solid black;
        border-top: 50px solid black;
    }

This is a simple div. With a very simple CSS. So a layman can understand. Div has dimensions 150 x 150 pixels with the border 50 pixels. The image is attached:

enter image description here

STEP 2: JSfiddle Link:

#triangle {
    background: purple;
    width :150px;
    height:150PX;
    border-left: 50px solid yellow ;
    border-right: 50px solid green;
    border-bottom: 50px solid red;
    border-top: 50px solid blue;
}

Now I just changed the border-color of all 4 sides. The image is attached.

enter image description here

STEP:3 JSfiddle Link:

#triangle {
    background: purple;
    width :0;
    height:0;
    border-left: 50px solid yellow ;
    border-right: 50px solid green;
    border-bottom: 50px solid red;
    border-top: 50px solid blue;
}

Now I just changed the height & width of div from 150 pixels to zero. The image is attached

enter image description here

STEP 4: JSfiddle:

#triangle {
    background: purple;
    width :0px;
    height:0px;
    border-left: 50px solid transparent;
    border-right: 50px solid transparent;
    border-bottom: 50px solid red;
    border-top: 50px solid transparent;
}

Now I have made all the borders transparent apart from the bottom border. The image is attached below.

enter image description here

STEP 5: JSfiddle Link:

#triangle {
    background: white;
    width :0px;
    height:0px;
    border-left: 50px solid transparent;
    border-right: 50px solid transparent;
    border-bottom: 50px solid red;
    border-top: 50px solid transparent;
}

Now I just changed the background color to white. The image is attached.

enter image description here

Hence we got the triangle we needed.

查看更多
临风纵饮
3楼-- · 2018-12-31 00:04

Start with a basic square and borders. Each border will be given a different color so we can tell them apart:

.triangle {
    border-color: yellow blue red green;
    border-style: solid;
    border-width: 200px 200px 200px 200px;
    height: 0px;
    width: 0px;
}

which gives you this:

square with four borders

But there's no need for the top border, so set its width to 0px. Now our border-bottom of 200px will make our triangle 200px tall.

.triangle {
    border-color: yellow blue red green;
    border-style: solid;
    border-width: 0px 200px 200px 200px;
    height: 0px;
    width: 0px;
}

and we will get this:

bottom half of square with four borders

Then to hide the two side triangles, set the border-color to transparent. Since the top-border has been effectively deleted, we can set the border-top-color to transparent as well.

.triangle {
    border-color: transparent transparent red transparent;
    border-style: solid;
    border-width: 0px 200px 200px 200px;
    height: 0px;
    width: 0px;
}

finally we get this:

triangular bottom border

查看更多
伤终究还是伤i
4楼-- · 2018-12-31 00:04

Almost all the answers focus on the triangle built using border so I am going to elaborate the linear-gradient method (as started in the answer of @lima_fil).

Using a degree value like 45° will force us to respect a specific ratio of height/width in order to obtain the triangle we want and this won't be responsive:

.tri {
  width:100px;
  height:100px;
  background:linear-gradient(45deg, transparent 50%,red 0);
  
  /*To illustrate*/
  border:1px solid;
}
Good one
<div class="tri"></div>
bad one
<div class="tri" style="width:150px"></div>
bad one
<div class="tri" style="height:30px"></div>

Instead of doing this we should consider predefined values of direction like to bottom, to top, etc. In this case we can obtain any kind of triangle shape while keeping it responsive.

1) Rectangle triangle

To obtain such triangle we need one linear-gradient and a diagonal direction like to bottom right, to top left, to bottom left, etc

.tri-1,.tri-2 {
  display:inline-block;
  width:100px;
  height:100px;
  background:linear-gradient(to bottom left, transparent 49.8%,red 50%);
  border:1px solid;
  animation:change 2s linear infinite alternate;
}
.tri-2 {
  background:linear-gradient(to top right, transparent 49.8%,red 50%);
  border:none;
}

@keyframes change {
  from {
    width:100px;
    height:100px;
  }
  to {
    height:50px;
    width:180px;
  }
}
<div class="tri-1"></div>
<div class="tri-2"></div>

2) isosceles triangle

For this one we will need 2 linear-gradient like above and each one will take half the width (or the height). It's like we create a mirror image of the first triangle.

.tri {
  display:inline-block;
  width:100px;
  height:100px;
  background-image:
  linear-gradient(to bottom right, transparent 49.8%,red 50%),
  linear-gradient(to bottom left, transparent 49.8%,red 50%);
  background-size:50.5% 100%; /* I use a value slightly bigger than 50% to avoid having a small gap between both gradient*/
  background-position:left,right;
  background-repeat:no-repeat;
  
  animation:change 2s linear infinite alternate;
}


@keyframes change {
  from {
    width:100px;
    height:100px;
  }
  to {
    height:50px;
    width:180px;
  }
}
<div class="tri"></div>

3) equilateral triangle

This one is a bit tricky to handle as we need to keep a relation between the height and width of the gradient. We will have the same triangle as above but we will make the calculation more complex in order to transform the isosceles triangle to an equilateral one.

To make it easy, we will consider that the width of our div is known and the height is big enough to be able to draw our triangle inside (height >= width).

enter image description here

We have our two gradient g1 and g2, the blue line is the width of the div w and each gradient will have 50% of it (w/2) and each side of the triangle sould be equal to w. The green line is the height of both gradient hg and we can easily obtain the formula below:

(w/2)² + hg² = w² ---> hg = (sqrt(3)/2) * w ---> hg = 0.866 * w

We can rely on calc() in order to do our calculation and to obtain the needed result:

.tri {
  --w:100px;
  width:var(--w);
  height:100px;
  display:inline-block;
  background-image:
  linear-gradient(to bottom right, transparent 49.8%,red 50%),
  linear-gradient(to bottom left, transparent 49.8%,red 50%);
  background-size:calc(var(--w)/2 + 0.5%)  calc(0.866 * var(--w));
  background-position:
    left bottom,right bottom;
  background-repeat:no-repeat;
  
}
<div class="tri"></div>
<div class="tri" style="--w:80px"></div>
<div class="tri" style="--w:50px"></div>

Another way is to control the height of div and keep the syntax of gradient easy:

.tri {
  --w:100px;
  width:var(--w);
  height:calc(0.866 * var(--w));
  display:inline-block;
  background:
   linear-gradient(to bottom right, transparent 49.8%,red 50%) left,
   linear-gradient(to bottom left,  transparent 49.8%,red 50%) right;
  background-size:50.2% 100%;
  background-repeat:no-repeat;
  
}
<div class="tri"></div>
<div class="tri" style="--w:80px"></div>
<div class="tri" style="--w:50px"></div>

4) Random triangle

To obtain a random triangle, it's easy as we simply need to remove the condition of 50% of each one BUT we should keep two condition (both should have the same height and the sum of both width should be 100%).

.tri-1 {
  width:100px;
  height:100px;
  display:inline-block;
  background-image:
  linear-gradient(to bottom right, transparent 50%,red 0),
  linear-gradient(to bottom left, transparent 50%,red 0);
  background-size:20% 60%,80% 60%;
  background-position:
    left bottom,right bottom;
  background-repeat:no-repeat;
  
 
}
<div class="tri-1"></div>

But what if we want to define a value for each side? We simply need to do calculation again!

enter image description here

Let's define hg1 and hg2 as the height of our gradient (both are equal to the red line) then wg1 and wg2 as the width of our gradient (wg1 + wg2 = a). I will not going to detail the calculation but at then end we will have:

wg2 = (a²+c²-b²)/(2a)
wg1 = a - wg2
hg1 = hg2 = sqrt(b² - wg1²) = sqrt(c² - wg2²)

Now we have reached the limit of CSS as even with calc() we won't be able to implement this so we simply need to gather the final result manually and use them as fixed size:

.tri {
  --wg1: 20px; 
  --wg2: 60px;
  --hg:30px; 
  width:calc(var(--wg1) + var(--wg2));
  height:100px;
  display:inline-block;
  background-image:
  linear-gradient(to bottom right, transparent 50%,red 0),
  linear-gradient(to bottom left, transparent 50%,red 0);

  background-size:var(--wg1) var(--hg),var(--wg2) var(--hg);
  background-position:
    left bottom,right bottom;
  background-repeat:no-repeat;
  
}
<div class="tri" ></div>

<div class="tri" style="--wg1:80px;--wg2:60px;--hg:100px;" ></div>

Bonus

We should not forget that we can also apply rotation and/or skew and we have more option to obtain more triangle:

.tri {
  --wg1: 20px; 
  --wg2: 60px;
  --hg:30px; 
  width:calc(var(--wg1) + var(--wg2));
  height:100px;
  display:inline-block;
  background-image:
  linear-gradient(to bottom right, transparent 50%,red 0),
  linear-gradient(to bottom left, transparent 50%,red 0);

  background-size:var(--wg1) var(--hg),var(--wg2) var(--hg);
  background-position:
    left bottom,right bottom;
  background-repeat:no-repeat;
  
}
<div class="tri" ></div>

<div class="tri" style="transform:skewY(25deg)"></div>

<div class="tri" style="--wg1:80px;--wg2:60px;--hg:100px;" ></div>


<div class="tri" style="--wg1:80px;--wg2:60px;--hg:100px;transform:rotate(20deg)" ></div>

And of course we should keep in mind the SVG solution which can be more suitable in some situation:

svg {
 width:100px;
 height:100px;
}

polygon {
  fill:red;
}
<svg viewBox="0 0 100 100"><polygon points="0,100 0,0 100,100" /></svg>
<svg viewBox="0 0 100 100"><polygon points="0,100 50,0 100,100" /></svg>
<svg viewBox="0 0 100 100"><polygon points="0,100 50,23 100,100" /></svg>
<svg viewBox="0 0 100 100"><polygon points="20,60 50,43 80,100" /></svg>

查看更多
看淡一切
5楼-- · 2018-12-31 00:13

And now something completely different...

Instead of using css tricks don't forget about solutions as simple as html entities:

&#9650;

Result:

See: What are the HTML entities for up and down triangles?

查看更多
忆尘夕之涩
6楼-- · 2018-12-31 00:13

CSS clip-path

This is something I feel this question has missed; clip-path

clip-path in a nutshell

Clipping, with the clip-path property, is akin to cutting a shape (like a circle or a pentagon) from a rectangular piece of paper. The property belongs to the “CSS Masking Module Level 1” specification. The spec states, “CSS masking provides two means for partially or fully hiding portions of visual elements: masking and clipping”.


clip-path will use the element itself rather than its borders to cut the shape you specify in its parameters. It uses a super simple percentage based co-ordinate system which makes editing it very easy and means you can pick it up and create weird and wonderful shapes in a matter of minutes.


Triangle Shape Example

div {
  -webkit-clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
  clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
  background: red;
  width: 100px;
  height: 100px;
}
<div></div>


Downside

It does have a major downside at the moment, one being it's major lack of support, only really being covered within -webkit- browsers and having no support on IE and only very partial in FireFox.


Resources

Here are some useful resources and material to help better understand clip-path and also start creating your own.

查看更多
与君花间醉酒
7楼-- · 2018-12-31 00:13

here is another fiddle:

.container:after {
    position: absolute;
    right: 0;
    content: "";
    margin-right:-50px;
    margin-bottom: -8px;
    border-width: 25px;
    border-style: solid;
    border-color: transparent transparent transparent #000;
    width: 0;
    height: 0;
    z-index: 10;
    -webkit-transition: visibility 50ms ease-in-out,opacity 50ms ease-in-out;
    transition: visibility 50ms ease-in-out,opacity 50ms ease-in-out;
    bottom: 21px;
}
.container {
    float: left;
    margin-top: 100px;
    position: relative;
    width: 150px;
    height: 80px;
    background-color: #000;
}

.containerRed {
    float: left;
    margin-top: 100px;
    position: relative;
    width: 100px;
    height: 80px;
    background-color: red;
}

https://jsfiddle.net/qdhvdb17/

查看更多
登录 后发表回答