From 945928d930574b79385cad0c452642b6ad846667 Mon Sep 17 00:00:00 2001 From: juanatsap Date: Tue, 25 Nov 2025 05:24:11 +0000 Subject: [PATCH] fix: Landscape mode photo layout and button backdrop improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed two critical landscape mode issues on mobile devices: 1. Button backdrop blur bar now shows in landscape mode - Added iOS-style blur backdrop with 70px height for landscape - Consistent visual experience between portrait and landscape - Supports dark mode with appropriate theming 2. Photo positioned on the right in landscape with better sizing - Changed from stacked single-column to two-column grid layout - Photo now positioned on right side (180px vs previous 120px) - Text (name, experience, intro) on left, photo on right - Better use of horizontal space in landscape orientation - Left-aligned text for cleaner layout with photo on right Test results (iPhone SE & iPhone 12 landscape): ✅ Two-column layout with photo on right ✅ Photo properly sized and positioned (180px) ✅ Button backdrop visible with blur effect ✅ No horizontal scroll ✅ All landscape tests passing Test: tests/mjs/59-landscape-photo-and-backdrop-test.mjs --- static/css/05-responsive/_breakpoints.css | 70 +++++++-- .../59-landscape-photo-and-backdrop-test.mjs | 138 ++++++++++++++++++ 2 files changed, 198 insertions(+), 10 deletions(-) create mode 100755 tests/mjs/59-landscape-photo-and-backdrop-test.mjs diff --git a/static/css/05-responsive/_breakpoints.css b/static/css/05-responsive/_breakpoints.css index 8503452..dcb6959 100644 --- a/static/css/05-responsive/_breakpoints.css +++ b/static/css/05-responsive/_breakpoints.css @@ -701,6 +701,30 @@ ======================================== */ @media (max-width: 915px) and (orientation: landscape) { + /* iOS-style blur backdrop for buttons in landscape */ + @supports (backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px)) { + .fixed-buttons-backdrop { + display: block !important; + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: 70px; /* Slightly smaller for landscape */ + background: rgba(255, 255, 255, 0.8); + backdrop-filter: blur(20px) saturate(180%); + -webkit-backdrop-filter: blur(20px) saturate(180%); + border-top: 0.5px solid rgba(0, 0, 0, 0.1); + z-index: 98; + pointer-events: none; + } + + [data-color-theme="dark"] .fixed-buttons-backdrop, + [data-color-theme="auto"] .fixed-buttons-backdrop { + background: rgba(30, 30, 30, 0.8); + border-top: 0.5px solid rgba(255, 255, 255, 0.1); + } + } + /* CRITICAL: Prevent horizontal scroll in landscape */ * { max-width: 100vw !important; @@ -783,33 +807,59 @@ .cv-name { font-size: 1.4rem !important; - text-align: center; + text-align: left !important; /* Left-aligned with photo on right */ } .years-experience { font-size: 1em !important; - text-align: center; + text-align: left !important; /* Left-aligned with photo on right */ } - /* Reduce photo size in landscape to ~50% width but keep visible */ - .cv-header-content { - flex-direction: column; - align-items: center; - gap: 0.5rem; + /* Two-column layout in landscape: text left, photo right */ + .cv-header-left { + display: grid !important; + grid-template-columns: 1fr auto !important; + grid-template-rows: auto auto auto !important; + gap: 0.5rem 1rem !important; + align-items: start !important; } + /* Name spans column 1, row 1 */ + .cv-name { + grid-column: 1; + grid-row: 1; + } + + /* Experience years spans column 1, row 2 */ + .years-experience { + grid-column: 1; + grid-row: 2; + margin: 0 !important; + } + + /* Intro text spans column 1, row 3 */ + .intro-text { + grid-column: 1; + grid-row: 3; + margin: 0 !important; + } + + /* Photo on the right side, spans all 3 rows */ .cv-photo { + grid-column: 2; + grid-row: 1 / 4; position: static !important; width: auto !important; height: auto !important; - max-width: 120px !important; - margin: 0.5rem auto !important; - text-align: center; + max-width: 180px !important; /* Bigger than before (was 120px) */ + margin: 0 !important; + align-self: start; } .cv-photo img { width: 100% !important; height: auto !important; + border-radius: 8px; } /* Compact action bar - keep hamburger menu visible */ diff --git a/tests/mjs/59-landscape-photo-and-backdrop-test.mjs b/tests/mjs/59-landscape-photo-and-backdrop-test.mjs new file mode 100755 index 0000000..7766ea2 --- /dev/null +++ b/tests/mjs/59-landscape-photo-and-backdrop-test.mjs @@ -0,0 +1,138 @@ +#!/usr/bin/env node + +import { chromium } from 'playwright'; + +const LANDSCAPE_VIEWPORT = { width: 667, height: 375 }; // iPhone SE landscape +const IPHONE_12_LANDSCAPE = { width: 844, height: 390 }; // iPhone 12 landscape + +(async () => { + const browser = await chromium.launch({ headless: true }); + + console.log('🧪 Testing Landscape Mode: Photo Layout & Button Backdrop\n'); + + // TEST 1: iPhone SE Landscape + console.log('📱 TEST 1: iPhone SE Landscape (667×375)\n'); + const seContext = await browser.newContext({ + viewport: LANDSCAPE_VIEWPORT, + userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1', + hasTouch: true + }); + const sePage = await seContext.newPage(); + + await sePage.goto('http://localhost:1999/?lang=en&view=extended'); + await sePage.waitForLoadState('networkidle'); + + const seLayout = await sePage.evaluate(() => { + const headerLeft = document.querySelector('.cv-header-left'); + const photo = document.querySelector('.cv-photo'); + const backdrop = document.querySelector('.fixed-buttons-backdrop'); + const cvName = document.querySelector('.cv-name'); + + const headerGrid = headerLeft ? window.getComputedStyle(headerLeft).gridTemplateColumns : 'N/A'; + const twoColumns = headerGrid.includes(' '); // Has space = multiple columns + + const photoRect = photo ? photo.getBoundingClientRect() : null; + const nameRect = cvName ? cvName.getBoundingClientRect() : null; + + // Photo should be on the right side (higher x position than name) + const photoOnRight = photoRect && nameRect && photoRect.left > nameRect.left; + + return { + viewport: `${window.innerWidth}×${window.innerHeight}`, + headerGrid: headerGrid, + twoColumns: twoColumns, + photoWidth: photoRect ? Math.round(photoRect.width) : 0, + photoLeft: photoRect ? Math.round(photoRect.left) : 0, + nameLeft: nameRect ? Math.round(nameRect.left) : 0, + photoOnRight: photoOnRight, + nameAlign: cvName ? window.getComputedStyle(cvName).textAlign : 'N/A', + backdropVisible: backdrop ? window.getComputedStyle(backdrop).display !== 'none' : false, + backdropHeight: backdrop ? window.getComputedStyle(backdrop).height : 'N/A', + hasHorizontalScroll: document.body.scrollWidth > window.innerWidth + }; + }); + + console.log('iPhone SE Landscape Layout:'); + console.log(` • Viewport: ${seLayout.viewport}`); + console.log(` • Header grid: ${seLayout.headerGrid}`); + console.log(` • Two-column layout: ${seLayout.twoColumns ? '✅' : '❌'}`); + console.log(` • Photo width: ${seLayout.photoWidth}px`); + console.log(` • Photo on right side: ${seLayout.photoOnRight ? '✅' : '❌'} (Photo: ${seLayout.photoLeft}px, Name: ${seLayout.nameLeft}px)`); + console.log(` • Name alignment: ${seLayout.nameAlign}`); + console.log(` • Button backdrop visible: ${seLayout.backdropVisible ? '✅' : '❌'}`); + console.log(` • Backdrop height: ${seLayout.backdropHeight}`); + console.log(` • Has horizontal scroll: ${seLayout.hasHorizontalScroll ? '❌ FAIL' : '✅'}\n`); + + await sePage.screenshot({ + path: 'tests/screenshots/landscape-photo-se.png', + fullPage: true + }); + + await seContext.close(); + + // TEST 2: iPhone 12 Landscape + console.log('📱 TEST 2: iPhone 12 Landscape (844×390)\n'); + const iphone12Context = await browser.newContext({ + viewport: IPHONE_12_LANDSCAPE, + userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1', + hasTouch: true + }); + const iphone12Page = await iphone12Context.newPage(); + + await iphone12Page.goto('http://localhost:1999/?lang=en&view=extended'); + await iphone12Page.waitForLoadState('networkidle'); + + const iphone12Layout = await iphone12Page.evaluate(() => { + const headerLeft = document.querySelector('.cv-header-left'); + const photo = document.querySelector('.cv-photo'); + const backdrop = document.querySelector('.fixed-buttons-backdrop'); + const cvName = document.querySelector('.cv-name'); + + const headerGrid = headerLeft ? window.getComputedStyle(headerLeft).gridTemplateColumns : 'N/A'; + const twoColumns = headerGrid.includes(' '); + + const photoRect = photo ? photo.getBoundingClientRect() : null; + const nameRect = cvName ? cvName.getBoundingClientRect() : null; + const photoOnRight = photoRect && nameRect && photoRect.left > nameRect.left; + + return { + viewport: `${window.innerWidth}×${window.innerHeight}`, + headerGrid: headerGrid, + twoColumns: twoColumns, + photoWidth: photoRect ? Math.round(photoRect.width) : 0, + photoLeft: photoRect ? Math.round(photoRect.left) : 0, + nameLeft: nameRect ? Math.round(nameRect.left) : 0, + photoOnRight: photoOnRight, + nameAlign: cvName ? window.getComputedStyle(cvName).textAlign : 'N/A', + backdropVisible: backdrop ? window.getComputedStyle(backdrop).display !== 'none' : false, + backdropHeight: backdrop ? window.getComputedStyle(backdrop).height : 'N/A', + hasHorizontalScroll: document.body.scrollWidth > window.innerWidth + }; + }); + + console.log('iPhone 12 Landscape Layout:'); + console.log(` • Viewport: ${iphone12Layout.viewport}`); + console.log(` • Header grid: ${iphone12Layout.headerGrid}`); + console.log(` • Two-column layout: ${iphone12Layout.twoColumns ? '✅' : '❌'}`); + console.log(` • Photo width: ${iphone12Layout.photoWidth}px`); + console.log(` • Photo on right side: ${iphone12Layout.photoOnRight ? '✅' : '❌'} (Photo: ${iphone12Layout.photoLeft}px, Name: ${iphone12Layout.nameLeft}px)`); + console.log(` • Name alignment: ${iphone12Layout.nameAlign}`); + console.log(` • Button backdrop visible: ${iphone12Layout.backdropVisible ? '✅' : '❌'}`); + console.log(` • Backdrop height: ${iphone12Layout.backdropHeight}`); + console.log(` • Has horizontal scroll: ${iphone12Layout.hasHorizontalScroll ? '❌ FAIL' : '✅'}\n`); + + await iphone12Page.screenshot({ + path: 'tests/screenshots/landscape-photo-iphone12.png', + fullPage: true + }); + + await iphone12Context.close(); + + const allPassed = seLayout.twoColumns && seLayout.photoOnRight && seLayout.backdropVisible && !seLayout.hasHorizontalScroll && + iphone12Layout.twoColumns && iphone12Layout.photoOnRight && iphone12Layout.backdropVisible && !iphone12Layout.hasHorizontalScroll; + + console.log(`${allPassed ? '✅' : '❌'} Tests ${allPassed ? 'PASSED' : 'FAILED'}\n`); + + await browser.close(); + process.exit(allPassed ? 0 : 1); +})();