InspectionJanuary 1, 2024ยท 9 min read
Mobile Responsiveness Testing: Is This Site Really Mobile-Friendly?
A practical Fusebox guide to mobile responsiveness testing.
Mobile Responsiveness Testing: Is This Site Really Mobile-Friendly?
Published: January 2024
Reading time: 8 minutes
Over 60% of web traffic is mobile. Yet many sites still break on phones and tablets. Here's how to detect responsive design issues, test mobile compatibility, and ensure sites work on every screen size.
Why Mobile Responsiveness Matters
- Mobile traffic dominates - 60%+ of users are on phones
- Google mobile-first indexing - Mobile version determines rankings
- Conversion rates - 53% leave if site takes >3 seconds
- User expectations - Seamless experience across devices
- Competition - Users switch to mobile-friendly alternatives
Quick Mobile Responsiveness Check
1. Viewport and Basic Mobile Detection
// Quick mobile responsiveness audit
(function mobileCheck() {
console.log('๐ฑ Mobile Responsiveness Check\n');
// 1. Check viewport meta tag
const viewport = document.querySelector('meta[name="viewport"]');
if (viewport) {
console.log('โ
Viewport meta tag found:', viewport.content);
// Check for proper configuration
const content = viewport.content;
const issues = [];
if (!content.includes('width=device-width')) {
issues.push('Missing width=device-width');
}
if (!content.includes('initial-scale=1')) {
issues.push('Missing initial-scale=1');
}
if (content.includes('maximum-scale=1')) {
issues.push('Zoom disabled (accessibility issue)');
}
if (content.includes('user-scalable=no')) {
issues.push('Zoom disabled (accessibility issue)');
}
if (issues.length > 0) {
console.log('โ ๏ธ Viewport issues:', issues.join(', '));
}
} else {
console.log('โ No viewport meta tag! Site not mobile-optimized');
}
// 2. Check current viewport
console.log(`\n๐ Current Viewport:`);
console.log(`Width: ${window.innerWidth}px`);
console.log(`Height: ${window.innerHeight}px`);
console.log(`Device Pixel Ratio: ${window.devicePixelRatio}`);
console.log(`Orientation: ${window.innerWidth > window.innerHeight ? 'Landscape' : 'Portrait'}`);
// 3. Check if actually on mobile
const isMobile = /Mobile|Android|iPhone|iPad/i.test(navigator.userAgent);
const hasTouch = 'ontouchstart' in window;
console.log(`\n๐ฑ Device Detection:`);
console.log(`Mobile User Agent: ${isMobile ? 'Yes' : 'No'}`);
console.log(`Touch Support: ${hasTouch ? 'Yes' : 'No'}`);
})();
2. Responsive Design Analysis
// Analyze responsive design implementation
function analyzeResponsiveDesign() {
console.log('\n๐จ Responsive Design Analysis:');
// 1. Check for media queries
const stylesheets = Array.from(document.styleSheets);
let mediaQueries = [];
stylesheets.forEach(sheet => {
try {
const rules = Array.from(sheet.cssRules || []);
rules.forEach(rule => {
if (rule.type === CSSRule.MEDIA_RULE) {
mediaQueries.push(rule.media.mediaText);
}
});
} catch(e) {
// Cross-origin stylesheets
}
});
if (mediaQueries.length > 0) {
console.log(`โ
Found ${mediaQueries.length} media queries`);
// Analyze breakpoints
const breakpoints = new Set();
mediaQueries.forEach(query => {
const widthMatch = query.match(/(?:min|max)-width:\s*(\d+)px/);
if (widthMatch) {
breakpoints.add(parseInt(widthMatch[1]));
}
});
console.log('Breakpoints detected:', Array.from(breakpoints).sort((a, b) => a - b));
} else {
console.log('โ No media queries found - not responsive?');
}
// 2. Check for responsive units
const computedStyles = window.getComputedStyle(document.body);
const responsiveUnits = {
'Viewport units (vw/vh)': false,
'Relative units (rem/em)': false,
'Percentage widths': false,
'Flexible images': false
};
// Sample elements for responsive units
document.querySelectorAll('*').forEach(el => {
const styles = window.getComputedStyle(el);
if (styles.width.includes('vw') || styles.height.includes('vh')) {
responsiveUnits['Viewport units (vw/vh)'] = true;
}
if (styles.fontSize.includes('rem') || styles.fontSize.includes('em')) {
responsiveUnits['Relative units (rem/em)'] = true;
}
if (styles.width.includes('%')) {
responsiveUnits['Percentage widths'] = true;
}
});
// Check images
const images = Array.from(document.images);
images.forEach(img => {
const styles = window.getComputedStyle(img);
if (styles.maxWidth === '100%' || styles.width.includes('%')) {
responsiveUnits['Flexible images'] = true;
}
});
console.log('\n๐ Responsive Units:');
Object.entries(responsiveUnits).forEach(([unit, used]) => {
console.log(`${used ? 'โ
' : 'โ'} ${unit}`);
});
}
analyzeResponsiveDesign();
3. Mobile Performance Check
// Check mobile-specific performance issues
function checkMobilePerformance() {
console.log('\nโก Mobile Performance Check:');
// 1. Check resource sizes
const resources = performance.getEntriesByType('resource');
let totalSize = 0;
let issues = [];
resources.forEach(resource => {
totalSize += resource.transferSize || 0;
// Flag large resources
if (resource.transferSize > 500000) { // 500KB
issues.push({
url: resource.name.split('/').pop(),
size: `${(resource.transferSize / 1024).toFixed(0)}KB`,
type: resource.initiatorType
});
}
});
console.log(`Total page size: ${(totalSize / 1024 / 1024).toFixed(2)}MB`);
if (totalSize > 5 * 1024 * 1024) {
console.log('โ ๏ธ Page too large for mobile (>5MB)');
}
if (issues.length > 0) {
console.log('\nโ Large resources detected:');
console.table(issues);
}
// 2. Check for mobile-unfriendly features
const mobileIssues = {
'Hover-only interactions': document.querySelectorAll(':hover').length > 10,
'Small touch targets': false,
'Horizontal scrolling': document.body.scrollWidth > window.innerWidth,
'Fixed positioning overuse': document.querySelectorAll('[style*="position: fixed"]').length > 3,
'Auto-playing videos': document.querySelectorAll('video[autoplay]').length > 0
};
// Check touch target sizes
const clickables = document.querySelectorAll('a, button, input, select, textarea');
let smallTargets = 0;
clickables.forEach(el => {
const rect = el.getBoundingClientRect();
if (rect.width < 44 || rect.height < 44) {
smallTargets++;
}
});
if (smallTargets > 0) {
mobileIssues['Small touch targets'] = true;
}
console.log('\n๐ฑ Mobile-Specific Issues:');
Object.entries(mobileIssues).forEach(([issue, found]) => {
console.log(`${found ? 'โ' : 'โ
'} ${issue}`);
});
}
checkMobilePerformance();
Testing Different Screen Sizes
1. Responsive Breakpoint Tester
// Test how site looks at different breakpoints
function testBreakpoints() {
const commonBreakpoints = [
{ name: 'Mobile S', width: 320 },
{ name: 'Mobile M', width: 375 },
{ name: 'Mobile L', width: 425 },
{ name: 'Tablet', width: 768 },
{ name: 'Laptop', width: 1024 },
{ name: 'Desktop', width: 1440 }
];
console.log('\n๐ฑ Breakpoint Analysis:');
console.log('(Resize browser to test each breakpoint)\n');
commonBreakpoints.forEach(device => {
console.log(`${device.name} (${device.width}px):`);
// Check what would happen at this width
const ratio = device.width / window.innerWidth;
if (ratio < 0.5) {
console.log(' ๐ฑ Mobile layout likely');
} else if (ratio < 0.8) {
console.log(' ๐ฑ Tablet layout likely');
} else {
console.log(' ๐ฅ๏ธ Desktop layout likely');
}
});
// Current responsive state
const currentWidth = window.innerWidth;
let currentLayout = 'Desktop';
if (currentWidth < 768) currentLayout = 'Mobile';
else if (currentWidth < 1024) currentLayout = 'Tablet';
console.log(`\nCurrent layout: ${currentLayout} (${currentWidth}px)`);
}
testBreakpoints();
2. Element Overflow Detection
// Find elements causing horizontal scroll
function detectOverflow() {
console.log('\n๐ Overflow Detection:');
const docWidth = document.documentElement.offsetWidth;
const overflowing = [];
document.querySelectorAll('*').forEach(el => {
const rect = el.getBoundingClientRect();
if (rect.right > docWidth || rect.left < 0) {
overflowing.push({
element: el.tagName + (el.className ? `.${el.className.split(' ')[0]}` : ''),
left: Math.round(rect.left),
right: Math.round(rect.right),
width: Math.round(rect.width)
});
}
});
if (overflowing.length > 0) {
console.log('โ Elements causing horizontal scroll:');
console.table(overflowing);
} else {
console.log('โ
No horizontal overflow detected');
}
// Check body scroll
if (document.body.scrollWidth > window.innerWidth) {
console.log(`\nโ ๏ธ Page has horizontal scroll!`);
console.log(`Page width: ${document.body.scrollWidth}px`);
console.log(`Viewport width: ${window.innerWidth}px`);
}
}
detectOverflow();
Touch and Gesture Support
1. Touch Capability Detection
// Comprehensive touch support check
function checkTouchSupport() {
console.log('\n๐ Touch & Gesture Support:');
const touchTests = {
'Touch Events': 'ontouchstart' in window,
'Pointer Events': 'PointerEvent' in window,
'Max Touch Points': navigator.maxTouchPoints > 0,
'Coarse Pointer': window.matchMedia('(pointer: coarse)').matches,
'Hover Capability': window.matchMedia('(hover: hover)').matches
};
Object.entries(touchTests).forEach(([test, result]) => {
console.log(`${result ? 'โ
' : 'โ'} ${test}`);
});
// Check for touch event listeners
const touchHandlers = {
touchstart: 0,
touchmove: 0,
touchend: 0,
click: 0
};
// This is approximate - checking for potential handlers
const scripts = Array.from(document.scripts);
scripts.forEach(script => {
if (script.textContent) {
Object.keys(touchHandlers).forEach(event => {
if (script.textContent.includes(`addEventListener('${event}'`) ||
script.textContent.includes(`on${event}`)) {
touchHandlers[event]++;
}
});
}
});
console.log('\n๐ฑ Touch Handler Detection:');
Object.entries(touchHandlers).forEach(([event, count]) => {
if (count > 0) {
console.log(`โ
${event} handlers found`);
}
});
}
checkTouchSupport();
2. Gesture and Interaction Analysis
// Check for mobile-friendly interactions
function analyzeInteractions() {
console.log('\n๐ค Interaction Analysis:');
// Check for problematic patterns
const issues = [];
// Small clickable areas
const links = document.querySelectorAll('a, button');
let smallTargets = 0;
links.forEach(link => {
const rect = link.getBoundingClientRect();
const area = rect.width * rect.height;
if (area > 0 && (rect.width < 44 || rect.height < 44)) {
smallTargets++;
}
});
if (smallTargets > 0) {
issues.push(`${smallTargets} touch targets smaller than 44x44px`);
}
// Links too close together
let closeLinkPairs = 0;
for (let i = 0; i < links.length - 1; i++) {
const rect1 = links[i].getBoundingClientRect();
const rect2 = links[i + 1].getBoundingClientRect();
const distance = Math.sqrt(
Math.pow(rect2.left - rect1.right, 2) +
Math.pow(rect2.top - rect1.top, 2)
);
if (distance < 8) {
closeLinkPairs++;
}
}
if (closeLinkPairs > 0) {
issues.push(`${closeLinkPairs} link pairs too close together`);
}
// Hover-dependent elements
const hoverRules = Array.from(document.styleSheets).reduce((count, sheet) => {
try {
const rules = Array.from(sheet.cssRules || []);
return count + rules.filter(r => r.selectorText?.includes(':hover')).length;
} catch {
return count;
}
}, 0);
if (hoverRules > 20) {
issues.push(`Heavy hover dependency (${hoverRules} :hover rules)`);
}
if (issues.length > 0) {
console.log('โ ๏ธ Mobile interaction issues:');
issues.forEach(issue => console.log(` - ${issue}`));
} else {
console.log('โ
No major interaction issues detected');
}
}
analyzeInteractions();
Real-World Mobile Issues
1. Common Mobile Problems Detection
// Detect common mobile UX problems
function detectCommonMobileIssues() {
console.log('\n๐จ Common Mobile Issues:');
const issues = {
// Text readability
'Small font size': Array.from(document.querySelectorAll('p, span, div')).some(el => {
const fontSize = parseInt(window.getComputedStyle(el).fontSize);
return fontSize < 14;
}),
// Form issues
'Missing input types': Array.from(document.querySelectorAll('input')).some(input => {
return (input.type === 'text' &&
(input.name?.includes('email') || input.placeholder?.includes('email')));
}),
// Images
'Non-responsive images': Array.from(document.images).some(img => {
const styles = window.getComputedStyle(img);
return styles.maxWidth !== '100%' && !img.srcset;
}),
// Tables
'Non-responsive tables': document.querySelectorAll('table').length > 0 &&
!document.querySelector('div[style*="overflow"]'),
// Pop-ups
'Intrusive interstitials': document.querySelectorAll('[class*="modal"], [class*="popup"]').length > 0,
// Fixed elements
'Too many fixed elements': document.querySelectorAll('[style*="position: fixed"]').length > 2
};
Object.entries(issues).forEach(([issue, found]) => {
console.log(`${found ? 'โ' : 'โ
'} ${issue}`);
});
}
detectCommonMobileIssues();
2. iOS/Android Specific Checks
// Platform-specific mobile checks
function checkPlatformSpecific() {
const ua = navigator.userAgent;
if (/iPhone|iPad/.test(ua)) {
console.log('\n๐ iOS Specific Checks:');
// iOS viewport
const viewport = document.querySelector('meta[name="viewport"]');
if (viewport && !viewport.content.includes('viewport-fit=cover')) {
console.log('โ ๏ธ Missing viewport-fit=cover for iPhone X+ safe areas');
}
// iOS web app capable
const webAppCapable = document.querySelector('meta[name="apple-mobile-web-app-capable"]');
console.log(`Web App Capable: ${webAppCapable ? 'โ
' : 'โ'}`);
// Status bar style
const statusBar = document.querySelector('meta[name="apple-mobile-web-app-status-bar-style"]');
console.log(`Status Bar Style: ${statusBar ? statusBar.content : 'Not set'}`);
// Touch icons
const touchIcon = document.querySelector('link[rel="apple-touch-icon"]');
console.log(`Touch Icon: ${touchIcon ? 'โ
' : 'โ'}`);
} else if (/Android/.test(ua)) {
console.log('\n๐ค Android Specific Checks:');
// Theme color
const themeColor = document.querySelector('meta[name="theme-color"]');
console.log(`Theme Color: ${themeColor ? themeColor.content : 'Not set'}`);
// Web app manifest
const manifest = document.querySelector('link[rel="manifest"]');
console.log(`Web App Manifest: ${manifest ? 'โ
' : 'โ'}`);
}
}
checkPlatformSpecific();
Mobile SEO Check
Mobile-First Indexing Readiness
// Check if site is ready for mobile-first indexing
function checkMobileSEO() {
console.log('\n๐ Mobile SEO Check:');
const seoChecks = {
'Viewport tag': !!document.querySelector('meta[name="viewport"]'),
'Mobile-friendly content': window.innerWidth < 400 ?
document.body.scrollWidth <= window.innerWidth : true,
'Readable font size': !Array.from(document.querySelectorAll('p')).some(p => {
return parseInt(window.getComputedStyle(p).fontSize) < 12;
}),
'Structured data': !!document.querySelector('script[type="application/ld+json"]'),
'Alt text on images': Array.from(document.images).every(img => img.alt),
'Lazy loading': Array.from(document.images).some(img => img.loading === 'lazy')
};
let score = 0;
Object.entries(seoChecks).forEach(([check, passed]) => {
console.log(`${passed ? 'โ
' : 'โ'} ${check}`);
if (passed) score++;
});
const percentage = (score / Object.keys(seoChecks).length * 100).toFixed(0);
console.log(`\nMobile SEO Score: ${percentage}%`);
if (percentage < 80) {
console.log('โ ๏ธ Improvements needed for mobile-first indexing');
}
}
checkMobileSEO();
Complete Mobile Audit
// Run comprehensive mobile audit
(function completeMobileAudit() {
console.log('๐ฑ Complete Mobile Responsiveness Audit\n');
console.log('โ'.repeat(50));
// Run all checks
mobileCheck();
analyzeResponsiveDesign();
checkMobilePerformance();
detectOverflow();
checkTouchSupport();
analyzeInteractions();
detectCommonMobileIssues();
checkPlatformSpecific();
checkMobileSEO();
// Summary
console.log('\n๐ Mobile Readiness Summary:');
console.log('โ'.repeat(50));
const summary = {
'Has Viewport': !!document.querySelector('meta[name="viewport"]'),
'No Horizontal Scroll': document.body.scrollWidth <= window.innerWidth,
'Touch Optimized': navigator.maxTouchPoints > 0 || 'ontouchstart' in window,
'Fast Loading': performance.timing.loadEventEnd - performance.timing.navigationStart < 3000,
'Mobile SEO Ready': !!document.querySelector('meta[name="viewport"]') &&
!document.querySelector('meta[name="viewport"]').content.includes('maximum-scale=1')
};
let passed = 0;
Object.entries(summary).forEach(([check, result]) => {
console.log(`${result ? 'โ
' : 'โ'} ${check}`);
if (result) passed++;
});
const score = (passed / Object.keys(summary).length * 100).toFixed(0);
console.log(`\nOverall Mobile Score: ${score}%`);
if (score === 100) {
console.log('๐ Excellent mobile optimization!');
} else if (score >= 80) {
console.log('๐ Good mobile experience with minor improvements needed');
} else if (score >= 60) {
console.log('โ ๏ธ Significant mobile improvements needed');
} else {
console.log('โ Poor mobile experience - major work required');
}
})();
The Bottom Line
Mobile responsiveness is non-negotiable:
- Most users are mobile - Design mobile-first
- Google ranks mobile version - SEO depends on it
- Users expect perfection - Smooth, fast, touch-friendly
- Competition is mobile-ready - Don't fall behind
- It's not just small screens - Touch, performance, interactions
Test on real devices. Fix mobile issues first. Desktop is the enhancement.
Test mobile responsiveness instantly: Fusebox analyzes responsive design, mobile performance, and touch optimization while you browse. Ensure perfect mobile experiences. $29 one-time purchase.