Hola a todos! That’s “Hello everyone!” in Spanish 😁 (yeah, I’m originally from Medellín, Colombia).

I’ve always said: “With a solid typography system, you can get away with zero images on your website”.

And I’m going to show you how to do just that .

Approach

I am a designer, not a developer, so in this tutorial there is a combination of basic math and “it-feels-just-right”, not intricate explanations or convoluted formulas.

Explanations are laid-back and simple with a friendly tone, just like I would explain things to my 8-year old son … see? lol.

Alright, let’s Rock n’ Roll!

Current Problems When Dealing With Typography

To give some context to this tutorial, let’s list a few problems we currently face when dealing with typography on the web:

Font sizes are too small or too big for screen. Create too many media queries only to control font sizes. Create too many font-size declarations to control font sizes. Lose track of the parent-child relationship when declaring font sizes. Anything else you’d like to add?… hehe

Main Objective

To create a solid and 100% responsive typography system based on a Modular Scale, for users to have a pleasant reading experience at any viewport.

Ok, So What’s a Modular Scale?

To quote one of the web typography gurus (and one of the creators of the tool we’re going to use) Tim Brown :

“A modular scale is a sequence of numbers that relate to one another in a meaningful way.”

In other words, a Modular Scale is made of numbers that in one way or another are related to each other.

When you get the chance, check out Tim’s article More Meaningful Typography on A List Apart.

How to create a Modular Scale?

For this we’re going to head over to modularscale.com. DON’T FREAK OUT! Give me a minute to explain .

This awesome tool is actually very easy to use. On the left column we see a couple of fields called Bases and Ratio, and on the right several font sizes with the phrase ‘The quick brown fox jumps over the lazy dog’.

Let’s create our Modular Scale:

Step 1: Define your base font size

On the Bases section we’re going to define our base font size of our project. It’s very common to use 16px as a base font size since most popular browsers use this value as their default.

Side note: Actually not all browsers set their default font size at 16px , so make sure to test.

I recommend you use relative units like em rather than absolute units like pixels, albeit, you can use pixels if you wan to create a Modular Scale.

Using em for font sizes is a common practice, and in this tutorial we are defining 16px as our base font size. This means that 16px = 1em . So let’s go ahead type 1em in the first Bases input field.



First Base value: base font size

Step 2: Define a second Base value

Click on the plus icon overlapping the first input field where we typed 1em . That creates a second input field. In it you can type anything you want. But remember the definition of Modular Scale that says “…numbers that relate to one another in a meaningful way.”? Yep, gotta keep that in mind

We’re going to find a number that in some way is related to our project. For this second number I often use the maximum width of the main container of my website. Since I usually design for 1280px screen width, I’ve found that a container with a maximum width of 1140px works very well:

It’s wide enough to design on.

Paragraphs are not too long and not too short if they need to expand the maximum width of that container.

And at 1280px-width screens there is enough white space on the sides for the website to “breathe”.

Let’s add 1140 to the second input field in the Bases section.

Side note: After typing 1140 , refresh the page and you’ll see that the application adds the unit em . Actually, it adds the unit to any values we use. That’s Ok because we want all the values to have the same units so the math that happens behind the scenes works correctly.



Second Base value: max width of our main container.

Step 3: Define a third Base value

For the third meaningful Base number we can use 12 , that’s because it’s a common practice to use a 12 column grid for layout. If your grid uses 16 columns then yeah, use that number.

Remember what the purpose is of all the numbers we use…? “…numbers that relate to one another in a meaningful way.”

Side note: More than three Base values is certainly an option and it may or may not yield a better Modular Scale. I personally haven’t had the need to use four or more Base values. If you want to explore that option, go for it, but for the purpose of this tutorial we’re going to stick with three Base values.



Third Base value: layout grid columns (12 columns for a grid is a common practice).

Step 4: Define a Ratio

