HTML and CSS course Β· Module 9: Modern CSS
Final Project: Responsive Landing Page "Egyptian Adventure"
In this lesson5
Welcome to the final chamber of our Egyptian journey! I am Mohamed and today we face the ultimate challenge: creating a complete, professional landing page that will combine ALL the skills you've gained in this module! You already know dozens of tools, but you have practiced each of them separately. The real test begins when the grid, containers, the form and accessibility have to work together on a single page.
Just as building the Great Pyramid of Giza required combining all the skills of ancient builders, from basics to the most advanced techniques, your final project will be a synthesis of all your HTML and CSS knowledge!
Project Goal
You'll create a responsive landing page for a fictional travel agency "Egyptian Adventure" that organizes trips to ancient Egypt. The page will utilize:
- Semantic HTML - accessible structure
- CSS Grid and Flexbox - advanced layouts
- Container Queries - responsive components
- Modern CSS - latest CSS features
- Animations - smooth transitions and effects
- Performance - optimization and best practices
Project Structure
Page Sections:
- Hero Section - main banner with CTA (Call To Action)
- Features - feature cards using container queries
- Tours - tour gallery in CSS Grid
- Testimonials - customer reviews with carousel
- Pricing - pricing section
- Contact Form - contact form
- Footer - footer with social links
Technical Requirements
1. Semantic HTML
We start with the skeleton: a header with navigation, sections with identifiers, cards in article elements, reviews in blockquote and a form with labels. The code is long, so read it section by section:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Discover the secrets of ancient Egypt with Egyptian Adventure - the best travel agency">
<title>Egyptian Adventure - Trips to Ancient Egypt</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<!-- Navigation -->
<header class="header" role="banner">
<nav class="nav" role="navigation" aria-label="Main navigation">
<div class="container">
<a href="#" class="logo">
<span class="logo-icon">β</span>
<span class="logo-text">Egyptian Adventure</span>
</a>
<ul class="nav-list">
<li><a href="#features">Why Us</a></li>
<li><a href="#tours">Tours</a></li>
<li><a href="#testimonials">Reviews</a></li>
<li><a href="#pricing">Pricing</a></li>
<li><a href="#contact" class="btn-primary">Contact</a></li>
</ul>
</div>
</nav>
</header>
<!-- Hero Section -->
<section class="hero" id="hero">
<div class="hero-content">
<h1 class="hero-title">
Discover the Secrets of<br>
<span class="gradient-text">Ancient Egypt</span>
</h1>
<p class="hero-subtitle">
Travel 5,000 years back in time and experience the wonders of pharaohs,
pyramids, and hieroglyphs on the unforgettable journey of a lifetime!
</p>
<div class="hero-cta">
<a href="#tours" class="btn btn-large btn-primary">
Browse Tours
</a>
<a href="#contact" class="btn btn-large btn-secondary">
Contact Us
</a>
</div>
</div>
<div class="hero-image">
<div class="pyramid-illustration"></div>
</div>
</section>
<!-- Features -->
<section class="features" id="features">
<div class="container">
<h2 class="section-title">Why Egyptian Adventure?</h2>
<p class="section-subtitle">
We have been leaders in organizing trips to Egypt for 20 years
</p>
<div class="features-grid">
<article class="feature-card">
<div class="feature-icon">β</div>
<h3>Exclusive Access</h3>
<p>Access to places unavailable to regular tourists,
including private visits to pyramid chambers</p>
</article>
<article class="feature-card">
<div class="feature-icon">β</div>
<h3>Egyptology Experts</h3>
<p>Guides with academic degrees, passionate about the history
of ancient Egypt</p>
</article>
<article class="feature-card">
<div class="feature-icon">β</div>
<h3>Luxury Hotels</h3>
<p>Accommodation in the best 5* hotels, with views of the Nile
and pyramids</p>
</article>
<article class="feature-card">
<div class="feature-icon">β</div>
<h3>Authentic Cuisine</h3>
<p>Tasting traditional Egyptian dishes prepared
by local chefs</p>
</article>
</div>
</div>
</section>
<!-- Tours Grid -->
<section class="tours" id="tours">
<div class="container">
<h2 class="section-title">Our Tours</h2>
<p class="section-subtitle">
Choose a tour tailored to your dreams
</p>
<div class="tours-grid">
<article class="tour-card tour-card--featured">
<div class="tour-image">
<img src="/api/placeholder/600/400" alt="Pyramids of Giza">
<span class="tour-badge">Bestseller</span>
</div>
<div class="tour-content">
<h3>Classic Egypt</h3>
<p class="tour-duration">β±7 days / 6 nights</p>
<p class="tour-description">
Cairo, Giza, Luxor, Valley of the Kings, Nile cruise
</p>
<ul class="tour-highlights">
<li>Great Pyramid of Giza</li>
<li>Egyptian Museum in Cairo</li>
<li>Karnak Temple</li>
<li>Nile River Cruise</li>
</ul>
<div class="tour-footer">
<div class="tour-price">
<span class="price-from">from</span>
<span class="price-amount">4,999</span>
<span class="price-currency">PLN</span>
</div>
<a href="#contact" class="btn btn-primary">Book Now</a>
</div>
</div>
</article>
<article class="tour-card">
<div class="tour-image">
<img src="/api/placeholder/600/400" alt="Abu Simbel">
</div>
<div class="tour-content">
<h3>Egyptian Wonders</h3>
<p class="tour-duration">β±10 days / 9 nights</p>
<p class="tour-description">
All major attractions + Abu Simbel and Alexandria
</p>
<ul class="tour-highlights">
<li>Everything from Classic Egypt</li>
<li>Abu Simbel Temples</li>
<li>Library of Alexandria</li>
<li>Siwa Oasis</li>
</ul>
<div class="tour-footer">
<div class="tour-price">
<span class="price-from">from</span>
<span class="price-amount">7,499</span>
<span class="price-currency">PLN</span>
</div>
<a href="#contact" class="btn btn-primary">Book Now</a>
</div>
</div>
</article>
<article class="tour-card">
<div class="tour-image">
<img src="/api/placeholder/600/400" alt="Diving in the Red Sea">
</div>
<div class="tour-content">
<h3>Egypt + Red Sea</h3>
<p class="tour-duration">β±12 days / 11 nights</p>
<p class="tour-description">
History + relaxation and diving in Hurghada
</p>
<ul class="tour-highlights">
<li>Monument sightseeing</li>
<li>5 days all inclusive in Hurghada</li>
<li>Diving with coral reef</li>
<li>Desert safari</li>
</ul>
<div class="tour-footer">
<div class="tour-price">
<span class="price-from">from</span>
<span class="price-amount">6,299</span>
<span class="price-currency">PLN</span>
</div>
<a href="#contact" class="btn btn-primary">Book Now</a>
</div>
</div>
</article>
</div>
</div>
</section>
<!-- Testimonials -->
<section class="testimonials" id="testimonials">
<div class="container">
<h2 class="section-title">What Our Clients Say</h2>
<div class="testimonials-grid">
<blockquote class="testimonial-card">
<div class="testimonial-rating">5/5</div>
<p class="testimonial-text">
"An incredible experience! The guides were extremely knowledgeable,
and the tour organization was top-notch. The pyramids in person
are a thousand times more impressive than in photos!"
</p>
<cite class="testimonial-author">
<strong>Anna Kowalska</strong>
<span>Warsaw</span>
</cite>
</blockquote>
<blockquote class="testimonial-card">
<div class="testimonial-rating">5/5</div>
<p class="testimonial-text">
"As an Egyptologist I was skeptical, but this tour exceeded
my expectations. Access to places not shown on
standard tours was priceless!"
</p>
<cite class="testimonial-author">
<strong>Dr. Jan Nowak</strong>
<span>Krakow</span>
</cite>
</blockquote>
<blockquote class="testimonial-card">
<div class="testimonial-rating">5/5</div>
<p class="testimonial-text">
"The trip of a lifetime! I was especially impressed by the Nile cruise and
visiting the Valley of the Kings. Everything was perfectly organized."
</p>
<cite class="testimonial-author">
<strong>Maria Wisniewska</strong>
<span>Gdansk</span>
</cite>
</blockquote>
</div>
</div>
</section>
<!-- Contact Form -->
<section class="contact" id="contact">
<div class="container">
<div class="contact-grid">
<div class="contact-info">
<h2>Book Your Adventure</h2>
<p>Fill out the form and our consultant will contact you
within 24 hours!</p>
<div class="contact-details">
<div class="contact-item">
<span class="contact-icon">β</span>
<div>
<strong>Phone</strong>
<p>+48 123 456 789</p>
</div>
</div>
<div class="contact-item">
<span class="contact-icon">β</span>
<div>
<strong>Email</strong>
<p>contact@egyptianadventure.com</p>
</div>
</div>
<div class="contact-item">
<span class="contact-icon">β</span>
<div>
<strong>Address</strong>
<p>123 Pyramid Street, 00-001 Warsaw</p>
</div>
</div>
</div>
</div>
<form class="contact-form" action="#" method="POST">
<div class="form-group">
<label for="name">Full Name</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="phone">Phone</label>
<input type="tel" id="phone" name="phone" required>
</div>
<div class="form-group">
<label for="tour">Choose a Tour</label>
<select id="tour" name="tour" required>
<option value="">Select...</option>
<option value="classic">Classic Egypt - 7 days</option>
<option value="wonders">Egyptian Wonders - 10 days</option>
<option value="red-sea">Egypt + Red Sea - 12 days</option>
</select>
</div>
<div class="form-group">
<label for="message">Message (optional)</label>
<textarea id="message" name="message" rows="4"></textarea>
</div>
<button type="submit" class="btn btn-primary btn-large">
Send Inquiry
</button>
</form>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-section">
<h3>Egyptian Adventure</h3>
<p>Discovering the secrets of ancient Egypt since 2004</p>
<div class="social-links">
<a href="#" aria-label="Facebook">Facebook</a>
<a href="#" aria-label="Instagram">Instagram</a>
<a href="#" aria-label="Twitter">Twitter</a>
<a href="#" aria-label="YouTube">YouTube</a>
</div>
</div>
<div class="footer-section">
<h4>Tours</h4>
<ul>
<li><a href="#">Classic Egypt</a></li>
<li><a href="#">Egyptian Wonders</a></li>
<li><a href="#">Egypt + Red Sea</a></li>
<li><a href="#">Group Tours</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Information</h4>
<ul>
<li><a href="#">About Us</a></li>
<li><a href="#">Terms & Conditions</a></li>
<li><a href="#">Privacy Policy</a></li>
<li><a href="#">FAQ</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Contact</h4>
<ul>
<li>+48 123 456 789</li>
<li>contact@egyptianadventure.com</li>
<li>123 Pyramid Street, Warsaw</li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>© 2024 Egyptian Adventure. All rights reserved.</p>
</div>
</div>
</footer>
</body>
</html>Every section has its own id, so links in the navigation scroll the page to the right place. Form fields are tied to their labels through for and id, and the required attribute blocks sending an empty field. lang="en" tells screen readers which language to read the content in.
2. CSS - Complete Styles
Comments divide the stylesheet into sections: reset and variables, header, hero, features, tours and the other parts of the page. The palette, spacing and shadows live in CSS variables on :root:
/* ==========================================
CSS RESET & BASE STYLES
========================================== */
*, *::before, *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
/* Colors - Egyptian theme */
--color-primary: #D4AF37; /* Gold */
--color-primary-dark: #B8941F;
--color-secondary: #2C3E50; /* Dark blue */
--color-accent: #E67E22; /* Orange */
--color-bg: #FFFFFF;
--color-bg-alt: #F8F9FA;
--color-text: #2C3E50;
--color-text-light: #6C757D;
/* Spacing */
--spacing-xs: 0.5rem;
--spacing-sm: 1rem;
--spacing-md: 2rem;
--spacing-lg: 4rem;
--spacing-xl: 6rem;
/* Typography */
--font-primary: 'Segoe UI', system-ui, sans-serif;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--font-size-xl: 1.25rem;
--font-size-2xl: 1.5rem;
--font-size-3xl: 2rem;
--font-size-4xl: 3rem;
/* Layout */
--container-max-width: 1200px;
--border-radius: 12px;
/* Shadows */
--shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
/* Transitions */
--transition: all 0.3s ease;
}
html {
scroll-behavior: smooth;
}
body {
font-family: var(--font-primary);
font-size: var(--font-size-base);
line-height: 1.6;
color: var(--color-text);
background: var(--color-bg);
}
img {
max-width: 100%;
height: auto;
display: block;
}
a {
color: inherit;
text-decoration: none;
}
ul {
list-style: none;
}
/* ==========================================
UTILITIES
========================================== */
.container {
max-width: var(--container-max-width);
margin: 0 auto;
padding: 0 var(--spacing-md);
}
.section-title {
font-size: var(--font-size-3xl);
font-weight: 700;
text-align: center;
margin-bottom: var(--spacing-sm);
color: var(--color-secondary);
}
.section-subtitle {
font-size: var(--font-size-lg);
text-align: center;
color: var(--color-text-light);
margin-bottom: var(--spacing-lg);
}
.gradient-text {
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Buttons */
.btn {
display: inline-block;
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--border-radius);
font-weight: 600;
text-align: center;
cursor: pointer;
transition: var(--transition);
border: 2px solid transparent;
}
.btn-primary {
background: var(--color-primary);
color: var(--color-secondary);
}
.btn-primary:hover {
background: var(--color-primary-dark);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.btn-secondary {
background: transparent;
border-color: var(--color-primary);
color: var(--color-primary);
}
.btn-secondary:hover {
background: var(--color-primary);
color: var(--color-secondary);
}
.btn-large {
padding: var(--spacing-md) var(--spacing-lg);
font-size: var(--font-size-lg);
}
/* ==========================================
HEADER & NAVIGATION
========================================== */
.header {
position: sticky;
top: 0;
background: rgba(255, 255, 255, 0.98);
backdrop-filter: blur(10px);
box-shadow: var(--shadow-sm);
z-index: 1000;
}
.nav .container {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: var(--spacing-sm);
padding-bottom: var(--spacing-sm);
}
.logo {
display: flex;
align-items: center;
gap: var(--spacing-sm);
font-size: var(--font-size-xl);
font-weight: 700;
color: var(--color-secondary);
}
.logo-icon {
font-size: var(--font-size-2xl);
}
.nav-list {
display: flex;
gap: var(--spacing-md);
align-items: center;
}
.nav-list a {
transition: var(--transition);
}
.nav-list a:not(.btn-primary):hover {
color: var(--color-primary);
}
/* ==========================================
HERO SECTION
========================================== */
.hero {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-lg);
align-items: center;
min-height: calc(100vh - 80px);
padding: var(--spacing-xl) var(--spacing-md);
background: linear-gradient(135deg, #F8F9FA 0%, #E9ECEF 100%);
}
.hero-content {
max-width: 600px;
}
.hero-title {
font-size: var(--font-size-4xl);
font-weight: 800;
line-height: 1.2;
margin-bottom: var(--spacing-md);
}
.hero-subtitle {
font-size: var(--font-size-xl);
color: var(--color-text-light);
margin-bottom: var(--spacing-lg);
}
.hero-cta {
display: flex;
gap: var(--spacing-md);
flex-wrap: wrap;
}
.hero-image {
display: flex;
justify-content: center;
align-items: center;
}
.pyramid-illustration {
width: 400px;
height: 400px;
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%);
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
animation: float 6s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
/* ==========================================
FEATURES SECTION
========================================== */
.features {
padding: var(--spacing-xl) 0;
background: var(--color-bg);
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--spacing-lg);
container-type: inline-size;
}
.feature-card {
background: white;
padding: var(--spacing-lg);
border-radius: var(--border-radius);
box-shadow: var(--shadow-md);
text-align: center;
transition: var(--transition);
}
.feature-card:hover {
transform: translateY(-8px);
box-shadow: var(--shadow-lg);
}
.feature-icon {
font-size: 4rem;
margin-bottom: var(--spacing-md);
}
.feature-card h3 {
font-size: var(--font-size-xl);
margin-bottom: var(--spacing-sm);
color: var(--color-secondary);
}
/* Container query for feature cards */
@container (max-width: 300px) {
.feature-card {
padding: var(--spacing-md);
}
.feature-icon {
font-size: 3rem;
}
}
/* ==========================================
TOURS SECTION
========================================== */
.tours {
padding: var(--spacing-xl) 0;
background: var(--color-bg-alt);
}
.tours-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: var(--spacing-lg);
}
.tour-card {
background: white;
border-radius: var(--border-radius);
overflow: hidden;
box-shadow: var(--shadow-md);
transition: var(--transition);
container-type: inline-size;
}
.tour-card:hover {
transform: translateY(-8px);
box-shadow: var(--shadow-lg);
}
.tour-card--featured {
grid-column: span 2;
}
@media (max-width: 768px) {
.tour-card--featured {
grid-column: span 1;
}
}
.tour-image {
position: relative;
aspect-ratio: 16 / 9;
overflow: hidden;
}
.tour-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: var(--transition);
}
.tour-card:hover .tour-image img {
transform: scale(1.1);
}
.tour-badge {
position: absolute;
top: var(--spacing-md);
right: var(--spacing-md);
background: var(--color-accent);
color: white;
padding: var(--spacing-xs) var(--spacing-md);
border-radius: 50px;
font-weight: 600;
font-size: 0.875rem;
}
.tour-content {
padding: var(--spacing-lg);
}
.tour-content h3 {
font-size: var(--font-size-2xl);
margin-bottom: var(--spacing-sm);
color: var(--color-secondary);
}
.tour-duration {
color: var(--color-text-light);
margin-bottom: var(--spacing-sm);
}
.tour-highlights {
margin: var(--spacing-md) 0;
}
.tour-highlights li {
padding: var(--spacing-xs) 0;
color: var(--color-text);
}
.tour-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: var(--spacing-md);
padding-top: var(--spacing-md);
border-top: 1px solid var(--color-bg-alt);
}
.tour-price {
display: flex;
align-items: baseline;
gap: var(--spacing-xs);
}
.price-amount {
font-size: var(--font-size-3xl);
font-weight: 700;
color: var(--color-primary);
}
.price-from,
.price-currency {
font-size: var(--font-size-base);
color: var(--color-text-light);
}
/* ==========================================
TESTIMONIALS SECTION
========================================== */
.testimonials {
padding: var(--spacing-xl) 0;
background: var(--color-bg);
}
.testimonials-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: var(--spacing-lg);
}
.testimonial-card {
background: white;
padding: var(--spacing-lg);
border-radius: var(--border-radius);
box-shadow: var(--shadow-md);
border-left: 4px solid var(--color-primary);
}
.testimonial-rating {
font-size: var(--font-size-lg);
margin-bottom: var(--spacing-md);
}
.testimonial-text {
font-size: var(--font-size-lg);
line-height: 1.8;
margin-bottom: var(--spacing-md);
font-style: italic;
color: var(--color-text);
}
.testimonial-author {
display: flex;
flex-direction: column;
gap: var(--spacing-xs);
font-style: normal;
}
.testimonial-author strong {
color: var(--color-secondary);
font-size: var(--font-size-lg);
}
.testimonial-author span {
color: var(--color-text-light);
}
/* ==========================================
CONTACT SECTION
========================================== */
.contact {
padding: var(--spacing-xl) 0;
background: var(--color-bg-alt);
}
.contact-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-xl);
}
@media (max-width: 768px) {
.contact-grid {
grid-template-columns: 1fr;
}
}
.contact-info h2 {
font-size: var(--font-size-3xl);
margin-bottom: var(--spacing-md);
color: var(--color-secondary);
}
.contact-details {
margin-top: var(--spacing-lg);
}
.contact-item {
display: flex;
gap: var(--spacing-md);
margin-bottom: var(--spacing-md);
}
.contact-icon {
font-size: var(--font-size-2xl);
}
.contact-form {
background: white;
padding: var(--spacing-lg);
border-radius: var(--border-radius);
box-shadow: var(--shadow-md);
}
.form-group {
margin-bottom: var(--spacing-md);
}
.form-group label {
display: block;
margin-bottom: var(--spacing-xs);
font-weight: 600;
color: var(--color-secondary);
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: var(--spacing-sm);
border: 2px solid var(--color-bg-alt);
border-radius: var(--border-radius);
font-family: inherit;
font-size: var(--font-size-base);
transition: var(--transition);
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--color-primary);
}
/* ==========================================
FOOTER
========================================== */
.footer {
background: var(--color-secondary);
color: white;
padding: var(--spacing-xl) 0 var(--spacing-md);
}
.footer-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: var(--spacing-lg);
margin-bottom: var(--spacing-lg);
}
.footer-section h3,
.footer-section h4 {
margin-bottom: var(--spacing-md);
color: var(--color-primary);
}
.footer-section ul li {
margin-bottom: var(--spacing-xs);
}
.footer-section a:hover {
color: var(--color-primary);
}
.social-links {
display: flex;
gap: var(--spacing-md);
margin-top: var(--spacing-md);
font-size: var(--font-size-2xl);
}
.footer-bottom {
text-align: center;
padding-top: var(--spacing-md);
border-top: 1px solid rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.7);
}
/* ==========================================
RESPONSIVE DESIGN
========================================== */
@media (max-width: 768px) {
.hero {
grid-template-columns: 1fr;
text-align: center;
}
.hero-title {
font-size: var(--font-size-3xl);
}
.pyramid-illustration {
width: 250px;
height: 250px;
}
.hero-cta {
justify-content: center;
}
.nav-list {
flex-wrap: wrap;
gap: var(--spacing-sm);
}
}Notice three places. The features grid is a container, so the cards change their spacing through @container. The featured tour takes two columns and goes back to one below 768px. The tour images keep their proportions thanks to aspect-ratio: 16 / 9.
3. The Eye of Horus: the :has() Selector
One more tool is missing. The :has() pseudo-class selects an element if it contains a specific child, so a parent can react to what it holds inside, like the Eye of Horus looking into a chamber:
/* A card with a badge (e.g. Bestseller) gets a golden outline */
.tour-card:has(.tour-badge) {
outline: 3px solid var(--color-primary);
}
/* The label of a field the user filled in incorrectly */
.form-group:has(input:user-invalid) label {
color: #c0392b;
}
/* An article whose direct child is an image */
article:has(> img) {
border: 2px solid gold;
}Only the card with the Bestseller badge gets the outline, because the others do not have one. :user-invalid kicks in only after the user interacts with a field, so an empty form does not glow red from the start. > img means a direct child, and article:not(:has(img, video)) selects articles without images or videos. :has() has worked in all major browsers since the end of 2023.
Implementation Tips
1. Performance
Load images below the first screen lazily, and announce key resources to the browser early:
<!-- Lazy loading for images -->
<img src="image.jpg" alt="Description" loading="lazy">
<!-- Preload for key resources -->
<link rel="preload" as="image" href="hero-image.jpg">Watch out for one thing: do not lazy-load the image in the hero section, because it is visible right away and you would delay its display. That is exactly what preload is for.
2. Accessibility
Semantic elements and ARIA labels help screen readers understand the page:
<!-- Use semantic elements -->
<nav role="navigation" aria-label="Main navigation">
<main role="main">
<footer role="contentinfo">
<!-- Add aria-labels where needed -->
<button aria-label="Close modal">x</button>The roles in this example are redundant, because nav, main and footer have them by default. The rule is: do not add a role the element already has. aria-label, on the other hand, is essential for buttons that contain only an icon.
3. SEO
Meta tags describe the page to search engines and social networks:
<!-- Meta tags for SEO -->
<meta name="description" content="Page description">
<meta name="keywords" content="egypt, tours, pyramids">
<meta property="og:title" content="Egyptian Adventure">
<meta property="og:description" content="Best tours to Egypt">
<meta property="og:image" content="og-image.jpg">The description text often appears in search results, and the Open Graph tags build the link preview. You can skip keywords, because Google has not taken it into account since 2009.
Summary
Congratulations, young builder! You've created a complete, professional landing page that combines:
- Semantic HTML - accessible structure
- CSS Grid & Flexbox - advanced layouts
- Container Queries - responsive components
- Modern CSS - latest features
- Animations - smooth effects
- Performance - optimization
Just as building the Great Pyramid of Giza was the culmination of ancient builders' knowledge, your final project is a synthesis of everything you've learned in this module! In the final task you will also build an Egyptian dashboard that combines Container Queries, :has(), nesting, @layer, logical properties, clamp(), aspect-ratio, :is() and :where(), scroll-snap, color-mix(), @supports and accent-color. My advice: add them one at a time and check the page in two browsers after each one. In the sandbox below you will find an Egyptian dashboard where you can practice these techniques.
Code for this lesson: index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Thoth's Master Architecture Dashboard</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="egyptian-container">
<header class="pharaoh-header">
<div class="header-content">
<span class="ankh-symbol">πΉ</span>
<h1 class="pharaoh-title">Thoth's Master Architecture Dashboard</h1>
<span class="eye-of-horus">π</span>
</div>
<p class="header-subtitle">Sum of all software architecture wisdom from the Necropolis of Giza</p>
</header>
<!-- Clean Architecture Layers -->
<section class="pyramid-section">
<h2 class="section-title">
<span class="hieroglyph">π</span>
Clean Architecture - Temple Hierarchy
</h2>
<div class="pyramid-diagram">
<div class="pyramid-layer pyramid-layer--entities">
<div class="layer-content">
<h3>Entities</h3>
<p>Business Entities</p>
<span class="layer-icon">β</span>
</div>
</div>
<div class="pyramid-layer pyramid-layer--usecases">
<div class="layer-content">
<h3>Use Cases</h3>
<p>Use Cases</p>
<span class="layer-icon">β</span>
</div>
</div>
<div class="pyramid-layer pyramid-layer--adapters">
<div class="layer-content">
<h3>Interface Adapters</h3>
<p>Interface Adapters</p>
<span class="layer-icon">β</span>
</div>
</div>
<div class="pyramid-layer pyramid-layer--frameworks">
<div class="layer-content">
<h3>Frameworks & Drivers</h3>
<p>Frameworks and Drivers</p>
<span class="layer-icon">β</span>
</div>
</div>
</div>
</section>
<!-- Design Patterns Categories -->
<section class="papyrus-section">
<h2 class="section-title">
<span class="hieroglyph">π</span>
Design Patterns - Scrolls of Wisdom
</h2>
<div class="patterns-grid">
<article class="pattern-category pattern-category--creational">
<div class="pattern-header">
<h3>Creational Patterns</h3>
<span class="pattern-icon">β</span>
</div>
<ul class="pattern-list">
<li class="pattern-item">Singleton</li>
<li class="pattern-item">Factory</li>
<li class="pattern-item">Builder</li>
<li class="pattern-item">Prototype</li>
</ul>
</article>
<article class="pattern-category pattern-category--structural">
<div class="pattern-header">
<h3>Structural Patterns</h3>
<span class="pattern-icon">β</span>
</div>
<ul class="pattern-list">
<li class="pattern-item">Adapter</li>
<li class="pattern-item">Decorator</li>
<li class="pattern-item">Facade</li>
<li class="pattern-item">Proxy</li>
</ul>
</article>
<article class="pattern-category pattern-category--behavioral">
<div class="pattern-header">
<h3>Behavioral Patterns</h3>
<span class="pattern-icon">β</span>
</div>
<ul class="pattern-list">
<li class="pattern-item">Observer</li>
<li class="pattern-item">Strategy</li>
<li class="pattern-item">Command</li>
<li class="pattern-item">State</li>
</ul>
</article>
<article class="pattern-category pattern-category--architectural">
<div class="pattern-header">
<h3>Architectural Patterns</h3>
<span class="pattern-icon">β</span>
</div>
<ul class="pattern-list">
<li class="pattern-item">MVC</li>
<li class="pattern-item">MVVM</li>
<li class="pattern-item">Hexagonal</li>
<li class="pattern-item">Layered</li>
</ul>
</article>
</div>
</section>
<!-- Microservices Patterns -->
<section class="obelisk-section">
<h2 class="section-title">
<span class="hieroglyph">π</span>
Microservices - Obelisks of the Empire
</h2>
<div class="microservices-grid">
<div class="microservice-card microservice-card--gateway">
<div class="card-icon">β</div>
<h3>API Gateway</h3>
<p>Palace Gate</p>
<div class="card-status status--active">Active</div>
</div>
<div class="microservice-card microservice-card--discovery">
<div class="card-icon">β</div>
<h3>Service Discovery</h3>
<p>Map of the Empire</p>
<div class="card-status status--active">Active</div>
</div>
<div class="microservice-card microservice-card--breaker">
<div class="card-icon">β</div>
<h3>Circuit Breaker</h3>
<p>Gate Guardian</p>
<div class="card-status status--active">Active</div>
</div>
<div class="microservice-card microservice-card--saga">
<div class="card-icon">β</div>
<h3>Saga Pattern</h3>
<p>Event Chronicles</p>
<div class="card-status status--active">Active</div>
</div>
<div class="microservice-card microservice-card--sourcing">
<div class="card-icon">β³</div>
<h3>Event Sourcing</h3>
<p>Kingdom History</p>
<div class="card-status status--active">Active</div>
</div>
<div class="microservice-card microservice-card--cqrs">
<div class="card-icon">β</div>
<h3>CQRS</h3>
<p>Division of Power</p>
<div class="card-status status--active">Active</div>
</div>
</div>
</section>
<!-- Performance Patterns -->
<section class="granary-section">
<h2 class="section-title">
<span class="hieroglyph">π</span>
Performance - Grain Warehouses
</h2>
<div class="performance-grid">
<div class="performance-metric">
<div class="metric-header">
<span class="metric-icon">β </span>
<h3>Caching</h3>
</div>
<div class="metric-strategies">
<span class="strategy-badge">LRU</span>
<span class="strategy-badge">TTL</span>
<span class="strategy-badge">Write-through</span>
<span class="strategy-badge">Cache-aside</span>
</div>
<div class="metric-bar">
<div class="metric-fill metric-fill--high"></div>
</div>
</div>
<div class="performance-metric">
<div class="metric-header">
<span class="metric-icon">β</span>
<h3>Load Balancing</h3>
</div>
<div class="metric-strategies">
<span class="strategy-badge">Round Robin</span>
<span class="strategy-badge">Least Conn</span>
<span class="strategy-badge">IP Hash</span>
</div>
<div class="metric-bar">
<div class="metric-fill metric-fill--high"></div>
</div>
</div>
<div class="performance-metric">
<div class="metric-header">
<span class="metric-icon">β</span>
<h3>Database Sharding</h3>
</div>
<div class="metric-strategies">
<span class="strategy-badge">Geographic</span>
<span class="strategy-badge">Hash-based</span>
<span class="strategy-badge">Range-based</span>
</div>
<div class="metric-bar">
<div class="metric-fill metric-fill--medium"></div>
</div>
</div>
<div class="performance-metric">
<div class="metric-header">
<span class="metric-icon">β</span>
<h3>Optimization</h3>
</div>
<div class="metric-strategies">
<span class="strategy-badge">Profiling</span>
<span class="strategy-badge">Indexing</span>
<span class="strategy-badge">Compression</span>
</div>
<div class="metric-bar">
<div class="metric-fill metric-fill--high"></div>
</div>
</div>
</div>
</section>
<!-- Security Layers -->
<section class="fortress-section">
<h2 class="section-title">
<span class="hieroglyph">π</span>
Security - The Pharaoh's Fortifications
</h2>
<div class="security-layers">
<div class="security-layer security-layer--authentication">
<div class="layer-number">1</div>
<div class="layer-details">
<h3>Authentication</h3>
<p>Identity authentication</p>
<div class="security-badges">
<span class="sec-badge">JWT</span>
<span class="sec-badge">OAuth 2.0</span>
<span class="sec-badge">SSO</span>
</div>
</div>
</div>
<div class="security-layer security-layer--authorization">
<div class="layer-number">2</div>
<div class="layer-details">
<h3>Authorization</h3>
<p>Access control and permissions</p>
<div class="security-badges">
<span class="sec-badge">RBAC</span>
<span class="sec-badge">ABAC</span>
<span class="sec-badge">ACL</span>
</div>
</div>
</div>
<div class="security-layer security-layer--validation">
<div class="layer-number">3</div>
<div class="layer-details">
<h3>Input Validation</h3>
<p>Data validation and sanitization</p>
<div class="security-badges">
<span class="sec-badge">XSS Protection</span>
<span class="sec-badge">SQL Injection</span>
<span class="sec-badge">CSRF</span>
</div>
</div>
</div>
<div class="security-layer security-layer--encryption">
<div class="layer-number">4</div>
<div class="layer-details">
<h3>Data Encryption</h3>
<p>Data encryption</p>
<div class="security-badges">
<span class="sec-badge">TLS/SSL</span>
<span class="sec-badge">AES-256</span>
<span class="sec-badge">RSA</span>
</div>
</div>
</div>
<div class="security-layer security-layer--monitoring">
<div class="layer-number">5</div>
<div class="layer-details">
<h3>Audit & Monitoring</h3>
<p>Logging and security monitoring</p>
<div class="security-badges">
<span class="sec-badge">SIEM</span>
<span class="sec-badge">IDS/IPS</span>
<span class="sec-badge">Alerting</span>
</div>
</div>
</div>
</div>
</section>
<!-- Footer with blessing -->
<footer class="pharaoh-footer">
<div class="footer-content">
<p class="blessing">
<span class="ankh-symbol">πΉ</span>
Blessing of Thoth - God of Wisdom and Writing
<span class="ankh-symbol">πΉ</span>
</p>
<p class="footer-quote">
"Architecture worthy of the Pyramids of Giza - enduring and monumental for millennia"
</p>
</div>
</footer>
</div>
</body>
</html>Remember: This is just the beginning of your journey! These foundations will allow you to build ever larger and more advanced projects. Good luck, young pharaoh of the internet!
Docs entries used in this lesson
Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What does the :has() pseudo-class do in CSS?
Hands-on tasks in the game
- Click in order
Arrange the :has() selector syntax for styling a parent that contains an image:
- Code editor
Create: article:has(img) { border: 2px solid gold }, article:has(video) { background: #1a1a2e }, article:not(:has(img, video)) { opacity: 0.7 }. Test with different content - the parent reacts to its children!