Sassy Lobotomised Owl

This article, part of the writing collection, was published on and last updated on .

Managing spacing between elements and components on your page can be a tiring task if undertaken manually. This is where the lobotomised owl comes in: a short, simple snippet of CSS that simplifies this whole process for you. In this article I’ll explain how I make use of it in a more dynamic way using a SCSS mixin.

This post expands on concepts and a mixin from a previous post, Variables for Both, and a more recent project, Bowhead, which I recommend you to read if you’re interested in the context around how the v mixin is used.

Skip to the full owl mixin solution.

The lobotomised owl technique, given graciously to us by the incredibly talented Heydon Pickering, takes away a great deal of pain that comes with setting up sensible spacing between elements and components on your page. Instead of specifically defining margin-bottom/margin-top for each component, we’ll make use of the * + * selector in CSS to perform the following:

For every direct child element of X which is not the first direct child of X, apply a margin-top.

And almost as if by magic, you’ll have a robust spacing system in place. All you need to do is decide which elements X can represent, and what the value of margin-top is going to be.

Working Backwards

Let’s say our goal is to end up with the following CSS output:

body > * + * {
margin-top: 4em;
}

main > * + * {
margin-top: 2em;
}

article > * + * {
margin-top: 1em;
}

Let’s start things off by pumping the excitement all the way up to 3. At its simplest, the mixin looks like this:

@mixin owl($size) {
& > * + * {
margin-top: $size;
}
}

In fact, if we know that more often than not we'll be using a specific value, we can use a default parameter value, like so:

@mixin owl($size: 1em) {
& > * + * {
margin-top: $size;
}
}

And we can use it like so:

body {
@include owl(4em);
}

main {
@include owl(2em);
}

article {
@include owl;
}

But now let’s take the excitement up to to 4.

Using the same concepts, and, in particular, the v mixin that I introduced in Variables for Both and have refined recently with a project called Bowhead, we need to set up some SCSS variables and assign them within a Map so that we can iterate through them and reference them using our chosen familiar words—small, medium, and large in this case.

$size-large:  4em;
$size-medium: 2em;
$size-small: 1em;

$sizes: (
large: $size-large,
medium: $size-medium,
small: $size-small
)

:root {
@each $key, $value in $sizes {
--size-#{$key}: #{$value};
}
}

And using Bowhead I can now rewrite my owl mixin with the above configuration in place.

@mixin owl($size: small) {
& > * + * {
@include v(margin-top, $size);
}
}

In congruency with the methodology behind Bowhead, we’ve now abstracted away the need to remember or look up the numeric values for the various sizes you might be using, and can instead refer to them as you might think about them or speak about them—using words like small, medium, large, and so on:

body {
@include owl(large);
}

main {
@include owl(medium);
}

article {
@include owl;
}

The Solution

@mixin owl($size: small) {
@if not map-has-key($sizes, $size) {
@error "There is no size named #{$size} in `$sizes`. size should be one of #{map-keys($sizes)}.";
}

& > * + *
{
@include v(margin-top, $size);
}
}

body {
@include owl(large);
}

main {
@include owl(medium);
}

article {
@include owl;
}

Because we’ve included some error-checking within the mixin, if we were to attempt to pass a parameter to the mixin that does not map to a defined size (small, medium, or large)…

header {
@include owl(gigantic);
}

We get the following error message in our console:

Error: There is no size named gigantic in `$sizes`. size should be one of small, medium, large.

And with that, I’m calling it a day!

27 Responses

  1. Avatar for dletorey Dave Letorey
  2. Avatar for piccalilli_ Andy Bell
  3. Avatar for hankchizljaw Andy Bell
  4. Avatar for mdmostafa312_md Prince Rasel
  5. Avatar for _erica Erica ☠️ ✨
  6. Avatar for jim_bateson James Bateson
  7. Avatar for hexagoncircle Ryan Mulligan
  8. Avatar for slightly_warped Slightly Warped
  9. Avatar for justinavery Justin Avery
  10. Avatar for justinavery Justin Avery
  11. Avatar for chopperoon Simon Kelly
  12. Avatar for chopperoon Simon Kelly
  13. Avatar for danielmunoz Daniel ☀
  14. Avatar for heydonworks Creepy Skeleton UI
  15. Avatar for nzjames nzjames
  16. Avatar for codemacabre Myles Lewando
  17. Avatar for Accudio Alistair Shepherd
  18. Avatar for fabianderijk fabianderijk™
  19. Avatar for heldinz Alice Rose Murphy
  20. Avatar for _kramsee grundlos
  21. Avatar for chrismardell 🏡 Christopher Mardell
  22. Avatar for tedly_ted Ted Barnes
  23. Avatar for bartmeinders Bart Meinders
  24. Avatar for ColmDelaney1 Colm Delaney
Replies:
  1. Avatar for dletorey
  2. Avatar for hankchizljaw
    This is a fantastic post by my pal, Chris Burnell. The lobotomised owl is a staple of my CSS (thanks, Heydon) and this handy Sass mixin will undoubtedly find its way into my projects! chrisburnell.com/article/sassy-… microblog.hankchizljaw.com/1579518557/
  3. Avatar for matpassmore