Bowhead v0.2.1

Memorable and maintainable design tokens in SCSS.
There are 3 star-gazers on GitHub and it was downloaded 108 times in the last month on npm.

A lovely bowhead whale. I’d love other suggestions! 😅

What?

Bowhead is a small SCSS framework on which to implement your design tokens, spitting out CSS Variables with optional fallbacks.

Why?

Implementing a design system or even just a series of simple design tokens can come with some unpredictable mental overhead. Bowhead aims to reduce that mental overhead by abstracting the specifics of design tokens into human-sensible formats and nomenclature.

This has a positive effect that ranges from giving the colours in your design system fun and memorable names, to the time and effort saved when communicating about these colours with collaborators without getting bogged down by details, because let’s be real: you don’t want or need to memorise the six-character hex value for all your colours, nor does anyone else! Now imagine that scenario when applied to multiple times more design tokens.

Installation

Yoink it from npm:

npm install @chrisburnell/bowhead

You can also just download it directly from GitHub: https://github.com/chrisburnell/bowhead/archive/master.zip

What are types of values?

selector {
property: value;
}

An important first step to using Bowhead is to understand how it organises CSS properties into different types. By and large, this is done by looking at what the expected values for a given property are:

selector {
background-color: #b22222;
color: #3cb371;
outline-color: #d2b48c;

padding: 0.5rem;
margin: 2rem;

display: flex;
align-items: flex-start;
justify-content: flex-end;
}

If we take the above snippet as an example, we can quickly identify two types of values present: colors and measures. Colors typically stand out quite easily, and, historically, developers have done well to assign colors to variables to simplify their use throughout the codebase. Measures (or sizes), on the other hand, are rarely seen reflected as design tokens in CSS despite their frequent prescence in other forms of design tokens, e.g. the space around a logo or iconography is usually defined in a brand's guidelines.

Despite being presented in different formats, we can confidently say that background-color, color, and outline-color expect a color-type value, not just because color is in their names, but because we can interchange the values between the properties and they still make sense.

Measures can take trickier forms to identify and categorise, and I recommend allowing for more measures than you might expect at first and paring it back later. Getting everything categorised is the hard part; swapping tokens later becomes very trivial off the back of this up-front effort. Regardless, I attach any kind of distance-related value to a measure, and, once again, we could interchange any of the values between padding, margin, border-width, or width and the CSS still makes sense.

Extrapolating from here across the vast variety of CSS properties and the types of values they expect, you end up with a map of most properties against value types:

colormeasurealignment
background-color
border-color
outline-color
color
fill
stroke
width
height
padding
margin
border-width
min-width
max-width
align-items
justify-content

With this knowledge under our belt, we can begin to define the design tokens for our particular project by fleshing out what values are available underneath each type:

colormeasurealignment
#b22222
#3cb371
#d2b48c
1em
20px
2px
flex-start
flex-end
center

Usage

ValuesDescription
$bowhead-variable-as-default
(optional)
true (default)
false
Decides whether or not use the CSS Variable or raw value when calling the @v function.
$bowhead-show-fallback
(optional)
true (default)
false
Decides whether or not to show a fallback value for the CSS Variable. Only works when $bowhead-variable-as-default is also true.
$bowhead-generate
(optional)
true (default)
false
Decides whether or not to generate CSS Variables for you.
$bowhead-property-map
(optional)
See below.Defines which types of values each CSS property should map against.
$bowhead-tokensSee below.Defines the design token values, categorised by types of values.

01. Variable As Default

$bowhead-variable-as-default: true;
body {
color: v(color, brick);
}
body {
color: var(--color-brick);
}
$bowhead-variable-as-default: false;
body {
color: v(color, brick);
}
body {
color: #b22222;
}

02. Show Fallback Value

$bowhead-variable-as-default: true;
$bowhead-show-fallback: true;
body {
@include v(color, desert);
}
body {
color: #d2b48c;
color: var(--color-desert);
}
$bowhead-variable-as-default: true;
$bowhead-show-fallback: false;
body {
@include v(color, desert);
}
body {
color: var(--color-desert);
}

When $bowhead-variable-as-default is false, $bowhead-show-fallback has no effect.

$bowhead-variable-as-default: false;
$bowhead-show-fallback: true;
body {
@include v(color, desert);
}
body {
color: #d2b48c;
}
$bowhead-variable-as-default: false;
$bowhead-show-fallback: false;
body {
@include v(color, desert);
}
body {
color: #d2b48c;
}

03. Generating CSS Variables

$bowhead-generate: true;
:root {
--measure-small: 0.5rem;
--measure-medium: 1rem;
--measure-large: 2rem;
--color-brick: #b22222;
--color-plankton: #3cb371;
--color-desert: #d2b48c;
--opacity-alpha: 0.8;
--opacity-beta: 0.5;
--opacity-gamma: 0.2;
--z-index-below: -1;
--z-index-root: 0;
--z-index-default: 1;
--z-index-above: 2;
}
$bowhead-generate: false;

Nothing is generated!

04. Property Map (optional)

$bowhead-property-map is another map that contains mappings from CSS properties (padding-left, border-bottom-right-radius, etc.) to our defined design token "types" (measure, color, etc.), i.e.

$bowhead-property-map: (
width: measure,
min-width: measure,
max-width: measure,
height: measure,
min-height: measure,
max-height: measure,
...
)

If you wish, you can create new mappings or overwrite existing defaults by defining your own property map, e.g.

$bowhead-property-map: (
vertical-align: alignments
);

Where alignments would be one of your design token "types", e.g.