The Ratio is what brings all the Base values together in a meaningful scale. The Modular Scale tool offers a great list of musical ratios that have been established to help build meaningful harmonies in music. We can use any of the predefined Ratios, or we can come up with our own.

The Ratio that works best with our Base numbers is 1.3 .

Keep reading to understan why 1.3 is the best Ratio for this Modular Scale.



Ratio, brings all Base values together to create a meaningful scale.

The Smooth Curve of Our Modular Scale

After creating many modular scales with this tool, I’ve learned that the best Modular Scale is the one with the ‘smoothest curve’ providing us with enough contrast between font sizes.

If we leave the default Ratio of 1.5 , the curve of our Modular Scale looks like this:



Curve not very smooth if you ask me.

What we’re trying to accomplish is a smooth curve in the scale, so with our current Base values of 1em , 1140em and 12em and a Ratio of 1.3 , our Modular Scale curve looks like this:



Now this is a smooth curve with enough contrast in the font sizes.

Nonetheless, the “smooth curve” idea is not something you have to abide by, at all. If you prefer a more pronounced curve with even more contrast in the font sizes, that is also totally valid.

Ok, here’s the link to the Modular Scale we just created:

https://www.modularscale.com/?1,1140,12&em&1.3

Side note: Notice that the Base values and Ratio are visible in the URL, pretty clever from Scott Kellum and Tim Brown, the developers of the app.

Create a Responsive Font

Let’s leave our Modular Scale on the back burner for moment and let’s start working with CSS… yeah, I know, this is the part most of you were waiting for, eh? lol.

Alright, let’s do this.

Create a base HTML

First we need to create some HTML, doh! lol. Let’s create headers from <h1> to <h6> and short paragraphs under each one. Let’s also wrapp all those headings and paragraphs in a container: <main class="main-ctnr">…</main> .

I’m going to use CodePen for this (oooobviously! hahaha). Use this anonymous demo to get you started.

If you don’t want to open the demo, no worries, here’s what it looks like:



CodePen demo with base HTML.

Now that we have our base HTML in place, let’s add this CSS:

body { font-size: calc(12px + 1vw); }

Side note: Browsers that do not support the calc() CSS function, will ignore the above declaration and fall back to their defult font size. I think you’re seeing the next phrase coming…: Make sure to test! .

That’s it! All hail the mighty power of the calc() CSS function! lol.

We now have fully responsive font, go ahead and expand and contract the viewport of the demo, you can see how the font grows and shrinks. Cool, eh?



Responsive font. Notice how the font size changes/adapts to the different viewport widths.

The values used in the calc() function are arbitrary, they’re just a starting point. However, the units used, the order and the operation ( px + vw ) ARE REQUIRED for a responsive behavior.

Side note: Times New Roman drives me crazy, lol. Let’s change it to Arial/Helvetica at least. The rule for the body should now read:

body { font-family: Arial, Helvetica, sans-serif; font-size: calc(12px + 1vw); }



Changed the font to Arial from Times New Roman, saaaaweet!

Side note: We could have used the font shorthand property. However, I prefer to leave the two declarations separated. This will help us later on when we 'wiggle' and play around with the font-size values. We can do this while keeping the font-family the same and it will be obvious which values work best.

Keep reading, you’ll see what mean in a bit .

Make the main container 1140px wide

To start pulling things together, we’re going to create a rule for the main container. We give it a width of 100%, a maximum width of 1140px and center it in the viewport:

.main-ctnr { width: 100%; /* For small screens */ max-width: 1140px; /* For large screens */ margin: auto; /* Center the container in the viewport */ }

Side note: Many people like to use margin: 0 auto; , which accomplishes the same thing as margin: auto; . I’ve used the second notation my entire career to center the main container of a website without any side effects whatsoever. The less I type, the faster I move on to more relevant work. Use the notation you feel comfortable with, it’s all a matter of personal preference.

Side note 2: Technically speaking, the declaration width: 100%; above isn’t required since block level elements are by default 100% wide. However, I included it for good measure to be 100% sure that the main container will always be as wide as the viewport if it gets below 1140px . Seriously, no pun intended, haha.

