Skip to header Skip to main navigation Skip to main content Skip to footer
Drupal Maintenance and Developments in Austin TX
Drupal Care

Main navigation

  • Home
  • Why Drupal Care?
  • What’s Included
  • About
  • Our Approach
  • Prices & Plans
  • Portfolio (opens in new tab)
  • Blog
  • Videos
  • Contact
  • Site Evaluation

CSS Units in a Drupal Theme: rem, px, em and the Gap

Alaa Haddad, professional Drupal developer based in Austin, TX   Drupal Care
  12:16 AM CDT, Wed September 16, 2026
Share

Inside a Drupal theme the unit you pick is not a style preference — it decides whether a reader who has set their browser text larger gets your narrow layout at the point their text actually needs it, and core's own front-end theme has already made that choice for you.

Most advice about rem against px is written for a single-page site. A Drupal theme is a different problem: the same numbers are written down in more than one place, in more than one language, and they have to agree.

Every figure below was counted from files on disk: Drupal core 11.4.6, plus two contributed projects I maintain.

The three places a Drupal theme writes a length

Before choosing a unit, know which files are going to carry the number.

  • Your stylesheets. Media queries and every width, padding and font size. This is the only one of the three that changes how the page looks.
  • The theme's breakpoints file. A YAML file of named media query strings, consumed by core's Breakpoint API.
  • Your JavaScript. Anything that measures the page at runtime.

They do not have to use the same unit, and in core they do not. That is worth knowing before you conclude that a theme is inconsistent.

Core declares pixels and writes rem

Olivero is the clearest worked example in core. Its olivero.breakpoints.yml declares its media queries in pixels: 500, 700, 1000, 1200, 1300 and 1440. Its stylesheets express the same six boundaries in rem.

Olivero: one set of boundaries, two files, two units
Breakpoint keyDeclared in olivero.breakpoints.ymlWritten in the CSS as
olivero.smmin-width: 500px31.25rem
olivero.mdmin-width: 700px43.75rem
olivero.lgmin-width: 1000px62.5rem
olivero.navmin-width: 1200px75rem
olivero.xlmin-width: 1300px81.25rem
olivero.grid-maxmin-width: 1440px90rem

I counted the media queries myself rather than trusting the impression: every min-width and max-width condition in Olivero's stylesheets is written in rem, and not one of them is in px. Claro, the admin theme, is the opposite picture: a mixture of em and rem with a single px query still in place, so the convention arrived over time rather than by decree.

The other half of core's choice is at the root. Olivero's css/base/base.css sets html { font-size: 100%; } at lines 19 to 21. It deliberately does not say 16px. That one declaration is what makes every rem in those queries respect the reader's own browser setting instead of overriding it.

What each unit is actually for

Choosing a unit in a theme
UnitRelative toReach for it whenWhere it bites
remthe root font sizebreakpoints, type scale, spacing rhythminside a media query it does not see your html rule
emthe element's own font sizepadding on a button that must track its labelcompounds through nested components
pxnothing; it is absolutehairlines, borders, shadow offsetsignores the reader's text-size preference entirely
vw1% of the viewport widthfull-bleed sectionsa visible scrollbar can push content into overflow

The short rule that survives contact with a real theme: rem for anything a reader might want bigger, px for anything that is a line rather than a length. A one-pixel border is a one-pixel border at every text size; a sidebar width is not.

If your stylesheets have drifted into a mixture and nobody is sure which rules are still live, the fix is organisational before it is numerical — SMACSS categorisation in .libraries.yml is how I keep that under control, and Drupal theming services covers it when the inherited stylesheet is beyond a tidy-up.

The boundary problem, and the very small number that solves it

Here is the trap. min-width: 36rem and max-width: 36rem are both true at exactly 36rem. If you write a pair of rules that way, at that one width both apply and the later one in the cascade wins. The symptom is a menu or a grid that flickers into the wrong layout at one specific window size and looks perfect either side of it.

