Search code examples
javascripthtmlcsssvgcss-shapes

Create Triangle Menu


I am trying to create a menu that contains right triangles formed together to form a square. This is what I envision:

https://i.ibb.co/h7pLqBt/Sample.png

This is what I hope to achieve:

  • Can be dynamically generated through javascript.
  • Scales to parent
  • Clip an image as background for each triangle (cannot be CSS)
  • Link to a site for each triangle
  • Update background and text color when hovering
  • Support multiple browsers

I have tried several different approaches, but encountered several issues with each one:

  • CSS Hack:
    • Cannot set text to correct location
    • Cannot set borders individually
  • SVG
    <div class="menu-box">
    <svg id="menu" style="border: black solid 1px" width="100" height="100" viewbox="0, 0, 100, 100">
      <polygon class = "top" points='0,0 0,100 100,0'  fill="none" stroke="red"/>
      <text x="-18" y="68" fill="black" transform="rotate(-45)">Item</text>
      <polygon  points='100,0 100,100 0,100'  fill="none" stroke="red" />
        <text x="-18" y="84" fill="black" transform="rotate(-45)">Item</text>
    </svg>
    </div>
  • Canvas
    • Difficult dynamically adjust to parent
  • Clip path:
    • Not supported by most browsers.

I understand this is quite ambitious, but any help would be appreciated. I am also open to other ideas, but these were the ones I found online.

Edit: Switch to a better image


Solution

  • Here is a idea using skew transformation:

    * {
      box-sizing: border-box;
    }
    
    .menu {
      width: 150px;
      border: 1px solid;
      overflow: hidden;
      display: flex;
      justify-content: center;
    }
    
    /*maitain ratio*/
    .menu:before {
      content: "";
      padding-top: 100%;
    }
    
    .menu a {
      position:relative;
      width: 100%;
      flex-shrink: 0;
      border: 3px solid red;
      transform: skew(-45deg);
      overflow:hidden;
      color:#fff;
      font-weight:bold;
      font-size:18px;
    }
    
    .menu a:first-child span {
      position: absolute;
      bottom:0;
      left: 50%;
      width: 141%;
      display: block;
      text-align: center;
      transform-origin: 86% 100%;
      transform: translate(-50%) skew(45deg) rotate(-45deg) translateX(86%);
    }
    
    .menu a:last-child span {
      position: absolute;
      top:0;
      left: 50%;
      width: 141%;
      display: block;
      text-align: center;
      transform-origin: 14% 0%;
      transform: translateX(-50%) skew(45deg) rotate(-45deg) translateX(-86%);
    }
    
    .menu a:before {
      content:"";
      position:absolute;
      top:0;
      left:-50%;
      right:-50%;
      bottom:0;
      background:url(https://picsum.photos/id/1069/500/500) center/cover;
      transform: skew(45deg);
    }
    .menu a:last-child:before {
      background:url(https://picsum.photos/id/1059/500/500) center/cover;
    }
    
    /*Hover */
    .menu a:hover {
      color:green;
      border-color:yellow;
    }
    .menu a:hover:before {
      filter:grayscale(100%);
    }
    <div class="menu">
      <a href="#"><span>Link 1</span></a>
      <a href="#"><span>Link 2</span></a>
    </div>
    
    <div class="menu" style="width:200px">
      <a href="#"><span>Link 1</span></a>
      <a href="#"><span>Link 2</span></a>
    </div>
    
    <div class="menu" style="width:250px">
      <a href="#"><span>Link 1</span></a>
      <a href="#"><span>Link 2</span></a>
    </div>

    You can also specify the image directly in the HTML code

    * {
      box-sizing: border-box;
    }
    
    .menu {
      width: 150px;
      border: 1px solid;
      overflow: hidden;
      display: flex;
      justify-content: center;
    }
    
    /*maitain ratio*/
    .menu:before {
      content: "";
      padding-top: 100%;
    }
    
    .menu a {
      position:relative;
      width: 100%;
      flex-shrink: 0;
      border: 3px solid red;
      transform: skew(-45deg);
      overflow:hidden;
      color:#fff;
      font-weight:bold;
      font-size:18px;
      background-size:0 0;
    }
    
    
    .menu a:first-child span {
      position: absolute;
      bottom:0;
      left: 50%;
      width: 141%;
      display: block;
      text-align: center;
      transform-origin: 86% 100%;
      transform: translate(-50%) skew(45deg) rotate(-45deg) translateX(86%);
    }
    
    .menu a:last-child span {
      position: absolute;
      top:0;
      left: 50%;
      width: 141%;
      display: block;
      text-align: center;
      transform-origin: 14% 0%;
      transform: translateX(-50%) skew(45deg) rotate(-45deg) translateX(-86%);
    }
    
    
    .menu a:before {
      content:"";
      position:absolute;
      top:0;
      left:-50%;
      right:-50%;
      bottom:0;
      background-image:inherit;
      background-position:center;
      background-size:cover;
      transform: skew(45deg);
    }
    
    /*Hover */
    .menu a:hover {
      color:green;
      border-color:yellow;
    }
    .menu a:hover:before {
      filter:grayscale(100%);
    }
    <div class="menu">
      <a href="#" style="background-image:url(https://picsum.photos/id/1069/500/500)"><span>Link 1</span></a>
      <a href="#" style="background-image:url(https://picsum.photos/id/1049/500/500)"><span>Link 2</span></a>
    </div>
    
    <div class="menu" style="width:200px">
      <a href="#" style="background-image:url(https://picsum.photos/id/1063/500/500)"><span>Link 1</span></a>
      <a href="#" style="background-image:url(https://picsum.photos/id/1069/500/500)"><span>Link 2</span></a>
    </div>
    
    <div class="menu" style="width:250px">
      <a href="#" style="background-image:url(https://picsum.photos/id/109/500/500)"><span>Link 1</span></a>
      <a href="#" style="background-image:url(https://picsum.photos/id/1069/500/500)"><span>Link 2</span></a>
    </div>