diff --git a/styles/base/global.css b/styles/base/global.css index 7d9c450..eb62334 100644 --- a/styles/base/global.css +++ b/styles/base/global.css @@ -81,24 +81,23 @@ main#maincontent { * backgroundColor is updated at runtime by * static/js/modules/ChromeTint.js. * - * visibility: hidden keeps the elements invisible to the user (so - * they don't appear as dark bars over the page content) while iOS - * Safari still samples their backgroundColor. The WebKit source - * explicitly notes that visibility: hidden elements ARE sampled. + * opacity: 0 makes the elements fully transparent to the user (so + * they don't appear as dark bars over the page content) while the + * backgroundColor is still readable by iOS Safari's sampler. * * The elements are kept as small as possible (12px tall) so they * stay well within the iOS safe-area insets. They have no background * by default — they only get a backgroundColor when the matching * element (header / footer) is in view. pointer-events: none ensures - * they never intercept clicks even if made visible. + * they never intercept clicks. * * (iOS 26 ignores ; this technique is * what replaced it.) */ .chrome-tint { + opacity: 0; pointer-events: none; position: fixed; - visibility: hidden; z-index: 9999; } diff --git a/tests/chrome-tint.spec.js b/tests/chrome-tint.spec.js index 4a6206f..a5512e1 100644 --- a/tests/chrome-tint.spec.js +++ b/tests/chrome-tint.spec.js @@ -61,7 +61,7 @@ test.describe("chrome tint", () => { width: Math.round(topR.width), widthPct: Math.round((topR.width / winW) * 100), position: topCs.position, - visibility: topCs.visibility, + opacity: parseFloat(topCs.opacity), }, bottom: { bottomFromViewport: Math.round(winH - bottomR.bottom), @@ -69,25 +69,25 @@ test.describe("chrome tint", () => { width: Math.round(bottomR.width), widthPct: Math.round((bottomR.width / winW) * 100), position: bottomCs.position, - visibility: bottomCs.visibility, + opacity: parseFloat(bottomCs.opacity), }, }; }); // Top element: position fixed, top ≤ 4px, width >= 80%, height >= 12px, - // visibility hidden (so it never appears as a dark bar over page content). + // opacity 0 (so it never appears as a dark bar over page content). expect(layout.top.position).toBe("fixed"); expect(layout.top.top).toBeLessThanOrEqual(4); expect(layout.top.widthPct).toBeGreaterThanOrEqual(80); expect(layout.top.height).toBeGreaterThanOrEqual(12); - expect(layout.top.visibility).toBe("hidden"); + expect(layout.top.opacity).toBe(0); // Bottom element: position fixed, within 3px of viewport bottom, width >= 80%, - // visibility hidden. + // opacity 0. expect(layout.bottom.position).toBe("fixed"); expect(layout.bottom.bottomFromViewport).toBeLessThanOrEqual(3); expect(layout.bottom.widthPct).toBeGreaterThanOrEqual(80); - expect(layout.bottom.visibility).toBe("hidden"); + expect(layout.bottom.opacity).toBe(0); }); test("fixed chrome tint elements are not visible to the user (no extra dark bars)", async ({ @@ -106,18 +106,18 @@ test.describe("chrome tint", () => { { timeout: 2000 }, ); // Even though the elements have background colors, they should - // be invisible to the user (visibility: hidden) so they don't - // show as dark bars over the page content. - const visibility = await page.evaluate(() => { + // be invisible to the user (opacity: 0) so they don't show as + // dark bars over the page content. + const opacities = await page.evaluate(() => { const top = document.querySelector(".chrome-tint--top"); const bottom = document.querySelector(".chrome-tint--bottom"); return { - top: getComputedStyle(top).visibility, - bottom: getComputedStyle(bottom).visibility, + top: parseFloat(getComputedStyle(top).opacity), + bottom: parseFloat(getComputedStyle(bottom).opacity), }; }); - expect(visibility.top).toBe("hidden"); - expect(visibility.bottom).toBe("hidden"); + expect(opacities.top).toBe(0); + expect(opacities.bottom).toBe(0); }); test("viewport meta includes viewport-fit=cover", async ({ page }) => {