Cours HTML et CSS · Module 9: CSS moderne

Projet final : Landing page réactive « Aventure égyptienne »

16 min de lecture
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 :

  1. HTML sémantique - structure accessible
  2. CSS Grid et Flexbox - mises en page avancées
  3. Container Queries - composants réactifs
  4. CSS moderne - dernières fonctionnalités CSS
  5. Animations - transitions et effets fluides
  6. Performance - optimisation et bonnes pratiques

Structure du projet

Sections de la page :

  1. Section Hero - bannière principale avec CTA (Call To Action)
  2. Atouts - cartes de fonctionnalités utilisant les container queries
  3. Circuits - galerie de circuits en CSS Grid
  4. Témoignages - avis clients avec carrousel
  5. Tarifs - section des prix
  6. Formulaire de contact - formulaire de contact
  7. 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>&copy; 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. 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 !

Articles utiles