Search code examples
csscss-transformscss-shapes

How to create quadrilateral (rhombus shape) with specific degree?


How can I create a quadrilateral with css, when I know which degree each corner has.

I already tried to recreate a quadrilateral I have with transform and skew.

However, this does not work really well.

This is what I try to archive.

CSS Quadrilateral diamond shape

The exact requirements are:

A div with this as background in one color. On the image are just construction lines. It should be a solid quadrilateral with these angles.

This was my first idea:

transform: rotate(-45deg) skew(27.5deg, 62.5deg)
transform-origin: top center;

Solution

  • I would consider multiple background to achieve this where I simply need to find the width/height of the element. Based on your illustration we have this:

    CSS rhombus shape

    From this we can have the following formula:

    tan(alpha) = W/H
    

    and

    tan(beta/2) = H/W
    

    We only need to use one of them and you will notice that there isn't one solution which is logical as you simply need to keep a ratio between H and W and the width of our element will simply be 2*W and its height 2*H.

    Since H/W is also the same as 2*H/2*W we can simply consider that width = tan(alpha)*height

    .box {
      height:var(--h);
      width:calc(1.92098213 * var(--h)); /* tan(62.5)xH */
      background:
       linear-gradient(to bottom right,transparent 49%,red 50%) top left,
       linear-gradient(to top    right,transparent 49%,red 50%) bottom left,
       linear-gradient(to bottom left ,transparent 49%,red 50%) top right,
       linear-gradient(to top    left ,transparent 49%,red 50%) bottom right;
      background-size:50% 50%;
      background-repeat:no-repeat;
    }
    <div class="box" style="--h:50px;"></div>
    
    <div class="box" style="--h:100px;"></div>
    
    <div class="box" style="--h:200px;"></div>

    You can adjust the gradient if you want only borders:

    .box {
      height:var(--h);
      width:calc(1.92098213 * var(--h)); /* tan(62.5)xH */
      background:
       linear-gradient(to bottom right,transparent 49%,red 50%,transparent calc(50% + 2px)) top left,
       linear-gradient(to top    right,transparent 49%,red 50%,transparent calc(50% + 2px)) bottom left,
       linear-gradient(to bottom left ,transparent 49%,red 50%,transparent calc(50% + 2px)) top right,
       linear-gradient(to top    left ,transparent 49%,red 50%,transparent calc(50% + 2px))  bottom right;
      background-size:50% 50%;
      background-repeat:no-repeat;
    }
    <div class="box" style="--h:50px;"></div>
    
    <div class="box" style="--h:100px;"></div>
    
    <div class="box" style="--h:200px;"></div>


    Using transform the idea is to rely on rotateX() in order to visually decrease the height to keep the formula defined previously. So we start by having Width=height (a square) then we rotate like below:

    enter image description here

    This is a view from the side. The green is our rotated element and the red the initial one. It's clear that we will see the height H1 after performing the rotation and we have this formula:

    cos(angle) = H1/H
    

    And we aleardy have tan(alpha)=W/H1 so we will have

    cos(angle) = W/(H*tan(alpha)) 
    

    and H=W since we defined a square initially so we will have cos(angle) = 1/tan(alpha) --> angle = cos-1(1/tan(alpha))

    .box {
      width:150px;
      height:150px;
      background:red;
      margin:50px;
      transform:rotateX(58.63017731deg) rotate(45deg); /* cos-1(0.52056)*/
    }
    <div class="box">
    
    </div>

    We can also apply the same logic using rotateY() to update the width in the situation where you will have beta bigger than 90deg and alpha smaller than 45deg. In this case we will have W < H and the rotateX() won't help us.

    The math can easily confirm this. when alpha is smaller than 45deg tan(alpha) will be smaller than 1 thus 1/tan(alpha) will bigger than 1 and cos is only defined between [-1 1] so there is no angle we can use with rotateX()

    Here is an animation to illustrate:

    .box {
      width:100px;
      height:100px;
      display:inline-block;
      background:red;
      margin:50px;
      animation:change 5s linear infinite alternate;
    }
    .alt {
      animation:change-alt 5s linear infinite alternate;
    }
    
    @keyframes change {
      from{transform:rotateX(0) rotate(45deg)}
      to{  transform:rotateX(90deg) rotate(45deg)}
    }
    @keyframes change-alt {
      from{transform:rotateY(0) rotate(45deg)}
      to{  transform:rotateY(90deg) rotate(45deg)}
    }
    <div class="box">
    
    </div>
    
    <div class="box alt">
    
    </div>