Why default Tailwind is not always an accessible choice
A while back, a project started breaking in a way I couldn't reproduce. Certain sections jumped to the wrong layout. Columns that should have been side by side stacked, cards overflowed, all on viewport widths I'd tested a dozen times. My CSS was fine. My root font size was the sensible 16px default. Eventually I found it: the reporter had bumped their browser's default font size, and Tailwind's breakpoints had quietly scaled with it. The layout was flipping at a narrower viewport than I'd ever designed for.
And it left me with something I'd never thought about before. If a user sets their browser's default font size to something other than 16px, would that quietly moves my Tailwind breakpoints?
It feels like it shouldn't matter. Breakpoints are about viewport width, font size is about text. Two different axes. Except they're quietly wired together, and once you see how, you start to wonder whether Tailwind's default is the one you actually want. Let me walk you through it. And I'll be honest, I got the answer wrong the first time I though about it.
First, what Tailwind actually ships
Since Tailwind v3.2, the default breakpoints are in rem, and v4 has kept that unchanged:
sm 40rem /* 640px */
md 48rem /* 768px */
lg 64rem /* 1024px */
xl 80rem /* 1280px */
2xl 96rem /* 1536px */
So md:flex really means @media (min-width: 48rem). The pixel values in the comments are just rem × 16. That × 16 is the whole story.
The gotcha: rem in a media query is not the rem you think
Your instinct (and mine) probably says: "rem is relative to the root element's font size, so if I do html { font-size: 10px }, my breakpoints shift."
Nope. Inside a media query condition, rem and em do not look at your CSS at all:
/* This does NOT change where `min-width: 48rem` triggers. */
html {
font-size: 10px;
}
@media (min-width: 48rem) {
/* still evaluated against the *initial* font size, */
/* not against your html { font-size } */
}
Per the Media Queries Level 4 spec, relative units in media queries resolve against the initial value of font-size. In its own words: "Relative units in media queries are based on the initial value, which means that units are never based on results of declarations." So your page's CSS is powerless here.
That's the part everyone learns first, and it's where a lot of blog posts stop. But it's only half the picture, and the other half is the interesting one.
The half nobody mentions: the user can still move them
"Initial value" doesn't mean "hardcoded 16px forever." It means the value the user agent starts from, and the user gets a say in that:
- Browser default font-size setting (Chrome:
chrome://settings/fonts→ Font size.
Firefox:about:preferences→ General → Fonts → Default size). This changes the UA's initial value. So it does move yourrem/embreakpoints. - Author CSS (
html { font-size }).
Does not move them. (This is the gotcha above.)
So here's my corrected mental model. Three different "make it bigger" gestures, three different behaviours:
| User does this | rem text |
rem/em breakpoints |
px breakpoints |
|---|---|---|---|
| Sets browser default font size | scales | scales | frozen |
| Page zoom (Cmd / Ctrl +/−) | scales | scales | scales |
Author sets html { font-size } |
scales | no effect | n/a |
Read that middle column again. With rem breakpoints, a user who bumps their default font size to 24px because they can't comfortably read 16px gets breakpoints that move with them. Your layout changes to give that bigger text more room. With px breakpoints, their text grows but your columns stay frozen at 768px, happily cramming 24px text into a layout designed for 16px. |
That's the case for Tailwind's default. It's a real accessibility win, and it's why the maintainers chose it.
So why is "not always accessible" in the title?
Because "good for the user's text" and "good for your layout" aren't guaranteed to be the same thing, and the divergence is design-dependent, not universal. When breakpoints move with font size, you've handed control of which layout renders to a setting you can't see and didn't test against. A few ways that bites:
- You tested at 16px. Your
md:grid-cols-3looked great. A user at 20px default trips into the 3-column layout at a narrower viewport than you ever checked, and your cards overflow. - Component libraries with tight internal breakpoints can flip layouts mid-scroll in ways that read as bugs.
- It's invisible. Page zoom you can reproduce instantly. The default-font-size setting is buried, rarely toggled, and almost never in your test matrix, so when it causes a layout issue, it's a nightmare to reproduce when you're not aware of it.
None of this means rem breakpoints are wrong. It means the default makes a trade you might not have known you were making: more responsive to user font preference, less predictable for you.
The actual debate
So which one should you actually reach for? That's the part worth debating.
px breakpoints give you a layout that triggers at exactly the width you designed for, every time, for everyone. They lean on page zoom (which scales everything, including px breakpoints) to do the accessibility heavy lifting. That's a defensible position: zoom is the gesture most users actually reach for, and it Just Works with px.
rem/em breakpoints additionally honour the default-font-size setting, a smaller but real group of users (often the ones who most need it) who set a larger base font instead of zooming. That's also defensible.
So is the real question "which is correct"? Probably not. The better question is: which user are you optimising for, and did you choose that, or did you inherit it?
Did you pick rem breakpoints because you reasoned about text-zoom users, or because it was the default and you never thought about it? That second case is the one worth talking about. A good default is one you'd have chosen anyway. An accessibility default you can't explain is just a default.
A note on what the standard actually requires
It's tempting to frame this as "px breakpoints fail accessibility," but that's not what the spec says. WCAG 1.4.4 (Resize Text) is satisfied as long as text can reach 200% through some mechanism the browser provides, and the W3C explicitly lists page zoom as a sufficient technique. Since zoom scales px breakpoints just fine, px breakpoints meet WCAG 1.4.4. They don't fail the standard.
So honouring the browser's default-font-size setting is not the floor, it's a step above it. Choosing rem breakpoints to serve the users who change that setting (rather than zoom) is a decision to go beyond what WCAG requires, not a decision to merely comply. That's a perfectly good reason to pick rem. It's just worth being honest that it's a choice to exceed the standard, not a box the standard makes you tick.
If you want to change it
Switching to px breakpoints in Tailwind v4 is a @theme override:
/* app.css */
@import "tailwindcss";
@theme {
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
}
Or, in v3's tailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
screens: {
sm: "640px",
md: "768px",
lg: "1024px",
xl: "1280px",
"2xl": "1536px",
},
},
};
You could also go the other direction and lean further in. Keep rem breakpoints and make sure your content is fully rem-based and tested at 200% text, if honouring the font-size setting is a goal you're choosing on purpose.
The point isn't the answer
The point is that "Tailwind's defaults are accessible" is doing a lot of quiet work in a lot of codebases, and it's worth knowing which accessibility it buys, which it doesn't, and whether that matches the users you actually have.
So: rem, em, or px for your breakpoints? I genuinely think reasonable people land in different places here.
Want to see it for yourself? There's a tiny live demo next to this post. Open it, change your browser's default font size, and watch the rem breakpoint move while the px one stays put.
Sources
- Media Queries Level 4, §6 "Using Units" (W3C) — the spec rule that relative units in media queries resolve against the initial
font-size, not author CSS: "Relative length units in media queries are based on the initial value, which means that units are never based on results of declarations." - Tailwind CSS: Responsive design — the official default breakpoints (
sm40rem,md48rem,lg64rem,xl80rem,2xl96rem) and the note that Tailwind usesremfor them. - Tailwind CSS v3.2 release notes — the release that switched the default breakpoints to
rem. - Tailwind CSS: Theme variables (
@theme) — how to override--breakpoint-*values in v4. - MDN:
font-sizeand MDN: CSS values and units — reference onrem/emresolution and the root/initial font size. - Understanding WCAG SC 1.4.4: Resize Text (W3C / WAI) — the accessibility criterion behind the
rem-breakpoints argument: text must resize to 200% without loss of content or function. Note it does not require staying within a breakpoint, sopxbreakpoints satisfied via page zoom are not inherently a failure.