Battle report · iOS 26 Safari · June 2026

The white bar that would not die.

You shipped a beautiful site. You open it on an iPhone and there it is: a glaring white strip over the notch, tinting the safe area in iOS 26 Safari and blocking your edge-to-edge dreams. viewport-fit=cover didn't fix it. theme-color didn't fix it. safe-area-inset-top padding didn't fix it. We spent a night fighting it. This is everything we tried, everything that failed, and the recipe that finally worked.

The crime scene

The symptom is always the same. You added viewport-fit=cover. You set your background colors. You read three blog posts and a WebKit bug thread. And iOS Safari still draws an opaque bar across the top of the screen, right where the notch lives, in a shade of white you never picked.

Here is the part nobody tells you up front: that bar contains zero of your pixels. No amount of overflow, clip, z-index or prayer will remove it, because it is Safari's own chrome, painted on top of your page. You cannot style it. You can only influence what color Safari picks for it, and whether Safari decides to show your content through it.

With iOS 26 and the Liquid Glass redesign, Apple rewrote the rules for how that color gets picked. They also forgot to write the rules down anywhere. The community reverse engineered most of it, and we verified the rest the hard way, on a real device, at 4am.

What Apple changed in iOS 26

For years the answer was a meta tag. You set theme-color, Safari painted its chrome in that color, everyone went home early. Safari 26 ended that arrangement. The tag still parses. Safari simply stopped caring about its static value.

Instead, Safari now derives the tint from your page itself, in this order: first it samples the background-color of any position: fixed or position: sticky element near the top edge of the viewport. If it finds none, it falls back to the background color of your html and body elements. If those are transparent, you get white. White like the bar you are staring at.

The sampler has opinions. It ignores absolutely positioned children. It ignores pseudo elements. It ignores anything that is in normal flow, even if it visually touches the top of the screen. It does read elements with opacity: 0 and pointer-events: none, so your hidden modal backdrop is busy tinting Safari's toolbar right now. Only display: none takes an element out of the game.

The graveyard of fixes that don't fix it

Things we tried, in roughly increasing order of desperation:

viewport-fit=cover alone. Necessary, insufficient. It extends your layout under the notch and unlocks env(safe-area-inset-top), which is great, but the tint layer sits on top of your extended layout and keeps being white.

Overflow and clipping tricks. The bar is browser chrome. Clipping your own content harder does nothing except clip your own content.

apple-mobile-web-app-status-bar-style: black-translucent. Applies to home screen web apps only. Safari the browser reads it, nods politely, and ignores it.

A static theme-color meta tag. See above. Safari 26 treats it as decorative.

Offsetting the sticky header below the safe area. This one hurt. We moved the safe area offset from the header's padding into its top and margin-top, so the sticky box never touched the screen edge. The community write-ups say the sampler only looks within a few pixels of the top. Our device said otherwise. The white bar survived. Sticky elements get sampled no matter where they stick.

The experiment that cracked it

The breakthrough came from a tiny A/B test on our own page. We wired up a script that fades the html and body backgrounds (plus the meta tag) toward our footer color as you approach the bottom of the page. Then we scrolled to the footer and looked at both ends of the screen.

Bottom of the screen: perfectly tinted in footer gray. Top of the screen: still glaring white, even though the fallback colors were now dark. Same page, same colors, two different results. The only difference between those two edges was that one of them had a sticky element parked on it. Our transparent sticky nav was feeding the sampler, the sampler was finding nothing it could use, and Safari was resolving that to opaque white.

We switched the nav to position: absolute and reloaded. Clean notch, content flowing underneath, tint following the page. Case closed. The navbar did it.

The recipe that works

Step 1: viewport-fit=cover. Still the entry ticket.

<meta name="viewport"
  content="width=device-width, initial-scale=1, viewport-fit=cover" />

Step 2: split your backgrounds. Give html and body an explicit color and treat them as the tint layer Safari reads. Paint the actual page on your app container, so the tint layer can change color without your content noticing.

html, body {
  /* the tint layer: Safari samples this as its fallback */
  background-color: #fafbff;
}
#app {
  /* the page itself paints here */
  min-height: 100dvh;
  background-color: #fafbff;
}

Step 3: drive the tint from JavaScript. Update the meta tag and both root backgrounds together on scroll. Safari picks the changes up, which means the safe areas can match whatever content is currently near them. We fade to the footer color over the last viewport of scroll:

const TOP = "#fafbff";    // page background
const BOTTOM = "#1e2023"; // footer color

function applyTint(color) {
  let meta = document.querySelector('meta[name="theme-color"]');
  if (!meta) {
    meta = document.createElement("meta");
    meta.setAttribute("name", "theme-color");
    document.head.appendChild(meta);
  }
  meta.setAttribute("content", color);
  document.documentElement.style.backgroundColor = color;
  document.body.style.backgroundColor = color;
}

function mix(a, b, t) {
  const ch = (h, i) => parseInt(h.slice(i, i + 2), 16);
  const hex = (n) => Math.round(n).toString(16).padStart(2, "0");
  return "#" + hex(ch(a, 1) + (ch(b, 1) - ch(a, 1)) * t)
             + hex(ch(a, 3) + (ch(b, 3) - ch(a, 3)) * t)
             + hex(ch(a, 5) + (ch(b, 5) - ch(a, 5)) * t);
}

let raf = null;
window.addEventListener("scroll", () => {
  if (raf) return;
  raf = requestAnimationFrame(() => {
    raf = null;
    const remaining = document.documentElement.scrollHeight
      - window.innerHeight - window.scrollY;
    const t = Math.min(1, Math.max(0, 1 - remaining / window.innerHeight));
    applyTint(mix(TOP, BOTTOM, t));
  });
}, { passive: true });

Step 4: keep fixed and sticky elements away from the top edge. This is the step everyone skips and the reason the other steps seem broken. Any position: fixed or position: sticky element will hijack the top tint, and a transparent one hijacks it to white. An absolutely positioned nav that scrolls away with the page keeps the notch clean.

/* clean notch */
header { position: absolute; top: env(safe-area-inset-top); }

/* white bar generator */
header { position: sticky; top: 0; background: transparent; }

Step 5: hide hidden overlays properly. Modal backdrops waiting in the DOM at opacity: 0 still feed the sampler. Use display: none until they actually open.

But I want my sticky navbar

We hear you. We wanted ours too. The honest answer is that on iOS 26 you are choosing between a permanently pinned position: sticky nav and a clean notch. You do not get both, because the sampler keys on the position value itself, even when the element is nowhere near the edge it tints.

If the nav matters more, keep it sticky and accept the bar. You can at least make the bar less offensive by giving the sticky element a real background color that matches your design instead of leaving it transparent, since transparent is what resolves to white.

If the notch matters more, there are two patterns that keep navigation reachable without triggering the sampler. One: a reveal-on-scroll-up nav, absolutely positioned, translated to the viewport with a transform while the user scrolls upward. Safari ignores transforms. Two: the simple version, a nav that lives at the top of the page and scrolls away, the way half the design-forward web already works. Your visitors will survive.

Testing without losing your mind

Test on a real iPhone running iOS 26. The Simulator reproduces some of the tinting behavior and quietly skips the rest, which is a fantastic way to ship a fix that does not work.

Know the two states. At scroll position zero, Safari paints the fallback color and will not composite your actual content behind the status bar. Once the page has any scroll at all, it can. If your first screen is a full-bleed image and the top still shows a flat color, that is this, and the workaround is a small scroll runway you jump past on load.

And remember the user setting. Safari has an Allow Website Tinting toggle, and when it is off, the chrome reverts to system colors no matter how clever your setup is. Design something that still looks intentional in plain white and plain black, because some fraction of your audience lives there.

The white bar, answered.

Why is there a white bar at the top of my website on iPhone?
Because iOS 26 Safari paints its own tint layer over the status bar / safe area, and it picks the color from your page. If it finds a fixed or sticky element near the top edge, it samples that element's background color. If that element is transparent, Safari gives up and paints the bar white. Your CSS is fine. Your header is the suspect.
How do I make the iOS Safari safe area match my page background?
Three things together: set viewport-fit=cover on the viewport meta tag, give html and body an explicit background-color (not transparent), and update both the theme-color meta tag and those backgrounds from JavaScript on scroll. Safari samples that combination for the safe area tint, so the status bar follows whatever content is currently near the top.
Does the theme-color meta tag still work in iOS 26 Safari?
Not on its own. Safari 26 stopped honoring a static theme-color meta tag for its chrome. What still works in practice is updating the meta tag AND the html and body background colors together from JavaScript. Safari picks up those changes on scroll, which is how you get the tint to follow your content.
Why doesn't viewport-fit=cover fix the white bar?
viewport-fit=cover does its job: it extends your layout viewport under the notch and makes env(safe-area-inset-top) return real values. But the tint layer Safari paints on top of your page is a separate thing. You need viewport-fit=cover as a prerequisite, then you still have to win the tinting game.
Can I have a sticky header without the white bar on iOS?
Not with position: sticky or position: fixed, no. Safari samples those elements for the status bar tint no matter what top offset you give them. We tested offsetting a sticky header below the safe area and the white bar survived. The workarounds are an absolutely positioned nav that scrolls away, or a nav that follows the viewport with a transform, which Safari's sampler ignores.
Does apple-mobile-web-app-status-bar-style black-translucent help?
Only if your site runs as a home screen web app. In the regular Safari browser that meta tag is ignored completely, so it will not remove the white bar for normal visitors.
How do I test Safari status bar tinting properly?
On a real iPhone running iOS 26, with Allow Website Tinting enabled in Safari settings. The Simulator misses several of these behaviors, and desktop Safari misses all of them. Also test at scroll position zero and mid-scroll, because Safari treats those states differently.

Stuck on a bug like this?

We debug the weird stuff for a living. Bring us the bar, the glitch, or the half-finished app, and get it back working in a week.