Side note 3: We could have use an element selector like main {…} for the above rule, but I decided to use a class slector .main-ctnr {…} to minimize specificity issues down the road.

Remember that we’re designing for a 1280px screen resolution, so 1140px is a good maximum width.

Ok, the entire CSS should look like this so far:

body { font-family: Arial, Helvetica, sans-serif; font-size: calc(12px + 1vw); /* Responsive base font size */ } .main-ctnr { width: 100%; /* For small screens */ max-width: 1140px; /* For large screens */ margin: auto; /* Center the container in the viewport */ }

Set the Viewport at 1140px wide

It’s very important to note that before we start working on any modifications to the font size values and bring in the Modular Scale, we should have our viewport width at the same width as the max-width of our main container.

Boy, that reads pretty weird, hehe. I’ll explain:

Our main container has a max-width: 1140px so we set our viewport to 1140px . In other words, if your main container is 960px then the viewport of your browser should be 960px as well. Otherwise the font size will not be the proper sizes in either small screens or large ones.

By setting the browser viewport at the same width of the main container, we will end up with responsive font sizes that are the right size at both small and large screens.

In the CodePen demo go ahead and make the viewport 1140px wide.

Timeout: Viewport Tooltip Arrrggghhh!

CodePen has a little tooltip that displays the width of the viewport, you can see it when you resize it:



CodePen’s viewport tooltip hiding, wwwwwwwwhy! lol. CodePen, I love you!

But there’s a small problem with CodePen’s viewport tooltip (it drives me nuts too, haha): It disappears when you stop resizing the viewport, so in order to remember our viewport size we actually have to resize it again to make the tooltip reappear. Then it goes away. Arggh! lol.

So, I have this script that a friend of mine helped me create, that adds a small box at the bottom left of your page with the dimensions of the viewport.

Just add this script in the JS editor of the demo:

/* Script to display the viewport size when working on responsive stuff. Adpted to vanilla JS by: Taylor Hunt - https://codepen.io/tigt/ */ var el = document.createElement("output"); document.body.append(el); Object.assign(el.style, { position: "fixed", bottom: 0, left: 0, background: "red", color: "white", padding: "5px", fontSize: "11px", opacity: 0.7 }); function updateOutput() { var html = document.documentElement; el.value = html.clientWidth + " × " + html.clientHeight; } window.addEventListener("resize", updateOutput); updateOutput();

Notice the small red box at the bottom left of the viewport:



Adding the viewport script to the CodePen demo.

Wiggle Time!

Ok, we are now entering the world of design where things are more “visceral” than mathematical, where the “how you feel about it” is more important than “the numbers don’t lie!”.

What I mean by this is that we now need to tweak (a.k.a. “wiggle”) the font size values we declared earlier in the body in order to match the 16px size of our body font.

This process is totally visual 🤷.

Our initial font size declaration was font-size: calc(12px + 1vw) . We can modify either value, you can experiment in the demo if you want. But I’m going to wiggle the 1vw value to find out which number makes the body font size look as similar as possible to 16px .

That value is: .35vw .

So our font size declaration should now look like this: font-size: calc(12px + .35vw);

And the entire CSS now looks like this:

body { font-family: Arial, Helvetica, sans-serif; font-size: calc(12px + .35vw); /* Responsive base font size */ } .main-ctnr { width: 100%; /* For small screens */ max-width: 1140px; /* For large screens */ margin: auto; /* Center the container in the viewport */ }

Here’s how adding and then breaking the font-size: 16px; declaration makes practically no difference (only 1px wider) in font size using font-size: calc(12px + .35vw); :



Testing the new vw value to determine that the font size is practically identical to 16px .

Bring the Modular Scale Now Already!

Aight! Sheesh!, lol.

Based on the Modular Scale we created:

The h1 is going to be = 1.912em

