A Live Developer Journal

reusable dropshadows in sass using mixins

Drop-shadows are a great way to give elements a sense of elevation. The bigger the drop-shadow is, the closer the element appears to be to the user. The smaller the drop-shadow, the closer the element appears to be to the page. Elevation is calculated along the z-axis (like x and y axis).

You can create drop-shadows in css by using the drop-shadow property like in the example below:

drop-shadow: 0 4px 5px 0 rgba(0,0,0,.2);

In the example above, the drop-shadow values represent, in order, an offset-x value, and offset-y value, a blur radius and a spred radius, as well as a color (black with an opacity of 20% in this example).

Level up your drop-shadow game

When most people apply drop-shadows to web elements, they only apply one drop-shadow. This is because when you add a more than one drop-shadow property, the last one overwrites the previous properties because of how the CSS cascade works.

However, if you use the filter property in css, you can apply multiple drop-shadow statements one after the other, and they will all be applied.

Alternatively, you can use SASS mixins to apply multiple drop-shadow statements, which is my preferred method.

Why multiple drop-shadows are important

Look around you and pick any object. Look at the shadows it casts. On first appearance, it might look like it only has one shadow. If you look closely, you will notice multiple layers of shadows.

For example, there is an empty plastic bottle sitting on my desk. There is a darker shadow that is cast very close to the bottle. The edges are pretty solid and follow the shape of the bottle. Then there is a softer shadow that overlays (or underlays) that. The shape is less solid. If the bottle wasn't there, I wouldn't be able to guess what it belongs to. There are also a lot of very light shadows, some of which are cast in opposite directions that are thrown from both the same an different light sources.

If the bottle only had one drop-shadow with no layers, it would look unnatural. So only having one drop-shadow for your element is also unnatural. That being said, it isn't possible to mimic the shadows of the real world closely, so we need to find a balance.

Creating more natural drop-shadows for the web

When creating drop-shadows for our web elements, we want them to look natural even thought it isn't possible to mimic the complexity of shadows in the real world. To acheive this, we can simplify things by using three layers of drop-shadows.

Elevation

Another important consideration when making drop-shadows is elevation. The closer an element is to the page, the smaller its three drop-shadows are. The closer an element is to the viewer (and not the page), the bigger its three drop-shadows are.

I prefer to have ten stages of elevation to choose from, ranging from smallest elevation to biggest elevation. While I may not use all of these levels in one project, I may use them all across all of my projects. Therefore, there is a huge amount of benefit in creating a bunch of reusable box-shadow styles.

10 reusable box-shadow styles

If you don't feel like spending half a day creating your own specific drop-down shadow styles, you can use the ones that I created.

The following 10 styles are not original. They are based on Google's material design guidelines. In fact, I downloaded their material design sketch styleguide and built a responsive web-version of it.

I have written out these styles as sass mixins. To convert them to CSS, just add each of the box-shadow properties one after the other using the 'filter' property.


@mixin elevation-1 {
  box-shadow: 0 1px 1px 0 rgba(black, .14);
  box-shadow: 0 2px 1px -1px rgba(black, .12);
  box-shadow: 0 1px 3px 0 rgba(black, .2);
}
@mixin elevation-2 {
  box-shadow: 0 2px 2px 0 rgba(black, .14);
  box-shadow: 0 3px 1px -2px rgba(black, .12);
  box-shadow: 0 1px 5px 0 rgba(black, .2);
}
@mixin elevation-3 {
  box-shadow: 0 3px 4px 0 rgba(black, .14);
  box-shadow: 0 3px 3px -2px rgba(black, .12);
  box-shadow: 0 1px 8px 0 rgba(black, .2);
}
@mixin elevation-4 {
  box-shadow: 0 4px 5px 0 rgba(black, .14);
  box-shadow: 0 1px 10px 0 rgba(black, .12);
  box-shadow: 0 2px 4px -1px rgba(black, .2);
}
@mixin elevation-5 {
  box-shadow: 0 6px 10px 0 rgba(black, .14);
  box-shadow: 0 1px 18px 0 rgba(black, .12);
  box-shadow: 0 3px 5px -1px rgba(black, .2);
}

.elevation-1 { @include elevation-1; }
.elevation-2 { @include elevation-2; }
.elevation-3 { @include elevation-3; }
.elevation-4 { @include elevation-4; }

Identifying patterns

There are only 5 of the 10 drop-shadows written up above. They are all reusable, but I feel uncomfortable with how much duplication is already here. So I spent a while seeing if there were any patterns to help me rewrite this in a better way

Looking at the first shadow in each of the cases, I can see that the x-offset stays the same at 0. The y-offset value increments by 1 pixel for the first 4 cases, and then by two for the last case. The blur radius increases by 1, then by 2, then by 1, then by 5. The spread stays the same.

The blur radius is the least consistent of these, whilst the y-offset has one jump in pattern. I'll have to look at the last 5 cases to see if the pattern changes here, so will write those up too.

Drop shadow 1/3

Starting with the first dropshadow style for all 10 cases

Okay, so the main pattern is that the values increase, but the increments in which they increase are pretty jumpy. Will make some time to think about how to improve this down the line, but for now the drop shadows are working and reusable.

The remaining 5 drop-shadow cases are:


@mixin elevation-5 {
  box-shadow: 0 6px 10px 0 rgba(black, .14);
  box-shadow: 0 1px 18px 0 rgba(black, .12);
  box-shadow: 0 3px 5px -1px rgba(black, .2);
}
@mixin elevation-6 {
  box-shadow: 0 8px 10px 1 rgba(black, .14);
  box-shadow: 0 3px 14px 2px rgba(black, .12);
  box-shadow: 0 5px 5px -3px rgba(black, .2);
}
@mixin elevation-7 {
  box-shadow: 0 9px 12px 1px rgba(black, .14);
  box-shadow: 0 3px 16px 2px rgba(black, .12);
  box-shadow: 0 5px 6px -3px rgba(black, .2);
}
@mixin elevation-8 {
  box-shadow: 0 12px 17px 2px rgba(black, .14);
  box-shadow: 0 5px 22px 4px rgba(black, .12);
  box-shadow: 0 7px 8px -4px rgba(black, .2);
}
@mixin elevation-9 {
  box-shadow: 0 16px 24px 2px rgba(black, .14);
  box-shadow: 0 6px 30px 5px rgba(black, .12);
  box-shadow: 0 8px 10px -5px rgba(black, .2);
}
@mixin elevation-10 {
  box-shadow: 0 24px 38px 3px rgba(black, .14);
  box-shadow: 0 9px 46px 8px rgba(black, .12);
  box-shadow: 0 11px 15px -7px rgba(black, .2);
}