Find the element causing horizontal scroll, before your users do.

wontfit is a free, open-source command-line tool that finds what breaks your mobile layout. It shows your running web app at phone and tablet widths side by side, in the browser you already have open, names the element that is too wide for a 375px screen, and fails your build when a layout breaks.

pipx install wontfit
Runtime deps
0
Python
3.9+
Binds to
127.0.0.1
License
MIT
wontfit check --pages /,/pricing,/terms --widths se --fail-on overflow
pagesizeoverflowsmall tapstext<12px
/375x800+401px (img.shot)91
/pricing375x800+583px (table.compare)31
/terms375x800fits310
FAIL (overflow)/ @ 375: overflow +401px (img.shot)/pricing @ 375: overflow +583px (table.compare)exit 1
Real output against the Ledgerly demo site that ships with wontfit.

At a glance

What it is
A free, open-source command-line tool that finds the element causing horizontal scroll on mobile and fails CI when a layout overflows.
Best for
Web developers who want to see several pages at phone widths while they work, get the overflowing element named in pixels, and gate pull requests on it.
Not for
Synced scrolling and device frames, where Polypane or Responsively is better, or pixel-by-pixel visual regression testing.
Compared with
DevTools device mode shows one page at one size and leaves you to spot the problem; wontfit checks many pages at several widths at once and names the culprit.
Price
Free and open source under MIT. Python 3.9+.

One column per page and width.

Pick pages and widths, or presets from iPhone SE to iPad. Each frame carries its own verdict, and the layout lives in the URL, so a review is a link you can paste into a pull request.

Three copies of a landing page at iPhone SE, iPhone 15 and Pixel 8 widths, each flagged for horizontal overflow by 364 to 401 pixels

What it catches.

Four checks run inside every frame. They are heuristics, kept few and explainable on purpose.

Overflow, with the culprit named

Not just a red frame. The pricing table is 583px too wide, and the footer says table.compare and its bounds. Only the outermost offender is listed, so a wide table does not report every row inside it.

A pricing page at 375px wide flagged with overflow of 583 pixels, culprit table.compare

Tap targets under 44px

Press t to outline every interactive element smaller than 44 by 44 CSS pixels inside the frame, the size WCAG 2.5.5 and Apple's guidelines ask for.

Dashed outlines drawn around small buttons and links on a phone-width page

Inspect across every frame

Press i and hover an element in one frame. It is highlighted with its size in every other frame, so you see the same heading at 375, 393 and 412 at once.

The same heading highlighted with its dimensions in three phone frames

Landscape

Swap width and height with one toggle. Here the app's fixed header grows to two rows and covers the numbers it was meant to introduce.

Two landscape phone frames where a fixed header covers the dashboard cards

Then it gates the pull request.

The same diagnostics run headlessly. wontfit check exits 1 on overflow and writes a JSON report; wontfit shoot writes a PNG per page and width plus a contact sheet for the review.

pip install 'wontfit[shoot]'
playwright install --with-deps chromium

wontfit check --pages /,/pricing \
  --widths se,iphone15,pixel8 \
  --fail-on overflow --json report.json

wontfit shoot --pages /,/pricing \
  --widths se,iphone15,pixel8 --out shots

A complete GitHub Actions job, a pre-push hook and recipes for twelve stacks are in the docs.

A contact sheet tiling six screenshots: three pages at two phone widths

Why is your page wider than the phone?

Horizontal scroll on mobile almost always comes from one element wider than the viewport. These are the usual suspects, and wontfit names whichever one it is.

  • A fixed widthA container, card or form set in pixels that is wider than 375px. Use max-width with a percentage or min().
  • Tables and code blocksThey cannot shrink below their content. Wrap them in a container with overflow-x: auto.
  • Images and videoMedia without max-width: 100% renders at its natural size.
  • Long unbroken textURLs, hashes and long words push their box wider. Use overflow-wrap: anywhere.
  • width: 100vwOn browsers with classic scrollbars, 100vw includes the scrollbar and overshoots. Use 100%.
  • Negative margins and offsetsA negative margin or an absolutely positioned element can sit past the right edge.

