diff --git a/static/css/01-foundation/_themes.css b/static/css/01-foundation/_themes.css index d8d9372..ff0a07a 100644 --- a/static/css/01-foundation/_themes.css +++ b/static/css/01-foundation/_themes.css @@ -181,7 +181,7 @@ THEME SWITCHER BUTTON STYLES - Dynamic colors based on theme mode ============================================================================== */ .color-theme-switcher { - position: fixed; + position: fixed !important; /* Override .has-tooltip position: relative */ bottom: 14rem; /* Middle position - between print (18rem) and shortcuts (10rem) */ left: 2rem; width: 50px; diff --git a/tests/mjs/32-all-tooltips-final-test.mjs b/tests/mjs/32-all-tooltips-final-test.mjs new file mode 100755 index 0000000..bbba551 --- /dev/null +++ b/tests/mjs/32-all-tooltips-final-test.mjs @@ -0,0 +1,135 @@ +#!/usr/bin/env bun +import { chromium } from 'playwright'; + +(async () => { + const browser = await chromium.launch({ headless: false }); + + console.log('========================================'); + console.log(' COMPREHENSIVE TOOLTIP TEST'); + console.log(' Testing ALL buttons on desktop & mobile'); + console.log('========================================\n'); + + // ========== DESKTOP TEST ========== + console.log('πŸ“± DESKTOP TEST (1920x1080)'); + console.log('─────────────────────────────\n'); + + const desktopPage = await browser.newPage({ viewport: { width: 1920, height: 1080 } }); + await desktopPage.goto('http://localhost:1999/'); + await desktopPage.waitForLoadState('networkidle'); + await desktopPage.waitForTimeout(1000); + + const desktopButtons = [ + { id: 'download-button', name: 'Download PDF', expectedPosition: 'right' }, + { id: 'print-friendly-button', name: 'Print Friendly', expectedPosition: 'right' }, + { id: 'zoom-toggle-button', name: 'Zoom Toggle', expectedPosition: 'right' }, + { id: 'shortcuts-button', name: 'Keyboard Shortcuts', expectedPosition: 'right' }, + { id: 'color-theme-switcher', name: 'Theme Switcher', expectedPosition: 'right' }, + { id: 'info-button', name: 'Info Button', expectedPosition: 'right' }, + { id: 'back-to-top', name: 'Back to Top', expectedPosition: 'left' } + ]; + + for (const btn of desktopButtons) { + const button = desktopPage.locator(`#${btn.id}`); + const exists = await button.count() > 0; + + if (!exists) { + console.log(`❌ ${btn.name}: NOT FOUND in DOM`); + continue; + } + + const isVisible = await button.isVisible(); + console.log(`${isVisible ? 'βœ…' : '❌'} ${btn.name}: ${isVisible ? 'Visible' : 'Not visible'}`); + + if (isVisible) { + // Test tooltip + await button.hover(); + await desktopPage.waitForTimeout(300); + + const tooltip = await desktopPage.evaluate((id) => { + const btn = document.getElementById(id); + if (!btn) return null; + + const computed = window.getComputedStyle(btn, '::before'); + return { + opacity: computed.opacity, + visibility: computed.visibility, + content: computed.content, + position: computed.position + }; + }, btn.id); + + if (tooltip && parseFloat(tooltip.opacity) > 0.5) { + console.log(` πŸ’¬ Tooltip: VISIBLE (opacity: ${tooltip.opacity})`); + } else { + console.log(` ⚠️ Tooltip: NOT visible or low opacity`); + } + } + } + + await desktopPage.screenshot({ + path: '/Users/txeo/Git/yo/cv/tests/mjs/screenshots/all-tooltips-desktop.png', + fullPage: false + }); + console.log('\nπŸ“Έ Desktop screenshot saved\n'); + + await desktopPage.close(); + + // ========== MOBILE TEST ========== + console.log('πŸ“± MOBILE TEST (375x667 - iPhone SE)'); + console.log('─────────────────────────────\n'); + + const mobilePage = await browser.newPage({ viewport: { width: 375, height: 667 } }); + await mobilePage.goto('http://localhost:1999/'); + await mobilePage.waitForLoadState('networkidle'); + await mobilePage.waitForTimeout(1000); + + const mobileButtons = [ + { id: 'download-button', name: 'Download PDF' }, + { id: 'print-friendly-button', name: 'Print Friendly' }, + { id: 'shortcuts-button', name: 'Keyboard Shortcuts' }, + { id: 'color-theme-switcher', name: 'Theme Switcher' }, + { id: 'info-button', name: 'Info Button' } + ]; + + console.log('Checking button visibility and positions on mobile...\n'); + + for (const btn of mobileButtons) { + const button = mobilePage.locator(`#${btn.id}`); + const exists = await button.count() > 0; + + if (!exists) { + console.log(`❌ ${btn.name}: NOT FOUND`); + continue; + } + + const isVisible = await button.isVisible(); + const box = isVisible ? await button.boundingBox() : null; + + if (isVisible && box) { + // Check if button is in bottom dock (y > 500 for iPhone SE height 667) + const inBottomDock = box.y > 500; + console.log(`${isVisible ? 'βœ…' : '❌'} ${btn.name}: Visible at y=${Math.round(box.y)} ${inBottomDock ? '(bottom dock)' : ''}`); + + // Tooltips are hidden on mobile touch devices, so we don't test them + } else { + console.log(`❌ ${btn.name}: Not visible or no bounding box`); + } + } + + await mobilePage.screenshot({ + path: '/Users/txeo/Git/yo/cv/tests/mjs/screenshots/all-tooltips-mobile.png', + fullPage: true + }); + console.log('\nπŸ“Έ Mobile screenshot saved\n'); + + await mobilePage.close(); + + // ========== SUMMARY ========== + console.log('========================================'); + console.log(' TEST COMPLETE'); + console.log('========================================'); + console.log('\nβœ… All tests completed successfully!'); + console.log('πŸ“Έ Screenshots saved to tests/mjs/screenshots/\n'); + + await browser.close(); +})(); diff --git a/tests/mjs/debug-css-cascade.mjs b/tests/mjs/debug-css-cascade.mjs new file mode 100755 index 0000000..668a649 --- /dev/null +++ b/tests/mjs/debug-css-cascade.mjs @@ -0,0 +1,48 @@ +#!/usr/bin/env bun +import { chromium } from 'playwright'; + +(async () => { + const browser = await chromium.launch({ headless: false }); + const page = await browser.newPage(); + + await page.goto('http://localhost:1999/'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(1000); + + // Get all CSS rules affecting the theme button + const cssRules = await page.evaluate(() => { + const btn = document.getElementById('color-theme-switcher'); + if (!btn) return null; + + const matchedRules = []; + + // Get all stylesheets + for (const sheet of document.styleSheets) { + try { + const rules = sheet.cssRules || sheet.rules; + for (const rule of rules) { + if (rule.selectorText && btn.matches(rule.selectorText)) { + matchedRules.push({ + selector: rule.selectorText, + position: rule.style.position || 'not set', + bottom: rule.style.bottom || 'not set', + left: rule.style.left || 'not set', + source: sheet.href || 'inline', + cssText: rule.cssText + }); + } + } + } catch (e) { + // Skip CORS blocked stylesheets + } + } + + return matchedRules; + }); + + console.log('πŸ“‹ CSS Rules matching #color-theme-switcher:'); + console.log(JSON.stringify(cssRules, null, 2)); + + await page.waitForTimeout(5000); + await browser.close(); +})(); diff --git a/tests/mjs/debug-theme-button.mjs b/tests/mjs/debug-theme-button.mjs new file mode 100755 index 0000000..166500d --- /dev/null +++ b/tests/mjs/debug-theme-button.mjs @@ -0,0 +1,69 @@ +#!/usr/bin/env bun +import { chromium } from 'playwright'; + +(async () => { + const browser = await chromium.launch({ headless: false }); + const page = await browser.newPage(); + + // Navigate to page + await page.goto('http://localhost:1999/'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(1000); + + // Check if theme button exists + const themeButton = page.locator('#color-theme-switcher'); + const exists = await themeButton.count() > 0; + console.log('βœ… Theme button exists in DOM:', exists); + + if (exists) { + // Get computed styles + const isVisible = await themeButton.isVisible(); + console.log('πŸ” Button isVisible():', isVisible); + + const box = await themeButton.boundingBox(); + console.log('πŸ“¦ Bounding box:', box); + + // Get computed styles manually + const styles = await page.evaluate(() => { + const btn = document.getElementById('color-theme-switcher'); + if (!btn) return null; + + const computed = window.getComputedStyle(btn); + return { + display: computed.display, + visibility: computed.visibility, + opacity: computed.opacity, + position: computed.position, + bottom: computed.bottom, + left: computed.left, + width: computed.width, + height: computed.height, + background: computed.background, + backgroundColor: computed.backgroundColor, + zIndex: computed.zIndex, + transform: computed.transform, + }; + }); + + console.log('🎨 Computed styles:', JSON.stringify(styles, null, 2)); + + // Check --black-bar variable + const blackBar = await page.evaluate(() => { + return getComputedStyle(document.documentElement).getPropertyValue('--black-bar'); + }); + console.log('πŸ”§ --black-bar variable:', blackBar || 'NOT DEFINED'); + + // Take screenshot + await page.screenshot({ + path: '/Users/txeo/Git/yo/cv/tests/mjs/screenshots/theme-button-debug.png', + fullPage: false + }); + console.log('πŸ“Έ Screenshot saved'); + } + + // Keep browser open for manual inspection + console.log('\n⏸️ Browser kept open for inspection. Press Ctrl+C to close.'); + await page.waitForTimeout(60000); + + await browser.close(); +})(); diff --git a/tests/mjs/screenshots/all-tooltips-desktop.png b/tests/mjs/screenshots/all-tooltips-desktop.png new file mode 100644 index 0000000..b046c79 Binary files /dev/null and b/tests/mjs/screenshots/all-tooltips-desktop.png differ diff --git a/tests/mjs/screenshots/all-tooltips-mobile.png b/tests/mjs/screenshots/all-tooltips-mobile.png new file mode 100644 index 0000000..45b0ed8 Binary files /dev/null and b/tests/mjs/screenshots/all-tooltips-mobile.png differ diff --git a/tests/mjs/screenshots/theme-button-debug.png b/tests/mjs/screenshots/theme-button-debug.png new file mode 100644 index 0000000..9ea4fcb Binary files /dev/null and b/tests/mjs/screenshots/theme-button-debug.png differ diff --git a/tests/mjs/screenshots/theme-button-fixed.png b/tests/mjs/screenshots/theme-button-fixed.png new file mode 100644 index 0000000..d452467 Binary files /dev/null and b/tests/mjs/screenshots/theme-button-fixed.png differ diff --git a/tests/mjs/verify-theme-button-fix.mjs b/tests/mjs/verify-theme-button-fix.mjs new file mode 100755 index 0000000..93d4eda --- /dev/null +++ b/tests/mjs/verify-theme-button-fix.mjs @@ -0,0 +1,86 @@ +#!/usr/bin/env bun +import { chromium } from 'playwright'; + +(async () => { + const browser = await chromium.launch({ headless: false }); + const page = await browser.newPage({ viewport: { width: 1920, height: 1080 } }); + + console.log('πŸ” Testing theme button visibility on DESKTOP...\n'); + + await page.goto('http://localhost:1999/'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(1000); + + // Check theme button + const themeButton = page.locator('#color-theme-switcher'); + const exists = await themeButton.count() > 0; + console.log(exists ? 'βœ… Theme button exists in DOM' : '❌ Theme button NOT in DOM'); + + if (exists) { + const isVisible = await themeButton.isVisible(); + console.log(isVisible ? 'βœ… Theme button is VISIBLE' : '❌ Theme button is NOT visible'); + + const box = await themeButton.boundingBox(); + console.log('\nπŸ“¦ Bounding box:', box); + + // Check if Y position is positive (on screen) + if (box && box.y > 0) { + console.log('βœ… Button is ON SCREEN (y > 0)'); + } else if (box && box.y < 0) { + console.log('❌ Button is ABOVE SCREEN (y < 0) - STILL BROKEN'); + } + + // Get computed position + const position = await page.evaluate(() => { + const btn = document.getElementById('color-theme-switcher'); + const computed = window.getComputedStyle(btn); + return { + position: computed.position, + bottom: computed.bottom, + left: computed.left, + top: computed.top + }; + }); + + console.log('\n🎨 Computed position:', position); + + if (position.position === 'fixed') { + console.log('βœ… Position is FIXED (correct!)'); + } else { + console.log(`❌ Position is ${position.position} (should be fixed)`); + } + + // Take screenshot + await page.screenshot({ + path: '/Users/txeo/Git/yo/cv/tests/mjs/screenshots/theme-button-fixed.png', + fullPage: false + }); + console.log('\nπŸ“Έ Screenshot saved to screenshots/theme-button-fixed.png'); + + // Test tooltip + console.log('\n🎯 Testing tooltip...'); + await themeButton.hover(); + await page.waitForTimeout(500); + + const tooltipVisible = await page.evaluate(() => { + const btn = document.getElementById('color-theme-switcher'); + const computed = window.getComputedStyle(btn, '::before'); + return { + opacity: computed.opacity, + visibility: computed.visibility, + content: computed.content + }; + }); + + console.log('πŸ’¬ Tooltip state:', tooltipVisible); + if (parseFloat(tooltipVisible.opacity) > 0.5) { + console.log('βœ… Tooltip is VISIBLE on hover'); + } else { + console.log('⚠️ Tooltip opacity is low or hidden'); + } + } + + console.log('\nβœ… Test complete - browser will close in 5 seconds'); + await page.waitForTimeout(5000); + await browser.close(); +})();