DarcCode DarcCode - 6 months ago 21
CSS Question

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?

Answer

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.