DEV Community

loading...
Cover image for Beveled Corners with Sass

Beveled Corners with Sass

Ari
Aspiring software engineer.
Originally published at ceiphr.com on ・6 min read

Go to borderlands.com, marvel.com and whereismaurice.com. All three sites contain elements with beveled (specifically, chamfered) corners. Each site has taken an entirely different approach when making them. Here are those approaches, their strengths and their shortcomings:

Sprite Maps

On desktop, borderlands.com looks pretty clean. You wouldn’t notice it, but they’re leveraging a sprite map for a lot of their UI. Specifically, this sprite map:

Borderlands.com Sprite Map

Using a sprite map for beveled corners can be useful because it will work with much older browsers, since a background-image for the sprite map and flexbox for connecting sprites is all you need:

.sprite {
  background-image: url('https://example.com/sprite.png');
  background-size: 475px 440px;

  width: 28px;
  height: 61px;
}

.button {
  display: flex;

  &__left {
    @extend .sprite;
    background-position: -286px -341px;
  }

  &__right {
    @extend .sprite;
    background-position: -257px -341px;
  }
}
Enter fullscreen mode Exit fullscreen mode

For the button’s body, you can use some Sass, since there isn’t anything fancy about it. But, if you’re going to use sprites, you might as well make this a sprite too for consistency. I mean, that is what the borderlands.com developers did after all:

.button__inner {
  background-image: url('https://example.com/sprite.png');
  background-size: 100% 100%;

  height: 61px;
  max-width: 12em;
  min-width: 165px;
  margin-left: -1px;
  margin-right: -1px;

  display: inline-block;
  flex: 1 1 auto;
  text-align: center;

  p {
    display: inherit;
    margin-top: 1.25rem;

    font-weight: bold;
    font-style: italic;
    text-transform: uppercase;
  }
}
Enter fullscreen mode Exit fullscreen mode

Of course because the sides of the button are sprites, this approach is rigid. Each element may need multiple sprites. They must be appropriately sized. These elements can’t stretch or shrink. And, if an element needs to be tweaked, you can’t do much with a style sheet.

It’s also important to mention, this approach needs three div elements (one for each sprite):

<div class="button">
  <div class="button__left"></div>
  <div class="button__inner">
    <p>Sprite Button</p>
  </div>
  <div class="button__right"></div>
</div>
Enter fullscreen mode Exit fullscreen mode

I would only recommend this approach if you’re going to use it on elements with fixed sizes. You’ll also need to spin up Photoshop anytime you want to tweak a button’s color or dimensions, which isn’t fun.

Note: I didn’t make the sprites used for that button. They were taken from borderlands.com and are owned by Gearbox Software LLC.

Pseudo Elements

marvel.com takes a slightly nicer approach. ::before and ::after are used to create the top and bottom halves of elements. The pseudo-elements of the button create the rectangular border. The inner contents use another set of pseudo-elements for a repeating-linear-gradient as their background image, which is a white line at a 45-degree angel.

The main caveat with this approach is the insane number of properties required to get it working. It took me two hours and 70 lines of Sass to create a working example in CodePen. Here is the main bit:

.button {
  background-color: transparent;
  display: inline-block;
  margin: 15px auto;
  position: relative;

  &:after,
  &:before {
    border-color: white;
    border-style: solid;
    content: "";

    display: block;
    height: 16px;
  }

  &:before {
    top: 0;
    right: 0;
    border-width: 1px 1px 0 0;
    margin-left: 16px;
  }

  &:after {
    bottom: 0;
    left: 0;
    border-width: 0 0 1px 1px;
    margin-right: 16px;
  }
}
Enter fullscreen mode Exit fullscreen mode

That chunk of code does everything except the beveled corners. Yes, 34 lines of code for making half an outline for a button. Here are the other 40 LINES OF CODE for the beveled corners:

.button__inner {
  display: block;
  padding: 0 35px;

  text-align: center;
  text-transform: uppercase;
  line-height: 16px;

  background-color: transparent;
  border-left: 1px solid white;
  border-right: 1px solid white;
  color: white;

  &:after,
  &:before {
    border-style: solid;
    border-color: transparent;
    border-width: 0 0 16px 16px;
    content: "";
    position: absolute;

    background-size: 16px 24px;
    background-image: repeating-linear-gradient(
        -45deg,
        white,
        white 1px,
        transparent 0,
        transparent 16px
    );
  }

  &:before {
    top: 0;
    left: 0;
  }

  &:after {
    bottom: 0;
    right: 0;
    transform: rotate(180deg);
  }
}
Enter fullscreen mode Exit fullscreen mode

Nearly 80 lines, just for a button. But in the end, you didn’t have to use sprites, and you get the ability to modify color and sizing using Sass, which is great. As a bonus, you only need a div and a span for this to work:

<div class="button">
  <span class="button__inner">
    Button Out.
  </span>
</div>
Enter fullscreen mode Exit fullscreen mode

No need to boot up Photoshop every time a button needs a color tweaked. Instead, get a migraine whenever you need to debug all that pseudo-element goodness.

Useful to mention: Depending on the browser you’re using, if you zoom in on that button, you may notice one or two minor disconnects. I thought this was a problem with my implementation, but, from my testing, this jankiness is also on the buttons at marvel.com.

Clip Paths

whereismaurice.com (ahem, a project of mine), takes the most flexible approach. Let me introduce the clip-path. It works just like Photoshop’s clipping mask. Currently, all browsers except IE support it with shapes and inline SVGs (Firefox includes support for external SVGs).

The other two examples I’ve given have two key problems which makes them impractical for anything other than fixed UI elements. First, they are dependent on the size of the content and second, they cannot handle content overflow/scrolling. So, for anything other than buttons, they can be a disaster.

The fantastic Chris Coyier on CSS-Tricks found that if you apply a beveled polygon to a clip path, it can make any element look great beveled. And from my testing, that includes overflowing and scrollable elements.

Want beveled corners on opposite sides of an element? Use @include bevel--opp() to apply this Sass mixin:

/* 
 * @param $size Bevel size.
 * @param $alt Use alternate corners.
 */
@mixin bevel--opp($size: 12px, $alt: false) {
  @if $alt {
    clip-path: polygon(
        0% $size,
        $size 0%,
        100% 0%,
        100% 100%,
        100% calc(100% - #{$size}),
        calc(100% - #{$size}) 100%,
        100% 100%,
        0% 100%
    );
  } @else {
    clip-path: polygon(
        0% 0%,
        0% 0%,
        calc(100% - #{$size}) 0%,
        100% $size,
        100% 100%,
        100% 100%,
        $size 100%,
        0 calc(100% - #{$size})
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Well, that’s great and all, but what if you want all corners to be beveled corners? Here is a much simpler mixin for that:

/* 
 * @param $size Bevel size.
 */
@mixin bevel--full($size: 12px) {
  clip-path: polygon(
      0% $size,
      $size 0%,
      calc(100% - #{$size}) 0%,
      100% $size,
      100% calc(100% - #{$size}),
      calc(100% - #{$size}) 100%,
      $size 100%,
      0 calc(100% - #{$size})
  );
}
Enter fullscreen mode Exit fullscreen mode

The only caveat? None. My code is divine.

Just kidding. Outlines. Outlines are annoying to implement. Here is some example Sass for beveled buttons, outline style included:

.button {
  color: white;
  background-color: turquoise;
  display: inline-block;
  padding: 1em;
  @include bevel--opp();

  &--alt {
    @extend .button;
    @include bevel--opp($alt: true);
  }

  &--outline {
    @extend .button;
    background-color: white;
    padding: 0;

    .button {
      background-color: black;
      margin: 1px;
      padding: calc(1em - 1px);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Not as bad as the pseudo-elements, but still not a Van Gogh. Regardless, the HTML isn’t too bad either:

<!-- Standard Button -->
<div class="button">Button</div>

<!-- Alternative Button -->
<div class="button--alt">Button</div>

<!-- Fancy Outline Button -->
<div class="button--outline">
  <div class="button">Button Out.</div>
</div>
Enter fullscreen mode Exit fullscreen mode

Overall, I think this solution works best. It applies to flexible and fixed elements. You don’t need to worry about scrollable div elements or overflow. And the code is simpler than the other solutions I’ve mention.

Wrap Up

Those were three ways to implement beveled corners with Sass. Of course, I’m going to recommend using clip-path since that’s the approach I’ve taken with my projects. But, at the end of the day, if any of these approaches work for you, that’s great!

If you’ve found another, scarier, way to implement beveled corners, please let me know. I love to see nightmarish spaghetti code for something as mundane as a border style.

P.S. In case you’re from the future and one of the example websites has been updated to no longer use beveled corners, here are Wayback Machine links for borderlands.com, marvel.com and whereismaurice.com. I hope you are doing well future person.

Discussion (0)