In the modern web development landscape, creating sites that look great on every device isn't just good practice—it's essential. Today, I'm excited to share a powerful tool I've developed called "Responsive Tester Pro" that makes cross-device testing incredibly simple.
Try it yourself: Check out the Responsive Tester Pro and see how any website renders across different screen sizes!
The Problem With Responsive Testing 🤔
As developers, we've all been there—frantically resizing browser windows or reaching for every device we own to check if our layouts work. This approach is:
- Time-consuming (constantly switching between physical devices)
- Inconsistent (missing common breakpoints)
- Impractical (who owns every possible device size?)
Enter Responsive Tester Pro 🚀
I built this tool to solve these exact problems. The application lets you:
- Test any website across predefined device dimensions
- Create custom screen sizes for specific testing needs
- Toggle between portrait and landscape orientations
- View websites in dark mode to test color schemes
- Reload frames and view sites in full screen
The beauty lies in its simplicity—just enter a URL, select a device size, and instantly see how that site renders.
How It Works: The Technical Breakdown
Behind the scenes, the application uses three core technologies that work together seamlessly:
The Structural Foundation (HTML)
The HTML creates an intuitive interface with several strategic elements:
- A clean, accessible header that clearly communicates the tool's purpose
- Feature toggles positioned for easy access
- A prominent URL input field with validation
- Device presets that match common screen dimensions
- A preview iframe that safely renders websites without security risks
The iframe is particularly important—it provides a sandboxed environment where external websites can load without interacting with our application's code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive Tester Pro</title>
<link rel="stylesheet" href="styles.css">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
</head>
<body>
<div class="container">
<header>
<h1>Responsive Tester Pro</h1>
<p>Preview your website across devices</p>
</header>
<div class="features">
<button class="btn feature-btn" id="reloadBtn">Reload</button>
<button class="btn feature-btn" id="fullScreenBtn">Full Screen</button>
<button class="btn feature-btn" id="darkModeBtn">Dark Mode</button>
<button class="btn feature-btn" id="orientationBtn">Landscape</button>
</div>
<div class="controls">
<div class="url-input">
<input type="url" id="urlInput" placeholder="Enter website URL (e.g., https://learncomputer.in)">
<button id="loadBtn" class="btn primary">Test Now</button>
</div>
<div class="presets">
<button class="preset-btn" data-width="375" data-height="667">Mobile</button>
<button class="preset-btn" data-width="768" data-height="1024">Tablet</button>
<button class="preset-btn" data-width="1366" data-height="768">Laptop</button>
<button class="preset-btn" data-width="1920" data-height="1080">Desktop</button>
<button class="preset-btn custom" id="customBtn">Custom</button>
</div>
<div class="custom-sizes" id="customSizes" style="display: none;">
<input type="number" id="customWidth" placeholder="Width (px)" min="100">
<input type="number" id="customHeight" placeholder="Height (px)" min="100">
<button class="btn secondary" id="applyCustom">Apply</button>
</div>
</div>
<div class="preview-container">
<div class="device-frame" id="deviceFrame">
<div class="device-header">
<div class="device-info">
<span id="deviceSize">375x667</span>
<button class="rotate-btn" id="rotateBtn">↻</button>
</div>
</div>
<div class="loading-overlay" id="loadingOverlay">
<div class="loader"></div>
</div>
<iframe id="preview" sandbox="allow-same-origin allow-scripts allow-forms"></iframe>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
The Visual Experience (CSS)
The styling transforms the basic structure into an intuitive, attractive interface through:
- Neumorphic design elements with subtle shadows creating depth
- A responsive layout that adapts to the user's own screen size
- Smooth transitions between states for a polished feel
- A thoughtfully designed dark mode that protects eyes during late-night coding sessions
- Visual feedback systems that communicate the application state
The CSS employs modern techniques like flexbox for layout management and CSS variables for theming consistency.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background: #f0f2f5;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
transition: background 0.3s;
}
body.dark-mode {
background: #1a1a2e;
}
.container {
max-width: 1200px;
width: 100%;
background: #ffffff;
border-radius: 20px;
padding: 30px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
transition: background 0.3s;
}
.dark-mode .container {
background: #16213e;
}
header {
text-align: center;
margin-bottom: 30px;
}
h1 {
color: #1a1a2e;
font-size: 2.2em;
font-weight: 700;
}
.dark-mode h1 {
color: #e0e0e0;
}
p {
color: #666;
font-size: 1.1em;
}
.dark-mode p {
color: #a0a0a0;
}
.features {
display: flex;
justify-content: center;
gap: 15px;
margin-bottom: 30px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 12px;
cursor: pointer;
font-weight: 600;
background: #ffffff;
box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.1),
-5px -5px 10px rgba(255, 255, 255, 0.8);
transition: all 0.3s;
}
.dark-mode .btn {
background: #16213e;
color: #e0e0e0;
box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.2),
-5px -5px 10px rgba(40, 50, 80, 0.4);
}
.btn:hover {
box-shadow: inset 2px 2px 5px rgba(0, 0, 0, 0.1),
inset -2px -2px 5px rgba(255, 255, 255, 0.7);
}
.primary {
background: #4e4feb;
color: white;
}
.secondary {
background: #00cc99;
color: white;
}
.url-input {
display: flex;
gap: 15px;
margin-bottom: 25px;
}
input[type="url"],
input[type="number"] {
flex: 1;
padding: 12px 15px;
border: none;
border-radius: 12px;
font-size: 1em;
background: #f0f2f5;
box-shadow: inset 2px 2px 5px rgba(0, 0, 0, 0.1),
inset -2px -2px 5px rgba(255, 255, 255, 0.7);
transition: all 0.3s;
}
.dark-mode input[type="url"],
.dark-mode input[type="number"] {
background: #0f1626;
color: #e0e0e0;
}
input:focus {
outline: none;
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.15),
inset -1px -1px 3px rgba(255, 255, 255, 0.9);
}
.presets {
display: flex;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 20px;
justify-content: center;
}
.preset-btn {
padding: 10px 18px;
border: none;
border-radius: 10px;
cursor: pointer;
background: #f0f2f5;
box-shadow: 3px 3px 6px rgba(0, 0, 0, 0.1),
-3px -3px 6px rgba(255, 255, 255, 0.8);
transition: all 0.3s;
}
.dark-mode .preset-btn {
background: #0f1626;
color: #e0e0e0;
}
.preset-btn:hover,
.preset-btn.active {
background: #4e4feb;
color: white;
box-shadow: inset 2px 2px 5px rgba(0, 0, 0, 0.2);
}
.custom-sizes {
display: flex;
gap: 12px;
}
.preview-container {
display: flex;
justify-content: center;
}
.device-frame {
background: #ffffff;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
position: relative;
transition: all 0.3s;
}
.dark-mode .device-frame {
background: #16213e;
}
.device-header {
background: #4e4feb;
padding: 10px;
color: white;
display: flex;
justify-content: center;
border-radius: 15px 15px 0 0;
}
.device-info {
display: flex;
align-items: center;
gap: 15px;
}
.rotate-btn {
background: none;
border: none;
color: white;
font-size: 20px;
cursor: pointer;
transition: transform 0.3s;
}
.rotate-btn:hover {
transform: rotate(90deg);
}
.loading-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s;
border-radius: 15px;
}
.loading-overlay.active {
opacity: 1;
visibility: visible;
}
.loader {
width: 40px;
height: 40px;
border: 4px solid #fff;
border-bottom-color: #4e4feb;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
iframe {
border: none;
width: 375px;
height: 667px;
border-radius: 0 0 15px 15px;
transition: all 0.3s;
}
@media (max-width: 768px) {
.container {
padding: 20px;
}
.url-input {
flex-direction: column;
}
.features {
flex-wrap: wrap;
}
}
The Intelligent Behavior (JavaScript)
The JavaScript brings everything to life by:
- Managing URL loading: Validating input, handling protocols, and providing error feedback
- Controlling device dimensions: Translating preset selections into precise pixel measurements
- Handling orientation changes: Swapping width and height values while maintaining context
- Providing utility features: Implementing dark mode, full-screen viewing, and reload functionality
- Creating visual feedback: Showing loading states during transitions between websites
One particularly interesting function is the orientation handling, which doesn't just swap width and height values but also updates the UI to reflect the current orientation state.
document.addEventListener('DOMContentLoaded', () => {
const urlInput = document.getElementById('urlInput');
const loadBtn = document.getElementById('loadBtn');
const preview = document.getElementById('preview');
const deviceFrame = document.getElementById('deviceFrame');
const deviceSize = document.getElementById('deviceSize');
const rotateBtn = document.getElementById('rotateBtn');
const presetButtons = document.querySelectorAll('.preset-btn');
const customBtn = document.getElementById('customBtn');
const customSizes = document.getElementById('customSizes');
const customWidth = document.getElementById('customWidth');
const customHeight = document.getElementById('customHeight');
const applyCustom = document.getElementById('applyCustom');
const reloadBtn = document.getElementById('reloadBtn');
const fullScreenBtn = document.getElementById('fullScreenBtn');
const darkModeBtn = document.getElementById('darkModeBtn');
const orientationBtn = document.getElementById('orientationBtn');
const loadingOverlay = document.getElementById('loadingOverlay');
let currentWidth = 375;
let currentHeight = 667;
let isRotated = false;
loadBtn.addEventListener('click', loadWebsite);
urlInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') loadWebsite();
});
function loadWebsite() {
const url = urlInput.value.trim();
if (url) {
const finalUrl = url.startsWith('http') ? url : `https://${url}`;
loadingOverlay.classList.add('active');
preview.src = finalUrl;
}
}
presetButtons.forEach(button => {
button.addEventListener('click', () => {
if (button.classList.contains('custom')) {
customSizes.style.display = 'flex';
return;
}
currentWidth = parseInt(button.dataset.width);
currentHeight = parseInt(button.dataset.height);
updatePreview();
presetButtons.forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
});
});
applyCustom.addEventListener('click', () => {
const width = parseInt(customWidth.value);
const height = parseInt(customHeight.value);
if (width >= 100 && height >= 100) {
currentWidth = width;
currentHeight = height;
updatePreview();
customSizes.style.display = 'none';
}
});
rotateBtn.addEventListener('click', () => {
isRotated = !isRotated;
updatePreview();
});
function updatePreview() {
const width = isRotated ? currentHeight : currentWidth;
const height = isRotated ? currentWidth : currentHeight;
preview.style.width = `${width}px`;
preview.style.height = `${height}px`;
deviceSize.textContent = `${width}x${height}`;
orientationBtn.textContent = isRotated ? 'Portrait' : 'Landscape';
}
reloadBtn.addEventListener('click', () => {
loadingOverlay.classList.add('active');
preview.contentWindow.location.reload();
});
fullScreenBtn.addEventListener('click', () => {
if (deviceFrame.requestFullscreen) {
deviceFrame.requestFullscreen();
}
});
darkModeBtn.addEventListener('click', () => {
document.body.classList.toggle('dark-mode');
darkModeBtn.textContent = document.body.classList.contains('dark-mode')
? 'Light Mode'
: 'Dark Mode';
});
orientationBtn.addEventListener('click', () => {
isRotated = !isRotated;
updatePreview();
});
// Handle iframe load events
preview.addEventListener('load', () => {
loadingOverlay.classList.remove('active');
});
preview.addEventListener('error', () => {
loadingOverlay.classList.remove('active');
alert('Failed to load website. Please check the URL and try again.');
});
// Initial setup
updatePreview();
});
Learning From The Code
Building this tool taught me several valuable lessons about web development:
1. User Experience Considerations
Notice how the loading overlay provides immediate feedback when changing URLs. This prevents the jarring experience of seeing a blank iframe while content loads. Small details like this make applications feel professional and thoughtful.
2. Progressive Enhancement
The tool works at its core even if JavaScript features like full-screen mode aren't available. This demonstrates the principle of progressive enhancement—start with a functional base and layer on enhancements.
3. Responsive Design Itself
Ironically, a tool for testing responsive design must itself be responsive! The media queries ensure the interface remains usable even on smaller screens.
4. Security Considerations
The iframe uses the sandbox attribute to prevent potentially malicious websites from accessing our application. Always consider security implications when loading external content.
Taking It Further
If you're inspired to build on this project, consider these enhancements:
- Adding device-specific frames (like phone bezels) for more realistic previews
- Implementing screenshot functionality for documentation
- Creating shareable test URLs so teammates can view the same test
- Adding network throttling to simulate various connection speeds
- Incorporating accessibility testing tools alongside visual testing
Why This Matters
Responsive design isn't just about aesthetics—it's about inclusion. When websites work well across devices, we create more accessible digital experiences for everyone, regardless of their technology access.
This simple tool embodies an important principle: testing should be easy enough that developers actually do it. When testing becomes frictionless, the web becomes more accessible for everyone.
Have you created your own development tools? What problems did they solve? I'd love to hear about your experiences in the comments below!
Top comments (0)