fix: Differentiate zoom and info button colors, fix button visibility in responsive mode

Issues fixed:
1. Zoom button now uses purple color (rgba(155, 89, 182, 0.7)) instead of blue
2. Info button keeps blue color (rgba(52, 152, 219, 0.7))
3. Both buttons now show distinct colors in default state, not just on hover
4. Device detection now considers viewport width, not just user agent
5. Buttons no longer hide in responsive mode at desktop viewport sizes

Changes:
- Updated zoom-toggle-btn to use purple background color
- Updated info-button to use blue background color (explicit, not var)
- Modified device-detection.js to check viewport width (≤900px) in addition to UA
- Added resize listener to update device class dynamically
- Created test (67-button-colors-and-visibility-test.mjs) to verify fixes

Testing:
- Desktop (1278px): All buttons visible with distinct colors
- Mobile (375px): Zoom/shortcuts hidden, core buttons visible
- Device detection now viewport-aware (prevents hiding at desktop sizes)
This commit is contained in:
juanatsap
2025-11-25 06:41:56 +00:00
parent 0be8972429
commit da483ae9f1
4 changed files with 311 additions and 14 deletions
+279
View File
@@ -0,0 +1,279 @@
#!/usr/bin/env node
import { chromium } from 'playwright';
/**
* TEST: Button Colors and Visibility Across Viewports
*
* Verifies:
* 1. Zoom button is purple (#9b59b6), not blue
* 2. Info button is blue (#3498db) - different from zoom
* 3. All buttons visible at desktop viewport (1278px) even in mobile mode
* 4. Zoom/keyboard buttons properly hidden at mobile viewport (<900px)
*/
const VIEWPORTS = {
desktop: {
width: 1278,
height: 800,
name: 'Desktop (1278px)',
// Simulate mobile user agent to test that buttons still show at desktop size
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
},
mobile: {
width: 375,
height: 667,
name: 'Mobile Portrait (375px)',
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
}
};
/**
* Convert RGB color to hex format for comparison
*/
function rgbToHex(rgb) {
const match = rgb.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
if (!match) return rgb;
const r = parseInt(match[1]);
const g = parseInt(match[2]);
const b = parseInt(match[3]);
return '#' + [r, g, b].map(x => {
const hex = x.toString(16);
return hex.length === 1 ? '0' + hex : hex;
}).join('');
}
async function testViewport(browser, viewport) {
const context = await browser.newContext({
viewport: { width: viewport.width, height: viewport.height },
userAgent: viewport.userAgent,
hasTouch: viewport.hasTouch
});
const page = await context.newPage();
await page.goto('http://localhost:1999/?lang=en');
await page.waitForLoadState('networkidle');
// Wait a bit for device detection to run
await page.waitForTimeout(500);
const results = await page.evaluate(() => {
// Get all button elements
const downloadBtn = document.querySelector('.download-btn');
const printBtn = document.querySelector('.print-friendly-btn');
const themeBtn = document.querySelector('.color-theme-switcher');
const zoomToggle = document.querySelector('.zoom-toggle-btn');
const shortcutsBtn = document.querySelector('.shortcuts-btn');
const infoBtn = document.querySelector('.info-button');
const backToTop = document.querySelector('.back-to-top');
// Get device class
const hasDesktopClass = document.documentElement.classList.contains('is-desktop');
const hasMobileClass = document.documentElement.classList.contains('is-mobile-device');
// Helper to check if element is visible
function isVisible(el) {
if (!el) return false;
const style = window.getComputedStyle(el);
return style.display !== 'none' &&
style.visibility !== 'hidden' &&
style.opacity !== '0';
}
// Get button colors (background-color)
function getBackgroundColor(el) {
if (!el) return 'N/A';
return window.getComputedStyle(el).backgroundColor;
}
return {
viewport: {
width: window.innerWidth,
height: window.innerHeight
},
deviceClass: {
isDesktop: hasDesktopClass,
isMobile: hasMobileClass
},
buttons: {
download: {
exists: !!downloadBtn,
visible: isVisible(downloadBtn),
color: getBackgroundColor(downloadBtn)
},
print: {
exists: !!printBtn,
visible: isVisible(printBtn),
color: getBackgroundColor(printBtn)
},
theme: {
exists: !!themeBtn,
visible: isVisible(themeBtn),
color: getBackgroundColor(themeBtn)
},
zoomToggle: {
exists: !!zoomToggle,
visible: isVisible(zoomToggle),
color: getBackgroundColor(zoomToggle),
hoverColor: zoomToggle ? window.getComputedStyle(zoomToggle, ':hover').backgroundColor : 'N/A'
},
shortcuts: {
exists: !!shortcutsBtn,
visible: isVisible(shortcutsBtn),
color: getBackgroundColor(shortcutsBtn)
},
info: {
exists: !!infoBtn,
visible: isVisible(infoBtn),
color: getBackgroundColor(infoBtn)
},
backToTop: {
exists: !!backToTop,
visible: isVisible(backToTop),
color: getBackgroundColor(backToTop)
}
}
};
});
await page.screenshot({
path: `tests/screenshots/button-test-${viewport.width}px.png`,
fullPage: false
});
await context.close();
return results;
}
(async () => {
const browser = await chromium.launch({ headless: true });
console.log('🎨 Button Colors and Visibility Test\n');
console.log('Testing: Color differentiation and responsive visibility\n');
const allResults = {};
const failures = [];
for (const [key, viewport] of Object.entries(VIEWPORTS)) {
console.log(`📱 Testing: ${viewport.name}`);
console.log('='.repeat(60));
const results = await testViewport(browser, viewport);
allResults[key] = results;
console.log(`\nViewport: ${results.viewport.width}×${results.viewport.height}`);
console.log(`Device class: ${results.deviceClass.isDesktop ? 'Desktop' : 'Mobile'}\n`);
// Print button status
console.log('Button Visibility:');
console.log(` 📥 Download: ${results.buttons.download.visible ? '✅ Visible' : '❌ Hidden'}`);
console.log(` 🖨️ Print: ${results.buttons.print.visible ? '✅ Visible' : '❌ Hidden'}`);
console.log(` 🎨 Theme: ${results.buttons.theme.visible ? '✅ Visible' : '❌ Hidden'}`);
console.log(` 🔍 Zoom: ${results.buttons.zoomToggle.visible ? '✅ Visible' : '❌ Hidden'}`);
console.log(` ⌨️ Shortcuts: ${results.buttons.shortcuts.visible ? '✅ Visible' : '❌ Hidden'}`);
console.log(` ️ Info: ${results.buttons.info.visible ? '✅ Visible' : '❌ Hidden'}`);
console.log(` ⬆️ Back: ${results.buttons.backToTop.visible ? '✅ Visible' : '❌ Hidden'}`);
// Color verification for zoom and info buttons
if (results.buttons.zoomToggle.exists) {
const zoomColor = rgbToHex(results.buttons.zoomToggle.color);
console.log(`\n 🔍 Zoom color: ${zoomColor} ${results.buttons.zoomToggle.color}`);
}
if (results.buttons.info.exists) {
const infoColor = rgbToHex(results.buttons.info.color);
console.log(` ️ Info color: ${infoColor} ${results.buttons.info.color}`);
}
// Validation rules
const issues = [];
if (viewport.width > 900) {
// DESKTOP VIEWPORT (even with mobile UA)
console.log('\n📋 Validating desktop viewport (>900px)...');
// All buttons should be visible (except back-to-top which appears after scrolling)
if (!results.buttons.download.visible) issues.push('Download button not visible at desktop size');
if (!results.buttons.print.visible) issues.push('Print button not visible at desktop size');
if (!results.buttons.theme.visible) issues.push('Theme button not visible at desktop size');
if (!results.buttons.zoomToggle.visible) issues.push('Zoom toggle button not visible at desktop size');
if (!results.buttons.shortcuts.visible) issues.push('Shortcuts button not visible at desktop size');
if (!results.buttons.info.visible) issues.push('Info button not visible at desktop size');
// Note: Back-to-top button is hidden until user scrolls - this is expected behavior
// Device class should be desktop at this viewport
if (!results.deviceClass.isDesktop) {
issues.push('Device class should be "is-desktop" at viewport >900px');
}
// Verify zoom button is NOT blue (should be purple #9b59b6 or similar)
if (results.buttons.zoomToggle.exists) {
const zoomColor = rgbToHex(results.buttons.zoomToggle.color);
const infoColor = results.buttons.info.exists ? rgbToHex(results.buttons.info.color) : null;
// The colors should be different
if (zoomColor === infoColor) {
issues.push(`Zoom and Info buttons have the SAME color (${zoomColor}) - they should be different!`);
}
}
} else {
// MOBILE VIEWPORT
console.log('\n📋 Validating mobile viewport (≤900px)...');
// Core buttons should be visible
if (!results.buttons.download.visible) issues.push('Download button not visible on mobile');
if (!results.buttons.print.visible) issues.push('Print button not visible on mobile');
if (!results.buttons.theme.visible) issues.push('Theme button not visible on mobile');
if (!results.buttons.info.visible) issues.push('Info button not visible on mobile');
if (!results.buttons.backToTop.visible) issues.push('Back to top button not visible on mobile');
// Zoom and shortcuts should be hidden on mobile
if (results.buttons.zoomToggle.visible) issues.push('Zoom toggle should be hidden at mobile viewport');
if (results.buttons.shortcuts.visible) issues.push('Shortcuts button should be hidden at mobile viewport');
// Device class should be mobile
if (!results.deviceClass.isMobile) {
issues.push('Device class should be "is-mobile-device" at viewport ≤900px with mobile UA');
}
}
if (issues.length > 0) {
console.log('\n❌ ISSUES FOUND:');
issues.forEach(issue => {
console.log(` - ${issue}`);
failures.push(`${viewport.name}: ${issue}`);
});
} else {
console.log('\n✅ All checks passed');
}
console.log('\n');
}
// Final summary
console.log('='.repeat(60));
console.log('FINAL SUMMARY\n');
if (failures.length === 0) {
console.log('✅ ALL TESTS PASSED\n');
console.log('Button colors are distinct:');
console.log(' 🔍 Zoom button: Purple (#9b59b6)');
console.log(' ️ Info button: Blue (#3498db)');
console.log('\nButton visibility works correctly:');
console.log(' ✅ All buttons visible at desktop viewport (>900px)');
console.log(' ✅ Zoom/shortcuts hidden at mobile viewport (≤900px)');
console.log(' ✅ Device detection considers viewport width\n');
} else {
console.log(`${failures.length} ISSUE(S) FOUND:\n`);
failures.forEach((failure, i) => {
console.log(`${i + 1}. ${failure}`);
});
console.log('');
}
await browser.close();
process.exit(failures.length === 0 ? 0 : 1);
})();