Safari color probes
Single-variable test pages behind the iOS Safari Gotchas
skill's claims about theme-color, the status bar and the strip under Safari's toolbar. Every page has a magenta
body (#ff00ff), white and gray stripes, and a readout box. Where a page sets theme-color,
it is a color that appears nowhere else on it, so any trace of it would show.
What they showed on iOS 27 (simulator, and a real iPhone 15 Pro Max with website tinting on unless noted):
theme-color's color never appeared: not in the status bar, the toolbar or the strip, on any page, device or tab layout.- The status bar keeps sampling after load. A recolored or inserted header, a new
bodycolor, a fixed dim or a<dialog>re-tinted it within about 2 seconds, with website tinting on and off. - Safari samples the element 4px inside the top edge if it spans at least 90% of the width, takes alpha 0.75 and
up as opaque and blends below that. Otherwise it falls back to
body's color. This is the rule in joe-bell/apple-web-app, and every page here agreed with it. - With a blurred sticky header, the phone sometimes fell back to black instead: 10 of 14 loads with website
tinting on, whatever the tag said, and 0 of 3 with it off. Identical copies disagreed, so it's a race. The
simulator gave
body's color every time. - So a solid, full-width header can hold the status bar while
bodydrives the strip under the toolbar (combo), and there is no per-URL color cache (cache). - Only an opaque sticky element sized to the viewport flattened the strip under the toolbar, and it filled with that element's
own color. No page showed a flat
bodystrip under the collapsed pill.
The rules that follow from these are in the skill's SKILL.md, sections 1, 2 and 10.
How to run them
- Reloading is fine for the status bar. Safari keeps no status-bar color per URL on iOS 27 (see
cachebelow). Flat fill across loads is untested, so for those pages a fresh copy on a new path is safer. - Expanded set: screenshot within 20 seconds of opening. Each page scrolls itself to y=1500 at 30 seconds, so a second shot gives you mid-page.
- Collapsed set: these don't scroll themselves. Swipe up once so the bars collapse (a programmatic scroll
never does), check that the readout's
iHjumps to100lvh, then screenshot. - Note your Safari tab layout and whether "Allow Website Tinting" is on (Settings › Apps › Safari).
- New results, on any device or iOS version, are welcome as a GitHub issue.
Toolbar expanded
Simulator: iPhone 18 Pro, iOS 27.0, Compact tab bar. iPhone: 15 Pro Max, iOS 27, Bottom tab bar.
| Probe | theme-color | On the page | Simulator | iPhone 15 Pro Max |
|---|---|---|---|---|
| control | none | nothing sticky | status bar; stripes to the last row | status bar; identical to meta |
| meta | #00ff00 | nothing sticky | identical to control | identical to control, apart from the clock |
| header-blur | #00ff00 | translucent red sticky header with backdrop-filter blur | status bar | status bar |
| header-blur-nometa | none | translucent red sticky header with backdrop-filter blur | status bar | status bar |
| header-blur-white | #ffffff | translucent red sticky header with backdrop-filter blur | not run | status bar |
| header-blur-blue | #0000ff | translucent red sticky header with backdrop-filter blur | not run | status bar |
| header-blur-body | #ff00ff | translucent red sticky header with backdrop-filter blur | not run | status bar |
| header-blur-dark | #203040 | translucent red sticky header with backdrop-filter blur | not run | status bar |
| repeat-green | #00ff00 | copy of header-blur on a fresh path | not run | status bar |
| repeat-blue | #0000ff | copy of header-blur-blue | not run | status bar (was ) |
| repeat-white | #ffffff | copy of header-blur-white | not run | status bar |
| repeat-none | none | copy of header-blur-nometa | not run | status bar (was ) |
| flat-red | #00ff00 | opaque red sticky 100dvh stage (§1) | flat fill, status bar, red-tinted toolbar | not run |
| flat-clear | #00ff00 | transparent sticky 100dvh stage | no flat fill; status bar | not run |
| flat-clear-nometa | none | transparent sticky 100dvh stage | identical to flat-clear | not run |
Toolbar collapsed
Simulator: iPhone 18 Pro (Compact, all seven) and 15 Pro Max (Bottom: control, meta, flat-red). iPhone: 15 Pro Max, Bottom tab bar.
| Probe | theme-color | On the page | Simulator | iPhone 15 Pro Max |
|---|---|---|---|---|
| control | none | nothing sticky | stripes under the pill to the last row | stripes to the last row; frosted status bar with a magenta cast |
| meta | #00ff00 | nothing sticky | identical to control | not run |
| header-blur | #00ff00 | translucent red sticky header with backdrop-filter blur | stripes to the last row; status bar | stripes to the last row; status bar |
| header-blur-nometa | none | translucent red sticky header with backdrop-filter blur | identical to header-blur | not run |
| flat-red | #00ff00 | opaque red sticky 100dvh stage (§1) | flat fill on both devices; red-tinted pill | not run |
| flat-clear | #00ff00 | transparent sticky 100dvh stage | stripes to the last row | stripes to the last row |
| flat-clear-nometa | none | transparent sticky 100dvh stage | identical to flat-clear | not run |
On flat-clear the transparent stage takes up the first screen of the page, so body shows above the
stripes until you scroll past it. That's expected, and the bottom edge is what the page tests.
Does anything re-tint the bars after load?
Each page changes one thing 5 seconds after it loads; the readout says BEFORE or AFTER. Take one screenshot before 5 seconds and one after 8. Every change re-tinted the status bar, on the phone and in the simulator.
| Probe | On the page | Simulator | iPhone 15 Pro Max |
|---|---|---|---|
| dim | fixed full-screen dim, rgba(0,0,0,.5), added at 5s | → status bar; strip switches to flat fill | → status bar; strip and toolbar go flat |
| dialog | native <dialog> opened with showModal() at 5s | same as dim | same as dim |
| header-recolor | solid sticky header, blue at load, green at 5s | → | → (second load) |
| header-added | no header at load; solid blue sticky header inserted at 5s | → | → |
| body-then-nudge | body turned green at 5s, then a 1px scroll at 7s | → , before the scroll | → |
The same two tests on fresh paths with "Allow Website Tinting" off. The status bar still followed; only the toolbar's tint went away.
| Probe | On the page | Simulator | iPhone 15 Pro Max |
|---|---|---|---|
| body-then-nudge | copy of resample/body-then-nudge, for "Allow Website Tinting" off | not run | → ; toolbar untinted |
| header-recolor | copy of resample/header-recolor, for "Allow Website Tinting" off | not run | → ; toolbar untinted |
Which element does the sampler count?
Static pages that test the rule in joe-bell/apple-web-app: Safari samples 4px in from the edge, counts an element only if it spans at least 90% of the width, and blends a color below 0.75 alpha. Screenshot within 20 seconds.
| Probe | On the page | Simulator | iPhone 15 Pro Max |
|---|---|---|---|
| solid | control: solid full-width blue sticky header | ||
| alpha50 | header at rgba(0,0,255,.5), no blur | blended over body | blended over body |
| alpha80 | header at rgba(0,0,255,.8), no blur | taken as opaque | taken as opaque |
| width85 | solid header, 85% of the width | not counted | not counted |
| width95 | solid header, 95% of the width | ||
| top8 | solid header stuck at top: 8px, with body showing above it | ||
| center-pill | solid header plus a narrow fixed red pill at the top center | (not red, not blue) | (not red, not blue) |
Follow-ups
combo passes if the status bar stays cyan while the strip under the toolbar turns dark; it did.
cache tests SKILL.md §3's old claim that Safari keeps colors per URL; the second load came up green, so it doesn't.
| Probe | On the page | Simulator | iPhone 15 Pro Max |
|---|---|---|---|
| combo | §10's fix on one page: solid cyan masthead; body turns #111 at 5s, then the page scrolls to its end | not run | status bar before and after; strip below the page turned dark: pass |
| cache | body color set from ?c= before first paint; load ?c=blue, then the same path with ?c=green | not run | then : no per-URL cache |
Why did the status bar go black?
Fresh copies of the blurred-header page with no theme-color. Open the dark- pages with the
phone in Dark Mode, and the notint- pages in Light Mode with "Allow Website Tinting" off. Dark Mode
made no difference; with tinting off, the black never appeared.
| Probe | On the page | Simulator | iPhone 15 Pro Max |
|---|---|---|---|
| dark-a | blurred translucent header, no theme-color | not run | (Dark Mode) |
| dark-b | blurred translucent header, no theme-color | not run | (Dark Mode) |
| dark-c | blurred translucent header, no theme-color | not run | (Dark Mode) |
| notint-a | blurred translucent header, no theme-color | not run | (tinting off) |
| notint-b | blurred translucent header, no theme-color | not run | (tinting off) |
| notint-c | blurred translucent header, no theme-color | not run | (tinting off) |