is going to be = The h2 = 1.616em

= The h3 = 1.471em

= The h4 = 1.3em

= The h5 = 1.243em

= And the h6 = 1.132em

Remember that our paragraphs are simulating 16px font size via the font-size: calc(12px + .35vw); declaration in the body rule. Makes sense?

The complete CSS should now look like this:

/* Modular Scale located here: https://www.modularscale.com/?1,1140,12&em&1.3 */ body { font-family: Arial, Helvetica, sans-serif; font-size: calc(12px + .35vw); /* Responsive base font size */ } .main-ctnr { width: 100%; /* For small screens */ max-width: 1140px; /* For large screens */ margin: auto; /* Center the container in the viewport */ } h1 { font-size: 1.912em; } h2 { font-size: 1.616em; } h3 { font-size: 1.471em; } h4 { font-size: 1.3em; } h5 { font-size: 1.243em; } h6 { font-size: 1.132em; }

Side note: For good measure, I added a comment at the top showing the link to the Modular Scale we created in case we need to reference it again in the future.

This is how our newly created typography system scales at different viewport widths:



Fully responsive typography system using our Modular Scale

Now we have a full blown, high quality typography system in place based on a meaningful Modular Scale.

And that’s it!

…

…

…or is it?

There’s something critical missing here, something actually quite important in a typography system: a Vertical Rhythm.

Vertical Rhythm

Let me quote Mr. Chris Peak to explain what vertical rhythm is (if you don’t already know ):

Vertical Rhythm is simply when a body of text is aligned to evenly spaced horizontal lines (think of your lined paper from grade school), making it more cohesive and easier to read.

I STRONGLY recommend you read his 2012 article Vertical Rhythm In Typography . Yeah, it’s still totally relevant today, nothing in that subject has changed since then. And by the way, he talks about the Modular Scale there as well .

Vertical Rhythm is accomplished by manipulating the line heights in the text. And well, go figure, there’s a line-height CSS property after all specifically for this!

Let’s add a responsive Vertical Rhythm to our already robust typography system.

Creating a Responsive Vertical Rhythm

As I said in this comment , it’s a common practice in typography to declare your line height between 120% - 150% of your base font size.

For this tutorial I’m going to use 150% of my base font size which is 16px . Yep, that’s an arbitrary decision, because as designer I “feel” that I prefer to work with too much white space than with too little, and tweak things until they feel right. Welcome to the world of design! Hahaha!

So: 16px + 150% = 24px .

Now, we need to turn those 24px into em , so we do:

24px ÷ 16px = 1.5em

Then in our CSS we would add a line-height property:

body { font-family: Arial, Helvetica, sans-serif; font-size: calc(12px + .35vw); /* Responsive base font size */ line-height: 1.5em; }

Side note: It’s best practice to declare your line-height with a unitless value . The reason for that is because using a unitless value allows the line height to proportionally increase/decrease in relation to our base font size if the base font size were to change.

There’s a problem though?… Any ideas?… Yep, the line height is not responsive. Yet.

To make it responsive, we’re going to “wiggle” the vw unit in the line-height value until we can visually tell it matches 1.5em .

We now set our viewport in the CodePen demo to 1140px and add this declaration to the CSS: line-height: calc(12px + 1vw); . The body rule now looks like this:

body { font-family: Arial, Helvetica, sans-serif; font-size: calc(12px + .35vw); /* Responsive base font size */ line-height: calc(12px + 1vw); /* Responsive Vertical Rhythm */ }

After wiggling the 1vw for a bit, we can see that a value of 1.05vw exactly resembles 1.5em .

Now the body rule looks like this instead:

body { font-family: Arial, Helvetica, sans-serif; font-size: calc(12px + .35vw); /* Responsive base font size */ line-height: calc(12px + 1.05vw); /* Responsive Vertical Rhythm */ }

Our body copy (paragraphs) now have a responsive Vertical Rhythm, as you can see the line height incrases/decreases depending on the width of the viewport.

