DEV Community

Cover image for Relative Units in CSS: A Comprehensive Guide
Mustapha Aouas
Mustapha Aouas

Posted on

Relative Units in CSS: A Comprehensive Guide

In the ever-evolving world of web development, creating flexible, responsive, and accessible designs is crucial. One of the most powerful tools in a developer's CSS toolkit is the use of relative units.
These units allow our designs to be more flexible, responsive, and accessible. In this article, we'll dive deep into the world of relative units, exploring how to use them effectively and why they're so crucial in modern web development.

Why Does It Matter?

Before we delve into the specifics, let's understand why relative units are so vital in modern web development:

  • Responsiveness: Relative units are the backbone of responsive design, allowing elements to scale fluidly across different device sizes.
  • Accessibility: Using relative units for font sizes ensures that text remains readable when users adjust their browser's default font size.
  • Maintainability: With relative units, global layout changes often require updating just a few values, rather than dozens of hardcoded pixel values.
  • Consistency: Relative units help maintain proportional relationships between elements, even as the overall scale changes.

Let's explore the various types of relative units and how to use them effectively.

Ems and Rems

The two most commonly used relative units are em and rem. Both are based on font size, but they behave differently in important ways.

Understanding Ems

An em is a unit relative to the font size of the element on which it's used. Here's a detailed example:

.parent {
  font-size: 16px;
}

.child {
  font-size: 1.5em;      /* 24px (16px * 1.5) */

  padding: 1em;          /* 24px */
  border-radius: 0.25em; /* 6px */
}
Enter fullscreen mode Exit fullscreen mode

In this example, all the child element's properties are relative to its own font size. This creates a scalable component where all properties maintain their proportions if the font size changes.

Typically, ems are used for setting font sizes, but as you saw in the example above, their potential extends far beyond that. By using ems, you can define many properties of an element and then easily scale the entire thing up or down with a single change to the font size. This flexibility makes ems incredibly useful for various properties, not just fonts.

A Potential Pitfall

Ems can be tricky when used for defining font sizes of nested elements, as they compound.
Let's look at an example:

<section>
<ul>
  <li>First level item</li>
  <ul>
    <li>Second level item</li>
    <ul>
      <li>Third level item</li>
      <ul>
        <li>Fourth level item</li>
      </ul>
    </ul>
  </ul>
</ul>
</section>
Enter fullscreen mode Exit fullscreen mode

Now, let's apply some CSS using em:

section {
  font-size: 16px;
}
ul { 
 font-size: 0.9em; 
}
Enter fullscreen mode Exit fullscreen mode

This will result in this:

img

Let's break down what happens at each level:

  • First level - Font size: 0.9em of 16px = 14.4px
  • Second level - Font size: 0.9em of 14.4px = 12.96px
  • Third level - Font size: 0.9em of 12.96px = 11.66px
  • Fourth level - Font size: 0.9em of 11.66px = 10.50px

As you can see, the font size gets progressively smaller with each nested level. This compounding effect can lead to text becoming unreadably small in deeply nested structures.
This is both a feature and a potential pitfall, depending on your design needs.

Rems: Root Ems

To avoid the compounding issue with em, we have rem units. These are always relative to the root element's font size (usually the <html> element).
Let’s suppose for the next examples, the current font-size of the root element is 16px:

.deeply-nested-element {
  font-size: 1.5rem; /* Always 24px, regardless of nesting */ 
  padding: 1rem; /* Always 16px */ 
  margin: 0.5rem; /* Always 8px */ 
}
Enter fullscreen mode Exit fullscreen mode

Rems provide consistency across your document, making them ideal for font sizes and many other properties!

Practical Example

Let's create an accessible button component that scales nicely using a combination of em and rem:

.button { 
  font-size: 1rem; /* 16px (or the prefered size set by the user) */ 

  padding: .5em 1em; /* 8px 16px, using em to scale with the current element font-size */ 
  border-radius: .25em; /* 4px */ 
  transition: font-size .3s ease, padding .3s ease, border-radius .3s ease; 
} 

@media (prefers-reduced-motion: no-preference) {
  .button:hover { 
    font-size: 1.1rem; /* Grows to 17.6px on hover */
  }
}
Enter fullscreen mode Exit fullscreen mode

In this example, the button's padding and border-radius will scale proportionally if the user changes the font size of the html document :)

Viewport-Relative Units

While ems and rems are incredibly useful, sometimes we need units that are relative to the size of the viewport (screen). That's where viewport-relative units come in:

  • vw: 1% of the viewport width
  • vh: 1% of the viewport height
  • vmin: 1% of the smaller dimension (width or height)
  • vmax: 1% of the larger dimension (width or height)

These units are particularly useful for creating full-screen layouts or elements that need to scale dramatically with the viewport size.

.hero-section { 
  height: 100vh;
  width: 100vw;
  /* ... */
}

.hero-section-image { 
  width: 50vmin; 
  height: 50vmin; 
  object-fit: cover; 
  /* ... */
}
Enter fullscreen mode Exit fullscreen mode

This creates a full-height hero section with content that scales based on the viewport size, and an image that's always square and takes up half of the smaller viewport dimension.

