a toy ruler
Web Development |

A guide to CSS units — pt. 3: percents, viewports, magic and best practices

Moritz Jacobs

February 5, 2021

tl;dr quick summary
Welcome to our four part series on units in CSS, where we dive into each available unit, explain the relationship between them and try to find out when to use which. We will talk about accessibility, best practices for developer teams and the history of sizing elements on screens. In this part we will talk about percentage based units like viewport units, magic numbers and best practices for sizing.

Did you miss part 1 or part 2 of this series?

One unit used quite often in CSS is %. It is mostly interpreted as percentage fractions of the same property in the parent element. So a width: 66.66% will make an element two thirds of its parent element wide, but a font-size: 150% will increase the currently set font size by 50%. There are a few quirky exceptions, for example, padding-top: 50% does not relate to a parent's padding but to its width. See this article about the evil parts of CSS for more... Also, please do not use them wrong 🙏

Digression: surprise quiz!

Let's see if you can answer this quiz:

*, *:before, *:after {
	box-sizing: border-box;
  /* If this means nothing to you, read here:
     https://www.paulirish.com/2012/box-sizing-border-box-ftw/ */
}

body {
  font-size: 0.75rem;
}

header {
  max-width: 50rem;
  margin: auto;
}

.logo {
  width: 25%;
}
<!DOCTYPE html>
<html>
  <head>

  </head>

  <body>
    <header>
      <img class="logo" src="" alt="" />

      <p>Utility Muffin Research Kitchen™️</p>
    </header>
  </body>
</html>
  • Put in px, how wide would the logo be rendered for the majority of users on a large screen?
  • Put in px, how wide would the logo be rendered for the majority of users on a phone with a screen width of 380px?
  • Put in px, how large would the text in the <p> below the logo be rendered for the majority of users?

Answers (click to reveal):

1. the logo will be 200px wide, one fourth the width of 50rem, which is 12.5rem × 16px = 200px

2. 95px. The header will use 100% of the viewport witdth, so 25% × 380px = 95px

3. 12px — because 0.75rem == 0.75 × the default font size, which is 16px: 16 * 0.75 == 12

Good job! If you want to investigate further, here's a pen.

Viewport units

vw (viewport width) and vh (viewport height) are percent values (0—100) relative to the browser viewport (the browser window excluding the UI). So on a 380px wide phone screen, 50vw would be rendered as 190px, independent of context or parent elements. On desktop browsers these units relate to the inside of the browser window (or in javascript speak, the window).

vmin and vmax are either vw or vh depending on which side of the viewport is larger/smaller. You will rarely use these, but CSS-Tricks has a good example application.

Using percentage based values like 50% or 25vh for widths and heights is great for code readability, because it immediately evokes a sense for how an element is constructed in a design. If your values get too obscure (6.25%, 56,25%, …) you should add a comment ("1/16th" or "aspect ratio 16:9"). Which brings us to the topic of…

Magic numbers

"Magic numbers" (values with unexplained meaning which could/should be replaced with named constants) are a real developer™️'s nightmare: out of nowhere, an unnamed, uncommented padding-left jumps you and bites your face off!

Some project teams deal with this by maintaining gigantic theme files, where every padding gets a name that is ideally (but in reality rarely) reused in multiple places. So your padding-left: 2.25rem just became padding-left: ${theme.sizes.infoBoxLeftPadding}, and you are super confident that when some other developer comes along in a few months they will say "hey! I have the same left padding in my component's design, maybe I can just reuse this". But then later, a design change decreases the left padding for their component and suddenly your original work looks different. Super...

I think of it like this: CSS translates design choices (from a designer's canvas) to the browser. These choices are not always the conclusion of a logical process, they can be mere expressions of taste and experience with aesthetics, typography, colors and so on. It is a good CSS developer's job to distinguish between the declarations that are worthy of being abstracted and globally reused and the local exemptions and quirks of a pretty design.

Your designers use a consistent font size ratio and color scheme? Great! Put them into a theme file and use descriptive names! More importantly: get everybody on the team on board with these names, so they will be reused accordingly. Make a good overview for all of this in your storybook or whatever you use.

A component was designed with a headline size of 1.25rem? Do other components use this size? Is the whole font style (weight, color, line height, letter spacing, etc) maybe worth an abstraction to a sub-component? If the answer to all of these questions is no, I personally just write the size directly into the CSS of that component and don't bother extending the theme file. To me, that's not a magic number, it's just a design choice I have to — and can — live with.

There are magic numbers in CSS, though! Oftentimes you want to make your designer happy and you end up writing something like left: 9% or width: calc(100% - 3.3rem) — values that you found by trial and error. It might be worth thinking about maybe doing it another way. If that's not possible, leave a comment on how to reproduce your decision for the next developer.

Best practices

Some more (totally subjective) tips and tricks for sizing things in CSS:

  • Using rem for most things became common practice, 1rem mostly will be rendered as 16px (remember?). For those reasons it is a good idea for your project team (designers and devs) to stipulate, that widths, lengths, heights and sizes in the design files should always be multiples of 4px, so the rem conversion is easier. 4 is small enough to be flexible and big enough so the decimal fractions don't become too crazy. I acknowledge that font sizes will probably need finer granularity but for everything else, this helps visual balance as well.
  • For border/outline widths and other very small values: use px so they are rendered sharp and not blurry (we will talk more about this in part 4)
  • If you really like thinking in px and using rem makes you uneasy, there's tooling that can help: pxtorem
  • For typography, harmonious type scales can be pretty and make calculations a little easier for devs too!
  • Use a grid (CSS grid preferably) and let the grid do the work of positioning and sizing.
  • Use em in media queries since it will scale your breakpoints with user font size settings (rem does too, but there's bugs in Safari)
  • For widths and heights, try to find suitable, readable relations like 66% (two thirds), 80vw (four fifths) etc.
  • Inline elements (links, buttons, icons, …) are often not explicitly sized. For example a button will often just be as big as it needs to be, depending on it's text content (intrinsic size). In this case we only need to worry about font size and padding.
  • Devs, talk to your designers to find out why they chose to size an element the way they did!
  • It is a good designer's job to be aware of the constraints and the behavior of the web platform. It is not enough to make your design look good on your sketch canvas, if a developer then later has to guesstimate for example how to position a background image. Be explicit and try to construct around grids and relative values. If you find yourself nudging elements around the canvas, at least try to leave a note explaining your choices and why they are important to the design.
  • In other words: talk to each other, don't guess!
  • If you're working within a framework that allows for reusable components, avoid margins for layout spacing and use spacer components instead. Margin can be considered harmful.
  • Whatever you do, try to be consistent within your project.

That rounds up everything I believe in about sizing in CSS. What a ride... Next up, we will finish this series with the rest of the gang: angles, time units and a few surprises!

Header Photo by Markus Spiske on Unsplash

CSS units

viewport units

percent

magic numbers

best practices

Peerigon logo