CSS - Clipping and masking

The anatomy of visually-hidden

Visually-hidden styles are used to hide content from most users, while keeping it accessible to assistive technology users.

It works because the content is technically visible and displayed — it appears in the accessibility tree and the render tree, both of which are used by assistive technologies — it’s just that the rendered size is zero.

Our industry has largely settled on a standard CSS pattern for this, refined over years of testing and iteration, by many people. This pattern:

Code language: CSS

.visually-hidden {
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}

[…]

This article is not about when or why you would use visually-hidden content. There’s a number of excellent articles that discuss these questions in detail, notably Scott O’Hara’s Inclusively Hidden. But most of them don’t go into much detail about the specific CSS involved — why do we use this particular pattern, with these specific properties? So today I’m going to dissect it, looking at each of the properties in turn, why it’s there, and why it isn’t something else.

Animating with Clip-Path

clip-path is one of those CSS properties we generally know is there but might not reach for often for whatever reason. It’s a little intimidating in the sense that it feels like math class because it requires working with geometric shapes, each with different values that draw certain shapes in certain ways.

We’re going to dive right into clip-path in this article, specifically looking at how we can use it to create pretty complex animations. I hope you’ll see just how awesome the property and it’s shape-shifting powers can be.

Bevelled box corners with CSS and Sass

Say you’re trying to pull off a design effect where the corner of an element are cut off. Maybe you’re a Battlestar Galactica fan? Or maybe you just like the unusual effect of it, since it avoids looking like a typical rectangle.

[…]

I suspect there are many ways to do it. Certainly, you could use multiple backgrounds to place images in the corners. You could just as well use a flexible SVG shape placed in the background. I bet there is also an exotic way to use gradients to pull it off.

But, I like the idea of simply taking some scissors and clipping off the dang corners. We essentially can do just that thanks to clip-path. We can use the polygon() function, provide it a list of X and Y coordinates and clip away what is outside of them.

Code language: CSS

.box {
  --notch-size: 2em;
 
  clip-path: polygon(
    0% var(--notch-size),
    var(--notch-size) 0%,
    calc(100% - var(--notch-size)) 0%,
    100% var(--notch-size),
    100% calc(100% - var(--notch-size)),
    calc(100% - var(--notch-size)) 100%,
    var(--notch-size) 100%,
    0% calc(100% - var(--notch-size))
  );
}

I’ve written a Sass mixin to make this easier to reuse:

Code language: Sass

///
/// Bevel all four corners of an element by the specified amount.
///
/// This is based on the linked article by Chris Coyier.
///
/// @author Matei "Ambient.Impact" Stanca
///
/// @link https://ambientimpact.com/web/snippets/bevelled-box-corners-with-css-and-sass
///
/// @param {Number} $size - The amount to bevel by.
///
@mixin bevel($size) {
  clip-path: polygon(
    // Top left corner points.
    0%          #{$size},
    #{$size}    0%,
    // Top right corner points.
    calc(100% - #{$size}) 0%,
    100%        #{$size},
    // Bottom right corner points.
    100%        calc(100% - #{$size}),
    calc(100% - #{$size}) 100%,
    // Bottom left corner points.
    #{$size}    100%,
    0%          calc(100% - #{$size})
  );
}

You can use it like so:

Code language: Sass

.box {
  // Really simple version:
  @include bevel(1em);
 
  // If you want to use CSS custom properties for browsers that support them,
  // you can do so. The benefits are that these cascade like other CSS
  // properties, so they can be inherited, and modified in real time. This must
  // come after the simple version.
  --bevel-amount: 1em;
  @include bevel(var(--bevel-amount));
}