Complete Guide to HTML and CSS Performance

Table of Contents

Table of Contents

  1. Introduction to Web Performance
  2. HTML Performance Optimization
  3. CSS Performance Optimization
  4. Critical Rendering Path
  5. Resource Loading Strategies
  6. Image Optimization
  7. Font Optimization
  8. JavaScript and CSS Interactions
  9. Layout and Rendering Performance
  10. Performance Testing and Monitoring
  11. Real-World Performance Examples
  12. 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

  1. HTML Optimization: Minimize DOM size, use semantic markup, implement lazy loading
  2. CSS Optimization: Remove unused CSS, use efficient selectors, inline critical CSS
  3. Critical Rendering Path: Optimize render-blocking resources, prioritize above-the-fold content
  4. Resource Loading: Use preload, prefetch, preconnect, and appropriate caching strategies
  5. Images: Choose modern formats, compress, use responsive images with srcset
  6. Fonts: Use font-display, subset fonts, consider variable fonts
  7. Layout Performance: Avoid layout thrashing, use GPU-accelerated properties
  8. 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/

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper