Requirements

At the outset of the project I had two requirements and both stem from wanting to simplify and automate things (aka laziness).

Changing the body text size should change the size of every other text element.

Solution: Use rem and em units for text elements. Size of the body text should be decided by the viewport.

Solution: Use vw unit for the base font size.

The solutions seem to be no-brainer but after coding and trying out in the browser I found the body text decreased way too rapidly with screen size; it becomes too small to read even at laptop screen sizes. So, I kept the base font size constant at 18px below certain screen size (tablets) and based it on viewport above that screen size.

html { font-size : 18 px ; } @media only screen and ( min-width : $desktop-min ) { html { font-size : calc ( 100 vw * 22 / 1920 ) ; } }

Hurdle #1: Too small body text on smaller screens

The first part was good, 18px worked well at mobile and tablet sizes. But the viewport-based font size for laptop/desktop screen sizes was still too small. So, I had to think up a better solution to better control the font size variation between screen sizes.

I designed the visuals (in Photohop) desktop-first at 1920px and I know 22px worked best for body text. So, I know the font size should proportionately increase from 18px to 22px as the screen size increases from 1025px ($desktop-min, the Sass variable I used) to 1920px. Putting this into the calc() function:

@media only screen and ( min-width : $desktop-min ) { html { font-size : calc ( 18 px + ( ( 100 vw - # { $desktop-min } ) * ( 22 - 18 ) / ( 1920 - 1024 ) ) ) ; } }

The calc() function checks how much the device’s screen size varies from the base screen size of 1024px and adds the proportionate increase in font size to the base font size of 18px. It worked and I guess this step is a no-brainer too.

Hurdle #2: Responsive typographic scale

My reqirement #2 is now settled completely but not #1. I derived the headings, sub headings, supporting text, etc., from the body text using a ratio (1.165). Found the ratio was too big for smaller screens; the headings were too big and even smaller words were breaking up into multiple lines. So, the ratio should change according to the screen size, i.e., the ratio should be responsive.

I wanted the ratio to change fluidly. So, I tried CSS custom properties (CSS variables). I created a custom property to calculate the ratio based on the viewport and then used the ratio to calculate the size of headings and other elements.

: root { --ratio : calc ( 1.1 + ( ( 100 vw - 320 ) * ( 1.165 - 1.1 ) / ( 1920 - 320 ) ) ) ; } h4 { font-size : calc ( 1 rem * var ( --ratio ) * var ( --ratio ) ) ; } figcaption { font-size : calc ( 1 rem / var ( --ratio ) ) ; }

The problem is, it didn’t work. The calc() function requires --ratio to be unitless but here the --ratio calculated based on the viewport has a length unit. I don’t know how to strip of the unit. So, I resorted to Sass mixin. I grouped all the font-size declarations under a mixin named font-size, with the ratio as a variable. Then I passed in a different ratio value for each screen size range using media queries.

@mixin font-size ( $ratio ) { h4 { font-size : calc ( 1 rem * # { $ratio } * # { $ratio } ) ; } figcaption { font-size : calc ( 1 rem / # { $ratio } ) ; } } @media only screen and ( max-width : $mobile-max ) { @include font-size ( 1.1 ) ; } @media only screen and ( min-width : $tablet-min ) and ( max-width : $tablet-max ) { @include font-size ( 1.12 ) ; } @media only screen and ( min-width : 1024 px ) { @include font-size ( 1.13 ) ; } @media only screen and ( min-width : 1280 px ) { @include font-size ( 1.14 ) ; } @media only screen and ( min-width : 1440 px ) { @include font-size ( 1.165 ) ; }

I would’ve loved a fluidly changing ratio but for now I’ll have to make do with this. This is how it looks: