Files
cv-site/tests/mjs/56-landscape-debug-test.mjs
juanatsap 639a99b8ea fix: Complete mobile UX overhaul - horizontal scroll, landscape mode, and centering
Fixes three critical mobile issues across Android and iPhone:

1. HORIZONTAL SCROLL ELIMINATION (CRITICAL)
   - Added overflow-x: hidden to html and body globally
   - Landscape: Aggressive max-width: 100vw on all containers
   - Fixed .cv-page, .cv-container overflow issues
   - Prevented scale transform from causing overflow

2. LANDSCAPE MODE COMPLETE FIX
   - Single column layout enforced (grid-template-columns: 1fr)
   - Photo visible and sized appropriately (max-width: 120px)
   - Hamburger menu visible and accessible
   - Action bar simplified (center controls hidden)
   - Language selector compact
   - Smaller buttons (40px) with recalculated positions
   - Typography reduced for better fit

3. BUTTON CENTERING (VERIFIED WORKING)
   - Confirmed perfect centering in portrait mode
   - Android: 290px bar centered at viewport center (188px)
   - iPhone: Identical centering behavior
   - Landscape: 240px bar for 5 buttons (40px each)

Files modified:
- static/css/01-foundation/_reset.css - Global overflow-x fix
- static/css/05-responsive/_breakpoints.css - Comprehensive landscape overhaul
- tests/mjs/54-landscape-mode-test.mjs - Landscape validation (Android + iPhone)
- tests/mjs/55-button-centering-test.mjs - Button centering validation
- tests/mjs/56-landscape-debug-test.mjs - Media query debugging tool
- tests/mjs/57-horizontal-scroll-debug.mjs - Scroll width debugging tool

Test results:
 Portrait: Buttons perfectly centered (0px offset)
 Landscape: Single column, no horizontal scroll
 Hamburger visible and accessible in landscape
 Photo visible in all orientations
 Android + iPhone parity confirmed
2025-11-25 05:09:05 +00:00

65 lines
2.6 KiB
JavaScript
Executable File

#!/usr/bin/env node
import { chromium } from 'playwright';
const LANDSCAPE_VIEWPORT = { width: 667, height: 375 };
(async () => {
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: LANDSCAPE_VIEWPORT,
userAgent: 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36',
hasTouch: true
});
const page = await context.newPage();
await page.goto('http://localhost:1999/?lang=en&view=extended');
await page.waitForLoadState('networkidle');
const debug = await page.evaluate(() => {
const page1 = document.querySelector('.page-1 .page-content');
const computed = window.getComputedStyle(page1);
// Check media query matches
const landscapeMatch = window.matchMedia('(max-width: 915px) and (orientation: landscape)').matches;
const maxWidth915 = window.matchMedia('(max-width: 915px)').matches;
const orientationLandscape = window.matchMedia('(orientation: landscape)').matches;
const maxWidth768 = window.matchMedia('(max-width: 768px)').matches;
return {
viewport: {
width: window.innerWidth,
height: window.innerHeight,
ratio: window.innerWidth / window.innerHeight
},
mediaQueries: {
'max-width: 915px': maxWidth915,
'orientation: landscape': orientationLandscape,
'max-width: 915px AND landscape': landscapeMatch,
'max-width: 768px': maxWidth768
},
gridColumns: computed.gridTemplateColumns,
bodyOverflow: window.getComputedStyle(document.body).overflowX,
htmlOverflow: window.getComputedStyle(document.documentElement).overflowX
};
});
console.log('Landscape Debug Info:\n');
console.log('Viewport:');
console.log(` • Width: ${debug.viewport.width}px`);
console.log(` • Height: ${debug.viewport.height}px`);
console.log(` • Ratio: ${debug.viewport.ratio.toFixed(2)} (${debug.viewport.ratio > 1 ? 'LANDSCAPE' : 'PORTRAIT'})\n`);
console.log('Media Query Matches:');
Object.entries(debug.mediaQueries).forEach(([query, matches]) => {
console.log(`${query}: ${matches ? '✅ YES' : '❌ NO'}`);
});
console.log(`\nComputed Styles:`);
console.log(` • Grid columns: ${debug.gridColumns}`);
console.log(` • Body overflow-x: ${debug.bodyOverflow}`);
console.log(` • HTML overflow-x: ${debug.htmlOverflow}\n`);
await browser.close();
})();