$bowhead-tokens: (
alignments: (
default: baseline,
alternate: middle
),
...
);

Bowhead will merge new types in your defined map into its own defaults automatically! Any that you re-declare will overwrite what exists as a default from Bowhead.

05. Tokens

$bowhead-tokens expects an SCSS map of "types" of tokens. These types could be a measure, color, opacity, z-index, etc.

$bowhead-tokens: (
measure: (
small: 0.5rem,
medium: 1rem,
large: 2rem,
),
color: (
brick: #b22222,
plankton: #3cb371,
desert: #d2b48c
),
opacity: (
alpha: 0.8,
beta: 0.5,
gamma: 0.2
),
z-index: (
below: -1,
root: 0,
default: 1,
above: 2
)
);

Then you’ll have to include Bowhead in your SCSS somehow. You could use Webpack or something like that, or if you’re using npm, the below code snippet should suffice.

Take note that you need to define any of your Bowhead variables ($bowhead-tokens, $bowhead-show-fallback, $bowhead-generate(, $bowhead-property-map)) before importing Bowhead into your SCSS!

$bowhead-tokens: (
...
);
$bowhead-show-fallback: true;
$bowhead-generate: true;
$bowhead-property-map: (
...
);

@import "node_modules/@chrisburnell/bowhead/bowhead";

Finally, you can use either Bowhead's @v function, @v mixin, both, or just the CSS Variables it can spit out. However you use it is totally up to you! :)

.thing {
@include v(background-color, desert);
@include v(color, brick);
border: v(measure, small) solid v(color, plankton);
padding: v(measure, medium) v(measure, large);
@include v(z-index, above);
opacity: var(--opacity-alpha);
// 1. if you just want the raw value, this is not really recommended:
text-decoration-color: map-get(map-get($bowhead-tokens, "color"), "brick");
// 2. this does the same for you:
text-decoration-color: v(color, brick, true);
// 3. so does this, although it includes the CSS Variable too:
@include v(text-decoration-color, brick, true);
}

will generate…

.thing {
background-color: #d2b48c;
background-color: var(--color-desert);
color: #b22222;
color: var(--color-brick);
border: var(--measure-small) solid var(--color-plankton);
padding: var(--measure-medium) var(--measure-large);
z-index: 2;
z-index: var(--z-index-above);
opacity: var(--opacity-alpha);
/* 1 */
text-decoration-color: #b22222;
/* 2 */
text-decoration-color: #b22222;
/* 3 */
text-decoration-color: #b22222;
text-decoration-color: var(--color-brick);
}

Extras

Need a negative value? Use calc():

.thing {
margin-left: calc(#{v(measure, medium)} * -1);
}

Combining values? Same idea:

.thing {
margin-left: calc(#{v(measure, medium)} + #{v(measure, small)});
}

What about multiple values in a function? Just make sure you're using the raw values from the v() function:

.thing {
background-color: rgba(v(color, desert, true), v(opacity, alpha, true));
}

29 Responses

  1. monovoce’s AvatarMichael Bernth
  2. MicheBarks’s AvatarMichelle Barker
  3. hankchizljaw’s AvatarAndy Bell
  4. bulltwitting’s AvatarBill Rafferty
  5. drjecker’s AvatarJennifer Ecker
  6. mpdecamp’s AvatarMatt DeCamp
  7. Epwnaz’s AvatarJess 🏳️‍🌈
  8. fractaledmind’s AvatarStephen Margheim
  9. drjecker’s AvatarJennifer Ecker
  10. MikeRiethmuller’s AvatarMike Riethmuller
  11. bulltwitting’s AvatarBill Rafferty
  12. cooldezign’s Avatartanangular
  13. felipesere’s AvatarFelipe Sere
  14. blobduff’s AvatarBobby Duff
  15. koitaki’s AvatarChristopher Adams
  16. bloqhead’s AvatarDaryn St. Pierre
  17. BrianDMilne’s AvatarBrian Milne
  18. hexagoncircle’s AvatarRyan Mulligan
  19. jhwlls’s AvatarJack Howells
  20. forstar_ikke’s AvatarSashko
  21. murb’s Avatarmaarten brouwers 🇺🇳
  22. thejeffbyrnes’s AvatarJeff Byrnes
  23. jasonleecooksey’s AvatarJason Lee Cooksey
  24. jamcow’s AvatarJamCow
  25. JakeDChampion’s AvatarJake
Replies:
  1. Avatar for chrisburnell.com
    📦 Packaged up a bit of SCSS I’ve been happily using into a thing called Bowhead (it’s a whale, but I totally swear the filesize is tiny) and I’d love your feedback! https://chrisburnell.com/bowhead/
  2. hankchizljaw’s Avatar
    This is exceptionally smart stuff by Chris. Love to see anything related to making tokens easier—especially with Sass.
  3. Avatar for chrisburnell.com
    🚚 Bowhead is now at version 0.1.8, which adds a few things and fixes others, namely cleaning up what files are packaged for npm so as to send as little as necessary. If you're looking for a barebones SCSS solution for managing design tokens or even for a tool that will generate CSS Variables for you, I’d be honoured if you gave it a try: npm install @chrisburnell/bowhead
  4. Avatar for chrisburnell.com
    Over the last couple of months I’ve made some small improvements and additions to 🐋 Bowhead, which is now at version 0.2.1. Since last posting about it, I’ve cleaned up the documentation, added some minor bits and pieces, and used it across a number of projects. The most rewarding has been seeing it being used both “in the wild” and with other developers on professional work.