惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

雷峰网
雷峰网
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tailwind CSS Blog
F
Fortinet All Blogs
Microsoft Azure Blog
Microsoft Azure Blog
Jina AI
Jina AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
N
Netflix TechBlog - Medium
B
Blog RSS Feed
Blog — PlanetScale
Blog — PlanetScale
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
The Blog of Author Tim Ferriss
D
Docker
博客园 - 聂微东
博客园 - 【当耐特】
博客园 - 三生石上(FineUI控件)
L
LangChain Blog
量子位
宝玉的分享
宝玉的分享
博客园 - 司徒正美
The Cloudflare Blog
G
Google Developers Blog
Microsoft Security Blog
Microsoft Security Blog
腾讯CDC

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Tabular Numbers in CSS: font-variant-numeric vs Monospace...
Alan West · 2026-05-17 · via DEV Community

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

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

Enter fullscreen mode Exit fullscreen mode

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)

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

Enter fullscreen mode Exit fullscreen mode

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)

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

Enter fullscreen mode Exit fullscreen mode

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:

<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>

Enter fullscreen mode Exit fullscreen mode

.score.mono {
  font-family: "Menlo", monospace;
  font-size: 14px;
  /* Bumped font-size down to match visual weight of the body font */
}

Enter fullscreen mode Exit fullscreen mode

After:

<div class="leaderboard">
  <span class="player">jdoe</span>
  <span class="score">1,247</span>
</div>

Enter fullscreen mode Exit fullscreen mode

.score {
  font-variant-numeric: tabular-nums;
  /* Inherits the design system font — no override needed */
}

Enter fullscreen mode Exit fullscreen mode

The migration path is pretty mechanical:

  1. Search your stylesheets for font-family.*mono — anywhere you forced monospace just to align numbers
  2. Replace the font-family override with font-variant-numeric: tabular-nums
  3. Visually verify the font actually supports tabular figures (some don't — you'll see no change)
  4. 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:

.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;
}

Enter fullscreen mode Exit fullscreen mode

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.