Accessibility

フォントサイズのアクセシビリティ:フォントサイズにpxを使ってはいけない理由

Updated 2月 24, 2026
フォントサイズにpxを使うと、視覚障害のあるユーザーのブラウザズームが機能しなくなります。なぜremが必須で、正しく実装する方法。

Font Size Accessibility: Why You Should Never Use px for Font Sizes

There is a piece of CSS that appears in an enormous number of codebases — developer boilerplate, CSS resets, design system foundations — that silently breaks accessibility for a significant portion of users:

html {
  font-size: 16px;
}

This declaration seems harmless. It establishes a baseline font size that matches the browser default. It makes the math easy: 1rem = 16px, so 2rem = 32px, and so on. Many developers use it to normalize behavior across browsers. But this pattern — setting the root font size in px — undermines a core browser accessibility mechanism that low-vision users depend on.

Understanding why requires understanding exactly how browser font size preferences and CSS units interact.

How Browser Zoom Works (and How px Breaks It)

Modern browsers offer two distinct mechanisms for making content larger: browser zoom and default font size preference.

Browser zoom (Ctrl/Cmd + Plus, or pinch-to-zoom on mobile) scales the entire rendered page — text, images, layout, everything — by a multiplier. It works by adjusting the CSS pixel ratio. When you zoom to 150%, the browser treats each CSS pixel as 1.5 device pixels. Every dimension in CSS — px, rem, em, %, vw — scales proportionally. Browser zoom is described in WCAG 1.4.4, which requires content to remain functional at 200% zoom.

Default font size preference is a setting in the browser's accessibility options. In Chrome, it is under Settings → Appearance → Font size. In Firefox, Preferences → Fonts. The default is typically 16px, but users who need larger text can set it to 20px, 24px, or higher. This setting changes the browser's root font size — the value that 1rem resolves to.

Here is where px fails: pixel values in CSS are not affected by the browser's default font size setting. If a user sets their browser default to 24px for readability, but your CSS says font-size: 16px, they get 16px. Their accessibility preference is completely overridden.

The rem unit, by contrast, is defined relative to the root font size. If the user has set their browser default to 24px, 1rem = 24px. 1.25rem = 30px. Their preference propagates through every rem-based dimension on the page.

/* This overrides user preference — inaccessible */
html {
  font-size: 16px; /* Locks root to 16px regardless of user setting */
}

body {
  font-size: 16px; /* Also overrides user preference */
}

/* This respects user preference — accessible */
html {
  /* No font-size declaration — inherits browser default */
}

body {
  font-size: 1rem; /* Equals whatever the user has set */
}

The "62.5% trick" — setting html { font-size: 62.5% } to make 1rem = 10px for easy mental math — is also problematic, though less severely. Percentage values are relative to the browser default, so a user who sets their preference to 24px gets 0.625 × 24px = 15px as their root, which is at least proportional to their preference rather than fixed. However, this approach still compresses the effective font size range for users with very large preferences, and it introduces cognitive overhead for accessibility reviewers who need to verify that 1.6rem actually produces an accessible size.

rem for Font Sizes: The Accessible Default

The clean, accessible approach is to avoid setting html { font-size: ... } at all, let the browser default prevail, and express all font sizes in rem:

/* Accessible type scale using rem */

/* Body text — matches user's browser preference */
body {
  font-size: 1rem;
  line-height: 1.6;
}

/* Small text — still relative to user preference */
small, .text-sm {
  font-size: 0.875rem; /* 14px at 16px default */
}

/* Caption / fine print */
.caption {
  font-size: 0.8125rem; /* 13px at 16px default */
}

/* Slightly larger body */
.intro, .lead {
  font-size: 1.125rem; /* 18px at 16px default */
}

/* Heading scale */
h6 { font-size: 1rem; }
h5 { font-size: 1.125rem; }
h4 { font-size: 1.25rem; }
h3 { font-size: 1.5rem; }
h2 { font-size: 1.875rem; }
h1 { font-size: 2.25rem; }

A user who has set their browser default to 20px now gets: body text at 20px, h1 at 45px, small text at 17.5px. Every size scales proportionally to their preference. The hierarchy is preserved, the reading experience is optimized for their vision, and no CSS needs to change.

Spacing should also use relative units. Using em for padding and margin within components keeps spacing proportional to the component's font size. Using rem for spacing in layout components keeps it proportional to the user preference.

/* Component using em for internal spacing — scales with component font size */
.button {
  font-size: 1rem;       /* rem — relative to user preference */
  padding: 0.75em 1.5em; /* em — relative to button's font-size */
  border-radius: 0.25em;
}

/* Layout spacing using rem — consistent with type scale */
.section {
  padding: 4rem 2rem;
  margin-bottom: 3rem;
}

/* Line length using ch — always proportional to font */
.article-body {
  max-width: 70ch;
}

The ch unit — the width of the "0" character in the current font — is particularly powerful for line length. max-width: 70ch always produces a readable line length of approximately 70 characters regardless of the user's font size preference, because ch scales with rem.