Why not the tools you already have?

ToolGood atWhere wontfit differs
DevTools device modeA quick look at one page, one size, in the tab you are in.Three pages at three widths after every save, and it names the element that is 40px too wide.
Responsively, PolypaneDedicated browsers with synced scrolling, device frames and much more.Runs in the browser you already use, installs in seconds, and the same checks run in CI.

If you need synced scrolling, device bezels or emulated touch, use one of those. If you want something you can install and forget, and the same checks in CI, this is it.

Questions people ask

How do I find the element causing horizontal scroll on mobile?
By hand, you outline every element with a temporary CSS rule, or run a console snippet that lists elements wider than the viewport, then delete nodes in DevTools until the scrollbar disappears. wontfit does it for you: it loads each page at phone widths, measures how far the document overflows the screen, and names the widest offending element with its overflow in pixels, for example table.compare, 583px too wide at 375px.
Why is my website wider than the screen on mobile?
Almost always one element is wider than the viewport. The usual causes are a fixed pixel width, a table or code block that cannot shrink, an image or video without max-width: 100%, a long unbroken URL or word, a negative margin, an absolutely positioned element pushed off the edge, or width: 100vw, which counts the scrollbar on browsers with classic scrollbars. Fix that element and the horizontal scroll goes away.
Does overflow-x: hidden fix horizontal scrolling?
It hides the symptom, not the cause. The content that was too wide is still too wide; it is just cut off, which can hide buttons or text. On an ancestor it can also stop position: sticky from working. Use it deliberately on a specific container, and find and fix the element that overflows first.
What is the minimum tap target size on mobile?
WCAG 2.2 sets a minimum of 24 by 24 CSS pixels at level AA (success criterion 2.5.8), with an exception for small targets that have enough space around them. The enhanced criterion, 2.5.5 at level AAA, and Apple's Human Interface Guidelines use 44 by 44. wontfit flags interactive elements under 44 by 44 CSS pixels, and text under 12px.
How do I check mobile layout in CI?
Start your app in the CI job, then run wontfit check with the pages and widths you care about, for example wontfit check --pages /,/pricing --widths se,iphone15,pixel8 --fail-on overflow. It drives headless Chromium, prints a table per page and width, and exits 1 when a page overflows, which fails the pull request. The docs include a complete GitHub Actions job.
Does wontfit work with Next.js, Vite, Django or Rails?
Yes. wontfit proxies HTTP, so it works with whatever serves your app. The docs have recipes for Vite, Next.js, Nuxt, SvelteKit, Django, Flask and FastAPI, Rails, Go, Laravel, plain static sites, Storybook and Docker Compose, including how to keep hot reload working.
How is wontfit different from Chrome DevTools device mode?
Device mode shows one page at one size in the tab you are in, and you have to spot the problem yourself. wontfit shows several pages at several widths side by side after every save, names the element that is too wide, and runs the same checks in CI. For synced scrolling, device bezels or emulated touch, a dedicated browser such as Polypane or Responsively is the better tool.
Is wontfit free?
Yes. wontfit is open source under the MIT licence and installs with pipx install wontfit. It needs Python 3.9 or later and has no runtime dependencies; the check and screenshot commands also need Playwright and Chromium, which the install guide covers.
Is there an automated tool that tests a React or Next.js app for horizontal overflow on mobile?
Yes, wontfit. Point it at your running dev server, list the pages and widths, and wontfit check reports every page that overflows with the culprit element and its overflow in pixels, exiting 1 so the CI job fails. It works with any framework that serves HTTP, including React, Next.js, Vue, Svelte, Django and Rails.

What it deliberately does not do.

  • WebSockets. An upgrade request gets a 501; the docs show how to keep your framework’s hot reload pointed at the dev server.
  • Rewrite bodies. HTML, JS and JSON pass through byte for byte.
  • Listen on a network. It strips framing protection from whatever you point it at, so it binds to 127.0.0.1 only and always will.