In short
In this final project you will build a responsive landing page with modern CSS: variables, dark mode, clamp(), Flexbox, Grid, container queries and transitions. What you will build You will style a full landing page for "TaskFlow", an imaginary to-do app.
In this final project you will build a responsive landing page with modern CSS: variables, dark mode, clamp(), Flexbox, Grid, container queries and transitions.
What you will build
You will style a full landing page for "TaskFlow", an imaginary to-do app. It has six parts:
- A header with a logo, navigation links and a theme toggle.
- A hero section with a big heading and two buttons.
- A features grid that changes its number of columns by itself.
- Pricing cards that adapt to their own width with a container query.
- An FAQ built with
<details>and<summary>. - A footer.
It will support light and dark mode, use fluid text sizes, have smooth hover effects, and respect users who prefer less motion. Almost every topic from this advanced module comes together here.
Create a folder with two files: index.html and styles.css.
The HTML for the landing page
Here is the complete HTML. You will write it once and then only work on the CSS.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>TaskFlow - Plan your day in minutes</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header class="site-header">
<div class="container header__inner">
<a class="logo" href="#">TaskFlow</a>
<nav class="nav" aria-label="Main">
<ul class="nav__list">
<li><a class="nav__link" href="#features">Features</a></li>
<li><a class="nav__link" href="#pricing">Pricing</a></li>
<li><a class="nav__link" href="#faq">FAQ</a></li>
</ul>
<button class="theme-toggle" type="button" onclick="document.documentElement.dataset.theme = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark'">Toggle theme</button>
</nav>
</div>
</header>
<main>
<section class="hero">
<div class="container hero__inner">
<p class="hero__eyebrow">New: shared team lists</p>
<h1 class="hero__title">Plan your day in minutes, not hours</h1>
<p class="hero__text">TaskFlow keeps your tasks, notes and deadlines in one calm place, on every device.</p>
<div class="hero__actions">
<a class="btn btn--primary" href="#pricing">Start for free</a>
<a class="btn btn--ghost" href="#features">See features</a>
</div>
</div>
</section>
<section class="section" id="features">
<div class="container">
<h2 class="section__title">Everything you need to stay on track</h2>
<p class="section__intro">Simple tools that work together, so you can focus on the work itself.</p>
<div class="features">
<article class="feature">
<span class="feature__icon" aria-hidden="true">✓</span>
<h3 class="feature__title">Smart lists</h3>
<p>Group tasks by project, place or priority with one tap.</p>
</article>
<article class="feature">
<span class="feature__icon" aria-hidden="true">⏰</span>
<h3 class="feature__title">Gentle reminders</h3>
<p>Get a nudge at the right time, never a flood of alerts.</p>
</article>
<article class="feature">
<span class="feature__icon" aria-hidden="true">☁</span>
<h3 class="feature__title">Sync everywhere</h3>
<p>Your lists update instantly on phone, tablet and laptop.</p>
</article>
<article class="feature">
<span class="feature__icon" aria-hidden="true">★</span>
<h3 class="feature__title">Focus mode</h3>
<p>Hide everything except the one task you are doing now.</p>
</article>
</div>
</div>
</section>
<section class="section section--alt" id="pricing">
<div class="container">
<h2 class="section__title">Simple, honest pricing</h2>
<p class="section__intro">Start free. Upgrade only when you need more.</p>
<div class="pricing">
<div class="plan-wrap">
<article class="plan">
<div class="plan__header">
<h3 class="plan__name">Free</h3>
<p class="plan__price">$0<span>/month</span></p>
<a class="btn btn--ghost" href="#">Get started</a>
</div>
<ul class="plan__features">
<li>Up to 3 lists</li>
<li>Reminders</li>
<li>Sync on 2 devices</li>
</ul>
</article>
</div>
<div class="plan-wrap">
<article class="plan plan--featured">
<div class="plan__header">
<p class="plan__badge">Most popular</p>
<h3 class="plan__name">Pro</h3>
<p class="plan__price">$6<span>/month</span></p>
<a class="btn btn--primary" href="#">Try Pro free</a>
</div>
<ul class="plan__features">
<li>Unlimited lists</li>
<li>Focus mode</li>
<li>Sync on all devices</li>
<li>File attachments</li>
</ul>
</article>
</div>
<div class="plan-wrap">
<article class="plan">
<div class="plan__header">
<h3 class="plan__name">Team</h3>
<p class="plan__price">$12<span>/user</span></p>
<a class="btn btn--ghost" href="#">Contact us</a>
</div>
<ul class="plan__features">
<li>Everything in Pro</li>
<li>Shared team lists</li>
<li>Admin controls</li>
</ul>
</article>
</div>
</div>
</div>
</section>
<section class="section" id="faq">
<div class="container container--narrow">
<h2 class="section__title">Frequently asked questions</h2>
<details class="faq">
<summary class="faq__question">Is TaskFlow really free?</summary>
<p class="faq__answer">Yes. The Free plan has no time limit. You only pay if you choose Pro or Team.</p>
</details>
<details class="faq">
<summary class="faq__question">Can I cancel at any time?</summary>
<p class="faq__answer">Of course. Cancel in your settings with two clicks, and keep your data.</p>
</details>
<details class="faq">
<summary class="faq__question">Does it work offline?</summary>
<p class="faq__answer">Yes. Changes are saved on your device and sync when you are back online.</p>
</details>
</div>
</section>
</main>
<footer class="site-footer">
<div class="container footer__inner">
<p>© 2026 TaskFlow. Made for learning.</p>
<ul class="footer__links">
<li><a href="#">Privacy</a></li>
<li><a href="#">Terms</a></li>
<li><a href="#">Contact</a></li>
</ul>
</div>
</footer>
</body>
</html>
The HTML uses BEM class names, so each part of the page is easy to find in the CSS. Each pricing card has a wrapper, .plan-wrap, because a container query needs a parent to measure. The theme button uses the one-line toggle you learned in the dark mode lesson.
Step 1: Design tokens, dark mode and base styles
Start styles.css with the variables. Every color uses light-dark(), so dark mode works automatically. Spacing and text sizes use clamp().
/* ==========================================================
1. Tokens and dark mode
========================================================== */
:root {
color-scheme: light dark;
--bg: light-dark(#ffffff, #0f172a);
--surface: light-dark(#f1f5f9, #1e293b);
--card: light-dark(#ffffff, #1e293b);
--text: light-dark(#1e293b, #e2e8f0);
--muted: light-dark(#475569, #94a3b8);
--primary: light-dark(#4f46e5, #a5b4fc);
--primary-strong: light-dark(#4338ca, #c7d2fe);
--on-primary: light-dark(#ffffff, #0f172a);
--border: light-dark(#e2e8f0, #334155);
--font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
--radius: 14px;
--shadow: 0 10px 30px rgb(15 23 42 / 0.12);
--space-s: clamp(0.75rem, 0.6rem + 0.75vw, 1.25rem);
--space-m: clamp(1.25rem, 1rem + 1.5vw, 2.5rem);
--space-l: clamp(3rem, 2rem + 5vw, 6rem);
}
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }
/* ==========================================================
2. Reset and base
========================================================== */
*, *::before, *::after {
box-sizing: border-box;
}
body {
margin: 0;
font-family: var(--font);
font-size: 1rem;
line-height: 1.6;
background: var(--bg);
color: var(--text);
}
h1, h2, h3 {
line-height: 1.2;
margin: 0 0 0.5em;
}
p {
margin: 0 0 1em;
}
a {
color: var(--primary);
}
ul[class] {
list-style: none;
margin: 0;
padding: 0;
}
:focus-visible {
outline: 3px solid var(--primary);
outline-offset: 3px;
}
html {
scroll-behavior: smooth;
scroll-padding-top: 5rem;
}
.container {
width: min(100% - 2rem, 1100px);
margin-inline: auto;
}
.container--narrow {
width: min(100% - 2rem, 720px);
}
Open the page now. It has clean fonts and spacing, and it already switches to dark colors if your device uses dark mode. min(100% - 2rem, 1100px) keeps a 1rem gap on each side on phones and stops the content at 1100px on big screens. scroll-padding-top stops the sticky header from covering a section heading after a jump link.
Note: In dark mode, the primary color becomes a light purple, so the button text (
--on-primary) switches to dark. This keeps good contrast in both themes.
Step 2: Buttons, header and navigation
/* ==========================================================
3. Buttons
========================================================== */
.btn {
display: inline-block;
padding: 0.75rem 1.4rem;
border: 2px solid var(--primary);
border-radius: 999px;
font-weight: 600;
text-decoration: none;
transition: background-color 0.2s ease, color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: var(--shadow);
}
.btn--primary {
background: var(--primary);
color: var(--on-primary);
}
.btn--primary:hover {
background: var(--primary-strong);
border-color: var(--primary-strong);
}
.btn--ghost {
background: transparent;
color: var(--primary);
}
.btn--ghost:hover {
background: var(--primary);
color: var(--on-primary);
}
/* ==========================================================
4. Header and navigation
========================================================== */
.site-header {
position: sticky;
top: 0;
z-index: 10;
background: var(--bg);
border-bottom: 1px solid var(--border);
}
.header__inner {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: var(--space-s);
padding-block: 0.9rem;
}
.logo {
font-size: 1.35rem;
font-weight: 800;
color: var(--text);
text-decoration: none;
}
.nav {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--space-s);
}
.nav__list {
display: flex;
gap: 0.25rem;
}
.nav__link {
display: block;
padding: 0.4rem 0.75rem;
border-radius: 8px;
color: var(--text);
text-decoration: none;
transition: background-color 0.2s ease, color 0.2s ease;
}
.nav__link:hover {
background: var(--surface);
color: var(--primary);
}
.theme-toggle {
padding: 0.4rem 0.9rem;
border: 1px solid var(--border);
border-radius: 999px;
background: var(--surface);
color: var(--text);
font: inherit;
cursor: pointer;
}
The header sticks to the top while you scroll. The logo sits on the left and the navigation on the right thanks to justify-content: space-between. On a narrow phone, flex-wrap: wrap lets the navigation drop below the logo instead of squeezing. Buttons lift slightly on hover.
Step 3: The hero section with fluid typography
/* ==========================================================
5. Hero
========================================================== */
.hero {
padding-block: var(--space-l);
text-align: center;
background: radial-gradient(circle at top, var(--surface), var(--bg) 70%);
}
.hero__inner {
max-width: 800px;
animation: fade-up 0.8s ease-out both;
}
.hero__eyebrow {
display: inline-block;
padding: 0.25rem 0.8rem;
border-radius: 999px;
background: var(--surface);
color: var(--primary);
font-size: 0.875rem;
font-weight: 600;
}
.hero__title {
font-size: clamp(2.25rem, 1.2rem + 4.5vw, 4rem);
letter-spacing: -0.02em;
}
.hero__text {
max-width: 60ch;
margin-inline: auto;
color: var(--muted);
font-size: clamp(1.05rem, 1rem + 0.4vw, 1.3rem);
}
.hero__actions {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: var(--space-s);
margin-top: var(--space-m);
}
@keyframes fade-up {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
The hero heading grows smoothly from 2.25rem on phones to 4rem on large screens, with no media query. The content fades and slides up when the page loads. The two buttons sit side by side and wrap under each other on very small screens.
Step 4: A responsive features grid with auto-fit
/* ==========================================================
6. Sections and features
========================================================== */
.section {
padding-block: var(--space-l);
}
.section--alt {
background: var(--surface);
}
.section__title {
font-size: clamp(1.75rem, 1.2rem + 2vw, 2.5rem);
text-align: center;
}
.section__intro {
max-width: 60ch;
margin: 0 auto var(--space-m);
text-align: center;
color: var(--muted);
}
.features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr));
gap: var(--space-s);
}
.feature {
padding: var(--space-m);
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius);
transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.feature:hover {
transform: translateY(-4px);
box-shadow: var(--shadow);
}
.feature__icon {
display: grid;
place-items: center;
width: 3rem;
aspect-ratio: 1;
margin-bottom: 1rem;
border-radius: 12px;
background: var(--surface);
color: var(--primary);
font-size: 1.4rem;
}
.feature__title {
font-size: 1.2rem;
}
.feature p {
margin: 0;
color: var(--muted);
}
The repeat(auto-fit, minmax(min(100%, 230px), 1fr)) line does all the responsive work. On a wide screen you see four feature cards in a row, on a tablet two, and on a phone one. Each card lifts on hover. The icon box is a perfect square thanks to aspect-ratio: 1.
Step 5: Pricing cards with a container query
/* ==========================================================
7. Pricing (container query)
========================================================== */
.pricing {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
gap: var(--space-m);
align-items: start;
}
.plan-wrap {
container-type: inline-size;
}
.plan {
display: grid;
gap: 1.25rem;
padding: var(--space-m);
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius);
transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.plan:hover {
transform: translateY(-4px);
box-shadow: var(--shadow);
}
.plan--featured {
border: 2px solid var(--primary);
box-shadow: var(--shadow);
}
.plan__badge {
display: inline-block;
margin-bottom: 0.75rem;
padding: 0.2rem 0.7rem;
border-radius: 999px;
background: var(--primary);
color: var(--on-primary);
font-size: 0.8rem;
font-weight: 700;
}
.plan__price {
font-size: clamp(2rem, 12cqi, 3rem);
font-weight: 800;
line-height: 1;
}
.plan__price span {
font-size: 1rem;
font-weight: 400;
color: var(--muted);
}
.plan__features li {
padding-block: 0.4rem;
border-bottom: 1px solid var(--border);
}
.plan__features li::before {
content: "✓ ";
color: var(--primary);
font-weight: 700;
}
@container (min-width: 460px) {
.plan {
grid-template-columns: 1fr 1fr;
align-items: center;
}
}
On a big screen you see three pricing cards side by side, each with its details stacked. The .plan-wrap around each card is the container. When a card has room to be at least 460px wide, like on a small tablet where the cards stack in one wide column, the card switches to two columns: price and button on the left, feature list on the right. The price size also follows the card width with cqi.
Tip: Try it in DevTools. Turn on device mode and drag the width slowly. Watch each card change its layout based on its own width, not the screen breakpoint.
Step 6: FAQ, footer and reduced motion
/* ==========================================================
8. FAQ
========================================================== */
.faq {
margin-bottom: 0.75rem;
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius);
transition: border-color 0.2s ease;
}
.faq[open] {
border-color: var(--primary);
}
.faq__question {
padding: 1rem 1.25rem;
font-weight: 600;
cursor: pointer;
transition: color 0.2s ease;
}
.faq__question:hover {
color: var(--primary);
}
.faq__answer {
margin: 0;
padding: 0 1.25rem 1rem;
color: var(--muted);
}
/* ==========================================================
9. Footer
========================================================== */
.site-footer {
padding-block: var(--space-m);
border-top: 1px solid var(--border);
color: var(--muted);
font-size: 0.9rem;
}
.footer__inner {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: var(--space-s);
}
.footer__inner p {
margin: 0;
}
.footer__links {
display: flex;
gap: var(--space-s);
}
.footer__links a {
color: var(--muted);
}
.footer__links a:hover {
color: var(--primary);
}
/* ==========================================================
10. Reduced motion
========================================================== */
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
The FAQ items open and close when you click them, and an open item gets a purple border. The footer puts the copyright on one side and the links on the other, and stacks them on phones. Finally, users with "reduce motion" turned on get no sliding, lifting or smooth scrolling.
Common mistake: Forgetting to test both themes. Click the toggle button and check every section in dark mode. Look for text that is hard to read or a hard-coded color like
whitethat you missed.
The complete final CSS
Here is the full styles.css in one piece, so you can compare it with yours or copy it in one go.
/* ==========================================================
1. Tokens and dark mode
========================================================== */
:root {
color-scheme: light dark;
--bg: light-dark(#ffffff, #0f172a);
--surface: light-dark(#f1f5f9, #1e293b);
--card: light-dark(#ffffff, #1e293b);
--text: light-dark(#1e293b, #e2e8f0);
--muted: light-dark(#475569, #94a3b8);
--primary: light-dark(#4f46e5, #a5b4fc);
--primary-strong: light-dark(#4338ca, #c7d2fe);
--on-primary: light-dark(#ffffff, #0f172a);
--border: light-dark(#e2e8f0, #334155);
--font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
--radius: 14px;
--shadow: 0 10px 30px rgb(15 23 42 / 0.12);
--space-s: clamp(0.75rem, 0.6rem + 0.75vw, 1.25rem);
--space-m: clamp(1.25rem, 1rem + 1.5vw, 2.5rem);
--space-l: clamp(3rem, 2rem + 5vw, 6rem);
}
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }
/* ==========================================================
2. Reset and base
========================================================== */
*, *::before, *::after {
box-sizing: border-box;
}
body {
margin: 0;
font-family: var(--font);
font-size: 1rem;
line-height: 1.6;
background: var(--bg);
color: var(--text);
}
h1, h2, h3 {
line-height: 1.2;
margin: 0 0 0.5em;
}
p {
margin: 0 0 1em;
}
a {
color: var(--primary);
}
ul[class] {
list-style: none;
margin: 0;
padding: 0;
}
:focus-visible {
outline: 3px solid var(--primary);
outline-offset: 3px;
}
html {
scroll-behavior: smooth;
scroll-padding-top: 5rem;
}
.container {
width: min(100% - 2rem, 1100px);
margin-inline: auto;
}
.container--narrow {
width: min(100% - 2rem, 720px);
}
/* ==========================================================
3. Buttons
========================================================== */
.btn {
display: inline-block;
padding: 0.75rem 1.4rem;
border: 2px solid var(--primary);
border-radius: 999px;
font-weight: 600;
text-decoration: none;
transition: background-color 0.2s ease, color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: var(--shadow);
}
.btn--primary {
background: var(--primary);
color: var(--on-primary);
}
.btn--primary:hover {
background: var(--primary-strong);
border-color: var(--primary-strong);
}
.btn--ghost {
background: transparent;
color: var(--primary);
}
.btn--ghost:hover {
background: var(--primary);
color: var(--on-primary);
}
/* ==========================================================
4. Header and navigation
========================================================== */
.site-header {
position: sticky;
top: 0;
z-index: 10;
background: var(--bg);
border-bottom: 1px solid var(--border);
}
.header__inner {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: var(--space-s);
padding-block: 0.9rem;
}
.logo {
font-size: 1.35rem;
font-weight: 800;
color: var(--text);
text-decoration: none;
}
.nav {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--space-s);
}
.nav__list {
display: flex;
gap: 0.25rem;
}
.nav__link {
display: block;
padding: 0.4rem 0.75rem;
border-radius: 8px;
color: var(--text);
text-decoration: none;
transition: background-color 0.2s ease, color 0.2s ease;
}
.nav__link:hover {
background: var(--surface);
color: var(--primary);
}
.theme-toggle {
padding: 0.4rem 0.9rem;
border: 1px solid var(--border);
border-radius: 999px;
background: var(--surface);
color: var(--text);
font: inherit;
cursor: pointer;
}
/* ==========================================================
5. Hero
========================================================== */
.hero {
padding-block: var(--space-l);
text-align: center;
background: radial-gradient(circle at top, var(--surface), var(--bg) 70%);
}
.hero__inner {
max-width: 800px;
animation: fade-up 0.8s ease-out both;
}
.hero__eyebrow {
display: inline-block;
padding: 0.25rem 0.8rem;
border-radius: 999px;
background: var(--surface);
color: var(--primary);
font-size: 0.875rem;
font-weight: 600;
}
.hero__title {
font-size: clamp(2.25rem, 1.2rem + 4.5vw, 4rem);
letter-spacing: -0.02em;
}
.hero__text {
max-width: 60ch;
margin-inline: auto;
color: var(--muted);
font-size: clamp(1.05rem, 1rem + 0.4vw, 1.3rem);
}
.hero__actions {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: var(--space-s);
margin-top: var(--space-m);
}
@keyframes fade-up {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ==========================================================
6. Sections and features
========================================================== */
.section {
padding-block: var(--space-l);
}
.section--alt {
background: var(--surface);
}
.section__title {
font-size: clamp(1.75rem, 1.2rem + 2vw, 2.5rem);
text-align: center;
}
.section__intro {
max-width: 60ch;
margin: 0 auto var(--space-m);
text-align: center;
color: var(--muted);
}
.features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr));
gap: var(--space-s);
}
.feature {
padding: var(--space-m);
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius);
transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.feature:hover {
transform: translateY(-4px);
box-shadow: var(--shadow);
}
.feature__icon {
display: grid;
place-items: center;
width: 3rem;
aspect-ratio: 1;
margin-bottom: 1rem;
border-radius: 12px;
background: var(--surface);
color: var(--primary);
font-size: 1.4rem;
}
.feature__title {
font-size: 1.2rem;
}
.feature p {
margin: 0;
color: var(--muted);
}
/* ==========================================================
7. Pricing (container query)
========================================================== */
.pricing {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
gap: var(--space-m);
align-items: start;
}
.plan-wrap {
container-type: inline-size;
}
.plan {
display: grid;
gap: 1.25rem;
padding: var(--space-m);
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius);
transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.plan:hover {
transform: translateY(-4px);
box-shadow: var(--shadow);
}
.plan--featured {
border: 2px solid var(--primary);
box-shadow: var(--shadow);
}
.plan__badge {
display: inline-block;
margin-bottom: 0.75rem;
padding: 0.2rem 0.7rem;
border-radius: 999px;
background: var(--primary);
color: var(--on-primary);
font-size: 0.8rem;
font-weight: 700;
}
.plan__price {
font-size: clamp(2rem, 12cqi, 3rem);
font-weight: 800;
line-height: 1;
}
.plan__price span {
font-size: 1rem;
font-weight: 400;
color: var(--muted);
}
.plan__features li {
padding-block: 0.4rem;
border-bottom: 1px solid var(--border);
}
.plan__features li::before {
content: "✓ ";
color: var(--primary);
font-weight: 700;
}
@container (min-width: 460px) {
.plan {
grid-template-columns: 1fr 1fr;
align-items: center;
}
}
/* ==========================================================
8. FAQ
========================================================== */
.faq {
margin-bottom: 0.75rem;
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius);
transition: border-color 0.2s ease;
}
.faq[open] {
border-color: var(--primary);
}
.faq__question {
padding: 1rem 1.25rem;
font-weight: 600;
cursor: pointer;
transition: color 0.2s ease;
}
.faq__question:hover {
color: var(--primary);
}
.faq__answer {
margin: 0;
padding: 0 1.25rem 1rem;
color: var(--muted);
}
/* ==========================================================
9. Footer
========================================================== */
.site-footer {
padding-block: var(--space-m);
border-top: 1px solid var(--border);
color: var(--muted);
font-size: 0.9rem;
}
.footer__inner {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: var(--space-s);
}
.footer__inner p {
margin: 0;
}
.footer__links {
display: flex;
gap: var(--space-s);
}
.footer__links a {
color: var(--muted);
}
.footer__links a:hover {
color: var(--primary);
}
/* ==========================================================
10. Reduced motion
========================================================== */
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Open index.html in your browser and try everything: resize the window, switch your device theme, click the toggle, hover the cards and buttons, open the FAQ items and press Tab to move through the links. You have built a modern, responsive, accessible landing page with no CSS framework at all.
Practice exercise
Make the project your own. Add a "Testimonials" section between pricing and FAQ with three quote cards. Use aspect-ratio and object-fit for round profile photos, align the cards with subgrid, and make sure it looks good in both themes.
Hint: Reuse the .section and .section__title classes, and copy the .feature card styles as a starting point for a new .quote block.
What's next: learning JavaScript
Congratulations, you have finished the CSS course! You can now style anything from a simple card to a full responsive website with modern techniques.
You may have noticed a few places where CSS reached its limit. The theme toggle forgets your choice after a reload. The FAQ could close other items when one opens. A pricing switch between monthly and yearly prices would need the page to change. All of this is the job of JavaScript, the language that makes web pages interactive.
In the JavaScript course you will learn to react to clicks, change content and styles on the fly, save settings in the browser and load data from the internet. With HTML for structure, CSS for style and JavaScript for behavior, you will have the three core skills of every web developer.
Key takeaways
- Design tokens with
light-dark()give you dark mode with almost no extra code. clamp()andmin()handle fluid text, spacing and widths without many media queries.- Flexbox handles rows like the header and footer; Grid with
auto-fithandles card layouts. - A container query lets pricing cards adapt to their own width.
- Smooth transitions, visible focus styles and
prefers-reduced-motionmake the page feel polished and accessible.