Cours HTML et CSS · Module 9: CSS moderne
Projet final : Landing page réactive « Aventure égyptienne »
Dans cette leçon5
Bienvenue dans la dernière chambre de notre voyage égyptien ! Je suis Mohamed et aujourd'hui, nous affrontons le défi ultime : créer une landing page complète et professionnelle qui combinera TOUTES les compétences que tu as acquises dans ce module ! Tu connais déjà des dizaines d'outils, mais tu t'es exercé à chacun séparément. La vraie épreuve commence quand la grille, les conteneurs, le formulaire et l'accessibilité doivent fonctionner ensemble sur une même page.
Tout comme la construction de la Grande Pyramide de Gizeh nécessitait de combiner toutes les compétences des anciens bâtisseurs, des bases aux techniques les plus avancées, ton projet final sera une synthèse de toutes tes connaissances en HTML et CSS !
Objectif du projet
Tu vas créer une landing page réactive pour une agence de voyage fictive « Aventure égyptienne » qui organise des voyages dans l'Égypte antique. La page utilisera :
- HTML sémantique - structure accessible
- CSS Grid et Flexbox - mises en page avancées
- Container Queries - composants réactifs
- CSS moderne - dernières fonctionnalités CSS
- Animations - transitions et effets fluides
- Performance - optimisation et bonnes pratiques
Structure du projet
Sections de la page :
- Section Hero - bannière principale avec CTA (Call To Action)
- Atouts - cartes de fonctionnalités utilisant les container queries
- Circuits - galerie de circuits en CSS Grid
- Témoignages - avis clients avec carrousel
- Tarifs - section des prix
- Formulaire de contact - formulaire de contact
- Footer - pied de page avec liens sociaux
Exigences techniques
1. HTML sémantique
On commence par le squelette : un en-tête avec la navigation, des sections avec des identifiants, des cartes dans des éléments article, des avis dans des blockquote et un formulaire avec des labels. Le code est long, alors lis-le section par section :
1<!DOCTYPE html>
2<html lang="fr">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <meta name="description" content="Découvre les secrets de l'Égypte antique avec Aventure égyptienne - la meilleure agence de voyage">
7 <title>Aventure égyptienne - Voyages dans l'Égypte antique</title>
8 <link rel="stylesheet" href="styles.css">
9</head>
10<body>
11 <!-- Navigation -->
12 <header class="header" role="banner">
13 <nav class="nav" role="navigation" aria-label="Navigation principale">
14 <div class="container">
15 <a href="#" class="logo">
16 <span class="logo-icon">◆</span>
17 <span class="logo-text">Aventure égyptienne</span>
18 </a>
19 <ul class="nav-list">
20 <li><a href="#features">Pourquoi nous</a></li>
21 <li><a href="#tours">Circuits</a></li>
22 <li><a href="#testimonials">Avis</a></li>
23 <li><a href="#pricing">Tarifs</a></li>
24 <li><a href="#contact" class="btn-primary">Contact</a></li>
25 </ul>
26 </div>
27 </nav>
28 </header>
29
30 <!-- Section Hero -->
31 <section class="hero" id="hero">
32 <div class="hero-content">
33 <h1 class="hero-title">
34 Découvre les secrets de<br>
35 <span class="gradient-text">l'Égypte antique</span>
36 </h1>
37 <p class="hero-subtitle">
38 Voyage 5 000 ans en arrière et vis les merveilles des pharaons,
39 des pyramides et des hiéroglyphes lors du voyage inoubliable d'une vie !
40 </p>
41 <div class="hero-cta">
42 <a href="#tours" class="btn btn-large btn-primary">
43 Parcourir les circuits
44 </a>
45 <a href="#contact" class="btn btn-large btn-secondary">
46 Nous contacter
47 </a>
48 </div>
49 </div>
50 <div class="hero-image">
51 <div class="pyramid-illustration"></div>
52 </div>
53 </section>
54
55 <!-- Atouts -->
56 <section class="features" id="features">
57 <div class="container">
58 <h2 class="section-title">Pourquoi Aventure égyptienne ?</h2>
59 <p class="section-subtitle">
60 Nous sommes leaders dans l'organisation de voyages en Égypte depuis 20 ans
61 </p>
62
63 <div class="features-grid">
64 <article class="feature-card">
65 <div class="feature-icon">◆</div>
66 <h3>Accès exclusif</h3>
67 <p>Accès à des lieux inaccessibles aux touristes ordinaires,
68 y compris des visites privées des chambres des pyramides</p>
69 </article>
70
71 <article class="feature-card">
72 <div class="feature-icon">◆</div>
73 <h3>Experts en égyptologie</h3>
74 <p>Des guides titulaires de diplômes universitaires, passionnés par l'histoire
75 de l'Égypte antique</p>
76 </article>
77
78 <article class="feature-card">
79 <div class="feature-icon">◆</div>
80 <h3>Hôtels de luxe</h3>
81 <p>Hébergement dans les meilleurs hôtels 5*, avec vue sur le Nil
82 et les pyramides</p>
83 </article>
84
85 <article class="feature-card">
86 <div class="feature-icon">◆</div>
87 <h3>Cuisine authentique</h3>
88 <p>Dégustation de plats égyptiens traditionnels préparés
89 par des chefs locaux</p>
90 </article>
91 </div>
92 </div>
93 </section>
94
95 <!-- Grille de circuits -->
96 <section class="tours" id="tours">
97 <div class="container">
98 <h2 class="section-title">Nos circuits</h2>
99 <p class="section-subtitle">
100 Choisis un circuit adapté à tes rêves
101 </p>
102
103 <div class="tours-grid">
104 <article class="tour-card tour-card--featured">
105 <div class="tour-image">
106 <img src="/api/placeholder/600/400" alt="Pyramides de Gizeh">
107 <span class="tour-badge">Meilleure vente</span>
108 </div>
109 <div class="tour-content">
110 <h3>Égypte classique</h3>
111 <p class="tour-duration">⏱7 jours / 6 nuits</p>
112 <p class="tour-description">
113 Le Caire, Gizeh, Louxor, la Vallée des Rois, croisière sur le Nil
114 </p>
115 <ul class="tour-highlights">
116 <li>Grande Pyramide de Gizeh</li>
117 <li>Musée égyptien du Caire</li>
118 <li>Temple de Karnak</li>
119 <li>Croisière sur le Nil</li>
120 </ul>
121 <div class="tour-footer">
122 <div class="tour-price">
123 <span class="price-from">à partir de</span>
124 <span class="price-amount">4 999</span>
125 <span class="price-currency">PLN</span>
126 </div>
127 <a href="#contact" class="btn btn-primary">Réserver</a>
128 </div>
129 </div>
130 </article>
131
132 <article class="tour-card">
133 <div class="tour-image">
134 <img src="/api/placeholder/600/400" alt="Abou Simbel">
135 </div>
136 <div class="tour-content">
137 <h3>Merveilles d'Égypte</h3>
138 <p class="tour-duration">⏱10 jours / 9 nuits</p>
139 <p class="tour-description">
140 Toutes les grandes attractions + Abou Simbel et Alexandrie
141 </p>
142 <ul class="tour-highlights">
143 <li>Tout le circuit Égypte classique</li>
144 <li>Temples d'Abou Simbel</li>
145 <li>Bibliothèque d'Alexandrie</li>
146 <li>Oasis de Siwa</li>
147 </ul>
148 <div class="tour-footer">
149 <div class="tour-price">
150 <span class="price-from">à partir de</span>
151 <span class="price-amount">7 499</span>
152 <span class="price-currency">PLN</span>
153 </div>
154 <a href="#contact" class="btn btn-primary">Réserver</a>
155 </div>
156 </div>
157 </article>
158
159 <article class="tour-card">
160 <div class="tour-image">
161 <img src="/api/placeholder/600/400" alt="Plongée en mer Rouge">
162 </div>
163 <div class="tour-content">
164 <h3>Égypte + mer Rouge</h3>
165 <p class="tour-duration">⏱12 jours / 11 nuits</p>
166 <p class="tour-description">
167 Histoire + détente et plongée à Hurghada
168 </p>
169 <ul class="tour-highlights">
170 <li>Visite des monuments</li>
171 <li>5 jours all inclusive à Hurghada</li>
172 <li>Plongée avec récif corallien</li>
173 <li>Safari dans le désert</li>
174 </ul>
175 <div class="tour-footer">
176 <div class="tour-price">
177 <span class="price-from">à partir de</span>
178 <span class="price-amount">6 299</span>
179 <span class="price-currency">PLN</span>
180 </div>
181 <a href="#contact" class="btn btn-primary">Réserver</a>
182 </div>
183 </div>
184 </article>
185 </div>
186 </div>
187 </section>
188
189 <!-- Témoignages -->
190 <section class="testimonials" id="testimonials">
191 <div class="container">
192 <h2 class="section-title">Ce que disent nos clients</h2>
193
194 <div class="testimonials-grid">
195 <blockquote class="testimonial-card">
196 <div class="testimonial-rating">5/5</div>
197 <p class="testimonial-text">
198 « Une expérience incroyable ! Les guides étaient extrêmement compétents,
199 et l'organisation du circuit était irréprochable. Les pyramides en vrai
200 sont mille fois plus impressionnantes qu'en photos ! »
201 </p>
202 <cite class="testimonial-author">
203 <strong>Anna Kowalska</strong>
204 <span>Varsovie</span>
205 </cite>
206 </blockquote>
207
208 <blockquote class="testimonial-card">
209 <div class="testimonial-rating">5/5</div>
210 <p class="testimonial-text">
211 « En tant qu'égyptologue, j'étais sceptique, mais ce circuit a dépassé
212 mes attentes. L'accès à des lieux non présentés lors des
213 circuits standards était inestimable ! »
214 </p>
215 <cite class="testimonial-author">
216 <strong>Dr Jan Nowak</strong>
217 <span>Cracovie</span>
218 </cite>
219 </blockquote>
220
221 <blockquote class="testimonial-card">
222 <div class="testimonial-rating">5/5</div>
223 <p class="testimonial-text">
224 « Le voyage d'une vie ! J'ai été particulièrement impressionnée par la croisière sur le Nil et
225 la visite de la Vallée des Rois. Tout était parfaitement organisé. »
226 </p>
227 <cite class="testimonial-author">
228 <strong>Maria Wisniewska</strong>
229 <span>Gdansk</span>
230 </cite>
231 </blockquote>
232 </div>
233 </div>
234 </section>
235
236 <!-- Formulaire de contact -->
237 <section class="contact" id="contact">
238 <div class="container">
239 <div class="contact-grid">
240 <div class="contact-info">
241 <h2>Réserve ton aventure</h2>
242 <p>Remplis le formulaire et notre conseiller te contactera
243 sous 24 heures !</p>
244
245 <div class="contact-details">
246 <div class="contact-item">
247 <span class="contact-icon">◆</span>
248 <div>
249 <strong>Téléphone</strong>
250 <p>+48 123 456 789</p>
251 </div>
252 </div>
253 <div class="contact-item">
254 <span class="contact-icon">◆</span>
255 <div>
256 <strong>Email</strong>
257 <p>contact@egyptianadventure.com</p>
258 </div>
259 </div>
260 <div class="contact-item">
261 <span class="contact-icon">◆</span>
262 <div>
263 <strong>Adresse</strong>
264 <p>123 rue des Pyramides, 00-001 Varsovie</p>
265 </div>
266 </div>
267 </div>
268 </div>
269
270 <form class="contact-form" action="#" method="POST">
271 <div class="form-group">
272 <label for="name">Nom complet</label>
273 <input type="text" id="name" name="name" required>
274 </div>
275
276 <div class="form-group">
277 <label for="email">Email</label>
278 <input type="email" id="email" name="email" required>
279 </div>
280
281 <div class="form-group">
282 <label for="phone">Téléphone</label>
283 <input type="tel" id="phone" name="phone" required>
284 </div>
285
286 <div class="form-group">
287 <label for="tour">Choisis un circuit</label>
288 <select id="tour" name="tour" required>
289 <option value="">Sélectionne...</option>
290 <option value="classic">Égypte classique - 7 jours</option>
291 <option value="wonders">Merveilles d'Égypte - 10 jours</option>
292 <option value="red-sea">Égypte + mer Rouge - 12 jours</option>
293 </select>
294 </div>
295
296 <div class="form-group">
297 <label for="message">Message (facultatif)</label>
298 <textarea id="message" name="message" rows="4"></textarea>
299 </div>
300
301 <button type="submit" class="btn btn-primary btn-large">
302 Envoyer la demande
303 </button>
304 </form>
305 </div>
306 </div>
307 </section>
308
309 <!-- Footer -->
310 <footer class="footer">
311 <div class="container">
312 <div class="footer-grid">
313 <div class="footer-section">
314 <h3>Aventure égyptienne</h3>
315 <p>À la découverte des secrets de l'Égypte antique depuis 2004</p>
316 <div class="social-links">
317 <a href="#" aria-label="Facebook">Facebook</a>
318 <a href="#" aria-label="Instagram">Instagram</a>
319 <a href="#" aria-label="Twitter">Twitter</a>
320 <a href="#" aria-label="YouTube">YouTube</a>
321 </div>
322 </div>
323
324 <div class="footer-section">
325 <h4>Circuits</h4>
326 <ul>
327 <li><a href="#">Égypte classique</a></li>
328 <li><a href="#">Merveilles d'Égypte</a></li>
329 <li><a href="#">Égypte + mer Rouge</a></li>
330 <li><a href="#">Circuits de groupe</a></li>
331 </ul>
332 </div>
333
334 <div class="footer-section">
335 <h4>Informations</h4>
336 <ul>
337 <li><a href="#">À propos de nous</a></li>
338 <li><a href="#">Conditions générales</a></li>
339 <li><a href="#">Politique de confidentialité</a></li>
340 <li><a href="#">FAQ</a></li>
341 </ul>
342 </div>
343
344 <div class="footer-section">
345 <h4>Contact</h4>
346 <ul>
347 <li>+48 123 456 789</li>
348 <li>contact@egyptianadventure.com</li>
349 <li>123 rue des Pyramides, Varsovie</li>
350 </ul>
351 </div>
352 </div>
353
354 <div class="footer-bottom">
355 <p>© 2024 Aventure égyptienne. Tous droits réservés.</p>
356 </div>
357 </div>
358 </footer>
359</body>
360</html>Chaque section a son propre id, ce qui permet aux liens de la navigation de faire défiler la page jusqu'au bon endroit. Les champs du formulaire sont reliés à leurs labels par for et id, et l'attribut required empêche d'envoyer un champ vide. lang="fr" indique aux lecteurs d'écran dans quelle langue lire le contenu.
2. CSS - Styles complets
Des commentaires découpent la feuille de style en sections : le reset et les variables, l'en-tête, le hero, les atouts, les circuits et les autres parties de la page. La palette, les espacements et les ombres vivent dans des variables CSS sur :root :
1/* ==========================================
2 CSS RESET & STYLES DE BASE
3 ========================================== */
4*, *::before, *::after {
5 margin: 0;
6 padding: 0;
7 box-sizing: border-box;
8}
9
10:root {
11 /* Couleurs - thème égyptien */
12 --color-primary: #D4AF37; /* Or */
13 --color-primary-dark: #B8941F;
14 --color-secondary: #2C3E50; /* Bleu foncé */
15 --color-accent: #E67E22; /* Orange */
16
17 --color-bg: #FFFFFF;
18 --color-bg-alt: #F8F9FA;
19 --color-text: #2C3E50;
20 --color-text-light: #6C757D;
21
22 /* Espacement */
23 --spacing-xs: 0.5rem;
24 --spacing-sm: 1rem;
25 --spacing-md: 2rem;
26 --spacing-lg: 4rem;
27 --spacing-xl: 6rem;
28
29 /* Typographie */
30 --font-primary: 'Segoe UI', system-ui, sans-serif;
31 --font-size-base: 1rem;
32 --font-size-lg: 1.125rem;
33 --font-size-xl: 1.25rem;
34 --font-size-2xl: 1.5rem;
35 --font-size-3xl: 2rem;
36 --font-size-4xl: 3rem;
37
38 /* Mise en page */
39 --container-max-width: 1200px;
40 --border-radius: 12px;
41
42 /* Ombres */
43 --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1);
44 --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
45 --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
46
47 /* Transitions */
48 --transition: all 0.3s ease;
49}
50
51html {
52 scroll-behavior: smooth;
53}
54
55body {
56 font-family: var(--font-primary);
57 font-size: var(--font-size-base);
58 line-height: 1.6;
59 color: var(--color-text);
60 background: var(--color-bg);
61}
62
63img {
64 max-width: 100%;
65 height: auto;
66 display: block;
67}
68
69a {
70 color: inherit;
71 text-decoration: none;
72}
73
74ul {
75 list-style: none;
76}
77
78/* ==========================================
79 UTILITAIRES
80 ========================================== */
81.container {
82 max-width: var(--container-max-width);
83 margin: 0 auto;
84 padding: 0 var(--spacing-md);
85}
86
87.section-title {
88 font-size: var(--font-size-3xl);
89 font-weight: 700;
90 text-align: center;
91 margin-bottom: var(--spacing-sm);
92 color: var(--color-secondary);
93}
94
95.section-subtitle {
96 font-size: var(--font-size-lg);
97 text-align: center;
98 color: var(--color-text-light);
99 margin-bottom: var(--spacing-lg);
100}
101
102.gradient-text {
103 background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%);
104 -webkit-background-clip: text;
105 -webkit-text-fill-color: transparent;
106 background-clip: text;
107}
108
109/* Boutons */
110.btn {
111 display: inline-block;
112 padding: var(--spacing-sm) var(--spacing-md);
113 border-radius: var(--border-radius);
114 font-weight: 600;
115 text-align: center;
116 cursor: pointer;
117 transition: var(--transition);
118 border: 2px solid transparent;
119}
120
121.btn-primary {
122 background: var(--color-primary);
123 color: var(--color-secondary);
124}
125
126.btn-primary:hover {
127 background: var(--color-primary-dark);
128 transform: translateY(-2px);
129 box-shadow: var(--shadow-md);
130}
131
132.btn-secondary {
133 background: transparent;
134 border-color: var(--color-primary);
135 color: var(--color-primary);
136}
137
138.btn-secondary:hover {
139 background: var(--color-primary);
140 color: var(--color-secondary);
141}
142
143.btn-large {
144 padding: var(--spacing-md) var(--spacing-lg);
145 font-size: var(--font-size-lg);
146}
147
148/* ==========================================
149 HEADER & NAVIGATION
150 ========================================== */
151.header {
152 position: sticky;
153 top: 0;
154 background: rgba(255, 255, 255, 0.98);
155 backdrop-filter: blur(10px);
156 box-shadow: var(--shadow-sm);
157 z-index: 1000;
158}
159
160.nav .container {
161 display: flex;
162 justify-content: space-between;
163 align-items: center;
164 padding-top: var(--spacing-sm);
165 padding-bottom: var(--spacing-sm);
166}
167
168.logo {
169 display: flex;
170 align-items: center;
171 gap: var(--spacing-sm);
172 font-size: var(--font-size-xl);
173 font-weight: 700;
174 color: var(--color-secondary);
175}
176
177.logo-icon {
178 font-size: var(--font-size-2xl);
179}
180
181.nav-list {
182 display: flex;
183 gap: var(--spacing-md);
184 align-items: center;
185}
186
187.nav-list a {
188 transition: var(--transition);
189}
190
191.nav-list a:not(.btn-primary):hover {
192 color: var(--color-primary);
193}
194
195/* ==========================================
196 SECTION HERO
197 ========================================== */
198.hero {
199 display: grid;
200 grid-template-columns: 1fr 1fr;
201 gap: var(--spacing-lg);
202 align-items: center;
203 min-height: calc(100vh - 80px);
204 padding: var(--spacing-xl) var(--spacing-md);
205 background: linear-gradient(135deg, #F8F9FA 0%, #E9ECEF 100%);
206}
207
208.hero-content {
209 max-width: 600px;
210}
211
212.hero-title {
213 font-size: var(--font-size-4xl);
214 font-weight: 800;
215 line-height: 1.2;
216 margin-bottom: var(--spacing-md);
217}
218
219.hero-subtitle {
220 font-size: var(--font-size-xl);
221 color: var(--color-text-light);
222 margin-bottom: var(--spacing-lg);
223}
224
225.hero-cta {
226 display: flex;
227 gap: var(--spacing-md);
228 flex-wrap: wrap;
229}
230
231.hero-image {
232 display: flex;
233 justify-content: center;
234 align-items: center;
235}
236
237.pyramid-illustration {
238 width: 400px;
239 height: 400px;
240 background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%);
241 clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
242 animation: float 6s ease-in-out infinite;
243}
244
245@keyframes float {
246 0%, 100% { transform: translateY(0); }
247 50% { transform: translateY(-20px); }
248}
249
250/* ==========================================
251 SECTION ATOUTS
252 ========================================== */
253.features {
254 padding: var(--spacing-xl) 0;
255 background: var(--color-bg);
256}
257
258.features-grid {
259 display: grid;
260 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
261 gap: var(--spacing-lg);
262 container-type: inline-size;
263}
264
265.feature-card {
266 background: white;
267 padding: var(--spacing-lg);
268 border-radius: var(--border-radius);
269 box-shadow: var(--shadow-md);
270 text-align: center;
271 transition: var(--transition);
272}
273
274.feature-card:hover {
275 transform: translateY(-8px);
276 box-shadow: var(--shadow-lg);
277}
278
279.feature-icon {
280 font-size: 4rem;
281 margin-bottom: var(--spacing-md);
282}
283
284.feature-card h3 {
285 font-size: var(--font-size-xl);
286 margin-bottom: var(--spacing-sm);
287 color: var(--color-secondary);
288}
289
290/* Container query pour les cartes d'atouts */
291@container (max-width: 300px) {
292 .feature-card {
293 padding: var(--spacing-md);
294 }
295
296 .feature-icon {
297 font-size: 3rem;
298 }
299}
300
301/* ==========================================
302 SECTION CIRCUITS
303 ========================================== */
304.tours {
305 padding: var(--spacing-xl) 0;
306 background: var(--color-bg-alt);
307}
308
309.tours-grid {
310 display: grid;
311 grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
312 gap: var(--spacing-lg);
313}
314
315.tour-card {
316 background: white;
317 border-radius: var(--border-radius);
318 overflow: hidden;
319 box-shadow: var(--shadow-md);
320 transition: var(--transition);
321 container-type: inline-size;
322}
323
324.tour-card:hover {
325 transform: translateY(-8px);
326 box-shadow: var(--shadow-lg);
327}
328
329.tour-card--featured {
330 grid-column: span 2;
331}
332
333@media (max-width: 768px) {
334 .tour-card--featured {
335 grid-column: span 1;
336 }
337}
338
339.tour-image {
340 position: relative;
341 aspect-ratio: 16 / 9;
342 overflow: hidden;
343}
344
345.tour-image img {
346 width: 100%;
347 height: 100%;
348 object-fit: cover;
349 transition: var(--transition);
350}
351
352.tour-card:hover .tour-image img {
353 transform: scale(1.1);
354}
355
356.tour-badge {
357 position: absolute;
358 top: var(--spacing-md);
359 right: var(--spacing-md);
360 background: var(--color-accent);
361 color: white;
362 padding: var(--spacing-xs) var(--spacing-md);
363 border-radius: 50px;
364 font-weight: 600;
365 font-size: 0.875rem;
366}
367
368.tour-content {
369 padding: var(--spacing-lg);
370}
371
372.tour-content h3 {
373 font-size: var(--font-size-2xl);
374 margin-bottom: var(--spacing-sm);
375 color: var(--color-secondary);
376}
377
378.tour-duration {
379 color: var(--color-text-light);
380 margin-bottom: var(--spacing-sm);
381}
382
383.tour-highlights {
384 margin: var(--spacing-md) 0;
385}
386
387.tour-highlights li {
388 padding: var(--spacing-xs) 0;
389 color: var(--color-text);
390}
391
392.tour-footer {
393 display: flex;
394 justify-content: space-between;
395 align-items: center;
396 margin-top: var(--spacing-md);
397 padding-top: var(--spacing-md);
398 border-top: 1px solid var(--color-bg-alt);
399}
400
401.tour-price {
402 display: flex;
403 align-items: baseline;
404 gap: var(--spacing-xs);
405}
406
407.price-amount {
408 font-size: var(--font-size-3xl);
409 font-weight: 700;
410 color: var(--color-primary);
411}
412
413.price-from,
414.price-currency {
415 font-size: var(--font-size-base);
416 color: var(--color-text-light);
417}
418
419/* ==========================================
420 SECTION TÉMOIGNAGES
421 ========================================== */
422.testimonials {
423 padding: var(--spacing-xl) 0;
424 background: var(--color-bg);
425}
426
427.testimonials-grid {
428 display: grid;
429 grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
430 gap: var(--spacing-lg);
431}
432
433.testimonial-card {
434 background: white;
435 padding: var(--spacing-lg);
436 border-radius: var(--border-radius);
437 box-shadow: var(--shadow-md);
438 border-left: 4px solid var(--color-primary);
439}
440
441.testimonial-rating {
442 font-size: var(--font-size-lg);
443 margin-bottom: var(--spacing-md);
444}
445
446.testimonial-text {
447 font-size: var(--font-size-lg);
448 line-height: 1.8;
449 margin-bottom: var(--spacing-md);
450 font-style: italic;
451 color: var(--color-text);
452}
453
454.testimonial-author {
455 display: flex;
456 flex-direction: column;
457 gap: var(--spacing-xs);
458 font-style: normal;
459}
460
461.testimonial-author strong {
462 color: var(--color-secondary);
463 font-size: var(--font-size-lg);
464}
465
466.testimonial-author span {
467 color: var(--color-text-light);
468}
469
470/* ==========================================
471 SECTION CONTACT
472 ========================================== */
473.contact {
474 padding: var(--spacing-xl) 0;
475 background: var(--color-bg-alt);
476}
477
478.contact-grid {
479 display: grid;
480 grid-template-columns: 1fr 1fr;
481 gap: var(--spacing-xl);
482}
483
484@media (max-width: 768px) {
485 .contact-grid {
486 grid-template-columns: 1fr;
487 }
488}
489
490.contact-info h2 {
491 font-size: var(--font-size-3xl);
492 margin-bottom: var(--spacing-md);
493 color: var(--color-secondary);
494}
495
496.contact-details {
497 margin-top: var(--spacing-lg);
498}
499
500.contact-item {
501 display: flex;
502 gap: var(--spacing-md);
503 margin-bottom: var(--spacing-md);
504}
505
506.contact-icon {
507 font-size: var(--font-size-2xl);
508}
509
510.contact-form {
511 background: white;
512 padding: var(--spacing-lg);
513 border-radius: var(--border-radius);
514 box-shadow: var(--shadow-md);
515}
516
517.form-group {
518 margin-bottom: var(--spacing-md);
519}
520
521.form-group label {
522 display: block;
523 margin-bottom: var(--spacing-xs);
524 font-weight: 600;
525 color: var(--color-secondary);
526}
527
528.form-group input,
529.form-group select,
530.form-group textarea {
531 width: 100%;
532 padding: var(--spacing-sm);
533 border: 2px solid var(--color-bg-alt);
534 border-radius: var(--border-radius);
535 font-family: inherit;
536 font-size: var(--font-size-base);
537 transition: var(--transition);
538}
539
540.form-group input:focus,
541.form-group select:focus,
542.form-group textarea:focus {
543 outline: none;
544 border-color: var(--color-primary);
545}
546
547/* ==========================================
548 FOOTER
549 ========================================== */
550.footer {
551 background: var(--color-secondary);
552 color: white;
553 padding: var(--spacing-xl) 0 var(--spacing-md);
554}
555
556.footer-grid {
557 display: grid;
558 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
559 gap: var(--spacing-lg);
560 margin-bottom: var(--spacing-lg);
561}
562
563.footer-section h3,
564.footer-section h4 {
565 margin-bottom: var(--spacing-md);
566 color: var(--color-primary);
567}
568
569.footer-section ul li {
570 margin-bottom: var(--spacing-xs);
571}
572
573.footer-section a:hover {
574 color: var(--color-primary);
575}
576
577.social-links {
578 display: flex;
579 gap: var(--spacing-md);
580 margin-top: var(--spacing-md);
581 font-size: var(--font-size-2xl);
582}
583
584.footer-bottom {
585 text-align: center;
586 padding-top: var(--spacing-md);
587 border-top: 1px solid rgba(255, 255, 255, 0.1);
588 color: rgba(255, 255, 255, 0.7);
589}
590
591/* ==========================================
592 DESIGN RÉACTIF
593 ========================================== */
594@media (max-width: 768px) {
595 .hero {
596 grid-template-columns: 1fr;
597 text-align: center;
598 }
599
600 .hero-title {
601 font-size: var(--font-size-3xl);
602 }
603
604 .pyramid-illustration {
605 width: 250px;
606 height: 250px;
607 }
608
609 .hero-cta {
610 justify-content: center;
611 }
612
613 .nav-list {
614 flex-wrap: wrap;
615 gap: var(--spacing-sm);
616 }
617}Remarque trois endroits. La grille des atouts est un conteneur, donc les cartes modifient leurs espacements grâce à @container. Le circuit mis en avant occupe deux colonnes, et sous 768px, il revient à une seule. Les photos des circuits gardent leurs proportions grâce à aspect-ratio: 16 / 9.
3. L'œil d'Horus : le sélecteur :has()
Il manque encore un outil. La pseudo-classe :has() sélectionne un élément s'il contient un enfant donné, le parent peut donc réagir à ce qu'il contient, comme l'œil d'Horus qui regarde à l'intérieur d'une chambre :
1/* Une carte avec un badge (par ex. Meilleure vente) reçoit un contour doré */
2.tour-card:has(.tour-badge) {
3 outline: 3px solid var(--color-primary);
4}
5
6/* Le label d'un champ que l'utilisateur a mal rempli */
7.form-group:has(input:user-invalid) label {
8 color: #c0392b;
9}
10
11/* Un article dont l'enfant direct est une image */
12article:has(> img) {
13 border: 2px solid gold;
14}Seule la carte avec le badge Meilleure vente recevra le contour, car les autres n'en ont pas. :user-invalid ne s'active qu'après une interaction avec le champ, donc un formulaire vide ne s'allume pas en rouge dès l'arrivée. L'écriture > img désigne un enfant direct, et article:not(:has(img, video)) sélectionnera les articles sans images ni vidéos. :has() fonctionne dans tous les principaux navigateurs depuis fin 2023.
Conseils de mise en œuvre
1. Performance
Charge en différé les images situées sous le premier écran, et annonce plus tôt au navigateur les ressources essentielles :
1<!-- Lazy loading pour les images -->
2<img src="image.jpg" alt="Description" loading="lazy">
3
4<!-- Preload pour les ressources clés -->
5<link rel="preload" as="image" href="hero-image.jpg">Attention à une chose : ne charge pas en différé l'image de la section hero, car elle est visible immédiatement et tu retarderais son affichage. C'est justement pour elle qu'existe preload.
2. Accessibilité
Les balises sémantiques et les labels ARIA aident les lecteurs d'écran à comprendre la page :
1<!-- Utiliser des éléments sémantiques -->
2<nav role="navigation" aria-label="Navigation principale">
3<main role="main">
4<footer role="contentinfo">
5
6<!-- Ajouter des aria-labels là où c'est nécessaire -->
7<button aria-label="Fermer la fenêtre modale">×</button>Les rôles de cet exemple sont superflus, car nav, main et footer les ont par défaut. La règle est simple : n'ajoute pas un rôle que l'élément possède déjà. aria-label est en revanche indispensable pour les boutons qui ne contiennent qu'une icône.
3. SEO
Les balises meta décrivent la page aux moteurs de recherche et aux réseaux sociaux :
1<!-- Balises meta pour le SEO -->
2<meta name="description" content="Description de la page">
3<meta name="keywords" content="egypte, circuits, pyramides">
4<meta property="og:title" content="Aventure égyptienne">
5<meta property="og:description" content="Les meilleurs circuits en Égypte">
6<meta property="og:image" content="og-image.jpg">Le texte de description apparaît souvent dans les résultats de recherche, et les balises Open Graph construisent l'aperçu du lien. Tu peux omettre keywords, car Google ne le prend plus en compte depuis 2009.
Résumé
Félicitations, jeune bâtisseur ! Tu as créé une landing page complète et professionnelle qui combine :
- HTML sémantique - structure accessible
- CSS Grid & Flexbox - mises en page avancées
- Container Queries - composants réactifs
- CSS moderne - dernières fonctionnalités
- Animations - effets fluides
- Performance - optimisation
Tout comme la construction de la Grande Pyramide de Gizeh fut l'aboutissement du savoir des anciens bâtisseurs, ton projet final est une synthèse de tout ce que tu as appris dans ce module ! Dans la tâche finale, tu construiras encore un tableau de bord égyptien dans lequel tu combineras les Container Queries, :has(), l'imbrication, @layer, les propriétés logiques, clamp(), aspect-ratio, :is() et :where(), scroll-snap, color-mix(), @supports et accent-color. Mon conseil : ajoute-les une par une et, après chacune, vérifie la page dans deux navigateurs. Dans le bac à sable ci-dessous, tu trouveras un tableau de bord égyptien sur lequel tu pourras t'exercer à ces techniques.
Code de cette leçon : index.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Tableau de bord d'architecture de Thot</title>
7 <link rel="stylesheet" href="style.css">
8</head>
9<body>
10 <div class="egyptian-container">
11 <header class="pharaoh-header">
12 <div class="header-content">
13 <span class="ankh-symbol">𓋹</span>
14 <h1 class="pharaoh-title">Tableau de bord d'architecture de Thot</h1>
15 <span class="eye-of-horus">𓂀</span>
16 </div>
17 <p class="header-subtitle">La somme de toute la sagesse de l'architecture logicielle de la nécropole de Gizeh</p>
18 </header>
19
20 <!-- Couches de la Clean Architecture -->
21 <section class="pyramid-section">
22 <h2 class="section-title">
23 <span class="hieroglyph">𓉐</span>
24 Clean Architecture - Hiérarchie du Temple
25 </h2>
26 <div class="pyramid-diagram">
27 <div class="pyramid-layer pyramid-layer--entities">
28 <div class="layer-content">
29 <h3>Entities</h3>
30 <p>Entités métier</p>
31 <span class="layer-icon">◆</span>
32 </div>
33 </div>
34 <div class="pyramid-layer pyramid-layer--usecases">
35 <div class="layer-content">
36 <h3>Use Cases</h3>
37 <p>Cas d'utilisation</p>
38 <span class="layer-icon">◆</span>
39 </div>
40 </div>
41 <div class="pyramid-layer pyramid-layer--adapters">
42 <div class="layer-content">
43 <h3>Interface Adapters</h3>
44 <p>Adaptateurs d'interface</p>
45 <span class="layer-icon">◆</span>
46 </div>
47 </div>
48 <div class="pyramid-layer pyramid-layer--frameworks">
49 <div class="layer-content">
50 <h3>Frameworks & Drivers</h3>
51 <p>Frameworks et pilotes</p>
52 <span class="layer-icon">◆</span>
53 </div>
54 </div>
55 </div>
56 </section>
57
58 <!-- Catégories de Design Patterns -->
59 <section class="papyrus-section">
60 <h2 class="section-title">
61 <span class="hieroglyph">𓉐</span>
62 Design Patterns - Rouleaux de la Sagesse
63 </h2>
64 <div class="patterns-grid">
65 <article class="pattern-category pattern-category--creational">
66 <div class="pattern-header">
67 <h3>Patterns de création</h3>
68 <span class="pattern-icon">◆</span>
69 </div>
70 <ul class="pattern-list">
71 <li class="pattern-item">Singleton</li>
72 <li class="pattern-item">Factory</li>
73 <li class="pattern-item">Builder</li>
74 <li class="pattern-item">Prototype</li>
75 </ul>
76 </article>
77
78 <article class="pattern-category pattern-category--structural">
79 <div class="pattern-header">
80 <h3>Patterns structurels</h3>
81 <span class="pattern-icon">◆</span>
82 </div>
83 <ul class="pattern-list">
84 <li class="pattern-item">Adapter</li>
85 <li class="pattern-item">Decorator</li>
86 <li class="pattern-item">Facade</li>
87 <li class="pattern-item">Proxy</li>
88 </ul>
89 </article>
90
91 <article class="pattern-category pattern-category--behavioral">
92 <div class="pattern-header">
93 <h3>Patterns comportementaux</h3>
94 <span class="pattern-icon">◆</span>
95 </div>
96 <ul class="pattern-list">
97 <li class="pattern-item">Observer</li>
98 <li class="pattern-item">Strategy</li>
99 <li class="pattern-item">Command</li>
100 <li class="pattern-item">State</li>
101 </ul>
102 </article>
103
104 <article class="pattern-category pattern-category--architectural">
105 <div class="pattern-header">
106 <h3>Patterns architecturaux</h3>
107 <span class="pattern-icon">◆</span>
108 </div>
109 <ul class="pattern-list">
110 <li class="pattern-item">MVC</li>
111 <li class="pattern-item">MVVM</li>
112 <li class="pattern-item">Hexagonal</li>
113 <li class="pattern-item">Layered</li>
114 </ul>
115 </article>
116 </div>
117 </section>
118
119 <!-- Patterns de microservices -->
120 <section class="obelisk-section">
121 <h2 class="section-title">
122 <span class="hieroglyph">𓉐</span>
123 Microservices - Obélisques de l'Empire
124 </h2>
125 <div class="microservices-grid">
126 <div class="microservice-card microservice-card--gateway">
127 <div class="card-icon">◆</div>
128 <h3>API Gateway</h3>
129 <p>Porte du Palais</p>
130 <div class="card-status status--active">Actif</div>
131 </div>
132
133 <div class="microservice-card microservice-card--discovery">
134 <div class="card-icon">◆</div>
135 <h3>Service Discovery</h3>
136 <p>Carte de l'Empire</p>
137 <div class="card-status status--active">Actif</div>
138 </div>
139
140 <div class="microservice-card microservice-card--breaker">
141 <div class="card-icon">◆</div>
142 <h3>Circuit Breaker</h3>
143 <p>Gardien de la Porte</p>
144 <div class="card-status status--active">Actif</div>
145 </div>
146
147 <div class="microservice-card microservice-card--saga">
148 <div class="card-icon">◆</div>
149 <h3>Saga Pattern</h3>
150 <p>Chroniques des Événements</p>
151 <div class="card-status status--active">Actif</div>
152 </div>
153
154 <div class="microservice-card microservice-card--sourcing">
155 <div class="card-icon">⏳</div>
156 <h3>Event Sourcing</h3>
157 <p>Histoire du Royaume</p>
158 <div class="card-status status--active">Actif</div>
159 </div>
160
161 <div class="microservice-card microservice-card--cqrs">
162 <div class="card-icon">◆</div>
163 <h3>CQRS</h3>
164 <p>Division du Pouvoir</p>
165 <div class="card-status status--active">Actif</div>
166 </div>
167 </div>
168 </section>
169
170 <!-- Patterns de performance -->
171 <section class="granary-section">
172 <h2 class="section-title">
173 <span class="hieroglyph">𓉐</span>
174 Performance - Greniers à Grain
175 </h2>
176 <div class="performance-grid">
177 <div class="performance-metric">
178 <div class="metric-header">
179 <span class="metric-icon">■</span>
180 <h3>Caching</h3>
181 </div>
182 <div class="metric-strategies">
183 <span class="strategy-badge">LRU</span>
184 <span class="strategy-badge">TTL</span>
185 <span class="strategy-badge">Write-through</span>
186 <span class="strategy-badge">Cache-aside</span>
187 </div>
188 <div class="metric-bar">
189 <div class="metric-fill metric-fill--high"></div>
190 </div>
191 </div>
192
193 <div class="performance-metric">
194 <div class="metric-header">
195 <span class="metric-icon">◆</span>
196 <h3>Load Balancing</h3>
197 </div>
198 <div class="metric-strategies">
199 <span class="strategy-badge">Round Robin</span>
200 <span class="strategy-badge">Least Conn</span>
201 <span class="strategy-badge">IP Hash</span>
202 </div>
203 <div class="metric-bar">
204 <div class="metric-fill metric-fill--high"></div>
205 </div>
206 </div>
207
208 <div class="performance-metric">
209 <div class="metric-header">
210 <span class="metric-icon">◆</span>
211 <h3>Database Sharding</h3>
212 </div>
213 <div class="metric-strategies">
214 <span class="strategy-badge">Geographic</span>
215 <span class="strategy-badge">Hash-based</span>
216 <span class="strategy-badge">Range-based</span>
217 </div>
218 <div class="metric-bar">
219 <div class="metric-fill metric-fill--medium"></div>
220 </div>
221 </div>
222
223 <div class="performance-metric">
224 <div class="metric-header">
225 <span class="metric-icon">◆</span>
226 <h3>Optimisation</h3>
227 </div>
228 <div class="metric-strategies">
229 <span class="strategy-badge">Profiling</span>
230 <span class="strategy-badge">Indexing</span>
231 <span class="strategy-badge">Compression</span>
232 </div>
233 <div class="metric-bar">
234 <div class="metric-fill metric-fill--high"></div>
235 </div>
236 </div>
237 </div>
238 </section>
239
240 <!-- Couches de sécurité -->
241 <section class="fortress-section">
242 <h2 class="section-title">
243 <span class="hieroglyph">𓉐</span>
244 Sécurité - Fortifications du Pharaon
245 </h2>
246 <div class="security-layers">
247 <div class="security-layer security-layer--authentication">
248 <div class="layer-number">1</div>
249 <div class="layer-details">
250 <h3>Authentication</h3>
251 <p>Authentification de l'identité</p>
252 <div class="security-badges">
253 <span class="sec-badge">JWT</span>
254 <span class="sec-badge">OAuth 2.0</span>
255 <span class="sec-badge">SSO</span>
256 </div>
257 </div>
258 </div>
259
260 <div class="security-layer security-layer--authorization">
261 <div class="layer-number">2</div>
262 <div class="layer-details">
263 <h3>Authorization</h3>
264 <p>Contrôle d'accès et permissions</p>
265 <div class="security-badges">
266 <span class="sec-badge">RBAC</span>
267 <span class="sec-badge">ABAC</span>
268 <span class="sec-badge">ACL</span>
269 </div>
270 </div>
271 </div>
272
273 <div class="security-layer security-layer--validation">
274 <div class="layer-number">3</div>
275 <div class="layer-details">
276 <h3>Input Validation</h3>
277 <p>Validation et assainissement des données</p>
278 <div class="security-badges">
279 <span class="sec-badge">XSS Protection</span>
280 <span class="sec-badge">SQL Injection</span>
281 <span class="sec-badge">CSRF</span>
282 </div>
283 </div>
284 </div>
285
286 <div class="security-layer security-layer--encryption">
287 <div class="layer-number">4</div>
288 <div class="layer-details">
289 <h3>Data Encryption</h3>
290 <p>Chiffrement des données</p>
291 <div class="security-badges">
292 <span class="sec-badge">TLS/SSL</span>
293 <span class="sec-badge">AES-256</span>
294 <span class="sec-badge">RSA</span>
295 </div>
296 </div>
297 </div>
298
299 <div class="security-layer security-layer--monitoring">
300 <div class="layer-number">5</div>
301 <div class="layer-details">
302 <h3>Audit & Monitoring</h3>
303 <p>Journalisation et surveillance de la sécurité</p>
304 <div class="security-badges">
305 <span class="sec-badge">SIEM</span>
306 <span class="sec-badge">IDS/IPS</span>
307 <span class="sec-badge">Alerting</span>
308 </div>
309 </div>
310 </div>
311 </div>
312 </section>
313
314 <!-- Pied de page avec bénédiction -->
315 <footer class="pharaoh-footer">
316 <div class="footer-content">
317 <p class="blessing">
318 <span class="ankh-symbol">𓋹</span>
319 Bénédiction de Thot - Dieu de la Sagesse et de l'Écriture
320 <span class="ankh-symbol">𓋹</span>
321 </p>
322 <p class="footer-quote">
323 "Une architecture digne des pyramides de Gizeh - durable et monumentale pour des millénaires"
324 </p>
325 </div>
326 </footer>
327 </div>
328</body>
329</html>Souviens-toi : Ce n'est que le début de ton voyage ! Ces fondations te permettront de construire des projets de plus en plus grands et avancés. Bonne chance, jeune pharaon de l'internet !
Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Que fait la pseudo-classe :has() en CSS ?
Tâches pratiques dans le jeu
- Clique dans l'ordre
Mets dans l'ordre la syntaxe du sélecteur :has() pour styliser un parent qui contient une image :
- Éditeur de code
Crée : article:has(img) { border: 2px solid gold }, article:has(video) { background: #1a1a2e }, article:not(:has(img, video)) { opacity: 0.7 }. Teste avec différents contenus - le parent réagit à ses enfants !