While these units are great, they were originally designed with desktop browsers in mind, where the viewport size remains constant unless the user manually resizes the window.
On mobile, it’s an other story.

The Dynamic Nature of Mobile Viewports

Many mobile browsers implement a user experience feature where the browser's UI elements, such as the address bar and navigation buttons, can appear or disappear based on user interaction. Here's how it typically works:

  • When a user first loads a page, these UI elements are visible, reducing the available viewport height.
  • As the user scrolls down the page, these elements often slide out of view, effectively increasing the viewport height.
  • When the user scrolls back up or taps near the top of the screen, these elements reappear, once again reducing the viewport height.

This dynamic behavior creates a fluctuating viewport size, which can lead to layout issues when using viewport-relative units like vh (for overlays, side-bars, etc…).

Enter Dynamic Viewport Units

To address these issues, new viewport units were introduced:

  • svh (Small Viewport Height): Represents the viewport height when mobile browser UI elements are visible.
  • lvh (Large Viewport Height): Represents the viewport height when mobile browser UI elements are hidden.
  • dvh (Dynamic Viewport Height): Dynamically adjusts between svh and lvh as the browser UI elements appear or disappear.

Image description

We have the same properties for width, and for smaller and larger dimension (width or height):
s(vh | vw | vmin | vmax)
l(vh | vw | vmin | vmax)
d(vh | vw | vmin | vmax)

Consider this example:

.side-menu { 
  height: 100dvh; 
}
Enter fullscreen mode Exit fullscreen mode

This element will dynamically adjust its height based on the current state of the mobile browser's UI elements, providing a more consistent user experience 👍.

Do not use dvh for scrollable sections: Imagine scrolling past multiple full-screen sections sing 100dvh, then scrolling up slightly. The content suddenly jumps waaaaay up.
This can cause major lag and it forces the browser to recalculate the entire page layout, which can hurt performance (layout thrashing).

Inline / Block Logical Properties

To ensure completeness, it’s important to mention an additional set of unit types: inline and block. These are known as "logical properties”. They function similarly to width and height but are designed to adapt to vertically-written languages like Mongolian, effectively transposing their behavior. These are vi , vb , svi , svb , lvi , lvb , dvi , dvb.

Browser Support and Fallbacks

Although support for dynamic viewport units is 93.21% (at the time of writing), you might want to provide fallbacks if you target older browsers:

.modal {
  height: 100vh;  /* Fallback for browsers that don't support dvh */
  height: 100dvh; /* Will be used by browsers that support it */
}
Enter fullscreen mode Exit fullscreen mode

Combining Units

Using calc Function

The calc() function is a game-changer for responsive design. It allows you to perform basic math operations with different units, making it easier to create flexible layouts. Here's what you need to know:

  • Supports addition (+), subtraction (-), multiplication (*), and division (/)
  • Always add spaces around operators

Example:

p { 
  font-size: calc(0.5em + 1svw); 
}
Enter fullscreen mode Exit fullscreen mode

In this example, the font size will scale with the viewport width but will never get smaller than 0.5rem.

Note: Always include em or rem units when using viewport units for font sizes. This ensures user font preferences are respected.

Using clamp Function

The clamp() function takes this concept even further, allowing you to set a preferred value with a minimum and maximum. clamp() takes three arguments:

  • Minimum value
  • Preferred value (can be an expression)
  • Maximum value

Have a look at this example:

h1 {
  font-size: clamp(1em, 5vw, 6em);
}
Enter fullscreen mode Exit fullscreen mode

This sets a font size that scales with the viewport width but is never smaller than 1em or larger than 6em.

Using Other Useful Functions

  • min(): Chooses the smallest value from a list
    • Example: width: min(200px, 20svw);
  • max(): Chooses the largest value from a list
    • Example: min-height: max(200px, 20svw);

Wrapping up

Mastering relative units in CSS is a game-changer for creating flexible and accessible designs. By understanding the nuances of ems, rems, viewport units, and how to combine them effectively, you can create layouts that adapt seamlessly to different screen sizes and user preferences.

The introduction of dynamic viewport units like dvh represents an important step in adapting web design to the unique challenges of mobile browsers. By using these units, you can create responsive layouts that handle the dynamic nature of mobile viewports.

However, the goal is not to eliminate pixels entirely, but to use them judiciously. Absolute units like pixels still have their place, especially for borders or when you need pixel-perfect control. The key is knowing when to use relative units for flexibility and when to use absolute units for precision.

As you build your next project or your next UIs, challenge yourself to use relative units wherever possible. You'll likely find that your stylesheets become more concise, your layouts more flexible, and your overall design more robust across different devices and user settings.

Until next time, happy coding!

Top comments (4)

Collapse
 
officialphaqwasi profile image
Isaac Klutse

Informative

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

Comprehensive

*looks inside*

no container units

Collapse
 
mustapha profile image
Mustapha Aouas • Edited

I may be wrong but I feel that they're a bit of a niche feature. Do you use them often? I'm curious about the use-cases.
Also, "comprehensive" doesn't necessarily mean "exhaustive" ^^

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

I don't use them a whole lot because of compatibility reasons, but they're no more niche than vh and vw. They're really useful when you're styling extrinsically sized components and can't know how much space you have available. They're close to % except you're not limited to direct parents and can mix axes.