From 2de3a06374d64ffac5034973cfeae227c431a2f9 Mon Sep 17 00:00:00 2001 From: Roy-Andre Date: Fri, 30 Oct 2026 07:36:52 +0100 Subject: [PATCH] Show a fingertip pointer inside the phone Replaces the mouse pointer in the framed page with a grey circle, like DevTools' touch pointer, that darkens while pressed. Hover and pointer media features are unchanged; the README says so. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CvvFqgufMiBtnuTH5srr62 --- CHANGELOG.md | 4 ++++ README.md | 3 ++- extension/overlay.js | 19 ++++++++++++++++--- test/e2e/preview.test.js | 1 + 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f07d08f..158865e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to Previewer are listed here. The format follows ## [Unreleased] +### Added + +- The mouse pointer is a fingertip inside the phone, like DevTools' touch pointer, and darkens while you press. + ## [1.0.0] - 2026-10-30 ### Added diff --git a/README.md b/README.md index bf2a32b..237288d 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,8 @@ controlled" banner on every page: - `navigator.userAgent` in JavaScript still reports desktop Chrome. Server-side detection works; client-side sniffing doesn't. -- Touch: `(hover: hover)` and `(pointer: fine)` still match, and you scroll with a mouse or trackpad. +- Touch: inside the phone the mouse pointer becomes a fingertip, but it is still a mouse. Hover effects still work, + `(hover: hover)` and `(pointer: fine)` still match, and you scroll with a mouse wheel or trackpad. - The rendering engine is Chrome's Blink, not Safari's WebKit. Previewer shows layout, not WebKit-specific bugs. If a link inside the phone leads to a different website that refuses to be shown in a frame, Chrome shows its own diff --git a/extension/overlay.js b/extension/overlay.js index 7d9928e..9ba8a60 100644 --- a/extension/overlay.js +++ b/extension/overlay.js @@ -35,6 +35,18 @@ tablet: `9:41${WIFI}100%${BATTERY}`, }; + // A fingertip like DevTools' touch pointer: grey with a light and a dark ring so + // it shows on any background, darker while pressed. Hotspot in the centre. + const touchCursor = (fill, r) => { + const svg = ``; + return `url("data:image/svg+xml,${encodeURIComponent(svg)}") 12 12, default`; + }; + const FRAME_CSS = ` + html { scrollbar-width: none; } + ::-webkit-scrollbar { display: none; } + *, *::before, *::after { cursor: ${touchCursor('rgba(70,74,82,.38)', 10)} !important; } + *:active, *:active::before, *:active::after { cursor: ${touchCursor('rgba(70,74,82,.7)', 9)} !important; }`; + let ui = null; // everything that exists while the preview is open function open({ devices, settings, css }) { @@ -265,13 +277,14 @@ } // Phones overlay their scrollbars; hide desktop scrollbars so the page gets - // its full width (Windows otherwise takes ~15px of a 402px viewport). + // its full width (Windows otherwise takes ~15px of a 402px viewport). Show a + // fingertip instead of the mouse pointer, so it looks and feels like touch. try { const sheet = new doc.defaultView.CSSStyleSheet(); - sheet.replaceSync('html { scrollbar-width: none; } ::-webkit-scrollbar { display: none; }'); + sheet.replaceSync(FRAME_CSS); doc.adoptedStyleSheets = [...doc.adoptedStyleSheets, sheet]; } catch { - // Not critical: the page just keeps its scrollbar. + // Not critical: the page keeps its scrollbar and mouse pointer. } doc.addEventListener('keydown', onKeydown, true); diff --git a/test/e2e/preview.test.js b/test/e2e/preview.test.js index 4e17b9f..eaa943c 100644 --- a/test/e2e/preview.test.js +++ b/test/e2e/preview.test.js @@ -120,6 +120,7 @@ test('shows the page at the phone width with the phone user agent', async () => assert.equal(await frame.evaluate(() => innerWidth), 402); assert.match(await frame.evaluate(() => navigator.userAgent), /Chrome/, 'JS user agent is not overridden (documented)'); assert.match(seenUserAgents.at(-1), /iPhone/); + assert.match(await frame.evaluate(() => getComputedStyle(document.querySelector('.hero')).cursor), /^url\("data:image\/svg\+xml/, 'touch pointer'); await page.waitForTimeout(300); await shoot(page, 'iphone-18-pro');