hrsetyono
hrsetyono

Reputation: 4464

CSS - How to create diagonal emboss?

I want to a 3d diagonal emboss button like seen in the picture below:

enter image description here

My first thought is to use shadow, then add two small triangle using ::before and ::after to close the gap.

But it's always very-slightly misaligned by less than 1 pixel (see snippet below). I used different color for easier coding.

.button {
  border: 1px solid red;
  box-shadow: -10px 10px red;
  
  display: inline-block; margin: 30px; position: relative; outline: none; font-size: 40px;
  padding: 10px 30px; background-color: #333; font-weight: 700;
  color: white; letter-spacing: 1px;
  line-height: 1; text-transform: uppercase; text-align: center;
}

/* create triangle */

.button::before { content: ""; position: absolute;
  top: -1px;
  left: -11px;
  width: 0; height: 0; font-size: 0; line-height: 0%; border-style: solid; border-color: transparent; border-width: 0 0 11px 11px; border-bottom-color: red; }

.button::after { content: ""; position: absolute;
  right: -1px;
  bottom: -11px;
  width: 0; height: 0; font-size: 0; line-height: 0%; border-style: solid; border-color: transparent; border-width: 11px 11px 0 0; border-top-color: red; }
<a class="button">Gallery</a>

Is there better implementation than using triangle and absolute-positioning it?

Upvotes: 6

Views: 666

Answers (5)

Harry
Harry

Reputation: 89760

One solution would be to use two angled linear-gradient strips - one on the left and other at bottom of the button element.

Imagine placing at 10px x 10px square on the top left and the bottom right of the pseudo-element such that its fill is transparent on one side of the diagonal and colored on the other side. It'd result a triangle shape. That is exactly the approach that we are using here.

A pseudo-element (which is a square or rectangle) is placed behind the parent and the triangular cuts are produced by using the angled linear-gradients. The gradients have 135 degrees and 315 degrees as angle (which is a 45 degree line or in other words the diagonal line of a square/rectangle). Since the imaginary square's size is 10px x 10px, the diagonal line will be 14px in length and so by setting color as transparent for 7px (half distance) and hotpink from 7px (other half), a triangular cut is produced.

The below are the steps that were performed:

  • Create a pseudo-element which is 10px taller and 10px wider than the parent .button element because the projection has to extend outside the element on the left and the bottom.
  • Place 2 small linear-gradient background strips on the top left and the bottom left of the pseudo-element. The size of the horizontal strip (the one on the left) is set as 10px 100%. This is because the thickness of the emboss (which is nothing but width of the strip) on the left is 10px.
  • Similarly the size of the vertical strip (the one on the bottom) is set as 100% 10px. Again because the thickness (in this case, thickness is the height of the emboss) on the bottom is 10px.
  • Then we have produced two angled linear-gradients with angles as 135deg and 315deg so that the triangular cut is seen on the bottom right and top left sides. The pixel color stop points within the gradient is calculated based on Pythagoras theorem. The value would be 10px/sqrt(2).
  • The 1px border for the element is achieved using box-shadow: inset 0px 0px 0px 1px hotpink because an inset shadow will not affect the positioning attributes of the pseudo-element unlike an extra border would.

.button {
  display: inline-block;
  margin: 30px;
  position: relative;
  outline: none;
  font-size: 40px;
  padding: 10px 30px;
  background-color: #333;
  font-weight: 700;
  color: white;
  letter-spacing: 1px;
  line-height: 1;
  text-transform: uppercase;
  text-align: center;
  box-shadow: inset 0px 0px 0px 1px hotpink; /* mimics the border */
}
.button:after {
  position: absolute;
  content: '';
  top: 0px;
  right: 0px; 
  height: calc(100% + 10px); /* since projection should extend outside bottom edge */
  width: calc(100% + 10px); /* since the projection should extend outside the left edge */
  background: linear-gradient(135deg, transparent 7px, hotpink 7px), linear-gradient(315deg, transparent 7px, hotpink 7px);
  background-size: 10px 100%, 100% 10px;
  background-position: top left, bottom left;
  background-repeat: no-repeat;
}
<a class="button">Gallery</a>