Migrating from px to rem

If you are working on an existing codebase that uses pixel values throughout, migration to rem can seem daunting. Here is a practical, phased approach.

Phase 1: Remove the html font-size reset (highest priority)

If your CSS has html { font-size: 16px } or html { font-size: 62.5% }, remove it. This is the single change with the highest accessibility impact. It will not break your layout if you are using px everywhere else — pixel values are already unaffected by the root font size. You are simply restoring user control over the root that rem values depend on.

Phase 2: Convert font-size declarations

The conversion is mechanical: divide the pixel value by 16 (the standard browser default) to get the rem value.

/* Before — px values */
.heading-xl { font-size: 48px; }
.heading-lg { font-size: 36px; }
.heading-md { font-size: 24px; }
.body       { font-size: 16px; }
.body-sm    { font-size: 14px; }

/* After — rem values */
.heading-xl { font-size: 3rem; }     /* 48 ÷ 16 = 3 */
.heading-lg { font-size: 2.25rem; }  /* 36 ÷ 16 = 2.25 */
.heading-md { font-size: 1.5rem; }   /* 24 ÷ 16 = 1.5 */
.body       { font-size: 1rem; }     /* 16 ÷ 16 = 1 */
.body-sm    { font-size: 0.875rem; } /* 14 ÷ 16 = 0.875 */

Phase 3: Convert spacing (optional but recommended)

Padding and margin values can remain in px and the layout will still function. But converting to rem or em makes the spacing responsive to user preference, which is the fuller accessible experience. Evaluate component by component rather than converting everything at once.

Phase 4: Convert max-width for text containers

Replace pixel-based max-widths for text columns with ch or rem equivalents:

/* Before */
.article {
  max-width: 680px;
}

/* After — responsive to font size */
.article {
  max-width: 70ch; /* Approximately 680px at default, but scales with font size */
}

Testing during migration: Use your browser's font size setting (not zoom) to verify changes. Set your browser font size to 24px and reload each page after changes. Text should be visually larger; layout should remain functional. Check for:

  • Text that did not scale (still using px)
  • Containers that overflow because text is larger
  • Spacing that looks disproportionate with larger text
  • Icons or images that do not scale with text

Testing with Browser Zoom and Screen Readers

Comprehensive font size accessibility testing requires verifying multiple mechanisms.

Browser zoom testing (WCAG 1.4.4)

Set zoom to 200% in your browser and check every page. Common failures: - Horizontal scrollbar appears (content overflows viewport) - Text truncates with ellipsis due to fixed-height containers - Navigation menus overlap or break - Modal dialogs extend beyond the viewport

/* Common overflow fix for zoom */
* {
  box-sizing: border-box; /* Prevents width from exceeding container */
}

.container {
  max-width: 100%;
  overflow-wrap: break-word; /* Prevents long words from causing overflow */
}

/* Avoid fixed heights on text containers */
.card {
  /* Before: height: 200px; — breaks at zoom */
  min-height: 200px; /* After: minimum, not fixed */
}

Font size preference testing (WCAG 1.4.4, broader)

Change your browser's default font size to 24px (1.5× default), then to 32px (2× default). Verify: - Body text scales as expected - Layout remains readable - No content disappears or overlaps

Screen reader testing

Screen readers do not alter the visual rendering, but they interact with font size in two indirect ways:

First, heading hierarchy — which depends on semantic HTML, not font size — is how screen readers navigate documents. A visually large <div> with big text is invisible to screen reader navigation; a properly nested <h2> inside a section is navigable.

Second, screen readers announce <abbr> expansions, <title> attributes, and aria-label content that is never visually rendered. If you have set font-size: 0 to hide text while keeping it accessible to screen readers, test that this technique works correctly — some screen reader and browser combinations have issues with font-size-0 patterns.

Automated testing

axe-core does not directly test whether you use px or rem for font sizes — that is a semantic linting concern, not a runtime contrast or structure issue. You can use Stylelint with the stylelint-a11y plugin to enforce rem usage:

{
  "rules": {
    "a11y/font-size-is-readable": true
  }
}

Or write a custom Stylelint rule to flag font-size declarations using px:

// .stylelintrc.js — custom rule to flag px font sizes
module.exports = {
  rules: {
    'unit-disallowed-list': [
      ['px'],
      {
        message: 'Use rem for font sizes to respect user preferences',
        severity: 'warning',
        // Apply only to font-size property
      }
    ]
  }
};

The investment in rem-based font sizing pays dividends beyond accessibility compliance. A type scale expressed in rem is easier to reason about, easier to scale across a responsive design, and automatically accommodates the growing proportion of users who adjust their browser defaults — whether for accessibility, reading comfort, or display characteristics. It is one of the simplest changes with the highest impact ratio in accessible front-end development.

Typography Terms

Try These Tools

Fonts Mentioned

Related Articles