What a frame proves, and what a frame quietly fakes
The width of an iframe is the viewport width for everything inside it. Media queries read it, vw units resolve against it, container queries measure their containers inside it, and a grid collapses at exactly the width your stylesheet says. For layout work, the preview above is honest.
Four things stay desktop no matter how narrow the frame gets, and each one has burned somebody shipping on a Friday:
- Pointer type. Your mouse is still a mouse, so
@media (hover: hover)and(pointer: fine)keep matching. A dropdown built on hover looks perfect here and becomes unreachable on a touchscreen. - Device pixel ratio. The frame renders at your monitor's ratio. A phone at 3x will pull the 3x image from your
srcsetand it will weigh three times what you saw. - User agent. Server side device sniffing sees your desktop browser, so any markup branching on the UA string sends you the desktop variant.
- Browser engine. Everything here runs your engine. Safari on iOS has its own rules for
100vh, sticky headers under the address bar, and date inputs, none of which reproduce in a Chrome or Firefox frame.
Use this for the pass where you find the width the layout breaks at. Use a real handset, or a device lab, for the pass where you find out whether the thing is usable.
Why the frame stays blank on most big sites
Point the tool at a bank, a search engine or a social network and you get an empty rectangle. The site refused embedding, and the refusal arrives in one of two response headers.
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self' https://toolexe.comX-Frame-Options is the older switch with three states: DENY blocks every embed, SAMEORIGIN allows only pages from the same origin, and the long dead ALLOW-FROM is ignored by current browsers. CSP frame-ancestors replaced it, takes a list of origins, and wins whenever both headers are present.
No browser extension or proxy trick around this belongs in a page like this one, because the header exists to stop clickjacking: an attacker stacking an invisible frame of your logged in bank under a fake button. The workable path is the other direction. On a staging host you own, permit the origin you preview from:
# nginx, staging only
add_header Content-Security-Policy "frame-ancestors 'self' https://toolexe.com" always;Then remove the line before that config reaches production. A staging site with loose framing rules and a copy of production data is its own problem.
Round numbers are device trivia, not breakpoints
The preset row above exists because people look for it, not because 393 pixels deserves a media query. Device widths churn every release cycle, and a stylesheet pinned to last year's iPhone is a stylesheet you rewrite next year.
Drag the slider slowly instead and watch the frame. The moment a headline wraps to three lines, a nav crushes into itself, or a table starts a horizontal scrollbar, you have found a breakpoint the content asked for. Round the number outward a little and write it down:
@media (min-width: 47.5rem) {.card-grid { grid-template-columns: repeat(2, 1fr); }}Writing the query in rem rather than px means the breakpoint moves when a visitor raises their default font size, which is what a reader at 24px base text actually needs. Sizing in px pins the layout to a text size somebody has already told the browser they do not want.
Two widths are worth a look even so. Around 320 pixels is the narrowest screen still in circulation, and a layout with a fixed 340 pixel element overflows there. Past roughly 1600 pixels, a text column with no max-width stretches to a line length nobody finishes reading.
CSS pixels, device pixels, and the number in the readout
The readout above prints both. A phone advertising a 1080 by 2400 screen reports a viewport of 360 by 800 to CSS, because its device pixel ratio is 3 and every CSS pixel covers a 3 by 3 block of hardware.
| Hardware | Physical pixels | Ratio | CSS viewport |
|---|---|---|---|
| Common Android | 1080 × 2400 | 3x | 360 × 800 |
| iPhone 15 | 1179 × 2556 | 3x | 393 × 852 |
| iPad, portrait | 1536 × 2048 | 2x | 768 × 1024 |
| 1080p laptop | 1920 × 1080 | 1x | 1920 × 1080 |
Media queries only ever see the right hand column, so build against those numbers. The ratio matters in one place: images. Ship a 360 pixel wide slot with a 360 pixel image and it looks soft on every phone made in the last decade. Let the browser pick:
<img src="hero-360.jpg"
srcset="hero-360.jpg 360w, hero-720.jpg 720w, hero-1080.jpg 1080w"
sizes="(min-width: 47.5rem) 50vw, 100vw"
width="360" height="240" alt="…">The width and height attributes are not decoration. They give the browser an aspect ratio to reserve space with, which is the difference between a page that settles and a page that shoves the paragraph you started reading down the screen.
The one tag that makes all of this apply
Without a viewport meta tag, mobile browsers pretend to be around 980 pixels wide, render the desktop layout, then shrink the whole thing to fit. Your media queries never fire, and the site looks like a scaled down screenshot.
<meta name="viewport" content="width=device-width, initial-scale=1">Nothing else belongs in that line. Adding maximum-scale=1 or user-scalable=no stops a reader zooming in on small text, which fails WCAG 1.4.4 and is a common finding in accessibility audits. If the layout only holds together because zoom is disabled, the layout is the bug.
The frame above sidesteps this entirely, since an iframe width is already the viewport width. A page with no viewport tag previews fine here and still breaks on a phone, so check the <head> as well as the preview.
Container queries changed where the breakpoint belongs
A media query asks how wide the window is. A card in a narrow sidebar has the same problem as a card on a phone, and the window width tells you nothing about the sidebar.
.sidebar, .main { container-type: inline-size; }
@container (min-width: 30rem) {.card { display: grid; grid-template-columns: 8rem 1fr; }}The card now switches on its own width, so one component behaves correctly in a sidebar, a modal and a full width section without a stack of viewport rules describing every context. Support landed across Chrome, Safari and Firefox in 2023, which puts it in reach for most projects now.
Media queries keep the jobs container queries never took over: page level structure, print styles, prefers-reduced-motion, prefers-color-scheme, and the pointer and hover checks worth remembering while you drag the slider above with a mouse.
Where this page stops being enough
A short list, so the preview does not get trusted past the point it earns:
- Any site sending a framing header shows nothing here. That covers a large share of the web.
- Pages behind a login stay behind it. The frame carries no session, so you preview the signed out view.
- Scripts inside the frame read their own dimensions correctly, but a script measuring
window.screenornavigator.userAgentreads your desktop. - Touch gestures, momentum scrolling, the iOS address bar collapsing on scroll, and on screen keyboards pushing fixed footers around have no equivalent in a frame.
- Performance numbers mean nothing here. You are on a desktop CPU over your own connection, which is the opposite of the mid range phone on patchy mobile data your slowest traffic arrives on.
