Learn Eleventy

Lesson 22: Global CSS and design tokens

Right, here we are, in the final module of this course. This is actually my favorite part—writing CSS.

Of course I enjoy Eleventy, but I’m a designer by trade, so the part where we make stuff look good is naturally going to be the part I enjoy the most.

If you haven’t already: I strongly recommend that you read this post that I wrote on CUBE CSS.

The main reason is that this is how we’re building the CSS for this site and it’ll be really helpful to be aware of the methodology behind it.

Starter files

The first thing we need to do in this module is apply our starter files, just like we did right at the start of the course. This just gets some of the more boring aspects like configuration out of the way.

Download starter files

Download the starter files and completely replace eleventy-from-scratch/src/scss with the scss folder in the ZIP archive that you downloaded.

Your eleventy-from-scratch/src/scss folder should now look like this:

scss
├── _config.scss
├── _reset.scss
├── blocks
├── critical.scss
└── utilities

Installing Gorko

To make working with the CUBE CSS methodology easier, I created a Sass tool called Gorko. It generates utility functions for us based on a configuration file. This allows us to have a single source of truth for our CSS.

We’ll take a look at various parts of Gorko as we come to them throughout this module, but first, let’s install it.

Open your terminal in eleventy-from-scratch and run the following command:

npm install gorko

Now that’s installed, open up eleventy-from-scratch/src/scss/critical.scss and add the following to the top of the file:

// First up: config and functions
@import 'config';

// Next: pull in gorko for design tokens
@import '../../node_modules/gorko/gorko.scss';

The first thing we do is import our _config.scss file, then grab Gorko out of the node_modules.

A quick tour of your scss folder

I’m going to quickly show you around the current scss folder so you know:

  • what’s going on
  • what the purpose of each file and folder is
  • how it all comes together

During this section, we’re just looking at the code so you don’t need to copy any of it to your project. I’ll let you know when you need to start copying code again.

Configuration

Take a quick look at eleventy-from-scratch/src/scss/_config.scss. Let me explain what’s going on.

The first thing you’ll notice is that there are a lot of comments. Rightly so, too, because this file is the beating heart of our CSS.

The first variable that we see is $gorko-base-size. This is set to 1rem. A REM Unit is a flexible unit that is based on the user’s system font size. If a user has made no changes to their system font size, this will be around 16px. There’s even more complexity around that calculation, but don’t worry, we’ve got your back over on Every Layout where we wrote a heck of a long post about units which you should read.

This base size does exactly what you'd expect it to: it’s the size that everything is based on. The best way to explain that is to show you this:

$gorko-size-scale: (
  '300': $gorko-base-size * 0.75,
  '400': $gorko-base-size,
  '500': $gorko-base-size * 1.33,
  '600': $gorko-base-size * 1.77,
  '700': $gorko-base-size * 2.36,
  '800': $gorko-base-size * 3.15,
  '900': $gorko-base-size * 4.2,
  'major': $gorko-base-size * 5.6
);

This is a size scale. We generate 8 sizes for the scale, which is a perfect fourth scale. This translates into handy utilities, both as utility classes that we apply to HTML and also helper functions in our Sass code.

The whole idea of a size scale is to create harmony and balance. We, as designers have been using typographic scales for centuries to do exactly this. We push this methodology even further now, with CUBE CSS and use that scale to size typography, margin and padding, amongst other things. This makes a huge difference in not only visual harmony, but to the amount of code that we write, too. Generally, working with a system like this results in much less layout code!

After our size scale, we define $gorko-colors, which are pretty self-explanatory. There are 7 different colors all with a glare and shade variant. The glare variant is lighter and the shade variant is darker than the base color.

We then finish up at $gorko-config which defines two key things for us: utility classes and breakpoints. The main thing is the utility classes.

If you look down the config, you’ll end up at this:

'gap-top': (
  'items': $gorko-size-scale,
  'output': 'responsive',
  'property': 'margin-top',
),
'leading': (
  'items': (
    'loose': 1.7,
    'tight': 1.3,
    'flat': 1.1,
  ),
  'output': 'standard',
  'property': 'line-height',
)

For each one of these blocks a utility class is generated. Let’s take the first one, gap-top. It uses the size scale, which means for each size, a utility class is generated, like this:

.gap-top-400 {
  margin-top: 1rem;
}

.gap-top-500 {
  margin-top: 1.33rem;
}

The class uses margin-top because that’s what we define in the property key. There are two options for output, too: standard or responsive.

standard will just generate the utilities as you define them. For example, the leading utility in that example code will only generate 3 utility classes. responsive, however, will generate the same utility for each breakpoint that’s defined in breakpoints. Let’s have a quick look at them.

'breakpoints': (
  'md': '(min-width: 37em)',
  'lg': '(min-width: 62em)',
)

We have two breakpoints here, which directly translate into media queries. For example, md directly translates to this:

