Responsive Web Apps with vw and vh

When it coms to response web design media queries with fixed breakpoints are quite nice to adjust sites to different screen sizes. But what about font sizes?

When it coms to response web design media queries with fixed breakpoints are quite nice to adjust sites to different screen sizes.

But what about font sizes?

Many times they will change from one fixed size to another at a certain trigger point, as does this web site. This makes sense of apps that change their layout at that point anyway. However I’d call that unnecessary micromanagement. Especially for apps that have a strong focus on fonts, as it has been the case with my most recent UI for Kings (You can download it from Google Play).

Screenshot

Enter vw and vh! These two CSS size units are 1/100th of the viewport width and height respectively. All measures in this app are specified in either vw or vh. Here is a taste:

.app h1 {
  font-size: 9vw;
  line-height: 10vw;
  margin: 5vw 0;
}

In fact it follows very strict rules that tend to work really well for this kind of (typography-heavy) UI:

As long as the width is greater than the height use vw. As soon as the height becomes greater than the the width use vh.

This is done via a media query:

@media screen and (min-aspect-ratio: 1/1) {
  .app h1 {
    font-size: 9vh;
    line-height: 10vh;
    margin: 5vh 0;
  }
}

Note that there is no visual break point as vw and vh are the same size when this media query “triggers”.

Unfortunately vw and vh are not supported by every browser. However there is a neat code snippet by Roman Rudenko that simulates vw with rem and some JavaScript. rem uses the root element’s font size as a reference. So 1rem is equal to the font size of the root element, 2rem double that and so on.

However I needed to adjust the snipped a bit to take into account vh as well:

(function (doc, win) {
    var docEl = doc.documentElement;
    var recalc = function () {

      var mq = win.matchMedia("(min-aspect-ratio: 1/1)");
      var reference;

      if (!mq.matches) {
        reference = docEl.clientWidth;
      } else {
        reference = docEl.clientHeight;
      }

      docEl.style.fontSize = (reference/100) + 'px';
      docEl.style.display = "none";
      docEl.clientWidth; // Force relayout - important to new Androids
      docEl.style.display = "";
    };

    if (!win.matchMedia) return;
    if (!doc.addEventListener) return;

    var hasSupportFor = function (unit) {
      var div = doc.createElement('div');
      div.setAttribute('style', 'font-size: 1' + unit);

      return (div.style.fontSize == '1' + unit);
    };

    if (hasSupportFor("vw") && hasSupportFor("vh")) return;
    if (!hasSupportFor("rem")) return;

    win.addEventListener('resize', recalc, false);
    recalc(); // make sure the DOM is loaded at this point.
})(document, window);

What this does is to make sure the font size of the root element is always set to 1/100th of either the view port width or height. This depends on the result of the media query.

In order for this to work we need to change our initial css to:

.app h1 {
  font-size: 27px;
  font-size: 9rem;
  font-size: 9vw;

  line-height: 30px;
  line-height: 10rem;
  line-height: 10vw;

  margin: 15px 0;
  margin: 5rem 0;
  margin: 5vw 0;
}

This uses a simple fall back trick. Browsers that support vw/vh will simply override the previous statements. Browsers that don’t support vw/vh will ignore these statements.

For Browsers that support neither vw/vh nor rem I provide a rough pixel value. It turned out that a factor of 3 is ok for mobile phones but is to small for tablets. However it’s still readable and doesn’t break the layout.


© 2022 Florian Klampfer

Powered by Hydejack v9.1.6