VVJ Tabs, a Views display-format module I maintain, solves it by leaving a deliberate gap. Each of its five breakpoint stylesheets opens with a min-width rule and closes with a max-width companion set fractionally below it. The values and the line numbers are consistent across all five files — the min-width at line 9, the max-width at line 43.

VVJ Tabs: exclusive breakpoint pairs
Stylesheetmin-widthPaired max-widthAt a 16px root
vvjt-576.css36rem35.99875rem576px / 575.98px
vvjt-768.css48rem47.99875rem768px / 767.98px
vvjt-992.css62rem61.99875rem992px / 991.98px
vvjt-1200.css75rem74.99875rem1200px / 1199.98px
vvjt-1400.css87.5rem87.49875rem1400px / 1399.98px

Why 0.00125rem and not simply 1px

The gap is 0.00125rem, which is 0.02px at a 16-pixel root. It is expressed in rem for the same reason the boundary is: subtract a whole pixel and the gap grows with the reader's font size, which puts a visible dead zone between your two rules. A fractional gap is also the safer side of the problem, because a viewport width is not always a whole number — a zoom level or a fractional device pixel ratio can land you between two integers, and a rule that only excludes whole pixels will leave a sliver uncovered.

JavaScript has no concept of rem

This is where mixed-unit themes actually break. Every width a browser hands to JavaScript — innerWidth, getBoundingClientRect(), the result of a matchMedia query you built by string concatenation — is in CSS pixels. There is no rem at that layer.

So a theme with rem breakpoints in CSS and pixel thresholds in a script has two ladders that agree only while the root font size is exactly 16 pixels. A reader who has set their browser to 20-pixel text moves every CSS breakpoint by a quarter and moves none of the JavaScript ones. Nothing throws an error; the two systems simply stop agreeing, usually at the menu.

VVJ Tabs handles this by keeping its pixel names purely as identifiers: VvjtConstants.php line 35 declares DEFAULT_BREAKPOINT as the string '992', which selects a stylesheet whose actual query is 62rem. The number is a label for a file, not a measurement anyone compares against a window width. The safe pattern is to let CSS own the boundary and have the script read the result: matchMedia with the same rem string your stylesheet uses. Do not re-derive the number in a second language. Where those boundaries should sit in the first place, and why the window is often the wrong thing to measure, is a separate question I have written up in full.

Common questions

Should I set html { font-size: 62.5% } to make 1rem equal 10px?

It makes the arithmetic easier and it overrides a reader's preference to do it, which is the opposite of why you chose rem. Core does not: Olivero sets 100%, and Solo's normalize stylesheet sets font-size: 100% too. Keep the root at 100% and accept the division.

Do I need to convert an existing pixel theme?

Not wholesale. Convert the media queries and the type scale, because those are what a reader's font-size setting should move. Leave borders and shadow offsets alone. A part-converted theme is normal — Solo's stylesheets are mostly rem with a minority still in px, and the Paragraphs Bundles suite is the same shape.

My sub-theme's units are being ignored entirely. Is that a unit problem?

Almost certainly not. A stylesheet that is not loading, is loading in the wrong order, or is being beaten by specificity looks identical to a unit mistake from the browser's side. Work through the sub-theme CSS checklist first; check the units second.

Where to go next

Grep your theme for @media and count the units. If the result is mixed, pick one for breakpoints, convert the queries alone, and leave everything else for a quieter week. I have been doing web development since 2005 and I author Drupal modules and themes at drupal.org/u/flashwebcenter; the flicker at one exact window width has cost me more hours than every other unit question combined. If a theme you inherited is past the point of counting, tell me what it is doing or ask for a scoped quote.

HTML

Footer menu

  • About
  • Privacy Policy
  • Terms & Conditions
  • Flash Web Center, LLC (opens in new tab)
  • Web Designer In Austin (opens in new tab)
  • Log in
  • Contact
  • Sitemap

Copyright © 2026 Flash Web Center, LLC | All rights reserved

Developed & Designed by Alaa Haddad