Building a Responsive Website Tester: Create Your Own Device Preview Tool

Have you ever wondered how your website looks on different devices? As web developers, ensuring our websites are responsive across multiple screen sizes is crucial for providing a seamless user experience. Today, I'll guide you through understanding a powerful tool I've built called "Responsive Tester Pro" that allows you to preview any website across different device dimensions.
You can try out this tool right now at https://playground.learncomputer.in/responsive-tester/ - simply enter any URL and see how it renders across various device sizes!
Why Responsive Testing Matters 🔍
Before diving into the code, let's understand why responsive testing is essential:
Mobile traffic accounts for over 60% of global web traffic
Google uses mobile-friendliness as a ranking factor
Users abandon websites that don't work well on their devices
Testing across multiple devices manually is time-consuming
A responsive tester allows developers to quickly validate their designs across different screen dimensions without needing physical devices or complex setup.
Understanding the Project Structure
Our responsive tester consists of three main components:
HTML - The structural foundation of our application
CSS - Styling that creates a clean, modern interface with dark mode support
JavaScript - Functionality that powers the preview and device simulation
Let's examine each component to understand how they work together.
The HTML Foundation 🏗️
The HTML structure creates a user-friendly interface with several key sections:
A header with the application title
Feature buttons for core functionality (reload, full screen, dark mode, orientation)
URL input area with a "Test Now" button
Device preset buttons (mobile, tablet, laptop, desktop, custom)
Custom size input fields
A preview container with a device frame and iframe
The structure uses semantic HTML elements for better accessibility and organization. The iframe is where the magic happens - it loads websites for preview while being properly sandboxed for security.
<!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 CSS Magic ✨
The styling transforms our basic structure into a polished, user-friendly interface. Some key styling features include:
A clean, modern design with soft shadows for a subtle 3D effect
Responsive layout that works well on different screen sizes
Dark mode support with smooth transitions
Interactive elements (buttons, inputs) with satisfying hover states
A device frame that mimics real devices
Loading indicators for better user experience
The CSS uses modern techniques like flexbox for layout, CSS variables for theming, and transitions for smooth animations.
* {
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;
}
}
JavaScript Functionality 🧠
The JavaScript code brings our application to life with several core functions:
Loading websites into the iframe preview
Handling device preset selection
Managing custom size inputs
Implementing orientation switching
Providing utility features (reload, full screen, dark mode)
Error handling for failed loads
Let's examine how each of these functions works:
Website Loading
When a user enters a URL and clicks "Test Now" (or presses Enter), the application:
Validates and formats the URL (adding https:// if needed)
Shows a loading overlay with animation
Sets the iframe source to the provided URL
Handles load completion and errors
Device Presets
The preset buttons (Mobile, Tablet, etc.) contain data attributes with width and height values. When clicked, the JavaScript:
Extracts these dimensions
Updates the current width and height variables
Calls the updatePreview function to resize the iframe
Visually highlights the active preset
Custom Sizing
The custom size feature allows users to input specific dimensions:
When the "Custom" button is clicked, the input fields are displayed
When "Apply" is clicked, the system validates inputs (ensuring they're at least 100px)
If valid, it updates the preview dimensions and hides the input fields
Orientation Switching
The orientation function:
Toggles between landscape and portrait modes
Swaps width and height values
Updates the button text to reflect the current orientation
Calls updatePreview to resize the iframe
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();
});
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.');
});
updatePreview();
});
Key Features Explained 🚀
The Device Frame
The device frame provides visual context for the preview, making it feel like you're looking at a real device. It includes:
A header with dimension information
A rotate button for quick orientation switching
Clean borders with rounded corners
A responsive design that maintains proportions
Dark Mode Toggle
The dark mode implementation:
Toggles a 'dark-mode' class on the body element
CSS variables and transition properties ensure smooth color changes
The button text updates to reflect the current mode
All UI elements have specific dark mode styles for consistent appearance
Loading Indicator
The loading overlay provides visual feedback during website loading:
A semi-transparent overlay covers the iframe
A spinning loader animation indicates loading progress
The overlay is shown when loading starts and hidden when complete
Error handling displays an alert if loading fails
Making It Your Own 🛠️
Here are some ways you could extend this project:
Add device frame options (phone, tablet bezels)
Implement screenshot functionality
Add URL history for quick access to frequently tested sites
Include responsive breakpoint indicators
Add network throttling to simulate different connection speeds
Conclusion
Building a responsive tester is not only a practical tool for web development but also an excellent learning project. It combines HTML structure, CSS styling, and JavaScript functionality in a real-world application that solves a common development challenge.
The next time you're developing a website, you'll have this handy tool to ensure your designs look great across all device sizes! 📱💻🖥️
Remember, responsive design isn't just about making things look good - it's about creating inclusive experiences that work for all users regardless of their device. Happy testing!






