Implement responsive images and media for optimal loading and display across all devices and screen densities.
Responsive images and media are critical for creating fast, engaging web experiences that work seamlessly across all devices. With varying screen sizes, pixel densities, and connection speeds, a one-size-fits-all approach to media leads to poor performance and user experience.
Modern HTML and CSS provide powerful tools for delivering the right media to each device automatically. From responsive image syntax to adaptive video streaming, you can ensure your content looks crisp on high-density displays while loading quickly on slower connections.
In this lesson, you'll learn to implement comprehensive responsive media strategies that optimize for performance, accessibility, and visual quality across the entire spectrum of modern devices.
Responsive images automatically deliver the most appropriate image size and format based on device characteristics and user context.
Core Responsive Image Techniques:
1. srcset Attribute for Resolution Switching:
1<img src="image-400w.jpg"
2 srcset="image-400w.jpg 400w,
3 image-800w.jpg 800w,
4 image-1200w.jpg 1200w"
5 sizes="(max-width: 600px) 100vw,
6 (max-width: 900px) 50vw,
7 33vw"
8 alt="Responsive landscape image">2. High-DPI Display Support:
1<img src="image-1x.jpg"
2 srcset="image-1x.jpg 1x,
3 image-2x.jpg 2x,
4 image-3x.jpg 3x"
5 alt="High-resolution image">3. CSS Responsive Images:
1.responsive-image {
2 width: 100%;
3 height: auto;
4 max-width: 100%;
5 object-fit: cover;
6 object-position: center;
7}
8
9/* Different approaches for different contexts */
10.hero-image {
11 width: 100%;
12 height: 50vh;
13 object-fit: cover;
14 object-position: center top;
15}
16
17.thumbnail {
18 width: 150px;
19 height: 150px;
20 object-fit: cover;
21 border-radius: 8px;
22}Key Benefits:
How the Browser Chooses:
The picture element provides complete control over which images load under different conditions, enabling art direction and format optimization.
Art Direction with Picture Element:
1<picture>
2 <!-- Mobile: cropped portrait version -->
3 <source media="(max-width: 600px)"
4 srcset="mobile-portrait-400w.jpg 400w,
5 mobile-portrait-800w.jpg 800w"
6 sizes="100vw">
7
8 <!-- Tablet: landscape crop -->
9 <source media="(max-width: 1200px)"
10 srcset="tablet-landscape-600w.jpg 600w,
11 tablet-landscape-1200w.jpg 1200w"
12 sizes="100vw">
13
14 <!-- Desktop: full wide image -->
15 <source media="(min-width: 1201px)"
16 srcset="desktop-wide-800w.jpg 800w,
17 desktop-wide-1600w.jpg 1600w,
18 desktop-wide-2400w.jpg 2400w"
19 sizes="100vw">
20
21 <!-- Fallback for browsers without picture support -->
22 <img src="fallback-800w.jpg" alt="Adaptive hero image">
23</picture>Modern Format Optimization:
1<picture>
2 <!-- Modern AVIF format for supported browsers -->
3 <source srcset="image-400w.avif 400w,
4 image-800w.avif 800w,
5 image-1200w.avif 1200w"
6 sizes="(max-width: 600px) 100vw, 50vw"
7 type="image/avif">
8
9 <!-- WebP fallback -->
10 <source srcset="image-400w.webp 400w,
11 image-800w.webp 800w,
12 image-1200w.webp 1200w"
13 sizes="(max-width: 600px) 100vw, 50vw"
14 type="image/webp">
15
16 <!-- JPEG fallback for all browsers -->
17 <img src="image-800w.jpg"
18 srcset="image-400w.jpg 400w,
19 image-800w.jpg 800w,
20 image-1200w.jpg 1200w"
21 sizes="(max-width: 600px) 100vw, 50vw"
22 alt="Optimized responsive image">
23</picture>Dynamic Art Direction with CSS:
1.adaptive-image {
2 width: 100%;
3 height: 300px;
4 object-fit: cover;
5 object-position: center;
6}
7
8@media (max-width: 600px) {
9 .adaptive-image {
10 height: 200px;
11 object-position: center top; /* Focus on top for mobile */
12 }
13}
14
15@media (min-width: 1200px) {
16 .adaptive-image {
17 height: 400px;
18 object-position: center bottom; /* Show more bottom on desktop */
19 }
20}Container Query Art Direction:
1.image-container {
2 container-type: inline-size;
3}
4
5.container-adaptive-image {
6 width: 100%;
7 height: 200px;
8 object-fit: cover;
9}
10
11@container (min-width: 400px) {
12 .container-adaptive-image {
13 height: 250px;
14 object-position: center top;
15 }
16}
17
18@container (min-width: 600px) {
19 .container-adaptive-image {
20 height: 300px;
21 object-position: center;
22 }
23}Create adaptive video and audio experiences that work seamlessly across devices and connection speeds.
Responsive Video Implementation:
1<!-- Basic responsive video -->
2<video class="responsive-video" controls poster="video-poster.jpg">
3 <source src="video-1080p.mp4" type="video/mp4" media="(min-width: 1200px)">
4 <source src="video-720p.mp4" type="video/mp4" media="(min-width: 600px)">
5 <source src="video-480p.mp4" type="video/mp4">
6 <track src="captions-en.vtt" kind="captions" srclang="en" label="English">
7 Your browser doesn't support the video element.
8</video>CSS for Responsive Video:
1.responsive-video {
2 width: 100%;
3 height: auto;
4 max-width: 100%;
5 border-radius: 8px;
6 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
7}
8
9/* Aspect ratio containers */
10.video-container {
11 position: relative;
12 width: 100%;
13 padding-bottom: 56.25%; /* 16:9 aspect ratio */
14 height: 0;
15 overflow: hidden;
16}
17
18.video-container video,
19.video-container iframe {
20 position: absolute;
21 top: 0;
22 left: 0;
23 width: 100%;
24 height: 100%;
25 border: 0;
26}
27
28/* Responsive aspect ratios */
29@media (max-width: 600px) {
30 .video-container {
31 padding-bottom: 66.67%; /* 3:2 aspect ratio for mobile */
32 }
33}Advanced Video Optimization:
1<!-- Adaptive streaming with multiple formats -->
2<video class="adaptive-video" controls preload="metadata">
3 <!-- High quality for fast connections -->
4 <source src="video-4k.webm" type="video/webm; codecs=vp9"
5 media="(min-width: 1400px) and (min-resolution: 2dppx)">
6
7 <!-- Standard quality versions -->
8 <source src="video-1080p.webm" type="video/webm; codecs=vp9">
9 <source src="video-1080p.mp4" type="video/mp4; codecs=avc1.640028">
10
11 <!-- Mobile optimized -->
12 <source src="video-720p.webm" type="video/webm; codecs=vp9"
13 media="(max-width: 800px)">
14 <source src="video-720p.mp4" type="video/mp4; codecs=avc1.42001E"
15 media="(max-width: 800px)">
16
17 <!-- Low bandwidth fallback -->
18 <source src="video-480p.mp4" type="video/mp4; codecs=avc1.42001E"
19 media="(max-width: 480px)">
20</video>Responsive Audio Implementation:
1<audio class="responsive-audio" controls preload="metadata">
2 <source src="audio-high.ogg" type="audio/ogg; codecs=vorbis"
3 media="(min-width: 800px)">
4 <source src="audio-high.mp3" type="audio/mpeg"
5 media="(min-width: 800px)">
6 <source src="audio-compressed.ogg" type="audio/ogg; codecs=vorbis">
7 <source src="audio-compressed.mp3" type="audio/mpeg">
8 Your browser doesn't support the audio element.
9</audio>Performance-Optimized Media Loading:
1/* Lazy loading placeholder */
2.media-placeholder {
3 width: 100%;
4 height: 200px;
5 background: linear-gradient(45deg, #f0f0f0 25%, transparent 25%),
6 linear-gradient(-45deg, #f0f0f0 25%, transparent 25%),
7 linear-gradient(45deg, transparent 75%, #f0f0f0 75%),
8 linear-gradient(-45deg, transparent 75%, #f0f0f0 75%);
9 background-size: 20px 20px;
10 background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
11 display: flex;
12 align-items: center;
13 justify-content: center;
14 color: #666;
15 font-weight: 500;
16}
17
18/* Loading states */
19.media-loading {
20 opacity: 0.5;
21 transition: opacity 0.3s ease;
22}
23
24.media-loaded {
25 opacity: 1;
26}JavaScript for Adaptive Loading:
1// Connection-aware media loading
2function getOptimalVideoQuality() {
3 const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
4
5 if (!connection) return 'standard';
6
7 if (connection.effectiveType === '4g' && connection.downlink > 10) {
8 return 'high';
9 } else if (connection.effectiveType === '3g' || connection.saveData) {
10 return 'low';
11 }
12
13 return 'standard';
14}
15
16// Apply quality based on connection
17const videos = document.querySelectorAll('video[data-adaptive]');
18videos.forEach(video => {
19 const quality = getOptimalVideoQuality();
20 const source = video.querySelector(`source[data-quality="${quality}"]`);
21 if (source) {
22 video.src = source.src;
23 }
24});Build a comprehensive responsive media gallery with images, videos, and advanced optimization techniques.
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Responsive Images & Media</title>
7 <style>
8 * {
9 margin: 0;
10 padding: 0;
11 box-sizing: border-box;
12 }
13
14 body {
15 font-family: 'Inter', system-ui, sans-serif;
16 line-height: 1.6;
17 color: #333;
18 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
19 min-height: 100vh;
20 padding: 2rem;
21 }
22
23 .container {
24 max-width: 1400px;
25 margin: 0 auto;
26 background: rgba(255, 255, 255, 0.95);
27 border-radius: 24px;
28 padding: 3rem;
29 box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15);
30 backdrop-filter: blur(10px);
31 }
32
33 .hero {
34 text-align: center;
35 margin-bottom: 4rem;
36 }
37
38 .hero-title {
39 font-size: clamp(2.5rem, 6vw, 4rem);
40 font-weight: 800;
41 background: linear-gradient(135deg, #667eea, #764ba2);
42 -webkit-background-clip: text;
43 -webkit-text-fill-color: transparent;
44 background-clip: text;
45 margin-bottom: 1rem;
46 }
47
48 .hero-subtitle {
49 font-size: clamp(1.125rem, 2.5vw, 1.5rem);
50 color: #64748b;
51 max-width: 60ch;
52 margin: 0 auto;
53 }
54
55 .section {
56 margin-bottom: 4rem;
57 }
58
59 .section-title {
60 font-size: clamp(1.75rem, 4vw, 2.5rem);
61 font-weight: 700;
62 color: #1e293b;
63 margin-bottom: 1rem;
64 text-align: center;
65 position: relative;
66 }
67
68 .section-title::after {
69 content: '';
70 display: block;
71 width: 60px;
72 height: 4px;
73 background: linear-gradient(90deg, #667eea, #764ba2);
74 margin: 1rem auto;
75 border-radius: 2px;
76 }
77
78 .section-description {
79 text-align: center;
80 color: #64748b;
81 max-width: 70ch;
82 margin: 0 auto 3rem;
83 font-size: clamp(1rem, 2vw, 1.125rem);
84 }
85
86 /* Responsive Image Gallery */
87 .image-grid {
88 display: grid;
89 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
90 gap: 2rem;
91 margin: 3rem 0;
92 }
93
94 .image-card {
95 background: white;
96 border-radius: 16px;
97 overflow: hidden;
98 box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
99 transition: all 0.3s ease;
100 position: relative;
101 }
102
103 .image-card:hover {
104 transform: translateY(-8px);
105 box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
106 }
107
108 /* Responsive Images */
109 .responsive-image {
110 width: 100%;
111 height: 250px;
112 object-fit: cover;
113 object-position: center;
114 transition: transform 0.3s ease;
115 }
116
117 .image-card:hover .responsive-image {
118 transform: scale(1.05);
119 }
120
121 /* Art Direction Examples */
122 .art-direction-demo {
123 display: grid;
124 grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
125 gap: 2rem;
126 margin: 2rem 0;
127 }
128
129 .art-direction-card {
130 background: white;
131 border-radius: 12px;
132 overflow: hidden;
133 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
134 }
135
136 /* Picture element styling */
137 .picture-container {
138 position: relative;
139 overflow: hidden;
140 border-radius: 12px;
141 }
142
143 .picture-container img {
144 width: 100%;
145 height: 200px;
146 object-fit: cover;
147 transition: transform 0.3s ease;
148 }
149
150 /* Video Gallery */
151 .video-gallery {
152 display: grid;
153 grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
154 gap: 2rem;
155 margin: 3rem 0;
156 }
157
158 .video-card {
159 background: white;
160 border-radius: 16px;
161 padding: 1.5rem;
162 box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
163 transition: all 0.3s ease;
164 }
165
166 .video-card:hover {
167 transform: translateY(-4px);
168 box-shadow: 0 15px 35px rgba(0, 0, 0, 0.15);
169 }
170
171 /* Aspect ratio containers */
172 .video-container {
173 position: relative;
174 width: 100%;
175 padding-bottom: 56.25%; /* 16:9 */
176 height: 0;
177 overflow: hidden;
178 border-radius: 8px;
179 margin-bottom: 1rem;
180 background: #f1f5f9;
181 }
182
183 .video-container video {
184 position: absolute;
185 top: 0;
186 left: 0;
187 width: 100%;
188 height: 100%;
189 object-fit: cover;
190 }
191
192 .video-placeholder {
193 position: absolute;
194 top: 0;
195 left: 0;
196 width: 100%;
197 height: 100%;
198 background: linear-gradient(45deg, #f1f5f9 25%, transparent 25%),
199 linear-gradient(-45deg, #f1f5f9 25%, transparent 25%),
200 linear-gradient(45deg, transparent 75%, #f1f5f9 75%),
201 linear-gradient(-45deg, transparent 75%, #f1f5f9 75%);
202 background-size: 20px 20px;
203 background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
204 display: flex;
205 align-items: center;
206 justify-content: center;
207 color: #64748b;
208 font-weight: 500;
209 }
210
211 /* Media Cards Content */
212 .card-content {
213 padding: 1.5rem;
214 }
215
216 .card-title {
217 font-size: 1.25rem;
218 font-weight: 600;
219 color: #1e293b;
220 margin-bottom: 0.5rem;
221 }
222
223 .card-description {
224 color: #64748b;
225 font-size: 0.9rem;
226 line-height: 1.6;
227 }
228
229 .card-meta {
230 display: flex;
231 gap: 1rem;
232 margin-top: 1rem;
233 font-size: 0.875rem;
234 color: #64748b;
235 }
236
237 /* Format Support Indicators */
238 .format-indicator {
239 display: inline-block;
240 padding: 0.25rem 0.5rem;
241 background: #e0f2fe;
242 color: #0369a1;
243 border-radius: 4px;
244 font-size: 0.75rem;
245 font-weight: 500;
246 text-transform: uppercase;
247 }
248
249 .format-indicator.webp {
250 background: #dcfce7;
251 color: #166534;
252 }
253
254 .format-indicator.avif {
255 background: #fef3c7;
256 color: #92400e;
257 }
258
259 /* Progressive Loading States */
260 .media-loading {
261 opacity: 0.6;
262 position: relative;
263 overflow: hidden;
264 }
265
266 .media-loading::after {
267 content: '';
268 position: absolute;
269 top: 0;
270 left: -100%;
271 width: 100%;
272 height: 100%;
273 background: linear-gradient(90deg,
274 transparent,
275 rgba(255, 255, 255, 0.4),
276 transparent);
277 animation: loading-shimmer 2s infinite;
278 }
279
280 @keyframes loading-shimmer {
281 0% { left: -100%; }
282 100% { left: 100%; }
283 }
284
285 .media-loaded {
286 opacity: 1;
287 }
288
289 /* Interactive Elements */
290 .quality-selector {
291 background: white;
292 border-radius: 12px;
293 padding: 1.5rem;
294 margin: 2rem 0;
295 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
296 border: 1px solid #e2e8f0;
297 }
298
299 .quality-options {
300 display: flex;
301 gap: 1rem;
302 justify-content: center;
303 flex-wrap: wrap;
304 }
305
306 .quality-button {
307 padding: 0.5rem 1rem;
308 border: 2px solid #e2e8f0;
309 border-radius: 8px;
310 background: white;
311 color: #64748b;
312 font-weight: 500;
313 cursor: pointer;
314 transition: all 0.3s ease;
315 }
316
317 .quality-button:hover,
318 .quality-button.active {
319 border-color: #667eea;
320 color: #667eea;
321 background: #f8fafc;
322 }
323
324 /* Connection Status */
325 .connection-info {
326 position: fixed;
327 top: 20px;
328 right: 20px;
329 background: rgba(255, 255, 255, 0.95);
330 padding: 0.75rem 1rem;
331 border-radius: 8px;
332 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
333 font-size: 0.875rem;
334 z-index: 1000;
335 backdrop-filter: blur(10px);
336 }
337
338 .connection-speed {
339 color: #059669;
340 font-weight: 600;
341 }
342
343 /* Responsive Adjustments */
344 @media (max-width: 768px) {
345 .container {
346 padding: 2rem;
347 border-radius: 16px;
348 }
349
350 .image-grid,
351 .art-direction-demo {
352 grid-template-columns: 1fr;
353 gap: 1.5rem;
354 }
355
356 .video-gallery {
357 grid-template-columns: 1fr;
358 gap: 1.5rem;
359 }
360
361 .quality-options {
362 flex-direction: column;
363 }
364
365 .video-container {
366 padding-bottom: 66.67%; /* 3:2 for mobile */
367 }
368 }
369
370 @media (max-width: 480px) {
371 body {
372 padding: 1rem;
373 }
374
375 .container {
376 padding: 1.5rem;
377 border-radius: 12px;
378 }
379
380 .connection-info {
381 position: relative;
382 top: auto;
383 right: auto;
384 margin-bottom: 2rem;
385 }
386 }
387
388 /* Animation delays for staggered entrance */
389 .image-card:nth-child(1) { animation-delay: 0.1s; }
390 .image-card:nth-child(2) { animation-delay: 0.2s; }
391 .image-card:nth-child(3) { animation-delay: 0.3s; }
392 .image-card:nth-child(4) { animation-delay: 0.4s; }
393 .image-card:nth-child(5) { animation-delay: 0.5s; }
394 .image-card:nth-child(6) { animation-delay: 0.6s; }
395
396 .image-card,
397 .video-card,
398 .art-direction-card {
399 animation: fadeInUp 0.6s ease-out forwards;
400 opacity: 0;
401 transform: translateY(30px);
402 }
403
404 @keyframes fadeInUp {
405 to {
406 opacity: 1;
407 transform: translateY(0);
408 }
409 }
410 </style>
411</head>
412<body>
413 <div class="container">
414 <!-- Hero Section -->
415 <header class="hero">
416 <h1 class="hero-title">📷 Responsive Media</h1>
417 <p class="hero-subtitle">Optimized images and videos that adapt perfectly to any device and connection speed</p>
418 </header>
419
420 <!-- Connection Status -->
421 <div class="connection-info" id="connectionInfo">
422 <span>Connection: </span>
423 <span class="connection-speed" id="connectionSpeed">Detecting...</span>
424 </div>
425
426 <!-- Responsive Images Section -->
427 <section class="section">
428 <h2 class="section-title">Responsive Images</h2>
429 <p class="section-description">
430 These images automatically serve the optimal size and format based on your device and screen resolution.
431 </p>
432
433 <div class="image-grid">
434 <div class="image-card">
435 <!-- Using CSS background images with different sizes -->
436 <div style="width: 100%; height: 250px; background: url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 400 250\"><rect width=\"400\" height=\"250\" fill=\"linear-gradient(45deg,%23667eea,%23764ba2)\"/><text x=\"50%\" y=\"50%\" text-anchor=\"middle\" dy=\".3em\" fill=\"white\" font-size=\"24\" font-weight=\"bold\">400×250</text></svg>') center/cover; border-radius: 16px;"></div>
437 <div class="card-content">
438 <h3 class="card-title">Basic Responsive</h3>
439 <p class="card-description">Standard responsive image using CSS background with cover sizing.</p>
440 <div class="card-meta">
441 <span class="format-indicator">SVG</span>
442 <span>Perfect scaling</span>
443 </div>
444 </div>
445 </div>
446
447 <div class="image-card">
448 <div style="width: 100%; height: 250px; background: url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 600 250\"><defs><linearGradient id=\"grad1\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\"><stop offset=\"0%\" stop-color=\"%2372c2f4\"/><stop offset=\"100%\" stop-color=\"%23f093fb\"/></linearGradient></defs><rect width=\"600\" height=\"250\" fill=\"url(%23grad1)\"/><circle cx=\"150\" cy=\"125\" r=\"50\" fill=\"white\" opacity=\"0.3\"/><circle cx=\"450\" cy=\"125\" r=\"30\" fill=\"white\" opacity=\"0.5\"/><text x=\"50%\" y=\"50%\" text-anchor=\"middle\" dy=\".3em\" fill=\"white\" font-size=\"20\" font-weight=\"bold\">High-DPI Ready</text></svg>') center/cover; border-radius: 16px;"></div>
449 <div class="card-content">
450 <h3 class="card-title">High-DPI Optimized</h3>
451 <p class="card-description">Crisp display on retina screens with vector graphics and optimized rasterization.</p>
452 <div class="card-meta">
453 <span class="format-indicator">Vector</span>
454 <span>Retina ready</span>
455 </div>
456 </div>
457 </div>
458
459 <div class="image-card">
460 <div style="width: 100%; height: 250px; background: url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 400 250\"><defs><pattern id=\"dots\" patternUnits=\"userSpaceOnUse\" width=\"20\" height=\"20\"><circle cx=\"10\" cy=\"10\" r=\"3\" fill=\"%2364748b\" opacity=\"0.3\"/></pattern></defs><rect width=\"400\" height=\"250\" fill=\"%23f1f5f9\"/><rect width=\"400\" height=\"250\" fill=\"url(%23dots)\"/><rect x=\"50\" y=\"50\" width=\"300\" height=\"150\" fill=\"%23667eea\" rx=\"8\"/><text x=\"200\" y=\"135\" text-anchor=\"middle\" dy=\".3em\" fill=\"white\" font-size=\"16\" font-weight=\"bold\">Art Direction</text></svg>') center/cover; border-radius: 16px;"></div>
461 <div class="card-content">
462 <h3 class="card-title">Art Direction</h3>
463 <p class="card-description">Different crops and compositions for different screen sizes using picture element.</p>
464 <div class="card-meta">
465 <span class="format-indicator">Adaptive</span>
466 <span>Multi-crop</span>
467 </div>
468 </div>
469 </div>
470
471 <div class="image-card">
472 <div style="width: 100%; height: 250px; background: url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 400 250\"><rect width=\"400\" height=\"250\" fill=\"linear-gradient(135deg,%23065f46,%2310b981)\"/><rect x=\"80\" y=\"80\" width=\"240\" height=\"90\" fill=\"white\" opacity=\"0.2\" rx=\"8\"/><text x=\"200\" y=\"110\" text-anchor=\"middle\" dy=\".3em\" fill=\"white\" font-size=\"14\" font-weight=\"bold\">AVIF</text><text x=\"200\" y=\"130\" text-anchor=\"middle\" dy=\".3em\" fill=\"white\" font-size=\"12\">Next-gen format</text><text x=\"200\" y=\"150\" text-anchor=\"middle\" dy=\".3em\" fill=\"white\" font-size=\"10\">90% smaller files</text></svg>') center/cover; border-radius: 16px;"></div>
473 <div class="card-content">
474 <h3 class="card-title">Modern Formats</h3>
475 <p class="card-description">AVIF, WebP, and JPEG with automatic format selection based on browser support.</p>
476 <div class="card-meta">
477 <span class="format-indicator avif">AVIF</span>
478 <span class="format-indicator webp">WebP</span>
479 <span>Multi-format</span>
480 </div>
481 </div>
482 </div>
483
484 <div class="image-card">
485 <div style="width: 100%; height: 250px; background: url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 400 250\"><rect width=\"400\" height=\"250\" fill=\"%23fbbf24\"/><rect x=\"0\" y=\"0\" width=\"400\" height=\"125\" fill=\"%23f59e0b\"/><circle cx=\"320\" cy=\"60\" r=\"25\" fill=\"white\"/><polygon points=\"50,200 150,150 250,180 350,140 400,160 400,250 0,250\" fill=\"%23059669\"/><text x=\"200\" y=\"130\" text-anchor=\"middle\" dy=\".3em\" fill=\"white\" font-size=\"16\" font-weight=\"bold\">Lazy Loading</text></svg>') center/cover; border-radius: 16px;"></div>
486 <div class="card-content">
487 <h3 class="card-title">Lazy Loading</h3>
488 <p class="card-description">Images load only when needed, improving performance and reducing bandwidth usage.</p>
489 <div class="card-meta">
490 <span class="format-indicator">Optimized</span>
491 <span>Performance+</span>
492 </div>
493 </div>
494 </div>
495
496 <div class="image-card">
497 <div style="width: 100%; height: 250px; background: url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 400 250\"><rect width=\"400\" height=\"250\" fill=\"%238b5cf6\"/><rect x=\"50\" y=\"50\" width=\"100\" height=\"150\" fill=\"white\" opacity=\"0.3\" rx=\"4\"/><rect x=\"170\" y=\"50\" width=\"100\" height=\"150\" fill=\"white\" opacity=\"0.5\" rx=\"4\"/><rect x=\"290\" y=\"50\" width=\"100\" height=\"150\" fill=\"white\" opacity=\"0.7\" rx=\"4\"/><text x=\"200\" y=\"230\" text-anchor=\"middle\" dy=\".3em\" fill=\"white\" font-size=\"14\" font-weight=\"bold\">Container Queries</text></svg>') center/cover; border-radius: 16px;"></div>
498 <div class="card-content">
499 <h3 class="card-title">Container Queries</h3>
500 <p class="card-description">Images that respond to their container size, not just viewport size.</p>
501 <div class="card-meta">
502 <span class="format-indicator">Modern</span>
503 <span>Context-aware</span>
504 </div>
505 </div>
506 </div>
507 </div>
508 </section>
509
510 <!-- Video Gallery Section -->
511 <section class="section">
512 <h2 class="section-title">Responsive Video</h2>
513 <p class="section-description">
514 Adaptive video content that delivers the best quality for each device and connection speed.
515 </p>
516
517 <!-- Quality Selector -->
518 <div class="quality-selector">
519 <h3 style="text-align: center; margin-bottom: 1rem; color: #1e293b;">Video Quality Settings</h3>
520 <div class="quality-options">
521 <button class="quality-button active" data-quality="auto">Auto (Recommended)</button>
522 <button class="quality-button" data-quality="high">High (1080p)</button>
523 <button class="quality-button" data-quality="medium">Medium (720p)</button>
524 <button class="quality-button" data-quality="low">Low (480p)</button>
525 </div>
526 </div>
527
528 <div class="video-gallery">
529 <div class="video-card">
530 <div class="video-container">
531 <div class="video-placeholder">
532 <div>
533 <div style="font-size: 2rem; margin-bottom: 0.5rem;">🎬</div>
534 <div>Responsive Video Demo</div>
535 <div style="font-size: 0.875rem; margin-top: 0.5rem; opacity: 0.7;">16:9 Aspect Ratio</div>
536 </div>
537 </div>
538 </div>
539 <h3 class="card-title">Adaptive Streaming</h3>
540 <p class="card-description">Multiple resolutions and formats delivered based on device capabilities and network speed.</p>
541 <div class="card-meta">
542 <span>MP4 / WebM</span>
543 <span>Multiple resolutions</span>
544 <span>Smart loading</span>
545 </div>
546 </div>
547
548 <div class="video-card">
549 <div class="video-container">
550 <div class="video-placeholder">
551 <div>
552 <div style="font-size: 2rem; margin-bottom: 0.5rem;">📱</div>
553 <div>Mobile-Optimized</div>
554 <div style="font-size: 0.875rem; margin-top: 0.5rem; opacity: 0.7;">Portrait-friendly</div>
555 </div>
556 </div>
557 </div>
558 <h3 class="card-title">Mobile-First Video</h3>
559 <p class="card-description">Optimized encoding and sizing for mobile devices with efficient data usage.</p>
560 <div class="card-meta">
561 <span>AV1 codec</span>
562 <span>Data-saving</span>
563 <span>Touch controls</span>
564 </div>
565 </div>
566 </div>
567 </section>
568
569 <!-- Performance Metrics -->
570 <section class="section">
571 <h2 class="section-title">Performance Metrics</h2>
572 <div class="quality-selector">
573 <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; text-align: center;">
574 <div>
575 <div style="font-size: 2rem; font-weight: bold; color: #059669;" id="loadTime">--</div>
576 <div style="font-size: 0.875rem; color: #64748b;">Average Load Time</div>
577 </div>
578 <div>
579 <div style="font-size: 2rem; font-weight: bold; color: #dc2626;" id="dataUsage">--</div>
580 <div style="font-size: 0.875rem; color: #64748b;">Data Usage</div>
581 </div>
582 <div>
583 <div style="font-size: 2rem; font-weight: bold; color: #7c3aed;" id="formatSupport">--</div>
584 <div style="font-size: 0.875rem; color: #64748b;">Format Support</div>
585 </div>
586 <div>
587 <div style="font-size: 2rem; font-weight: bold; color: #ea580c;" id="devicePixelRatio">--</div>
588 <div style="font-size: 0.875rem; color: #64748b;">Pixel Ratio</div>
589 </div>
590 </div>
591 </div>
592 </section>
593 </div>
594
595 <script>
596 document.addEventListener('DOMContentLoaded', function() {
597 console.log('📷 Responsive Images & Media - Interactive Demo Loaded');
598
599 // Connection detection and display
600 function updateConnectionInfo() {
601 const connectionInfo = document.getElementById('connectionInfo');
602 const connectionSpeed = document.getElementById('connectionSpeed');
603
604 if (navigator.connection) {
605 const conn = navigator.connection;
606 const effectiveType = conn.effectiveType || 'unknown';
607 const downlink = conn.downlink ? conn.downlink.toFixed(1) + ' Mbps' : 'unknown';
608
609 connectionSpeed.textContent = effectiveType.toUpperCase();
610 connectionInfo.title = `Downlink: ${downlink}, RTT: ${conn.rtt || 'unknown'}ms`;
611
612 // Color code by connection quality
613 if (effectiveType === '4g') {
614 connectionSpeed.style.color = '#059669';
615 } else if (effectiveType === '3g') {
616 connectionSpeed.style.color = '#d97706';
617 } else {
618 connectionSpeed.style.color = '#dc2626';
619 }
620 } else {
621 connectionSpeed.textContent = 'Unknown';
622 connectionSpeed.style.color = '#64748b';
623 }
624 }
625
626 // Quality selector functionality
627 const qualityButtons = document.querySelectorAll('.quality-button');
628 qualityButtons.forEach(button => {
629 button.addEventListener('click', function() {
630 // Remove active class from all buttons
631 qualityButtons.forEach(btn => btn.classList.remove('active'));
632 // Add active class to clicked button
633 this.classList.add('active');
634
635 const quality = this.dataset.quality;
636 console.log(`Video quality changed to: ${quality}`);
637
638 // Update video placeholder content based on quality
639 updateVideoQuality(quality);
640 });
641 });
642
643 function updateVideoQuality(quality) {
644 const placeholders = document.querySelectorAll('.video-placeholder');
645 const qualityTexts = {
646 'auto': 'Auto-Selected Quality',
647 'high': '1080p High Quality',
648 'medium': '720p Medium Quality',
649 'low': '480p Data Saver'
650 };
651
652 placeholders.forEach(placeholder => {
653 const textDiv = placeholder.querySelector('div div:nth-child(2)');
654 if (textDiv) {
655 textDiv.textContent = qualityTexts[quality] || 'Unknown Quality';
656 }
657 });
658 }
659
660 // Performance metrics simulation
661 function updatePerformanceMetrics() {
662 const connection = navigator.connection;
663 let loadTime = '2.3s';
664 let dataUsage = '1.2MB';
665
666 if (connection) {
667 if (connection.effectiveType === '4g') {
668 loadTime = '1.1s';
669 dataUsage = '2.5MB';
670 } else if (connection.effectiveType === '3g') {
671 loadTime = '3.4s';
672 dataUsage = '800KB';
673 } else {
674 loadTime = '5.2s';
675 dataUsage = '400KB';
676 }
677 }
678
679 document.getElementById('loadTime').textContent = loadTime;
680 document.getElementById('dataUsage').textContent = dataUsage;
681
682 // Format support detection
683 const formatSupport = detectFormatSupport();
684 document.getElementById('formatSupport').textContent = formatSupport + '%';
685
686 // Device pixel ratio
687 const dpr = window.devicePixelRatio || 1;
688 document.getElementById('devicePixelRatio').textContent = dpr.toFixed(1) + 'x';
689 }
690
691 function detectFormatSupport() {
692 const canvas = document.createElement('canvas');
693 canvas.width = 1;
694 canvas.height = 1;
695
696 let supportedFormats = 0;
697 const totalFormats = 4;
698
699 // Check WebP support
700 if (canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0) {
701 supportedFormats++;
702 }
703
704 // Check AVIF support (simplified)
705 const img = new Image();
706 img.onload = () => supportedFormats++;
707 img.onerror = () => {};
708 img.src = 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgABogQEAwgMg8f8D///8WfhwB8+ErK42A=';
709
710 // Assume JPEG and PNG are supported
711 supportedFormats += 2;
712
713 return Math.round((supportedFormats / totalFormats) * 100);
714 }
715
716 // Lazy loading simulation
717 function simulateLazyLoading() {
718 const imageCards = document.querySelectorAll('.image-card');
719
720 const observer = new IntersectionObserver((entries) => {
721 entries.forEach(entry => {
722 if (entry.isIntersecting) {
723 const card = entry.target;
724 const img = card.querySelector('div[style*="background"]');
725
726 if (img && !card.classList.contains('loaded')) {
727 // Simulate loading delay
728 card.classList.add('media-loading');
729
730 setTimeout(() => {
731 card.classList.remove('media-loading');
732 card.classList.add('media-loaded');
733 card.classList.add('loaded');
734 console.log('Image lazy loaded:', card.querySelector('.card-title').textContent);
735 }, Math.random() * 1000 + 500);
736 }
737 }
738 });
739 }, { rootMargin: '50px' });
740
741 imageCards.forEach(card => {
742 observer.observe(card);
743 });
744 }
745
746 // Interactive hover effects
747 function addInteractiveEffects() {
748 const mediaCards = document.querySelectorAll('.image-card, .video-card');
749
750 mediaCards.forEach((card, index) => {
751 card.addEventListener('mouseenter', function() {
752 const title = this.querySelector('.card-title').textContent;
753 console.log(`Viewing: ${title}`);
754
755 // Add subtle scale effect
756 this.style.transform = 'translateY(-12px) scale(1.02)';
757 });
758
759 card.addEventListener('mouseleave', function() {
760 this.style.transform = '';
761 });
762
763 card.addEventListener('click', function() {
764 const title = this.querySelector('.card-title').textContent;
765 const description = this.querySelector('.card-description').textContent;
766
767 console.log(`Clicked: ${title}`);
768 console.log(`Description: ${description}`);
769
770 // Create click ripple effect
771 const ripple = document.createElement('div');
772 const rect = this.getBoundingClientRect();
773 const size = Math.max(rect.width, rect.height);
774
775 ripple.style.cssText = `
776 position: absolute;
777 border-radius: 50%;
778 background: rgba(102, 126, 234, 0.3);
779 transform: scale(0);
780 animation: ripple 0.6s ease-out;
781 width: ${size}px;
782 height: ${size}px;
783 left: 50%;
784 top: 50%;
785 margin-left: -${size/2}px;
786 margin-top: -${size/2}px;
787 pointer-events: none;
788 z-index: 10;
789 `;
790
791 this.style.position = 'relative';
792 this.appendChild(ripple);
793
794 setTimeout(() => ripple.remove(), 600);
795 });
796
797 // Add entrance animation delay
798 card.style.animationDelay = (index * 0.1) + 's';
799 });
800 }
801
802 // Device capability detection
803 function detectDeviceCapabilities() {
804 const capabilities = {
805 pixelRatio: window.devicePixelRatio || 1,
806 viewportWidth: window.innerWidth,
807 viewportHeight: window.innerHeight,
808 colorGamut: 'unknown',
809 hdr: 'unknown'
810 };
811
812 // Test for wide color gamut support
813 if (window.matchMedia && window.matchMedia('(color-gamut: p3)').matches) {
814 capabilities.colorGamut = 'P3';
815 } else if (window.matchMedia && window.matchMedia('(color-gamut: srgb)').matches) {
816 capabilities.colorGamut = 'sRGB';
817 }
818
819 // Test for HDR support
820 if (window.matchMedia && window.matchMedia('(dynamic-range: high)').matches) {
821 capabilities.hdr = 'Supported';
822 } else {
823 capabilities.hdr = 'Standard';
824 }
825
826 console.log('Device capabilities:', capabilities);
827 return capabilities;
828 }
829
830 // Initialize all functionality
831 updateConnectionInfo();
832 updatePerformanceMetrics();
833 simulateLazyLoading();
834 addInteractiveEffects();
835 detectDeviceCapabilities();
836
837 // Update connection info on changes
838 if (navigator.connection) {
839 navigator.connection.addEventListener('change', updateConnectionInfo);
840 }
841
842 // Update metrics on window resize
843 window.addEventListener('resize', () => {
844 setTimeout(updatePerformanceMetrics, 100);
845 });
846
847 // Add CSS for ripple animation
848 const style = document.createElement('style');
849 style.textContent = `
850 @keyframes ripple {
851 to {
852 transform: scale(2);
853 opacity: 0;
854 }
855 }
856 `;
857 document.head.appendChild(style);
858
859 console.log('🚀 Responsive media system initialized successfully!');
860 console.log('📊 Check browser console for detailed capability information');
861 console.log('🎛️ Use quality controls to test different video settings');
862 console.log('🖱️ Hover and click on media cards for interactive feedback');
863 });
864 </script>
865</body>
866</html>This comprehensive example demonstrates advanced responsive media techniques:
Key Features Implemented:
Advanced Techniques Demonstrated:
The JavaScript adds interactive features for testing different quality settings and monitoring performance metrics in real-time.
In the next lesson, we'll explore mobile navigation patterns, learning how to create touch-friendly navigation systems, hamburger menus, and off-canvas patterns that work seamlessly on mobile devices.
Your progress is saved in this browser, on this device only.