On this page
Final Project: SaaS Landing Page
Final Project: Complete SaaS Landing Page
This final project brings together every concept from the course: utility classes, layout with flex and grid, responsive design, dark mode, custom theming, transitions, animations, forms, and accessibility. You will build a production-quality SaaS landing page.
What you are building
The FlowStack landing page includes:
- Fixed glassmorphism navigation with logo, desktop links, and a CTA button
- Hero section with gradient headline, animated badge, dual CTA buttons, and radial glow effects
- Features grid — responsive 3-column grid with hover effects on feature cards
- Pricing section — 3-tier pricing with a featured "Pro" plan, dark background, and accessible focus states
- Contact/newsletter form — accessible form with labels, required indicators, and visible focus rings
- Footer — minimal footer with semantic navigation
Architecture overview
The page uses a dark-first design (bg-gray-950 base) with carefully layered surfaces:
| Element | Background | Border |
|---|---|---|
| Page | bg-gray-950 |
— |
| Navbar | bg-gray-900/80 + backdrop-blur |
border-gray-800/60 |
| Feature cards | bg-gray-900 |
border-gray-800 |
| Pricing section | bg-gray-900/50 |
— |
| Pro plan | bg-brand-600 |
border-brand-500 |
| Footer | bg-gray-900 |
border-gray-800 |
Each surface is slightly lighter than the one below, creating depth without hard shadows.
Key techniques used
Glassmorphism navbar
<div class="bg-gray-900/80 backdrop-blur-md rounded-2xl border border-gray-800/60 shadow-lg">The backdrop-blur-md filter blurs content visible through the semi-transparent bg-gray-900/80 background. The border-gray-800/60 uses the opacity modifier for a subtle edge.
Gradient headline
<span class="bg-gradient-to-r from-brand-400 to-purple-400 bg-clip-text text-transparent">
10× faster
</span>bg-clip-text clips the gradient background to the text shape, and text-transparent makes the text color transparent so the background gradient shows through.
Animated status badge
<span class="w-1.5 h-1.5 rounded-full bg-brand-400 animate-pulse"></span>The animate-pulse creates a fading pulse effect that conveys live status.
Lift-on-hover card effect
<div class="hover:-translate-y-0.5 hover:shadow-lg hover:shadow-brand-600/30 transition-all duration-150">-translate-y-0.5 moves the element 2px upward on hover. shadow-brand-600/30 applies a colored shadow using the opacity modifier — a signature Tailwind v4 feature.
Featured pricing card
The middle pricing card uses bg-brand-600 background and a negative-margin label:
<div class="relative bg-brand-600 rounded-2xl">
<span class="absolute -top-3 left-1/2 -translate-x-1/2 bg-gradient-to-r from-amber-400 to-orange-400">
Most popular
</span>Accessible form
Every form field follows the accessibility pattern:
<label for="id">paired with<input id="id">aria-required="true"on required fields- Asterisk with
aria-hidden="true"(decorative only) focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20for visible focusinvalid:border-red-500for CSS-native validation states
Skip link
<a href="#main" class="sr-only focus-visible:not-sr-only focus-visible:fixed ...">
Skip to main content
</a>The sr-only hides the link visually. focus-visible:not-sr-only removes the hiding when the link receives keyboard focus, making it visible to keyboard users as the first focusable element.
Extensions to try
Once you have the base project working, try these extensions:
- Add a dark/light toggle — implement the toggle button in the navbar that saves preference to
localStorage. - Add a mobile menu — show a drawer with the navigation links when the hamburger button is clicked. Use CSS transitions for the slide-in animation.
- Add a testimonials section — a horizontal scrolling carousel of testimonial cards using
overflow-x-auto scrollbar-hide. - Add an FAQ section — an accordion using the
peervariant and a CSS-only open/close toggle. - Animate the hero — add
animate-fade-in-upto the headline, badge, and CTA buttons with staggereddelay-*values.
Checklist before shipping
Before considering the page production-ready, verify:
- All images have meaningful
altattributes - All interactive elements have visible focus indicators
- Color contrast meets WCAG AA on all text/background pairs
- The page is fully navigable by keyboard (Tab, Shift+Tab, Enter, Space)
- The skip link works correctly
- The page renders correctly on mobile (375px), tablet (768px), and desktop (1280px)
- All animations respect
prefers-reduced-motion - Forms have proper labels, error states, and accessible names
- The production CSS build is under 20 KB gzipped
<!DOCTYPE html>
<html lang="en" class="">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>FlowStack — Ship faster with Tailwind v4</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet" />
<style>
/* Inline Tailwind v4 — for playground use */
@import "tailwindcss";
@theme {
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--color-brand-500: oklch(57% 0.24 265);
--color-brand-600: oklch(50% 0.24 265);
--color-brand-700: oklch(42% 0.21 265);
}
</style>
</head>
<body class="bg-gray-950 text-gray-100 font-sans antialiased">
<!-- Skip link for accessibility -->
<a
href="#main"
class="sr-only focus-visible:not-sr-only focus-visible:fixed focus-visible:top-4 focus-visible:left-4 focus-visible:z-50 focus-visible:bg-white focus-visible:text-gray-900 focus-visible:px-4 focus-visible:py-2 focus-visible:rounded-lg focus-visible:ring-2 focus-visible:ring-brand-500 font-medium"
>
Skip to main content
</a>
<!-- ============================================================
NAVIGATION
============================================================ -->
<header class="fixed inset-x-0 top-0 z-50">
<nav
class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-4"
aria-label="Main navigation"
>
<div class="flex items-center justify-between bg-gray-900/80 backdrop-blur-md rounded-2xl px-5 py-3 border border-gray-800/60 shadow-lg">
<!-- Logo -->
<a href="/" class="flex items-center gap-2 text-lg font-extrabold tracking-tight outline-none focus-visible:ring-2 focus-visible:ring-brand-500 rounded">
<span class="w-7 h-7 bg-gradient-to-br from-brand-500 to-purple-500 rounded-lg flex items-center justify-center text-white text-xs font-black" aria-hidden="true">F</span>
FlowStack
</a>
<!-- Desktop nav -->
<ul class="hidden md:flex items-center gap-1 list-none m-0 p-0" role="list">
<li><a href="#features" class="text-sm font-medium text-gray-400 hover:text-white px-3 py-2 rounded-lg hover:bg-white/5 transition-colors outline-none focus-visible:ring-2 focus-visible:ring-brand-500">Features</a></li>
<li><a href="#pricing" class="text-sm font-medium text-gray-400 hover:text-white px-3 py-2 rounded-lg hover:bg-white/5 transition-colors outline-none focus-visible:ring-2 focus-visible:ring-brand-500">Pricing</a></li>
<li><a href="#faq" class="text-sm font-medium text-gray-400 hover:text-white px-3 py-2 rounded-lg hover:bg-white/5 transition-colors outline-none focus-visible:ring-2 focus-visible:ring-brand-500">FAQ</a></li>
</ul>
<!-- CTA -->
<div class="flex items-center gap-2">
<a href="/login" class="hidden sm:inline-flex text-sm font-semibold text-gray-300 hover:text-white px-3 py-2 rounded-lg hover:bg-white/5 transition-colors outline-none focus-visible:ring-2 focus-visible:ring-brand-500">
Sign in
</a>
<a href="/signup" class="inline-flex items-center justify-center bg-brand-600 hover:bg-brand-500 text-white text-sm font-semibold px-4 py-2 rounded-xl transition-colors outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 focus-visible:ring-offset-gray-900">
Get started free
</a>
</div>
</div>
</nav>
</header>
<main id="main">
<!-- ============================================================
HERO SECTION
============================================================ -->
<section class="relative min-h-screen flex items-center justify-center overflow-hidden pt-24 pb-16 px-4 sm:px-6 lg:px-8">
<!-- Radial gradient background glow -->
<div class="absolute inset-0 pointer-events-none" aria-hidden="true">
<div class="absolute top-1/4 left-1/2 -translate-x-1/2 w-[600px] h-[600px] bg-brand-600/20 rounded-full blur-3xl"></div>
<div class="absolute top-1/3 left-1/4 w-[300px] h-[300px] bg-purple-600/15 rounded-full blur-2xl"></div>
</div>
<div class="relative max-w-4xl mx-auto text-center">
<!-- Eyebrow label -->
<div class="inline-flex items-center gap-2 bg-brand-500/10 border border-brand-500/30 rounded-full px-4 py-1.5 mb-8">
<span class="w-1.5 h-1.5 rounded-full bg-brand-400 animate-pulse" aria-hidden="true"></span>
<span class="text-sm font-medium text-brand-300">Tailwind CSS v4.2 — Oxide engine</span>
</div>
<!-- Headline -->
<h1 class="text-5xl sm:text-6xl lg:text-7xl font-black text-white leading-none tracking-tight text-balance">
Ship your product<br />
<span class="bg-gradient-to-r from-brand-400 to-purple-400 bg-clip-text text-transparent">10× faster</span>
</h1>
<!-- Subheadline -->
<p class="mt-6 text-xl text-gray-400 leading-relaxed max-w-2xl mx-auto text-pretty">
FlowStack is the all-in-one development platform built with Tailwind CSS v4.
Design systems, components, and integrations — ready in minutes.
</p>
<!-- CTA row -->
<div class="mt-10 flex flex-col sm:flex-row items-center justify-center gap-4">
<a
href="/signup"
class="w-full sm:w-auto inline-flex items-center justify-center gap-2 bg-brand-600 hover:bg-brand-500 text-white font-bold px-8 py-4 rounded-2xl transition-all duration-150 hover:-translate-y-0.5 hover:shadow-lg hover:shadow-brand-600/30 outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 focus-visible:ring-offset-gray-950"
>
Start for free
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</a>
<a
href="#demo"
class="w-full sm:w-auto inline-flex items-center justify-center gap-2 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 text-white font-semibold px-8 py-4 rounded-2xl transition-all duration-150 outline-none focus-visible:ring-2 focus-visible:ring-white/30"
>
Watch demo
</a>
</div>
<!-- Social proof -->
<p class="mt-8 text-sm text-gray-500">
Trusted by <strong class="text-gray-300 font-semibold">12,000+</strong> developers. No credit card required.
</p>
</div>
</section>
<!-- ============================================================
FEATURES SECTION
============================================================ -->
<section id="features" class="py-24 px-4 sm:px-6 lg:px-8">
<div class="max-w-7xl mx-auto">
<!-- Section header -->
<div class="text-center max-w-2xl mx-auto mb-16">
<p class="text-sm font-semibold uppercase tracking-widest text-brand-400 mb-3">Features</p>
<h2 class="text-3xl sm:text-4xl font-extrabold text-white text-balance">Everything you need to move fast</h2>
<p class="mt-4 text-lg text-gray-400">A complete toolkit for modern front-end development.</p>
</div>
<!-- Feature grid -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Feature card -->
<div class="group bg-gray-900 hover:bg-gray-800 border border-gray-800 hover:border-brand-500/50 rounded-2xl p-6 transition-all duration-200">
<div class="w-10 h-10 rounded-xl bg-brand-500/10 flex items-center justify-center mb-4">
<svg class="w-5 h-5 text-brand-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
<h3 class="text-base font-bold text-white mb-2">Oxide Engine</h3>
<p class="text-sm text-gray-400 leading-relaxed">Rust-powered build pipeline delivers sub-2ms incremental builds on large projects.</p>
</div>
<div class="group bg-gray-900 hover:bg-gray-800 border border-gray-800 hover:border-purple-500/50 rounded-2xl p-6 transition-all duration-200">
<div class="w-10 h-10 rounded-xl bg-purple-500/10 flex items-center justify-center mb-4">
<svg class="w-5 h-5 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" />
</svg>
</div>
<h3 class="text-base font-bold text-white mb-2">CSS-First Config</h3>
<p class="text-sm text-gray-400 leading-relaxed">All configuration in a single CSS file with @theme. No more tailwind.config.js.</p>
</div>
<div class="group bg-gray-900 hover:bg-gray-800 border border-gray-800 hover:border-emerald-500/50 rounded-2xl p-6 transition-all duration-200">
<div class="w-10 h-10 rounded-xl bg-emerald-500/10 flex items-center justify-center mb-4">
<svg class="w-5 h-5 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
</div>
<h3 class="text-base font-bold text-white mb-2">Container Queries</h3>
<p class="text-sm text-gray-400 leading-relaxed">Built-in container query support with @container and responsive @sm:, @lg: variants.</p>
</div>
</div>
</div>
</section>
<!-- ============================================================
PRICING SECTION
============================================================ -->
<section id="pricing" class="py-24 px-4 sm:px-6 lg:px-8 bg-gray-900/50">
<div class="max-w-5xl mx-auto">
<div class="text-center mb-16">
<p class="text-sm font-semibold uppercase tracking-widest text-brand-400 mb-3">Pricing</p>
<h2 class="text-3xl sm:text-4xl font-extrabold text-white">Simple, honest pricing</h2>
<p class="mt-4 text-lg text-gray-400">Start free. Upgrade when you need more.</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 items-start">
<!-- Free plan -->
<div class="bg-gray-900 border border-gray-800 rounded-2xl p-6">
<h3 class="text-base font-semibold text-gray-300">Starter</h3>
<p class="mt-3 flex items-end gap-1">
<span class="text-4xl font-extrabold text-white">$0</span>
<span class="text-sm text-gray-500 mb-1">/month</span>
</p>
<p class="mt-2 text-sm text-gray-500">Perfect for side projects.</p>
<ul class="mt-6 space-y-3 text-sm text-gray-400">
<li class="flex items-center gap-2"><svg class="w-4 h-4 text-emerald-400 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>3 projects</li>
<li class="flex items-center gap-2"><svg class="w-4 h-4 text-emerald-400 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>Community support</li>
<li class="flex items-center gap-2"><svg class="w-4 h-4 text-emerald-400 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>Tailwind v4 ready</li>
</ul>
<a href="/signup" class="mt-6 block w-full text-center border border-gray-700 hover:border-gray-600 text-gray-300 hover:text-white font-semibold py-2.5 rounded-xl transition-colors outline-none focus-visible:ring-2 focus-visible:ring-gray-500">
Get started
</a>
</div>
<!-- Pro plan (featured) -->
<div class="relative bg-brand-600 border border-brand-500 rounded-2xl p-6 shadow-2xl shadow-brand-600/20">
<span class="absolute -top-3 left-1/2 -translate-x-1/2 bg-gradient-to-r from-amber-400 to-orange-400 text-gray-900 text-xs font-bold px-3 py-1 rounded-full">Most popular</span>
<h3 class="text-base font-semibold text-brand-100">Pro</h3>
<p class="mt-3 flex items-end gap-1">
<span class="text-4xl font-extrabold text-white">$29</span>
<span class="text-sm text-brand-200 mb-1">/month</span>
</p>
<p class="mt-2 text-sm text-brand-200">For professional developers.</p>
<ul class="mt-6 space-y-3 text-sm text-brand-100">
<li class="flex items-center gap-2"><svg class="w-4 h-4 text-white shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>Unlimited projects</li>
<li class="flex items-center gap-2"><svg class="w-4 h-4 text-white shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>Priority support</li>
<li class="flex items-center gap-2"><svg class="w-4 h-4 text-white shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>Custom design tokens</li>
<li class="flex items-center gap-2"><svg class="w-4 h-4 text-white shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>Dark mode toolkit</li>
</ul>
<a href="/signup?plan=pro" class="mt-6 block w-full text-center bg-white hover:bg-gray-100 text-brand-700 font-bold py-2.5 rounded-xl transition-colors outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-brand-600">
Start 14-day trial
</a>
</div>
<!-- Enterprise plan -->
<div class="bg-gray-900 border border-gray-800 rounded-2xl p-6">
<h3 class="text-base font-semibold text-gray-300">Enterprise</h3>
<p class="mt-3 flex items-end gap-1">
<span class="text-4xl font-extrabold text-white">$99</span>
<span class="text-sm text-gray-500 mb-1">/month</span>
</p>
<p class="mt-2 text-sm text-gray-500">For teams and organizations.</p>
<ul class="mt-6 space-y-3 text-sm text-gray-400">
<li class="flex items-center gap-2"><svg class="w-4 h-4 text-emerald-400 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>Everything in Pro</li>
<li class="flex items-center gap-2"><svg class="w-4 h-4 text-emerald-400 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>Team design system</li>
<li class="flex items-center gap-2"><svg class="w-4 h-4 text-emerald-400 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>SLA + dedicated support</li>
</ul>
<a href="/contact" class="mt-6 block w-full text-center border border-gray-700 hover:border-gray-600 text-gray-300 hover:text-white font-semibold py-2.5 rounded-xl transition-colors outline-none focus-visible:ring-2 focus-visible:ring-gray-500">
Contact sales
</a>
</div>
</div>
</div>
</section>
<!-- ============================================================
CONTACT / CTA FORM
============================================================ -->
<section class="py-24 px-4 sm:px-6 lg:px-8">
<div class="max-w-lg mx-auto">
<div class="text-center mb-10">
<h2 class="text-3xl font-extrabold text-white">Stay in the loop</h2>
<p class="mt-3 text-gray-400">Get notified when we ship new features and Tailwind tips.</p>
</div>
<form class="space-y-4" novalidate>
<div>
<label for="hero-name" class="block text-sm font-medium text-gray-300 mb-1">
Your name
</label>
<input
id="hero-name"
type="text"
autocomplete="name"
class="w-full bg-gray-900 border border-gray-700 rounded-xl px-4 py-3 text-sm text-white placeholder-gray-500 outline-none transition-all focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20"
placeholder="Alice Johnson"
/>
</div>
<div>
<label for="hero-email" class="block text-sm font-medium text-gray-300 mb-1">
Email address <span class="text-brand-400" aria-hidden="true">*</span>
</label>
<input
id="hero-email"
type="email"
required
autocomplete="email"
aria-required="true"
class="w-full bg-gray-900 border border-gray-700 rounded-xl px-4 py-3 text-sm text-white placeholder-gray-500 outline-none transition-all focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20 invalid:border-red-500"
placeholder="[email protected]"
/>
</div>
<button
type="submit"
class="w-full bg-brand-600 hover:bg-brand-500 active:bg-brand-700 text-white font-bold py-3.5 rounded-xl transition-all duration-150 active:scale-[0.99] outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 focus-visible:ring-offset-gray-950"
>
Notify me
</button>
<p class="text-xs text-center text-gray-500">No spam. Unsubscribe anytime.</p>
</form>
</div>
</section>
</main>
<!-- ============================================================
FOOTER
============================================================ -->
<footer class="bg-gray-900 border-t border-gray-800 py-12 px-4 sm:px-6 lg:px-8">
<div class="max-w-7xl mx-auto">
<div class="flex flex-col sm:flex-row items-center justify-between gap-4">
<span class="text-lg font-extrabold text-white tracking-tight">FlowStack</span>
<nav aria-label="Footer navigation">
<ul class="flex items-center gap-6 list-none m-0 p-0" role="list">
<li><a href="/privacy" class="text-sm text-gray-500 hover:text-white transition-colors outline-none focus-visible:ring-2 focus-visible:ring-brand-500 rounded">Privacy</a></li>
<li><a href="/terms" class="text-sm text-gray-500 hover:text-white transition-colors outline-none focus-visible:ring-2 focus-visible:ring-brand-500 rounded">Terms</a></li>
<li><a href="/docs" class="text-sm text-gray-500 hover:text-white transition-colors outline-none focus-visible:ring-2 focus-visible:ring-brand-500 rounded">Docs</a></li>
</ul>
</nav>
<p class="text-xs text-gray-600">© 2026 FlowStack. All rights reserved.</p>
</div>
</div>
</footer>
<!-- Dark mode toggle script -->
<script>
const toggle = document.getElementById('theme-toggle');
const html = document.documentElement;
if (toggle) {
toggle.addEventListener('click', () => {
html.classList.toggle('dark');
localStorage.setItem('theme', html.classList.contains('dark') ? 'dark' : 'light');
});
}
const saved = localStorage.getItem('theme');
if (saved === 'dark' || (!saved && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
html.classList.add('dark');
}
</script>
</body>
</html>
Sign in to track your progress