<a class="button">Gallery Button Big</a>

Note: The same approach can be employed without a pseudo-element also but it would become a lot more complex to understand and hence I've left it out. Here is a demo but as can be seen it is highly complex and I'd recommend going with the pseudo-element approach itself.

Upvotes: 4

Sebastian Brosch
Sebastian Brosch

Reputation: 43584

You can use box-shadow only to do this (no need of :after or :before! See the following solution:

.button {
  padding:10px;
  font-size: 30px;
  font-weight: bold;
  color: #FFF;
  display:inline-block;
  text-decoration:none;
  background: #333;
  border: solid 1px pink;
  box-shadow: -1px 0px 0px pink,-0px 1px 0px pink,
    -2px 1px 0px pink,-1px 2px 0px pink,
    -3px 2px 0px pink,-2px 3px 0px pink,
    -4px 3px 0px pink,-3px 4px 0px pink,
    -5px 4px 0px pink,-4px 5px 0px pink,
    -6px 5px 0px pink,-5px 6px 0px pink,
    -7px 6px 0px pink,-6px 7px 0px pink,
    -8px 7px 0px pink,-7px 8px 0px pink,
    -9px 8px 0px pink,-8px 9px 0px pink;
}
<a href="#" class="button">Gallery</a>

Hint: There is a little library with many different button styles: https://webdesignerhut.com/3d-buttons-with-css-dropshadow/. So you don't have to style your own buttons.

Upvotes: 1

Alexis
Alexis

Reputation: 5831

You can use only box-shadow, maybe less code than triangle.

Example :

.button {
  border: 1px solid pink;
  box-shadow: -10px 10px pink,-9px 9px pink,-8px 8px pink,-7px 7px pink,-6px 6px pink,-5px 5px pink,-4px 4px pink,-3px 3px pink,-2px 2px pink;
  
  display: inline-block; margin: 30px; position: relative; outline: none; font-size: 40px;
  padding: 10px 30px; background-color: #333; font-weight: 700;
  color: white; letter-spacing: 1px;
  line-height: 1; text-transform: uppercase; text-align: center;
}
<a class="button">Gallery</a>

To avoid pixel you can add 1px at each declaration.

.button{
 border: 1px solid pink;
 box-shadow: -10px 10px 1px pink,-9px 9px 1px pink,-8px 8px 1px pink,-7px 7px 1px pink,-6px 6px 1px pink,-5px 5px 1px pink,-4px 4px 1px pink,-3px 3px 1px pink,-2px 2px 1px pink,-1px 1px 1px pink,0px 0px 1px pink;
  
  display: inline-block; margin: 30px; position: relative; outline: none; font-size: 40px;
  padding: 10px 30px; background-color: #333; font-weight: 700;
  color: white; letter-spacing: 1px;
  line-height: 1; text-transform: uppercase; text-align: center;
  }
<a class="button">Gallery</a>

Upvotes: 4

Defesa Esquerdo
Defesa Esquerdo

Reputation: 320

I think the best solution for you would be using box-shadow.

You might also like to try this tool: http://www.cssmatic.com/box-shadow

Upvotes: 0

Mani
Mani

Reputation: 2655

Use this

<style>
  .test {
    display: inline-block;
    padding: 20px;
    margin: 20px;
    background: #000;
    color: white;
    font-weight: bold;
    text-decoration: none;
  }

  .test.shape {
    position: relative;
    box-shadow: -1px 1px #FC2CFF, -2px 2px #FC2CFF, -3px 3px #FC2CFF, -4px 4px #FC2CFF, -5px 5px #FC2CFF;
    transition: all 0.1s ease-in;
  }

</style>

<body>
  <div class="test shape">Box text</div>
</body>

Upvotes: 2

Related Questions