However, our headings have some issues, look:



Paragraphs have great responsive line height, but our headings are messed up!

Let’s fix those suckers! lol

Responsive Vertical Rhythm for the Headings

First, let’s fix the line height, as you can see the descenders are touching the characters below and overlapping with the ascenders. Yuck! haha .

After ‘wiggling’ the values for a couple of minutes on all headings, this is how the CSS looks now:

h1 { font-size: 1.912em; line-height: calc(18px + 1.8vw); /* Responsive Vertical Rhythm */ } h2 { font-size: 1.616em; line-height: calc(18px + 1vw); /* Responsive Vertical Rhythm */ } h3 { font-size: 1.471em; line-height: calc(18px + .7vw); /* Responsive Vertical Rhythm */ } h4 { font-size: 1.3em; } h5 { font-size: 1.243em; } h6 { font-size: 1.132em; } h4, h5, h6 { line-height: calc(18px + .2vw); /* Responsive Vertical Rhythm */ }

Side note: Headings h4 , h5 and h6 share the same line-height since at their smaller font size a line-height: calc(18px + .2vw); appears to work just fine since the text is perfectly readable.

Check out the demo:



Voilà, headings look awesome with their responsive line height.

Responsive Top and Bottom Margins for the Headings

And to finalize our responsive Vertical Rhythm, let’s add responsive margins to the top and bottom of all the headings.

Since we now know that our responsive line-height value for our base font size is calc(12px + 1.05vw) , all we need to do is use that same value in a margin short-hand property for all the headings, like this:

h1, h2, h3, h4, h5, h6 { margin: calc(12px + 1.05vw) 0; /* Responsive margins */ }

The first value calc(12px + 1.05vw) is for the top and bottom margins. The second value 0 is for the left and right margins.

What About Users That Resize Their Browser’s Default Font Size?

Covered. This responsive font method works beautifully in such situations as well, check it out:



Changing font size with responsive font system in place.

And we are done! Woooohooo! lol

This is how the entire, complete CSS looks like:

/* Modular Scale located here: https://www.modularscale.com/?1,1140,12&em&1.3 */ body { font-family: Arial, Helvetica, sans-serif; font-size: calc(12px + 0.35vw); /* Responsive base font size */ line-height: calc(12px + 1.05vw); /* Responsive Vertical Rhythm */ } .main-ctnr { width: 100%; /* For small screens */ max-width: 1140px; /* For large screens */ margin: auto; /* Center the container in the viewport */ } h1 { font-size: 1.912em; line-height: calc(18px + 1.8vw); /* Responsive Vertical Rhythm */ } h2 { font-size: 1.616em; line-height: calc(18px + 1vw); /* Responsive Vertical Rhythm */ } h3 { font-size: 1.471em; line-height: calc(18px + 0.7vw); /* Responsive Vertical Rhythm */ } h4 { font-size: 1.3em; } h5 { font-size: 1.243em; } h6 { font-size: 1.132em; } h4, h5, h6 { line-height: calc(18px + .2vw); /* Responsive Vertical Rhythm */ } h1, h2, h3, h4, h5, h6 { margin: calc(12px + 1.05vw) 0; /* Responsive margins */ }

This how the finished demo looks like:



100% Responsive Typography System using a Modular Scale 🤘

Check out the CodePen demo here!

100% Responsive Typography System using a Modular Scale

And that my friends, is how we create a 100% Responsive Typography System using a Modular Scale, yeah! 🤘.

Final Words

I hope you enjoyed this tutorial, but more important, that you found it helpful.

Please let me know in the comments any thoughts, suggestions, ideas or corrections that you may have, there’s always room for improvement.

If you found this tutorial valuable, don’t forget to Share it! 👉

Thanks for reading 🙏!

Nos vemos! (See ya!)

—

Ricardo.

PS. HUGE Thanks to my friends Allen May and Taylor Hunt for their help reviewing this article.