Sassy Lobotomised Owl

This article 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($measure) {
& > * + * {
margin-top: $measure;
}
}

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($measure: 1em) {
& > * + * {
margin-top: $measure;
}
}

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.

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

$measures: (
large: $measure-large,
medium: $measure-medium,
small: $measure-small
)

:root {
@each $key, $value in $measures {
--measure-#{$key}: #{$value};
}
}

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

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

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 measures 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($measure: small) {
@if not map-has-key($measures, $measure) {
@error "There is no measure named #{$measure} in `$measures`. measure should be one of #{map-keys($measures)}.";
}

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

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 measure (small, medium, or large)…

header {
@include owl(gigantic);
}

We get the following error message in our console:

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

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

30 Responses

  1. Avatar for dletoreyDave Letorey
  2. Avatar for dletoreyDave Letorey
  3. Avatar for piccalilli_Andy Bell
  4. Avatar for hankchizljawAndy Bell
  5. Avatar for mdmostafa312_mdPrince Rasel
  6. Avatar for _ericaErica ☠️ ✨
  7. Avatar for jim_batesonJames Bateson
  8. Avatar for hexagoncircleRyan Mulligan
  9. Avatar for slightly_warpedSlightly Warped
  10. Avatar for justinaveryJustin Avery
  11. Avatar for justinaveryJustin Avery
  12. Avatar for justinaveryJustin Avery
  13. Avatar for chopperoonSimon Kelly
  14. Avatar for chopperoonSimon Kelly
  15. Avatar for chopperoonSimon Kelly
  16. Avatar for danielmunozDaniel ☀
  17. Avatar for heydonworksCreepy Skeleton UI
  18. Avatar for nzjamesnzjames
  19. Avatar for codemacabreMyles Lewando
  20. Avatar for AccudioAlistair Shepherd
  21. Avatar for fabianderijkfabianderijk™
  22. Avatar for heldinzAlice Rose Murphy
  23. Avatar for _kramseegrundlos
  24. Avatar for chrismardell🏡 Christopher Mardell
  25. Avatar for tedly_tedTed Barnes
  26. Avatar for bartmeindersBart Meinders
  27. Avatar for ColmDelaney1Colm 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