@media (min-width: 37em) {
}

Back to the context of our responsive utilities, we get a version for each breakpoint, so the gap-top utilities look more like this:

.gap-top-400 {
  margin-top: 1rem;
}

.gap-top-500 {
  margin-top: 1.33rem;
}

.md\:gap-top-400 {
  margin-top: 1rem;
}

.md\:gap-top-500 {
  margin-top: 1.33rem;
}

.lg\:gap-top-400 {
  margin-top: 1rem;
}

.lg\:gap-top-500 {
  margin-top: 1.33rem;
}

The \ escapes the : for us, which makes it valid CSS. We then apply the classes like this:

<p class="gap-top-400 md:gap-top-500">I have responsive spacing</p>

This means that by default, we use the 400 size in our size scale to space elements, but if the viewport is equal to or greater than 37rem, we use the 500 size instead. Pretty handy, right?

Blocks and utilities

As per the CUBE CSS methodology, we have blocks and utilities. Gorko and our config have at this point generated most of the utilities, but you’ll see that there are a few set for us in eleventy-from-scratch/src/scss/utilities.

These utilities do one job for us, and they do it well. For example, the .wrapper utility creates a horizontally centered container that has a max-width. The .radius utility applies a consistent border-radius to the elements it’s applied to.

Next, we have blocks, which are like components. Blocks are specific CSS, but as you can see in the existing files, like _tags-list.scss, they’re pretty darn lightweight. This is because in most cases, the rest of our CSS has already done the hard work for us. We’ll be writing more complex compositional blocks soon, though.

Critical, global CSS

Now that we’ve had a bit of a tour, it’s time to start writing some code.

From now on in this lesson, you should implement the code samples that we look at.

Open up eleventy-from-scratch/src/scss/critical.scss add the following after the reset, on around line 8:

// Global styles start
:root {
	--flow-space: #{get-size('600')};
}

body {
	background: get-color('light');
	color: get-color('dark-shade');
	line-height: 1.5;
	font-size: get-size('500');

	@include apply-utility('font', 'base');
}

a:not([class]) {
	color: currentColor;
	text-decoration-color: get-color('quinary');
}

:focus {
	outline: 2px dashed get-color('primary');
	outline-offset: 0.25rem;
}

main:focus {
	outline: none;
}

h1,
h2,
h3,
h4 {
	font-weight: 900;

	@include apply-utility('font', 'sans');
	@include apply-utility('leading', 'flat');
}

// High contrast selection because it can help visually-impaired
// folks read the text easier
::selection {
	color: get-color('light');

	// We set this as an RGBA because that’s how you get a solid color, by using 0.99
	// alpha value. Browsers are wild.
	background: rgba(get-color('dark-shade'), 0.99);
}

// Scrollbar styles
::-webkit-scrollbar {
	height: 1rem;
}

::-webkit-scrollbar-track {
	background-color: transparent;
	border: 1px solid get-color('dark-glare');
	border-radius: 0.25rem;
}

::-webkit-scrollbar-thumb {
	background-color: get-color('dark');
	border-radius: 0.25rem;
}

There’s a lot happening here, so let’s do a breakdown:

  1. The first thing we do is set a --flow-space custom property at the root. This is what controls the .flow utility, which we’ll talk about some more in the next lesson.
  2. Next, we apply some styles to HTML elements, like the <body>. Gorko provides us with useful functions and mixins to apply our config values in our code. In this example we’re using get-size, which grabs the value of our size scale, based on the key and then apply-utility calculates the value of a pre-generated utility class, just like our gap-top one from earlier. There’s also a get-color utility, which grabs a color by key, much like get-size.
  3. We then move onto ::selection styles. These are the styles that get applied when a user highlights text with their pointing device, such as a mouse. To provide high contrast, we set a dark background and light text. This is because some users highlight text to make it easier to read, so by setting rules like this, we’re giving them a helping hand.
  4. Finally, we style our scrollbars. This will apply not just to the main scrollbar on the right, but also to the horizontal scrollbars which we’ll be seeing soon.

Now, if you haven’t already, run npm start in your terminal, in the eleventy-from-scratch folder.

When you open up your browser at http://localhost:8080, it should look like this:

A suddenly extremely styled home page that looks pretty darn good

You might be wondering why it looks so styled already? This is the magic of CUBE CSS. You might remember in the first module, all the markup that we added already contained the CSS classes that we are now applying CSS to. Most of those were utility classes that apply design tokens for us, so a lot of the hard work with CSS is already done.

Wrapping up

That was a heck of a tour, right? Now you hopefully understand roughly how the CSS works on this project, you should feel a lot more comfortable during the next few lessons as we apply more.

We’re only just getting warmed up with CSS and there’s a lot more to learn, so don’t fret: there’s plenty of time yet.


Lesson 21: Setting up images Lesson 23: Styling global blocks