Modular and Dynamic Type Scales with Plain CSS

Zoraida Cabrera-Mieles
HackerNoon.com

--

Type scales can be difficult to maintain, but CSS Custom Properties can help.

If you have ever had to design a type scale, you know that, like grids, it can require quite a lot of calculations. Therefore if not structured properly type scales can also result into messy and difficult code to maintain.

As Mikolaj Dobrucki demonstrates in his CSS Tricks article, the dynamism of Custom Properties makes them an excellent choice for defining responsive and flexible grid styles. However, Dobrucki’s article doesn’t consider that Custom Properties are also a good option for structuring the code for your responsive type scale.

Why not use Sass?

I don’t want to make this into an article about when to use Custom Properties instead of preprocessor variables. If you’re curious about that topic, checkout Michael Riethmuller’s article on Smashing Magazine. Yet I do think it’s important to talk a bit about why I use of Custom Properties here, and the reason is scope.

Unlike Sass variables, Custom Properties are dynamically scoped. As Riethmuller explains in his article:

Custom properties work differently. Where custom properties are concerned, dynamically scoped means they are subject to inheritance and the cascade. The property is tied to a selector and if the value changes, this affects all matching DOM elements just like any other CSS property.

A Strategy Guide To CSS Custom Properties, Michael Riethmuller

When you try to change the value of a Sass variable within a media query, you cannot. Instead, you create a different variable with the same name. Therefore the code that came before it is not changed, and you must re-declare the style again.

For example, if you want to change the color at different breakpoints, this will not work.

$color-change: blue; .sass-color { 
color: $color-change;
}
@media (max-width: 700px) {
$color-change: red;
}

Instead you will need to do this:

$color-change: blue; 
.sass-color {
color: $color-change;
}
@media (max-width: 700px) {
$color-change: red;
.sass-color {
color: $color-change;
}
}

On the other hand, when you change the color of a Custom Property inside a media query, you are essentially changing a CSS property, and what came before it is subject to change per the rules of the cascade.

.css-color { 
--color-change: green;
color: var(--color-change);
}
@media (max-width: 700px) {
.css-color {
--color-change: red;
}
}

If you are a visual person who also likes to experiment (like me!), you can check out this Code Pen and play around with some of the variables and properties.

Check out the dynamic CSS Custom Properties example in Code Pen

So what would the type scale code look like?

Because Custom Properties are rather new and best practices are still being developed, there are a few good ways to approach type scales. This is my approach.

Start of by setting the :root font-size. This will determine the values of rem measurements, which I will be using to assure consistency. If you do not set this value, most browsers will set it to 16px.

:root { 
font-size: 12px;
}

Afterwards we can set the base font-size for our type scale, and a scale value. These will be used to determine how big your smallest fonts are, and the ratio at which your font-sizes will increment. I recommend using a tool like Type-Scale.com to determine your base size and scale values.

:root { 
font-size: 12px;
--base-font-size: 1rem;
--scale: 1.25;
}

Later we can use Calc and Custom Properties to create functions for the type scale logic.

:root { 
font-size: 12px;
--base-font-size: 1rem;
--scale: 1.25;

--p-size: var(--base-font-size);
--h6-size: calc(var(--base-font-size) * var(--scale));
--h5-size: calc(var(--h6-size) * var(--scale));
--h4-size: calc(var(--h5-size) * var(--scale));
--h3-size: calc(var(--h4-size) * var(--scale));
--h2-size: calc(var(--h3-size) * var(--scale));
--h1-size: calc(var(--h2-size) * var(--scale)); }

If you want to use a different font-size for small and large devices, you can simply change the value of --scale and/or --base-font-size inside a media query. Joseph Mueller makes a few recommendations on how to create a scale that varies between devices in hisMedium article "Exploring Responsive Type Scales".

@media (min-width: 900px){ 
:root{
--base-font-size: 1.25rem;
--scale: 1.33;
}
}

Finally declare the sizes and styles for your text elements.

body, p, ul, ol { 
font-size: var(--p-size);
line-height: 1.5;
font-family: Helvetica;
}
h6, h5, h4, h3, h2, h1 {
font-weight: bold;
margin: var(--base-font-size) 0;
font-family: Georgia;
}
h6 {
font-size: var(--h6-size);
}
h5 { font-size: var(--h5-size);
}
h4 {
font-size: var(--h4-size);
}
h3 {
font-size: var(--h3-size);
}
h2 {
font-size: var(--h2-size);
}
h1 {
font-size: var(--h1-size);
}

Check out my Code Pen, to see what this looks like put together.

Check out the type scale code in Code Pen!

Advantages of this code structure

Some of you might be thinking this looks a bit overly complicated, but depending on your project, setting up your code this way can save you a few head aches later. Here’s a few advantages to consider:

Easy to maintain and change. Say I want to change my type scale later on. Instead of changing every single font-size to adjust to my new scale, I can simply change the --base-font value or the --scale value and this will result in a whole new cohesive type scale.

Self-documenting. Now, I don’t suggest skipping more formal documentation if you’re working on a team. However, by structuring your type scale in this way, you only need to refer to the :root declaration block to see the relationship between type sizes.

Separating responsive logic from declarations. By structuring the code this way, you set the logic for different devices on top and later declare the styles. You don’t need to worry about media queries when styling. Therefore, even though your code is longer, it’s also potentially easier to scan and read.

Reusable responsive sizes. Say I have a non-heading text item, such as the text within a button, which I want to have the size of my h6. I can simply reuse the --h6-size custom property.

button { 
font-size: var(--h6-size);
}

Conclusion

While they can take some getting used to, CSS Custom Properties offer a whole new way of structuring your CSS so it’s more readable, modular, and maintainable. The world of Custom Properties is rather new and best practices are still being developed, but I believe responsive typography is a great place to involve them. What advantages or disadvantages do you see with using Custom Properties for setting up type scales?

Originally published at https://www.zoracabrera.com on June 5, 2019.

--

--

Zoraida Cabrera-Mieles
HackerNoon.com

UX Designer/Researcher @ Harvard University. My work ranges from design and user research, to design facilitation, to CSS theming. www.zoracabrera.com.