fix: Complete mobile button fixes - transparency, color, and layout
Fixes three critical mobile UI issues: 1. Theme Button Transparency (FIXED) - Changed theme button from 50% to full opacity on mobile - Updated _themes.css with rgba(x, y, z, 1) for all theme modes - Added opacity: 1 !important to mobile media query 2. Info Button Color Differentiation (FIXED) - Changed info button from green (#27ae60) to blue (#3498db) - Now visually distinct from green back-to-top button - Updated all states: default, hover, at-bottom 3. Button Layout Reorganization (FIXED) - Added .is-mobile-device rules for 5-button layout (no shortcuts) - Buttons centered without gap: Download, Print, Theme, Info, Back-to-top - Total width: 290px (5 buttons + 4 gaps) vs 350px (6 buttons) Files modified: - static/css/01-foundation/_themes.css - Primary theme button fix - static/css/04-interactive/_scroll-behavior.css - Info color + layout - static/css/color-theme.css - Mobile device positioning sync - tests/mjs/53-final-mobile-fixes-test.mjs - Comprehensive validation Test results: ✅ Shortcuts hidden on real mobile (iPhone user agent) ✅ 5 buttons evenly spaced with no gap (60px spacing) ✅ Info button blue (52, 152, 219) vs back-to-top green (39, 174, 96) ✅ Theme button full opacity (alpha: 1, opacity: 1)
This commit is contained in:
@@ -290,40 +290,47 @@
|
||||
right: auto !important;
|
||||
width: 50px !important;
|
||||
height: 50px !important;
|
||||
/* Removed opacity: 1 !important to allow .footer-hovered to work */
|
||||
opacity: 1 !important; /* Full opacity on mobile (no transparency with blur bar) */
|
||||
transform: none !important;
|
||||
/* Position in 6-button layout: Download, Print, Shortcuts, Theme, Info, Back-to-top */
|
||||
/* Total width: 6 * 50px + 5 * 10px = 350px */
|
||||
left: calc(50% + 5px) !important; /* Fourth button */
|
||||
}
|
||||
|
||||
/* Show theme colors at 50% transparency by default on mobile */
|
||||
/* Show theme colors at FULL opacity on mobile (no transparency with blur bar) */
|
||||
.color-theme-switcher[data-theme-mode="light"] {
|
||||
background: rgba(212, 178, 0, 0.5) !important; /* Gold at 50% */
|
||||
background: rgba(212, 178, 0, 1) !important; /* Gold - full opacity */
|
||||
}
|
||||
|
||||
.color-theme-switcher[data-theme-mode="dark"] {
|
||||
background: rgba(1, 60, 119, 0.5) !important; /* Blue at 50% */
|
||||
background: rgba(1, 60, 119, 1) !important; /* Blue - full opacity */
|
||||
}
|
||||
|
||||
.color-theme-switcher[data-theme-mode="auto"] {
|
||||
background: rgba(155, 89, 182, 0.5) !important; /* Purple at 50% */
|
||||
background: rgba(155, 89, 182, 1) !important; /* Purple - full opacity */
|
||||
}
|
||||
|
||||
/* Full color opacity on hover */
|
||||
/* Full color opacity on hover (already at full opacity, just add transform) */
|
||||
.color-theme-switcher:hover[data-theme-mode="light"] {
|
||||
background: rgba(212, 178, 0, 1) !important; /* Full gold opacity */
|
||||
transform: translateY(-3px) !important;
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
.color-theme-switcher:hover[data-theme-mode="dark"] {
|
||||
background: rgba(1, 60, 119, 1) !important; /* Full blue opacity */
|
||||
transform: translateY(-3px) !important;
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
.color-theme-switcher:hover[data-theme-mode="auto"] {
|
||||
background: rgba(155, 89, 182, 1) !important; /* Full purple opacity */
|
||||
transform: translateY(-3px) !important;
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
/* REAL MOBILE DEVICES: 5 buttons without shortcuts */
|
||||
/* Download, Print, Theme, Info, Back-to-top */
|
||||
/* Total width: 5 * 50px + 4 * 10px = 290px */
|
||||
.is-mobile-device .color-theme-switcher {
|
||||
left: calc(50% - 25px) !important; /* Third button (no gap) */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,12 +96,12 @@
|
||||
opacity: 1;
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
|
||||
background: #27ae60;
|
||||
background: #3498db; /* Blue - different from back-to-top green */
|
||||
}
|
||||
|
||||
.info-button.at-bottom {
|
||||
opacity: 1;
|
||||
background: #27ae60;
|
||||
background: #3498db; /* Blue - different from back-to-top green */
|
||||
}
|
||||
|
||||
.info-button:active {
|
||||
@@ -160,7 +160,7 @@
|
||||
}
|
||||
|
||||
.info-button {
|
||||
background: rgba(39, 174, 96, 1) !important; /* Green - full opacity */
|
||||
background: rgba(52, 152, 219, 1) !important; /* Blue - full opacity, different from back-to-top */
|
||||
opacity: 1 !important; /* Override base opacity */
|
||||
}
|
||||
|
||||
@@ -209,6 +209,26 @@
|
||||
display: flex !important; /* Ensure it's always displayed */
|
||||
}
|
||||
|
||||
/* REAL MOBILE DEVICES: 5 buttons without shortcuts (no gap) */
|
||||
/* Total width: 5 * 50px + 4 * 10px = 290px, start at 50% - 145px */
|
||||
.is-mobile-device .download-btn {
|
||||
left: calc(50% - 145px) !important; /* First button */
|
||||
}
|
||||
|
||||
.is-mobile-device .print-friendly-btn {
|
||||
left: calc(50% - 85px) !important; /* Second button */
|
||||
}
|
||||
|
||||
/* Theme switcher on mobile - third position (see color-theme.css for override) */
|
||||
|
||||
.is-mobile-device .info-button {
|
||||
left: calc(50% + 35px) !important; /* Fourth button */
|
||||
}
|
||||
|
||||
.is-mobile-device .back-to-top {
|
||||
left: calc(50% + 95px) !important; /* Fifth button (last) */
|
||||
}
|
||||
|
||||
/* Always show back-to-top on mobile (don't wait for scroll) */
|
||||
.back-to-top:hover {
|
||||
opacity: 1 !important;
|
||||
@@ -236,7 +256,7 @@
|
||||
}
|
||||
|
||||
.info-button:hover {
|
||||
background: rgba(39, 174, 96, 1) !important; /* Full green opacity */
|
||||
background: rgba(52, 152, 219, 1) !important; /* Full blue opacity */
|
||||
transform: translateY(-3px) !important;
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
@@ -267,7 +287,7 @@
|
||||
}
|
||||
|
||||
.info-button.at-bottom {
|
||||
background: rgba(39, 174, 96, 1) !important; /* Full green opacity */
|
||||
background: rgba(52, 152, 219, 1) !important; /* Full blue opacity */
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
@@ -286,10 +286,17 @@
|
||||
height: 50px !important;
|
||||
opacity: 1 !important; /* Full opacity on mobile (no transparency with blur bar) */
|
||||
transform: none !important;
|
||||
/* Position before info button: 5 buttons total */
|
||||
/* Download, Print, Shortcuts, Theme, Info */
|
||||
/* Total width: 5 * 50px + 4 * 10px = 290px */
|
||||
left: calc(50% + 35px) !important; /* Fourth button */
|
||||
/* Desktop mobile view: 6 buttons with shortcuts */
|
||||
/* Download, Print, Shortcuts, Theme, Info, Back-to-top */
|
||||
/* Total width: 6 * 50px + 5 * 10px = 350px */
|
||||
left: calc(50% + 5px) !important; /* Fourth button */
|
||||
}
|
||||
|
||||
/* REAL MOBILE DEVICES: 5 buttons without shortcuts */
|
||||
/* Download, Print, Theme, Info, Back-to-top */
|
||||
/* Total width: 5 * 50px + 4 * 10px = 290px */
|
||||
.is-mobile-device .color-theme-switcher {
|
||||
left: calc(50% - 25px) !important; /* Third button (no gap) */
|
||||
}
|
||||
|
||||
/* Show theme colors at full opacity on mobile (no transparency with blur bar) */
|
||||
|
||||
Executable
+150
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { chromium } from 'playwright';
|
||||
|
||||
const MOBILE_VIEWPORT = { width: 375, height: 667 };
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
|
||||
console.log('🧪 Testing Final Mobile Fixes\n');
|
||||
|
||||
// Test with iPhone user agent (real mobile device)
|
||||
const context = await browser.newContext({
|
||||
viewport: MOBILE_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 page = await context.newPage();
|
||||
|
||||
await page.goto('http://localhost:1999/?lang=en&view=extended');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// FIX 1: Check button reorganization (no gap from hidden shortcuts)
|
||||
console.log('📱 FIX 1: Button Reorganization (5 buttons, no gap)\n');
|
||||
|
||||
const buttonPositions = await page.evaluate(() => {
|
||||
const buttons = {
|
||||
download: document.querySelector('.download-btn'),
|
||||
print: document.querySelector('.print-friendly-btn'),
|
||||
theme: document.querySelector('.color-theme-switcher'),
|
||||
info: document.querySelector('.info-button'),
|
||||
backToTop: document.querySelector('.back-to-top'),
|
||||
shortcuts: document.querySelector('.shortcuts-btn')
|
||||
};
|
||||
|
||||
const positions = {};
|
||||
for (const [name, btn] of Object.entries(buttons)) {
|
||||
if (btn) {
|
||||
const rect = btn.getBoundingClientRect();
|
||||
const computed = window.getComputedStyle(btn);
|
||||
positions[name] = {
|
||||
left: Math.round(rect.left),
|
||||
visible: computed.display !== 'none'
|
||||
};
|
||||
}
|
||||
}
|
||||
return positions;
|
||||
});
|
||||
|
||||
console.log('Button Positions:');
|
||||
Object.entries(buttonPositions).forEach(([name, pos]) => {
|
||||
console.log(` • ${name}: ${pos.visible ? 'VISIBLE' : 'HIDDEN'} at ${pos.left}px from left`);
|
||||
});
|
||||
|
||||
const shortcutsHidden = !buttonPositions.shortcuts.visible;
|
||||
const buttonsEvenlySpaced = Math.abs(
|
||||
(buttonPositions.print.left - buttonPositions.download.left) - 60
|
||||
) < 5; // Should be ~60px apart
|
||||
|
||||
console.log(`\n Shortcuts hidden: ${shortcutsHidden ? '✅' : '❌'}`);
|
||||
console.log(` Buttons evenly spaced: ${buttonsEvenlySpaced ? '✅' : '❌'}\n`);
|
||||
|
||||
// FIX 2: Check info button is BLUE (not green)
|
||||
console.log('🎨 FIX 2: Info Button Color (Blue vs Green)\n');
|
||||
|
||||
const buttonColors = await page.evaluate(() => {
|
||||
const info = document.querySelector('.info-button');
|
||||
const backToTop = document.querySelector('.back-to-top');
|
||||
|
||||
const infoStyles = window.getComputedStyle(info);
|
||||
const backToTopStyles = window.getComputedStyle(backToTop);
|
||||
|
||||
return {
|
||||
info: {
|
||||
background: infoStyles.backgroundColor,
|
||||
opacity: infoStyles.opacity
|
||||
},
|
||||
backToTop: {
|
||||
background: backToTopStyles.backgroundColor,
|
||||
opacity: backToTopStyles.opacity
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
console.log('Info Button:');
|
||||
console.log(` • Background: ${buttonColors.info.background}`);
|
||||
console.log(` • Opacity: ${buttonColors.info.opacity}`);
|
||||
console.log(` • Expected: rgb(52, 152, 219) - Blue\n`);
|
||||
|
||||
console.log('Back-to-Top Button:');
|
||||
console.log(` • Background: ${buttonColors.backToTop.background}`);
|
||||
console.log(` • Opacity: ${buttonColors.backToTop.opacity}`);
|
||||
console.log(` • Expected: rgb(39, 174, 96) - Green\n`);
|
||||
|
||||
const infoIsBlue = buttonColors.info.background.includes('52, 152, 219') ||
|
||||
buttonColors.info.background.includes('52,152,219');
|
||||
const backToTopIsGreen = buttonColors.backToTop.background.includes('39, 174, 96') ||
|
||||
buttonColors.backToTop.background.includes('39,174,96');
|
||||
const differentColors = buttonColors.info.background !== buttonColors.backToTop.background;
|
||||
|
||||
console.log(` Info is blue: ${infoIsBlue ? '✅' : '❌'}`);
|
||||
console.log(` Back-to-top is green: ${backToTopIsGreen ? '✅' : '❌'}`);
|
||||
console.log(` Different colors: ${differentColors ? '✅' : '❌'}\n`);
|
||||
|
||||
// FIX 3: Check theme button full opacity
|
||||
console.log('🌈 FIX 3: Theme Button Transparency\n');
|
||||
|
||||
const themeButtonStyles = await page.evaluate(() => {
|
||||
const theme = document.querySelector('.color-theme-switcher');
|
||||
const computed = window.getComputedStyle(theme);
|
||||
const bg = computed.backgroundColor;
|
||||
|
||||
// Extract rgba values
|
||||
const match = bg.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
||||
const alpha = match && match[4] ? parseFloat(match[4]) : 1.0;
|
||||
|
||||
return {
|
||||
background: bg,
|
||||
opacity: computed.opacity,
|
||||
alpha: alpha,
|
||||
themeMode: theme.getAttribute('data-theme-mode')
|
||||
};
|
||||
});
|
||||
|
||||
console.log('Theme Button:');
|
||||
console.log(` • Mode: ${themeButtonStyles.themeMode}`);
|
||||
console.log(` • Background: ${themeButtonStyles.background}`);
|
||||
console.log(` • Opacity: ${themeButtonStyles.opacity}`);
|
||||
console.log(` • Alpha: ${themeButtonStyles.alpha}`);
|
||||
|
||||
const themeFullOpacity = parseFloat(themeButtonStyles.opacity) === 1.0 &&
|
||||
themeButtonStyles.alpha === 1.0;
|
||||
|
||||
console.log(` • Full opacity: ${themeFullOpacity ? '✅' : '❌'}\n`);
|
||||
|
||||
// Take screenshot
|
||||
await page.screenshot({
|
||||
path: 'tests/screenshots/final-mobile-fixes.png',
|
||||
fullPage: true
|
||||
});
|
||||
console.log('📸 Screenshot: tests/screenshots/final-mobile-fixes.png\n');
|
||||
|
||||
const allPassed = shortcutsHidden && buttonsEvenlySpaced &&
|
||||
infoIsBlue && differentColors && themeFullOpacity;
|
||||
|
||||
console.log(`${allPassed ? '✅' : '❌'} Tests ${allPassed ? 'PASSED' : 'FAILED'}\n`);
|
||||
|
||||
await browser.close();
|
||||
process.exit(allPassed ? 0 : 1);
|
||||
})();
|
||||
Reference in New Issue
Block a user