AuthonAuthon Blog
comparison5 min read

Tabular Numbers in CSS: font-variant-numeric vs Monospace Hacks

A practical comparison of monospace fonts vs font-variant-numeric: tabular-nums, plus a migration guide for cleaning up jittery numbers in your UI.

AW
Alan West
Authon Team
Tabular Numbers in CSS: font-variant-numeric vs Monospace Hacks

Last month I was building a stopwatch component for a project dashboard. The numbers were doing the dance — every time the seconds ticked over, the whole layout shifted by a pixel or two. Looked janky. The fix was one line of CSS I'd been ignoring for years: font-variant-numeric: tabular-nums.

Let me share what I learned migrating a few projects off the old monospace hack.

The Problem

Proportional fonts (which is almost every font you use) give different widths to different digits. A "1" is narrow, an "8" is wide. So when a number flips from 11:11 to 12:23, the whole string can shift horizontally. For static text that's fine. For timers, leaderboards, financial tables, live prices — it's terrible UX.

The classic fixes:

  • Switch to a monospace font (ugly, often the wrong vibe)
  • Pad with text-align: right and pray
  • Use JS to set fixed widths
  • Make the whole UI in Helvetica from 1998

There's a better way that's been in CSS for years.

The Three Approaches

1. Monospace fonts

css
.timer {
  font-family: "JetBrains Mono", "SF Mono", monospace;
}

This works. Every character has the same width. But you're paying for it stylistically — your dashboard now looks like a terminal. Monospace fonts also tend to ship with wider character widths than proportional ones, so your layout has to accommodate.

2. font-variant-numeric (the modern fix)

css
.timer {
  /* Only the digits get equal widths — letters stay proportional */
  font-variant-numeric: tabular-nums;
}

This is what you actually want most of the time. Most modern fonts (Inter, Roboto, system fonts on macOS/Windows) ship with both proportional and tabular figure variants. The tabular-nums value tells the font to use the tabular set. Letters stay proportional, digits become uniform.

3. font-feature-settings (the low-level version)

css
.timer {
  font-feature-settings: "tnum" 1;
}

Same outcome, older API. It directly toggles the OpenType tnum feature. MDN recommends the higher-level font-variant-numeric property when both work, because it composes better with other CSS rules and doesn't accidentally clobber unrelated font features.

Side-by-Side

After migrating three different projects, here's how the approaches stack up:

| Approach | Pros | Cons |
|---|---|---|
| Monospace font | Always works, no font-feature dependency | Changes the whole visual feel; wider columns |
| font-variant-numeric: tabular-nums | Keeps your design font, semantic API | Requires font with tabular figure support |
| font-feature-settings: "tnum" | Same effect, very wide support | Low-level, can clobber other features |

The font-support thing is real but smaller than you'd think. Inter, Roboto, Source Sans, IBM Plex, system-ui on Apple, Segoe UI on Windows — they all support tnum. If it's a Google Font, you can usually toggle it on in the embed URL.

Migrating From Monospace to tabular-nums

Here's a before/after from a leaderboard component I cleaned up last week.

Before:

html
<div class="leaderboard">
  <span class="player">jdoe</span>
  <!-- Forced monospace just to stop the score column jittering -->
  <span class="score mono">1,247</span>
</div>
css
.score.mono {
  font-family: "Menlo", monospace;
  font-size: 14px;
  /* Bumped font-size down to match visual weight of the body font */
}

After:

html
<div class="leaderboard">
  <span class="player">jdoe</span>
  <span class="score">1,247</span>
</div>
css
.score {
  font-variant-numeric: tabular-nums;
  /* Inherits the design system font — no override needed */
}

The migration path is pretty mechanical:

  • Search your stylesheets for font-family.mono — anywhere you forced monospace just* to align numbers
  • Replace the font-family override with font-variant-numeric: tabular-nums
  • Visually verify the font actually supports tabular figures (some don't — you'll see no change)
  • Remove the size adjustments you probably added to compensate for monospace metrics
  • One gotcha: if you use Tailwind's tabular-nums utility, that's just the same CSS property under the hood — no magic, same font-support caveats.

    Combining Numeric Features

    font-variant-numeric accepts several values that can be combined:
    css
    .financial-table {
      /* Tabular figures + a slashed zero — easier to distinguish 0 from O */
      font-variant-numeric: tabular-nums slashed-zero;
    }
    
    .recipe {
      /* Real typographic fractions instead of flat "1/2" */
      font-variant-numeric: diagonal-fractions;
    }

    I haven't tested diagonal-fractions across many fonts thoroughly; it's font-dependent and a lot of faces don't include the glyphs. But tabular-nums slashed-zero is now my default in any data-heavy UI.

    When to Use What

    My rule of thumb after three migrations:

    • Any updating number (timers, counters, live scores, live prices): tabular-nums
    • Financial tables, spreadsheet-like UIs: tabular-nums slashed-zero
    • Code, terminal output, diffs: actual monospace — the whole point is that everything aligns, not just digits
    • Static prose with numbers in it (article body, marketing copy): nothing. Proportional figures look better in running text

    The mistake I made for years was reaching for monospace by default. Most of the time you don't want a code font; you want the numbers in your existing font to behave.

    Browser Support

    font-variant-numeric: tabular-nums works in all modern browsers and has for a long while. Per caniuse global support is above 96%. The bigger support question isn't the browser — it's whether your font includes the feature. A font without tnum glyphs will silently do nothing, and that's the failure mode I see people hit most often.

    The Short Version

    If you're using a monospace font purely to stop digits from jittering, you almost certainly want font-variant-numeric: tabular-nums instead. One property, no JS, no font swap, no design tradeoff. It's the small kind of thing that quietly upgrades every dashboard you ship going forward.

    Tabular Numbers in CSS: font-variant-numeric vs Monospace Hacks | Authon Blog