Table of Contents
- Introduction to Web Performance
- HTML Performance Optimization
- CSS Performance Optimization
- Critical Rendering Path
- Resource Loading Strategies
- Image Optimization
- Font Optimization
- JavaScript and CSS Interactions
- Layout and Rendering Performance
- Performance Testing and Monitoring
- Real-World Performance Examples
- Best Practices Checklist
Introduction to Web Performance
Web performance is about making websites load quickly, respond to user interactions instantly, and provide a smooth experience. Poor performance leads to higher bounce rates, lower conversions, and frustrated users.
Why Performance Matters
<!-- Performance Impact Statistics --> <div class="stats"> <ul> <li>47% of users expect a page to load in 2 seconds or less</li> <li>40% of users abandon a site that takes more than 3 seconds to load</li> <li>1 second delay = 7% reduction in conversions</li> <li>Faster sites rank higher in search results (Google uses page speed as a ranking factor)</li> </ul> </div>
Core Web Vitals
<!-- Core Web Vitals metrics --> <div class="metrics"> <h3>Google's Core Web Vitals</h3> <ul> <li><strong>Largest Contentful Paint (LCP):</strong> Loading performance (should be ≤ 2.5s)</li> <li><strong>First Input Delay (FID):</strong> Interactivity (should be ≤ 100ms)</li> <li><strong>Cumulative Layout Shift (CLS):</strong> Visual stability (should be ≤ 0.1)</li> </ul> </div>
HTML Performance Optimization
Minimize HTML Size
<!-- ❌ Bloated HTML --> <!DOCTYPE html> <html lang="en"> <head> <!-- Long comments --> <!-- ================================================ --> <!-- This is a very long comment that adds unnecessary --> <!-- bytes to the HTML file and slows down parsing --> <!-- ================================================ --> <!-- Unnecessary whitespace --> <title> Page Title </title> <!-- Inline styles - not cached, repeated on every page --> <style> .header { background: #333; color: white; } .footer { background: #333; color: white; } /* Many more styles... */ </style> </head> <body> <!-- Large amount of whitespace and comments --> <div class="container"> <div class="row"> <div class="col"> <div class="content"> <div class="inner-content"> <!-- Excessive nesting --> </div> </div> </div> </div> </div> </body> </html> <!-- ✅ Optimized HTML --> <!DOCTYPE html> <html lang="en"> <head> <title>Page Title</title> <link rel="stylesheet" href="styles.css"> </head> <body> <div class="content">Optimized content</div> </body> </html>
Use Semantic HTML
<!-- ❌ Non-semantic, hard to parse --> <div class="header"> <div class="logo">Logo</div> <div class="nav"> <div class="nav-item">Home</div> <div class="nav-item">About</div> </div> </div> <div class="main"> <div class="article"> <div class="title">Article Title</div> <div class="content">Article content</div> </div> </div> <!-- ✅ Semantic, easier to parse and more accessible --> <header> <h1>Logo</h1> <nav> <ul> <li><a href="/">Home</a></li> <li><a href="/about">About</a></li> </ul> </nav> </header> <main> <article> <h2>Article Title</h2> <p>Article content</p> </article> </main>
Optimize Critical HTML Structure
<!DOCTYPE html> <html lang="en"> <head> <!-- Critical: Always include charset --> <meta charset="UTF-8"> <!-- Critical: Viewport for responsive design --> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Critical: Page title for SEO --> <title>Optimized Page</title> <!-- Preconnect to important origins --> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://cdn.example.com"> <!-- Preload critical resources --> <link rel="preload" href="critical.css" as="style"> <link rel="preload" href="critical.js" as="script"> <!-- Load critical CSS inline --> <style> /* Critical CSS for above-the-fold content */ .header { background: #333; color: white; } .hero { background: url('hero.jpg') center/cover; min-height: 500px; } /* Minimal styles for initial render */ </style> <!-- Defer non-critical CSS --> <link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="styles.css"></noscript> </head> <body> <!-- Critical content above the fold --> <header class="header">...</header> <section class="hero">...</section> <!-- Defer non-critical content --> <div id="app"></div> <!-- Defer non-critical scripts --> <script src="app.js" defer></script> </body> </html> Reduce DOM Size
<!-- ❌ Deep nesting - slower DOM parsing and layout --> <div class="app"> <div class="container"> <div class="wrapper"> <div class="content"> <div class="inner"> <div class="text">Hello World</div> </div> </div> </div> </div> </div> <!-- ✅ Flatter structure - faster parsing --> <div class="app"> <div class="text">Hello World</div> </div> <!-- ❌ Many DOM elements - slow rendering --> <div class="list"> <div class="item">Item 1</div> <div class="item">Item 2</div> <div class="item">Item 3</div> <!-- 10,000 items... --> </div> <!-- ✅ Use virtual scrolling for large lists --> <div class="virtual-list" data-total="10000"></div>
CSS Performance Optimization
Minimize CSS File Size
/* ❌ Bloated CSS */ .header { background-color: #333333; background-image: url('bg.png'); background-repeat: no-repeat; background-position: center center; background-size: cover; border-style: solid; border-width: 1px; border-color: #cccccc; margin-top: 20px; margin-right: 20px; margin-bottom: 20px; margin-left: 20px; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; } /* ✅ Optimized CSS */ .header { background: #333 url('bg.png') center/cover no-repeat; border: 1px solid #ccc; margin: 20px; padding: 10px; } Remove Unused CSS
# Tools to remove unused CSS # PurgeCSS npx purgecss --css styles.css --content index.html # UnCSS npm install -g uncss uncss index.html styles.css > optimized.css
Use Efficient Selectors
/* ❌ Inefficient selectors (right to left matching) */ body div.container .content .text a { } * { margin: 0; } /* Universal selector - slow */ div > div > div > div > div { } /* Deep nesting - slow */ [class^="col-"] { } /* Attribute selectors - slower */ /* ✅ Efficient selectors */ .content-link { } /* Class selector - fast */ .nav-item { } /* Single class - fast */ #header { } /* ID selector - fastest */ Critical CSS Extraction
<!DOCTYPE html> <html> <head> <!-- Inline critical CSS for above-the-fold content --> <style> /* Critical CSS - extracted via tools like Critical, Penthouse */ .header { background: #333; padding: 1rem; } .hero { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 500px; } .hero h1 { font-size: 3rem; color: white; } .hero p { font-size: 1.2rem; color: rgba(255,255,255,0.9); } .button { background: #ff6b6b; padding: 12px 24px; border-radius: 5px; } </style> <!-- Defer non-critical CSS --> <link rel="preload" href="full-styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="full-styles.css"></noscript> </head> <body> <!-- Above-the-fold content --> <header class="header">...</header> <section class="hero"> <h1>Welcome</h1> <p>Subtitle</p> <a href="#" class="button">Get Started</a> </section> <!-- Below-the-fold content (styling loaded later) --> <section class="features">...</section> <footer>...</footer> </body> </html> Reduce CSS Complexity
/* ❌ Complex selectors - slow */ .main-content .sidebar .widget:nth-child(2n) .widget-title { } /* ✅ Simplified */ .widget-title-alt { } /* ❌ Too many layers */ .nav .nav-list .nav-item .nav-link .nav-text { } /* ✅ Flat structure */ .nav-link-text { } /* ❌ Repeated calculations */ .element { width: calc(100% - 20px - 20px - 10px); margin: calc(20px / 2); } /* ✅ Pre-calculated or variables */ .element { width: calc(100% - 50px); margin: 10px; } Use CSS Variables for Efficiency
/* ❌ Repeated values */ .button-primary { background: #3498db; color: white; border-radius: 4px; } .button-secondary { background: #2ecc71; color: white; border-radius: 4px; } .button-danger { background: #e74c3c; color: white; border-radius: 4px; } /* ✅ Variables - easier to maintain and update */ :root { --button-radius: 4px; --button-text: white; } .button-primary { --button-bg: #3498db; } .button-secondary { --button-bg: #2ecc71; } .button-danger { --button-bg: #e74c3c; } .button { background: var(--button-bg); color: var(--button-text); border-radius: var(--button-radius); } Critical Rendering Path
Understanding the Critical Rendering Path
<!-- The browser goes through several steps to render a page --> <div class="crp-steps"> <ol> <li>Parse HTML → DOM Tree</li> <li>Parse CSS → CSSOM Tree</li> <li>Combine DOM + CSSOM → Render Tree</li> <li>Layout (reflow) → Calculate positions</li> <li>Paint → Draw pixels</li> <li>Composite → Layers to screen</li> </ol> </div>
Optimize Critical Rendering Path
<!DOCTYPE html> <html> <head> <!-- 1. Minimize blocking resources --> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 2. Inline critical CSS --> <style> /* Critical CSS for above-the-fold */ body { margin: 0; font-family: sans-serif; } .header { background: #333; color: white; padding: 1rem; } .hero { min-height: 80vh; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } .hero h1 { font-size: 3rem; color: white; } </style> <!-- 3. Defer non-critical CSS --> <link rel="preload" href="styles.css" as="style" onload="this.rel='stylesheet'"> <!-- 4. Defer JavaScript --> <script src="app.js" defer></script> </head> <body> <!-- 5. Avoid layout shifts - specify dimensions --> <div class="hero" style="min-height: 500px;"> <h1>Welcome</h1> </div> <!-- 6. Image dimensions prevent layout shifts --> <img src="image.jpg" width="800" height="600" alt="Image"> </body> </html> Render-Blocking Resources
<!-- ❌ Render-blocking CSS --> <link rel="stylesheet" href="styles.css"> <!-- Blocks rendering --> <!-- ✅ Non-blocking CSS loading --> <link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="styles.css"></noscript> <!-- ❌ Render-blocking JavaScript --> <script src="app.js"></script> <!-- Blocks parsing and rendering --> <!-- ✅ Non-blocking JavaScript --> <script src="app.js" defer></script> <!-- Parses in parallel, executes after HTML --> <script src="app.js" async></script> <!-- Parses in parallel, executes when ready -->
Resource Loading Strategies
Preload, Prefetch, Preconnect
<head> <!-- Preload: Load critical resources early --> <link rel="preload" href="critical.css" as="style"> <link rel="preload" href="hero-image.jpg" as="image"> <link rel="preload" href="critical.js" as="script"> <link rel="preload" href="font.woff2" as="font" crossorigin> <!-- Prefetch: Load resources for future navigation --> <link rel="prefetch" href="next-page.html"> <link rel="prefetch" href="future-styles.css"> <!-- Preconnect: Establish early connection --> <link rel="preconnect" href="https://api.example.com"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <!-- DNS-Prefetch: Resolve domain names early --> <link rel="dns-prefetch" href="https://cdn.example.com"> <link rel="dns-prefetch" href="https://analytics.google.com"> </head>
Async and Defer
<!-- Standard script - blocks HTML parsing --> <script src="app.js"></script> <!-- Async - loads in parallel, executes when ready (non-blocking) --> <script async src="analytics.js"></script> <!-- Defer - loads in parallel, executes after HTML parsing (in order) --> <script defer src="app.js"></script> <script defer src="vendor.js"></script> <script defer src="main.js"></script> <!-- Module scripts are deferred by default --> <script type="module" src="app.js"></script> <!-- Nomodule for legacy browsers --> <script nomodule src="legacy.js"></script>
Lazy Loading
<!-- Native lazy loading for images --> <img src="image.jpg" loading="lazy" alt="Lazy loaded image"> <img src="image.jpg" loading="eager" alt="Eager loaded image"> <!-- Native lazy loading for iframes --> <iframe src="video.html" loading="lazy"></iframe> <!-- Intersection Observer for custom lazy loading --> <script> const lazyImages = document.querySelectorAll('img[data-src]'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.classList.add('loaded'); observer.unobserve(img); } }); }); lazyImages.forEach(img => observer.observe(img)); </script> Priority Hints
<!-- Prioritize critical resources --> <link rel="preload" href="critical.css" as="style" fetchpriority="high"> <img src="hero.jpg" fetchpriority="high" alt="Hero image"> <!-- De-prioritize non-critical resources --> <img src="footer-logo.jpg" fetchpriority="low" loading="lazy" alt="Footer logo"> <script src="analytics.js" fetchpriority="low" defer></script>
Image Optimization
Image Formats and Sizes
<!-- Choose the right format --> <picture> <!-- AVIF - best compression, modern browsers --> <source srcset="image.avif" type="image/avif"> <!-- WebP - good compression, wide support --> <source srcset="image.webp" type="image/webp"> <!-- JPEG/PNG - fallback for older browsers --> <img src="image.jpg" alt="Optimized image"> </picture> <!-- Responsive images with srcset --> <img srcset="image-320w.jpg 320w, image-480w.jpg 480w, image-800w.jpg 800w, image-1200w.jpg 1200w" sizes="(max-width: 320px) 280px, (max-width: 480px) 440px, (max-width: 800px) 760px, 1000px" src="image-800w.jpg" alt="Responsive image" loading="lazy"> <!-- Art direction with picture --> <picture> <source media="(max-width: 480px)" srcset="mobile.jpg"> <source media="(max-width: 768px)" srcset="tablet.jpg"> <source media="(max-width: 1024px)" srcset="desktop.jpg"> <img src="desktop.jpg" alt="Art directed image"> </picture>
Image Compression Tips
<!-- Image optimization checklist --> <div class="tips"> <ul> <li>✓ Use modern formats (AVIF > WebP > JPEG/PNG)</li> <li>✓ Compress images (tools: ImageOptim, TinyPNG, Squoosh)</li> <li>✓ Set appropriate dimensions to avoid scaling</li> <li>✓ Use vector graphics for logos and icons (SVG)</li> <li>✓ Implement lazy loading for offscreen images</li> <li>✓ Use srcset for responsive delivery</li> <li>✓ Leverage CDN for image optimization</li> </ul> </div> <!-- Image CDN with automatic optimization --> <img src="https://cdn.example.com/image.jpg?w=800&h=600&q=80&format=webp" alt="CDN optimized image">
Font Optimization
Font Loading Strategies
<head> <!-- Preload critical fonts --> <link rel="preload" href="/fonts/primary.woff2" as="font" type="font/woff2" crossorigin> <!-- Font display strategies --> <style> /* font-display: swap - show fallback, swap when ready (fast) */ @font-face { font-family: 'Primary'; src: url('/fonts/primary.woff2') format('woff2'); font-display: swap; } /* font-display: optional - use fallback if not ready (fastest) */ @font-face { font-family: 'Secondary'; src: url('/fonts/secondary.woff2') format('woff2'); font-display: optional; } /* font-display: block - invisible until ready (slowest) */ @font-face { font-family: 'Critical'; src: url('/fonts/critical.woff2') format('woff2'); font-display: block; } </style> </head> <!-- Using modern font formats --> <style> @font-face { font-family: 'MyFont'; src: url('font.woff2') format('woff2'), url('font.woff') format('woff'), url('font.ttf') format('truetype'); font-weight: 400; font-style: normal; font-display: swap; } </style> Subset Fonts
<!-- Subset fonts to include only needed characters --> <style> /* Full font - large */ @font-face { font-family: 'FullFont'; src: url('full-font.woff2') format('woff2'); } /* Subset font - only Latin characters */ @font-face { font-family: 'SubsetFont'; src: url('subset-latin.woff2') format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* Subset font - only Cyrillic */ @font-face { font-family: 'SubsetFont'; src: url('subset-cyrillic.woff2') format('woff2'); unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } </style> Variable Fonts
<!-- Variable fonts - single file for multiple weights --> <style> @font-face { font-family: 'VariableFont'; src: url('variable-font.woff2') format('woff2-variations'); font-weight: 100 900; font-stretch: 75% 125%; font-style: oblique 0deg 12deg; } body { font-family: 'VariableFont', sans-serif; font-weight: 400; } h1 { font-weight: 700; } </style> JavaScript and CSS Interactions
Minimize Layout Thrashing
// ❌ Bad - causes layout thrashing const elements = document.querySelectorAll('.item'); elements.forEach(element => { const width = element.offsetWidth; // Read element.style.width = width + 10 + 'px'; // Write (forces reflow) const height = element.offsetHeight; // Read (forces another reflow) element.style.height = height + 10 + 'px'; // Write }); // ✅ Good - batch reads and writes const elements = document.querySelectorAll('.item'); const widths = []; const heights = []; // Batch reads elements.forEach(element => { widths.push(element.offsetWidth); heights.push(element.offsetHeight); }); // Batch writes elements.forEach((element, i) => { element.style.width = widths[i] + 10 + 'px'; element.style.height = heights[i] + 10 + 'px'; }); Use requestAnimationFrame
// ❌ Bad - animations without optimization let position = 0; setInterval(() => { position += 10; element.style.transform = `translateX(${position}px)`; }, 16); // Not synchronized with browser refresh rate // ✅ Good - use requestAnimationFrame let position = 0; function animate() { position += 10; element.style.transform = `translateX(${position}px)`; requestAnimationFrame(animate); } requestAnimationFrame(animate); // Or use CSS animations/transitions when possible .element { transition: transform 0.3s ease; } .element.moved { transform: translateX(100px); } Avoid Forced Synchronous Layouts
// Properties that cause layout/reflow const layoutTriggers = [ 'offsetTop', 'offsetLeft', 'offsetWidth', 'offsetHeight', 'scrollTop', 'scrollLeft', 'scrollWidth', 'scrollHeight', 'clientTop', 'clientLeft', 'clientWidth', 'clientHeight', 'getComputedStyle', 'getBoundingClientRect' ]; // ❌ Bad - reading layout properties in loop for (let i = 0; i < 1000; i++) { const width = element.offsetWidth; // Triggers layout each time element.style.width = width + 1 + 'px'; } // ✅ Good - cache values let width = element.offsetWidth; // Layout once for (let i = 0; i < 1000; i++) { element.style.width = width + i + 'px'; } CSS Triggers and GPU Acceleration
/* Properties that trigger layout/reflow */ /* These are expensive: width, height, margin, padding, top, left, etc. */ /* Properties that trigger repaint only */ /* These are cheaper: color, background-color, border-color, etc. */ /* Properties that trigger composite only (GPU accelerated) */ /* These are cheapest: transform, opacity, filter, etc. */ /* ✅ Use transform instead of positioning properties */ /* ❌ Expensive */ .element { transition: left 0.3s; position: absolute; left: 0; } .element.moved { left: 100px; } /* ✅ Cheap (GPU accelerated) */ .element { transition: transform 0.3s; transform: translateX(0); } .element.moved { transform: translateX(100px); } /* ✅ Use opacity for fade effects */ /* ❌ Expensive */ .element { transition: visibility 0.3s; } .element.hidden { visibility: hidden; } /* ✅ Cheap (GPU accelerated) */ .element { transition: opacity 0.3s; } .element.hidden { opacity: 0; } /* Promote elements to their own layer */ .gpu-accelerated { will-change: transform; /* or */ transform: translateZ(0); backface-visibility: hidden; } Layout and Rendering Performance
Avoid Complex Selectors
/* ❌ Complex selectors - slow to match */ div.container > div.content > p.description span.highlight { } /* ✅ Simple selectors - fast */ .description-highlight { } /* ❌ Wildcard selectors - slow */ * { box-sizing: border-box; } /* ✅ Better */ html { box-sizing: border-box; } *, *::before, *::after { box-sizing: inherit; } Minimize Reflows and Repaints
/* Batch DOM changes */ const container = document.getElementById('container'); const fragment = document.createDocumentFragment(); for (let i = 0; i < 1000; i++) { const div = document.createElement('div'); div.textContent = `Item ${i}`; fragment.appendChild(div); } container.appendChild(fragment); // Single reflow /* Use CSS containment for isolated components */ .component { contain: layout style paint; /* Browser knows changes only affect this component */ } .card { contain: content; /* Shorthand for layout, paint, and style containment */ } /* Use content-visibility for offscreen content */ .offscreen-section { content-visibility: auto; contain-intrinsic-size: 0 500px; /* Estimated size before rendering */ } Optimize Animations
/* ✅ Animate only transform and opacity */ .animated-card { transition: transform 0.3s, opacity 0.3s; } .animated-card:hover { transform: scale(1.05); opacity: 0.9; } /* ✅ Use CSS animations instead of JavaScript when possible */ @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .fade-in { animation: fadeIn 0.5s ease-out; } /* ✅ Use will-change for animations */ .will-change { will-change: transform; } Performance Testing and Monitoring
Lighthouse Performance Audit
# Run Lighthouse from command line npx lighthouse https://example.com --view --preset=desktop npx lighthouse https://example.com --view --preset=mobile # Run with specific categories npx lighthouse https://example.com --only-categories=performance,accessibility
WebPageTest
<!-- WebPageTest is excellent for advanced performance testing --> <div class="tools"> <ul> <li>Test from multiple locations</li> <li>Waterfall charts for all resources</li> <li>First Byte Time, Start Render, Speed Index</li> <li>Video capture of page load</li> <li>Recommendations for optimization</li> </ul> </div>
Chrome DevTools Performance Tab
// Record performance in DevTools console.profile('MyProfile'); // ... code to profile ... console.profileEnd('MyProfile'); // Measure specific operations performance.mark('start'); // ... code to measure ... performance.mark('end'); performance.measure('Operation', 'start', 'end'); const measurements = performance.getEntriesByType('measure'); console.log(measurements); Real User Monitoring (RUM)
<!-- Collect real user metrics --> <script> // Largest Contentful Paint (LCP) new PerformanceObserver((entryList) => { const entries = entryList.getEntries(); const lastEntry = entries[entries.length - 1]; console.log('LCP:', lastEntry.renderTime || lastEntry.loadTime); }).observe({entryTypes: ['largest-contentful-paint']}); // First Input Delay (FID) new PerformanceObserver((entryList) => { const entries = entryList.getEntries(); entries.forEach(entry => { console.log('FID:', entry.processingStart - entry.startTime); }); }).observe({entryTypes: ['first-input']}); // Cumulative Layout Shift (CLS) new PerformanceObserver((entryList) => { const entries = entryList.getEntries(); let cls = 0; entries.forEach(entry => { if (!entry.hadRecentInput) { cls += entry.value; } }); console.log('CLS:', cls); }).observe({entryTypes: ['layout-shift']}); </script> Real-World Performance Examples
Example 1: Optimized Blog Post
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Optimized Blog Post</title> <!-- Preconnect to critical origins --> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://cdn.example.com"> <!-- Preload critical resources --> <link rel="preload" href="critical.css" as="style"> <link rel="preload" href="hero.jpg" as="image" fetchpriority="high"> <!-- Critical CSS inline --> <style> /* Critical styles for above-the-fold */ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: system-ui, -apple-system, sans-serif; line-height: 1.6; } .hero { min-height: 400px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); display: flex; align-items: center; justify-content: center; } .hero h1 { color: white; font-size: clamp(2rem, 5vw, 3rem); text-align: center; padding: 1rem; } .container { max-width: 800px; margin: 0 auto; padding: 2rem; } .loading-spinner { display: none; } </style> <!-- Defer non-critical CSS --> <link rel="preload" href="full-styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="full-styles.css"></noscript> <!-- Defer JavaScript --> <script defer src="analytics.js"></script> <script defer src="comments.js"></script> </head> <body> <div class="hero"> <h1>Optimized Blog Post Title</h1> </div> <div class="container"> <article> <p>First paragraph of content. This is above the fold content that gets styled with critical CSS.</p> <p>More content that will be styled when the full CSS loads...</p> <!-- Lazy load images below the fold --> <img src="placeholder.jpg" data-src="image-1.jpg" loading="lazy" width="800" height="600" alt="Description"> <img src="placeholder.jpg" data-src="image-2.jpg" loading="lazy" width="800" height="600" alt="Description"> </article> <!-- Lazy load comments section --> <div id="comments" data-lazy-load="/comments.html"></div> </div> <script> // Lazy load images with Intersection Observer const lazyImages = document.querySelectorAll('img[data-src]'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.classList.add('loaded'); observer.unobserve(img); } }); }); lazyImages.forEach(img => observer.observe(img)); // Lazy load comments section const commentsSection = document.getElementById('comments'); if (commentsSection && commentsSection.dataset.lazyLoad) { const commentsObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { fetch(commentsSection.dataset.lazyLoad) .then(response => response.text()) .then(html => commentsSection.innerHTML = html); commentsObserver.unobserve(commentsSection); } }); }); commentsObserver.observe(commentsSection); } </script> </body> </html> Example 2: Performance Dashboard
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Performance Dashboard</title> <style> /* CSS Grid layout - efficient */ .dashboard { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1rem; padding: 1rem; } .card { background: white; border-radius: 8px; padding: 1rem; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /* Use transform for animations */ .card:hover { transform: translateY(-2px); transition: transform 0.2s; } /* Optimize for high-density displays */ @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { .chart { background-image: url('[email protected]'); } } /* Reduce motion preference */ @media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } </style> </head> <body> <div class="dashboard" id="dashboard"></div> <script> // Virtual scrolling for large datasets class VirtualScroller { constructor(container, items, itemHeight = 50, renderItem) { this.container = container; this.items = items; this.itemHeight = itemHeight; this.renderItem = renderItem; this.visibleCount = Math.ceil(container.clientHeight / itemHeight); this.startIndex = 0; this.container.style.position = 'relative'; this.container.style.height = `${items.length * itemHeight}px`; this.scrollHandler = this.onScroll.bind(this); this.container.addEventListener('scroll', this.scrollHandler); this.render(); } onScroll() { const newStartIndex = Math.floor(this.container.scrollTop / this.itemHeight); if (newStartIndex !== this.startIndex) { this.startIndex = newStartIndex; this.render(); } } render() { const endIndex = Math.min(this.startIndex + this.visibleCount + 1, this.items.length); const visibleItems = this.items.slice(this.startIndex, endIndex); let html = ''; visibleItems.forEach((item, i) => { const top = (this.startIndex + i) * this.itemHeight; html += `<div style="position: absolute; top: ${top}px; width: 100%;">${this.renderItem(item)}</div>`; }); this.container.innerHTML = html; } destroy() { this.container.removeEventListener('scroll', this.scrollHandler); } } // Efficient rendering const items = Array.from({ length: 10000 }, (_, i) => ({ id: i, name: `Item ${i}` })); const scroller = new VirtualScroller( document.getElementById('dashboard'), items, 80, (item) => `<div class="card">Item ${item.id}: ${item.name}</div>` ); </script> </body> </html> Example 3: Progressive Web App with Performance Optimizations
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Progressive Web App</title> <!-- PWA Manifest --> <link rel="manifest" href="/manifest.json"> <meta name="theme-color" content="#3498db"> <!-- Service Worker registration --> <script> if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js') .then(reg => console.log('SW registered:', reg)) .catch(err => console.log('SW registration failed:', err)); } </script> <!-- Critical CSS --> <style> /* Critical styles for app shell */ body { margin: 0; font-family: system-ui; background: #f5f5f5; } .app-shell { display: flex; flex-direction: column; min-height: 100vh; } .header { background: #3498db; color: white; padding: 1rem; position: sticky; top: 0; } .main { flex: 1; padding: 1rem; } .loading { text-align: center; padding: 2rem; } </style> </head> <body> <div class="app-shell"> <header class="header"> <h1>PWA App</h1> </header> <main class="main" id="app"> <div class="loading">Loading...</div> </main> </div> <script> // Use IndexedDB for offline storage const DB_NAME = 'app_db'; const DB_VERSION = 1; let db; async function initDB() { return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, DB_VERSION); request.onerror = () => reject(request.error); request.onsuccess = () => { db = request.result; resolve(db); }; request.onupgradeneeded = (event) => { const db = event.target.result; if (!db.objectStoreNames.contains('data')) { db.createObjectStore('data', { keyPath: 'id' }); } }; }); } async function getData() { if (navigator.onLine) { // Fetch from network, cache in IndexedDB const response = await fetch('/api/data'); const data = await response.json(); const tx = db.transaction('data', 'readwrite'); const store = tx.objectStore('data'); await store.put({ id: 'app-data', value: data }); await tx.done; return data; } else { // Offline - get from IndexedDB const tx = db.transaction('data', 'readonly'); const store = tx.objectStore('data'); const result = await store.get('app-data'); return result ? result.value : null; } } // Render with performance in mind async function render() { await initDB(); const data = await getData(); const app = document.getElementById('app'); if (data) { app.innerHTML = ` <div class="grid"> ${data.map(item => ` <div class="card"> <h3>${item.title}</h3> <p>${item.description}</p> </div> `).join('')} </div> `; } else { app.innerHTML = '<div class="loading">No data available offline</div>'; } } render(); // Prefetch next page const prefetchLinks = ['/next-page', '/other-page']; prefetchLinks.forEach(url => { const link = document.createElement('link'); link.rel = 'prefetch'; link.href = url; document.head.appendChild(link); }); </script> </body> </html> Best Practices Checklist
HTML Performance Checklist
<!-- HTML Optimization Checklist --> <div class="checklist"> <ul> <li>✓ Use semantic HTML for better parsing</li> <li>✓ Minimize DOM depth and element count</li> <li>✓ Place scripts at the end of body or use defer/async</li> <li>✓ Set viewport meta tag</li> <li>✓ Specify image dimensions to prevent layout shifts</li> <li>✓ Use loading="lazy" for offscreen images</li> <li>✓ Implement prefetch, preconnect, and preload for critical resources</li> <li>✓ Minify HTML in production</li> <li>✓ Remove comments and whitespace</li> <li>✓ Use appropriate heading hierarchy</li> </ul> </div>
CSS Performance Checklist
/* CSS Optimization Checklist */ .performance-checklist { list-style: none; padding: 0; } .performance-checklist li { margin-bottom: 0.5rem; } /* 1. ✓ Minimize CSS file size */ /* Minify, compress, remove unused CSS */ /* 2. ✓ Inline critical CSS */ /* Load above-the-fold styles inline, defer rest */ /* 3. ✓ Use efficient selectors */ /* Avoid universal selectors, deep nesting, attribute selectors when possible */ /* 4. ✓ Reduce reflows and repaints */ /* Use transform and opacity for animations, batch DOM changes */ /* 5. ✓ Use CSS containment */ .component { contain: layout style paint; } /* 6. ✓ Optimize font loading */ @font-face { font-display: swap; } /* 7. ✓ Use will-change sparingly */ .element { will-change: transform; /* Only for elements that will animate */ } /* 8. ✓ Avoid @import */ /* ❌ Use @import */ @import url('styles.css'); /* ✅ Use link tag */ <link rel="stylesheet" href="styles.css"> /* 9. ✓ Use variables for maintainability */ :root { --spacing: 1rem; } /* 10. ✓ Implement responsive images */ <picture> <source srcset="image.webp" type="image/webp"> <img src="image.jpg" loading="lazy"> </picture> Resource Loading Checklist
<!-- Resource Loading Optimization --> <div class="checklist"> <ul> <li>✓ Preload critical resources (fonts, CSS, JS, images)</li> <li>✓ Preconnect to third-party origins</li> <li>✓ Prefetch next page resources</li> <li>✓ Use async/defer for non-critical JavaScript</li> <li>✓ Implement service worker for offline support</li> <li>✓ Use HTTP/2 or HTTP/3 for multiplexing</li> <li>✓ Set appropriate cache headers</li> <li>✓ Use CDN for static assets</li> <li>✓ Compress text resources (gzip, brotli)</li> <li>✓ Optimize images with modern formats (WebP, AVIF)</li> </ul> </div>
Performance Testing Checklist
<!-- Testing Checklist --> <div class="checklist"> <ul> <li>✓ Run Lighthouse performance audits (score ≥ 90)</li> <li>✓ Test on real devices and network conditions</li> <li>✓ Monitor Core Web Vitals</li> <li>✓ Analyze bundle size with tools like Bundlephobia</li> <li>✓ Check for layout shifts with Cumulative Layout Shift (CLS)</li> <li>✓ Verify first paint and first contentful paint times</li> <li>✓ Test with slower network throttling (3G, 4G)</li> <li>✓ Use Performance API for custom metrics</li> <li>✓ Implement real user monitoring (RUM)</li> <li>✓ Regular performance regression testing</li> </ul> </div>
Performance Tools and Resources
Essential Performance Tools
<div class="tools-section"> <h3>Testing & Analysis</h3> <ul> <li><strong>Lighthouse</strong> - Built into Chrome DevTools</li> <li><strong>WebPageTest</strong> - https://www.webpagetest.org/</li> <li><strong>GTmetrix</strong> - https://gtmetrix.com/</li> <li><strong>PageSpeed Insights</strong> - https://pagespeed.web.dev/</li> <li><strong>Chrome DevTools Performance Tab</strong></li> <li><strong>Coverage Tab</strong> - Find unused CSS/JS</li> </ul> <h3>Optimization Tools</h3> <ul> <li><strong>ImageOptim</strong> - Image compression</li> <li><strong>Squoosh</strong> - https://squoosh.app/</li> <li><strong>PurgeCSS</strong> - Remove unused CSS</li> <li><strong>Critical</strong> - Extract critical CSS</li> <li><strong>Webpack Bundle Analyzer</strong> - Analyze bundle size</li> <li><strong>ESLint Plugin Performance</strong> - Performance linting rules</li> </ul> </div>
Conclusion
Web performance is not just about speed—it's about user experience, conversions, and accessibility. This comprehensive guide covered:
Key Takeaways
- HTML Optimization: Minimize DOM size, use semantic markup, implement lazy loading
- CSS Optimization: Remove unused CSS, use efficient selectors, inline critical CSS
- Critical Rendering Path: Optimize render-blocking resources, prioritize above-the-fold content
- Resource Loading: Use preload, prefetch, preconnect, and appropriate caching strategies
- Images: Choose modern formats, compress, use responsive images with srcset
- Fonts: Use font-display, subset fonts, consider variable fonts
- Layout Performance: Avoid layout thrashing, use GPU-accelerated properties
- Testing: Regular performance audits with Lighthouse, WebPageTest, and real-user monitoring
Performance Mindset
Remember: Performance is a feature, not a nice-to-have. It requires continuous monitoring, testing, and optimization. Make performance part of your development workflow from day one.
Final Checklist
<!-- Before Deployment Checklist --> <div class="final-checklist"> <h3>Pre-Deployment Performance Checklist</h3> <ul> <li>✓ Lighthouse performance score ≥ 90</li> <li>✓ LCP < 2.5 seconds</li> <li>✓ FID < 100 milliseconds</li> <li>✓ CLS < 0.1</li> <li>✓ Images optimized and responsive</li> <li>✓ CSS minified and critical CSS inlined</li> <li>✓ JavaScript minified and deferred</li> <li>✓ Font loading optimized</li> <li>✓ Caching headers configured</li> <li>✓ Service worker implemented (for PWAs)</li> </ul> </div>
HTML & CSS Learning Guides, Exercises, Projects, Design Systems, Accessibility & Performance (Related to HTML & CSS Development)
HTML & CSS Quiz – Comprehensive Assessment:
This quiz evaluates core knowledge of HTML and CSS including structure, styling, layout, forms, and responsive design. It is used to test practical understanding of front-end fundamentals and identify skill levels in web development.
Read more: https://macronepal.com/bash/html-and-css-quiz-comprehensive-assessment/
Complete Guide to HTML & CSS Tooling & Automation:
This guide explains tools and automation workflows used in modern web development, such as preprocessors, build tools, and task runners that improve efficiency in HTML and CSS projects.
Read more: https://macronepal.com/bash/complete-guide-to-html-and-css-tooling-automation/
Complete HTML & CSS Exercises:
A collection of practical exercises designed to strengthen HTML and CSS skills through hands-on coding tasks, covering layout, styling, and responsive design concepts.
Read more: https://macronepal.com/bash/complete-html-and-css-exercises/
Complete HTML & CSS Landing Page Project:
This project focuses on building a full landing page using HTML and CSS, helping learners understand real-world website structure, layout design, and UI development.
Read more: https://macronepal.com/bash/complete-html-css-landing-page-project/
HTML & CSS Debugging and Testing Guide:
This guide teaches how to identify and fix errors in HTML and CSS code, along with testing techniques to ensure websites work correctly across browsers.
Read more: https://macronepal.com/bash/complete-guide-to-html-and-css-debugging-testing/
HTML & CSS Design Systems Guide:
A design system approach helps maintain consistency in UI development using reusable components, styles, and patterns across web projects.
Read more: https://macronepal.com/html-and-css/complete-guide-to-html-and-css-design-systems/
HTML & CSS Accessibility (A11y) Guide:
This guide focuses on making websites accessible for all users, including proper semantic HTML, keyboard navigation, alt text, and screen reader support.
Read more: https://macronepal.com/bash/complete-guide-to-html-css-accessibility-a11y/
HTML & CSS Performance Guide:
This topic explains optimization techniques such as reducing file size, improving loading speed, and writing efficient HTML and CSS for better website performance.
Read more: https://macronepal.com/bash/complete-guide-to-html-and-css-performance/
HTML & CSS Design Systems (Advanced Overview):
Design systems help developers maintain scalable and consistent UI components across large projects using structured HTML and reusable CSS patterns.
Read more: https://macronepal.com/html-and-css/complete-guide-to-html-and-css-design-systems/