Last Updated: March 02, 2016
·
24.54K
· larrywilliamson

Dynamic, Responsive font-sizes and spacing using em units and JavaScript

Dynamic responsive font-sizing is something I see missing on many “responsive” websites. Breakpoints are nice but I have better things to do with my time than defining infinite font-size adjustments. (so do you!)

To implement this, we’re going to take advantage of the em font sizing unit in CSS.

Click here to see what I'm talking about:
codepen.io/lawrencealan/full/eJqlu

Text sizing using the em unit

A single em is equal to the current font-size of the closest parent which has a font-size set.

If the font-size of the document is 12pt, 1em is equal to 12pt.
If the font-size of the document is 10px, 0.5em is equal to 5px.

If the font-size of the document is 8px but you have an element with a font-size of 20px, your target element will base it’s font-size off of the closest parent element up the chain with a font-size set. So if you set your target element font-size to 2em, it’s going to hit the 20px font-size’d parent and render at 40px font size.

You can also chain em scaling — watch out for this, it’s easy to overlook:

<div style="font-size:10px">
 <div style="font-size:1.5em">
  a
  <div style="font-size:1.5em">
   b
    <div style="font-size:1.5em">
    c
    </div> 
   </div>
 </div>
</div>

Produces each letter 1.5 times the size of the enclosing div, so the font-size for "a" would be 15px, "b" would be 22.5px, and "c" would be 33.75px.

Other em unit fun

You can also adjust things like, positions, padding, margins, dimensions pretty much anything that accepts a px value in CSS will accept an em size

For instance, if we change the above inline CSS definitions to margin-left instead of font-size, we can get an automatically increased margin for nested child elements!

This sort of thing comes in really handy, especially when you set a minium base pixel size in your dynamic sizing code.

Making the sizing relative, and giving width priority

Using jQuery, we'll get the width and height of the viewport, and if the width is a greater dimension than the height, we'll give available horizontal area priority when calculating the font-size to apply to our elements.

You can have elements inside our "dfs" classed elements that use em font size definitions.

Note that your dfs classed element will have inline font-size and line-height overridden. So you will need an inner element styled with em font sizing. Only font sizes declared in the em unit will resize, pixel font sizes are fixed in size, not relative.

(function ($) {
        var _window = { w: window };

        function adjust_dfs(e) {
            var _fsx, _fsy, _fs, _adj, _n;

            // get the window dimensions
            _window.x = _window.w.innerWidth || _window.e.clientWidth || _window.g.clientWidth;
            _window.y = _window.w.innerHeight || _window.e.clientHeight || _window.g.clientHeight;

            // we get the "base font-size" by dividing into our "core" dimension on 1024x768 and multiplying
            // the result by 16 (initial font size for most 
            var _fsx = (_window.x / 1024) * 16;
            var _fsy = (_window.y / 768) * 16;

            // if width > height, then we get the average font size from width and height calculations
            // otherwise, if the width of the window is less than the height, we use the width based size
            var _fs = (_window.x > _window.y ? ((_fsx + _fsy) * 0.5) : _fsx );

            // our minimum base font-size should be 8px, or whatever you want
            if (_fs < 8) {
                _fs = 8;
            }

            // our maximum base font-size should be 20px, or whatever you want
            if (_fs > 20) {
                _fs = 20;
            }


            // we bring the decimal point down to two places, so our performance doesn't take a hit
            // when trying to calculate text at a size of 8.1294129836928352903862391em 
            _n = parseFloat(_fs, 10).toFixed(2);

            // setup the css definition object once
            _adj = {
                fontSize: _n + 'px'
            };

            // set the base font size onto our dfs class elements                
            $('.dfs').css(_adj);

        }

        // run once on load
        adjust_dfs();

        // run on window resize
        $(window).on('resize', adjust_dfs);

    })(jQuery);

And the HTML:

<html>
  <body>
    <h1 class="dfs">
      <span>Test Heading</span>
    </h1>
    <p class="dfs">
      <span>Static sized text, and also</span> <em>dynamic</em>
    </p>

    <div class="dfs" id="example_div">
    <header>Lipsum.</header>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio sint nesciunt odio ad eaque! Iste quod fugiat debitis culpa quas nobis! Blanditiis recusandae illo nam tempora quod omnis fugiat totam.</p>
  </div>
  </body>
</html>

With CSS (SCSS):

body {
  padding:4px;

  h1.dfs {
    margin-left:2em;

    span {
      font-size:4.8em;
      line-height:0.8em;
    }
  }
  p.dfs {
        margin-left:3em;

    span {
     font-size:10px;
    }
    em {
      font-size:2em;
    }
  }
  #example_div {

    background: #eeeeee;           
    margin:2em;
    padding:1.5em 1em .5em 1em;
    width:24em;
    header {
     font-size:4em;
     line-height:0.8em;

    }
    p {
      font-size:12px;
      text-align:justify;
    }
  }
}

Again, you can see this in action here:
codepen.io/lawrencealan/full/eJqlu