safari26-liquid-glass — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited safari26-liquid-glass (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
iOS 26 and iPadOS 26 gave Safari a "Liquid Glass" chrome: the status bar (top) and the address/tab bar (bottom) are translucent overlays that show whatever the page paints behind them, live, as you scroll. Apple dropped `theme-color` as the bar tint — the bars just reflect the page now.
This is the iPhone Safari layout model. It also applies on iPadOS — and there it's gated by the browser window width, not the device: a narrow Safari window (Split View, Slide Over, a portrait small iPad, or just a narrow window) gets the iPhone model with the glass bars; a wide one gets the desktop-ish model. So decide "is this the glass model?" by width, not by user-agent (e.g. innerWidth <= 760), because the same iPad flips between the two.
Think of the screen as two viewports:
bars. 100vh, 100lvh, and window.innerHeight measure this. `innerHeight` is constant — it does NOT change when the bars show/hide or the keyboard opens.
window.visualViewport.height and 100svh measure this; `100dvh` tracks it dynamically (it shrinks when the keyboard opens).
The bars sit over the edges of the layout viewport and composite whatever DOM pixels are painted there.
html/body background is dark, the bars are dark. Paint it your page colour and the bars take that colour. This is also the flash you see on first load.
scrollable document paints behind the bars. A page sized to exactly the viewport does not bleed — it stops at the bar edge; the background shows behind the bars.
keyboard). Measure layout with innerHeight; detect keyboard/bar state with visualViewport.
flex:1 / overflow:auto child inside a dvh-sized box won't scroll (it grows to its content and the gesture falls through to the page). Use a definite px height for scroll containers.
layout change — changing a background colour or repainting a canvas alone leaves the bar stale**. A 1px scroll nudge forces a re-composite.
vh (large) ≠innerHeight/visualViewport (small), so a vh calculation leaves a strip.
position:fixed is the big oneA position:fixed element is clipped to the visual viewport, so:
edge. (A fixed button anchored with env(safe-area-inset-bottom) still has its drop-shadow chopped where the bar starts.)
bleed — once it's on screen, the rest of the page starts clipping at the bar edge too, and the clip can persist after the fixed element is gone** (until a scroll/layout change). We reproduced this just by opening & closing a position:fixed bottom-sheet + backdrop.
So the damage is sometimes just the element (cropped shadow), sometimes the entire page (collapsed bleed) — depending on the element and where it sits. Treat any position:fixed as a bleed-breaker on this model and verify. (A "fixed full-screen backdrop" that appears to tint the bars is a trap — an in-flow background behind it is doing the tinting; the fixed layer adds nothing and crops its children.)
the input visible.
innerHeight stays constant; visualViewport.height shrinks. iOS fires **onevisualViewport resize per transition* (one on open, one on close — not* one per animation frame).
visualViewport.offsetTopdoesn't reset to 0 and the height can stay ~24px short — so content looks "slid up by the keyboard height." Affects fixed/sticky elements and any scroll-positioned layout. (Even apple.com is affected; iOS 26.1 beta improved it.)
same whether the keyboard is open or closed — so you can restore it the moment the field blurs, without waiting for the (late) close event.
A plain, tall, scrolling page — normal background, content in flow, ordinary fixed header/footer, native inputs — bleeds correctly, tints the bars, and recovers from the keyboard on its own. You usually need to do nothing. A 1990s long-article page renders perfectly. If you see black bars or clipping on a simple page, you've added something from §4.
Immersive/edge-to-edge layouts, fixed full-screen canvases, custom drawers/ modals/sheets, gesture-driven panning, themeable/live backgrounds, canvas effects — these break the "just works" assumptions. Problems we hit:
body background.shell (height:100vh + overflow:hidden, a 100dvh flex app, or position:fixed; inset:0). It can't bleed.
position:fixed chrome, drawers,or backdrops (see §2).
dvh/vh (indefinite); the gesture scrolls the page instead.
and drifts an immersive/centred layout.
First, always: paint html/body your page colour in CSS (not JS — JS is too late and you get a first-paint flash). If the colour is dynamic, bake a sane default into CSS and update it live afterwards.
If you only need the bars to match a colour (content needn't go under them): the CSS background above is often enough. Make sure no opaque dark element covers the bar region.
If you need content to bleed under the bars (immersive/edge-to-edge):
innerHeight + 2·PAD px,PAD≈200) painted the page colour, and centre it by measurement (not vh) on mount + resize + orientationchange. It then overflows ≈PAD under each bar and fills both at once (a screen-height band can't — pushing it up to cover the top uncovers the bottom).
innerHeight), not `dvh` — dvhshrinks with the keyboard and would resize the whole thing.
region), with normal insets + env(safe-area-inset-*). Never `position: fixed` — that re-breaks the bleed and re-crops shadows.
touch-action:none on the interactivesurface (canvas does its own pan/zoom) + overscroll-behavior:none.
Drawers / bottom-sheets / modals (the part people get wrong):
position:absolute inside the band, NOTfixed (fixed collapses the bleed). Anchor the sheet under the bottom glass and add equal bottom padding** inside its scroll area so content clears the bar.
pin the header with position:absolute (outside the scroll body) so only the body rubber-bands.
overscroll-behavior:contain onthe sheet's scroll body + touch-action:none on the backdrop — not position:fixed/overflow:hidden on body (both break the bleed or jump the scroll).
Live/themeable background colour: update the document background live, then do a 1px scroll nudge (debounced) to force the bars to re-composite — otherwise they lag until the next scroll/layout.
Soft keyboard: capture the scroll position on focusin (page is settled then); on focusout restore it — you can do this immediately because innerHeight is constant, so the target is already correct. Back it up by also restoring when the visualViewport resize reports the height back near full, and finish with a 1px scroll jiggle to reset the stuck offsetTop. Don't pin the scroll while the keyboard animates (iOS re-scrolls and undoes it).
CanvasRenderingContext2D.filter (e.g. ctx.filter='blur(4px)') is ignored on iOS/iPadOS Safari (WebKit #198416) — the effect silently doesn't render, even though it works in Chrome. Use `shadowBlur` instead (draw the shape far off-canvas and keep only its blurred shadow, tinted as needed). SVG feGaussian Blur is fine; it's only the canvas 2D filter property that's unsupported.
Established iteratively, on-device, building the Made in Lantau "Lantau Type Map" (a full-screen canvas studio) for iOS Safari 26. Every fact in §2 and every problem in §4 was reproduced and fixed on a real iPhone/iPad.
References: WebKit #297779 (keyboard/visualViewport offset), WebKit #198416 (canvas ctx.filter).
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.