QA: glass overlay opacity, audit refinements, debug scripts
3 changed files +26 −3
added
qa/overflow-debug.mjs
+22 −0
@@ -0,0 +1,22 @@ | |||||
| 1 | +import { chromium } from "@playwright/test"; | ||||
| 2 | +const b = await chromium.launch(); | ||||
| 3 | +const p = await b.newPage({ viewport: { width: 390, height: 844 }, isMobile: true, hasTouch: true }); | ||||
| 4 | +await p.goto((process.env.QA_BASE ?? "http://localhost:3000") + (process.argv[2] ?? "/"), { waitUntil: "networkidle" }); | ||||
| 5 | +await p.waitForTimeout(800); | ||||
| 6 | +for (let y = 0; y < 9000; y += 800) { | ||||
| 7 | + await p.evaluate((yy) => window.scrollTo(0, yy), y); | ||||
| 8 | + await p.waitForTimeout(250); | ||||
| 9 | +} | ||||
| 10 | +const out = await p.evaluate(() => { | ||||
| 11 | + const vw = window.innerWidth; const res = []; | ||||
| 12 | + const inScroller = (el) => { let q = el.parentElement; while (q && q !== document.body) { const o = getComputedStyle(q).overflowX; if (o === 'auto' || o === 'scroll') return true; q = q.parentElement; } return false; }; | ||||
| 13 | + for (const el of document.querySelectorAll("body *")) { | ||||
| 14 | + const r = el.getBoundingClientRect(); | ||||
| 15 | + if (r.width > 0 && r.right > vw + 1 && r.left < vw && !el.closest(".snap-row, .marquee, [data-allow-overflow]") && !inScroller(el)) { | ||||
| 16 | + res.push({ tag: el.tagName, cls: (el.className || "").toString().slice(0, 120), text: el.textContent?.trim().slice(0, 60), right: Math.round(r.right), parent: (el.parentElement?.className || "").toString().slice(0, 100) }); | ||||
| 17 | + } | ||||
| 18 | + } | ||||
| 19 | + return { docOverflow: document.documentElement.scrollWidth - vw, res: res.slice(0, 8) }; | ||||
| 20 | +}); | ||||
| 21 | +console.log(JSON.stringify(out, null, 1)); | ||||
| 22 | +await b.close(); | ||||
modified
qa/responsive-qa.mjs
+2 −1
@@ -49,12 +49,13 @@ const AUDIT = `(() => { | |||||
| 49 | const vw = window.innerWidth; | 49 | const vw = window.innerWidth; | ||
| 50 | const doc = document.documentElement; | 50 | const doc = document.documentElement; | ||
| 51 | const out = { docOverflow: Math.max(doc.scrollWidth, document.body.scrollWidth) - vw, overflowing: [], smallTargets: [], clipped: [] }; | 51 | const out = { docOverflow: Math.max(doc.scrollWidth, document.body.scrollWidth) - vw, overflowing: [], smallTargets: [], clipped: [] }; | ||
| 52 | + const inScroller = (el) => { let p = el.parentElement; while (p && p !== document.body) { const o = getComputedStyle(p).overflowX; if (o === 'auto' || o === 'scroll' || o === 'hidden' || o === 'clip') return true; p = p.parentElement; } return false; }; | ||||
| 52 | const visible = (el) => { const r = el.getBoundingClientRect(); const s = getComputedStyle(el); return r.width > 0 && r.height > 0 && s.visibility !== 'hidden' && s.opacity !== '0' && r.bottom > 0 && r.top < window.innerHeight; }; | 53 | const visible = (el) => { const r = el.getBoundingClientRect(); const s = getComputedStyle(el); return r.width > 0 && r.height > 0 && s.visibility !== 'hidden' && s.opacity !== '0' && r.bottom > 0 && r.top < window.innerHeight; }; | ||
| 53 | const desc = (el) => (el.tagName.toLowerCase() + (el.id ? '#' + el.id : '') + (el.getAttribute('aria-label') ? '[' + el.getAttribute('aria-label') + ']' : '') + ' "' + (el.textContent || '').trim().slice(0, 40).replace(/\\s+/g, ' ') + '"'); | 54 | const desc = (el) => (el.tagName.toLowerCase() + (el.id ? '#' + el.id : '') + (el.getAttribute('aria-label') ? '[' + el.getAttribute('aria-label') + ']' : '') + ' "' + (el.textContent || '').trim().slice(0, 40).replace(/\\s+/g, ' ') + '"'); | ||
| 54 | for (const el of document.querySelectorAll('body *')) { | 55 | for (const el of document.querySelectorAll('body *')) { | ||
| 55 | if (!visible(el)) continue; | 56 | if (!visible(el)) continue; | ||
| 56 | const r = el.getBoundingClientRect(); | 57 | const r = el.getBoundingClientRect(); | ||
| 57 | − if (r.right > vw + 1 && r.left < vw && !el.closest('.snap-row, .marquee, [data-allow-overflow]')) out.overflowing.push({ el: desc(el), right: Math.round(r.right) }); | 58 | + if (r.right > vw + 1 && r.left < vw && !el.closest('.snap-row, .marquee, [data-allow-overflow]') && !inScroller(el)) out.overflowing.push({ el: desc(el), right: Math.round(r.right) }); | ||
| 58 | } | 59 | } | ||
| 59 | const coarse = matchMedia('(pointer: coarse)').matches || vw < 768; | 60 | const coarse = matchMedia('(pointer: coarse)').matches || vw < 768; | ||
| 60 | if (coarse) { | 61 | if (coarse) { | ||
modified
src/app/globals.css
+2 −2
@@ -137,7 +137,7 @@ | |||||
| 137 | --bg-muted: #e9e9e5; | 137 | --bg-muted: #e9e9e5; | ||
| 138 | --bg-elevated: #ffffff; | 138 | --bg-elevated: #ffffff; | ||
| 139 | --bg-inset: #ececea; | 139 | --bg-inset: #ececea; | ||
| 140 | − --bg-overlay: rgb(247 247 245 / 0.82); | 140 | + --bg-overlay: rgb(247 247 245 / 0.9); | ||
| 141 | --fg: #14151a; | 141 | --fg: #14151a; | ||
| 142 | --fg-muted: #5c6070; | 142 | --fg-muted: #5c6070; | ||
| 143 | --fg-subtle: #8b8f9d; | 143 | --fg-subtle: #8b8f9d; | ||
@@ -186,7 +186,7 @@ | |||||
| 186 | --bg-muted: #171923; | 186 | --bg-muted: #171923; | ||
| 187 | --bg-elevated: #13151d; | 187 | --bg-elevated: #13151d; | ||
| 188 | --bg-inset: #0e0f15; | 188 | --bg-inset: #0e0f15; | ||
| 189 | − --bg-overlay: rgb(11 12 16 / 0.78); | 189 | + --bg-overlay: rgb(11 12 16 / 0.9); | ||
| 190 | --fg: #f1f2f6; | 190 | --fg: #f1f2f6; | ||
| 191 | --fg-muted: #9a9fb1; | 191 | --fg-muted: #9a9fb1; | ||
| 192 | --fg-subtle: #676d7e; | 192 | --fg-subtle: #676d7e; | ||
| 193 | 193 | ||||