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/