Projekt Końcowy: Responsywna Strona Landing Page "Egipska Przygoda"
Kurs HTML i CSS · Moduł 9: Nowoczesny CSS
Witaj w ostatniej komnacie naszej egipskiej podróży! Jestem Mohamed i dzisiaj staniemy przed ostatecznym wyzwaniem: stworzeniem kompletnej, profesjonalnej strony landing page, która połączy WSZYSTKIE umiejętności zdobyte w tym module! Znasz już dziesiątki narzędzi, ale każde ćwiczyłeś osobno. Prawdziwy sprawdzian zaczyna się wtedy, gdy siatka, kontenery, formularz i dostępność muszą zagrać razem na jednej stronie.
Podobnie jak budowa Wielkiej Piramidy w Gizie wymagała połączenia wszystkich umiejętności starożytnych budowniczych, od podstaw po najbardziej zaawansowane techniki, tak i Twój projekt końcowy będzie syntezą całej wiedzy o HTML i CSS!
Cel projektu
Stworzysz responsywną stronę landing page dla fikcyjnej agencji turystycznej "Egipska Przygoda", która organizuje wycieczki do starożytnego Egiptu. Strona będzie wykorzystywać:
- HTML semantyczny - dostępna struktura
- CSS Grid i Flexbox - zaawansowane layouty
- Container Queries - responsywne komponenty
- Modern CSS - najnowsze funkcje CSS
- Animacje - płynne przejścia i efekty
- Performance - optymalizacja i najlepsze praktyki
Struktura projektu
Sekcje strony:
- Hero Section - główny baner z CTA (Call To Action)
- Features - karty z features używające container queries
- Tours - galeria wycieczek w CSS Grid
- Testimonials - opinie klientów z carousel
- Pricing - sekcja cenowa
- Contact Form - formularz kontaktowy
- Footer - stopka z social links
Wymagania techniczne
1. HTML Semantyczny
Zaczynamy od szkieletu. Nagłówek z nawigacją, sekcje z identyfikatorami, karty w elementach article, opinie w blockquote i formularz z etykietami. Kod jest długi, więc czytaj go sekcja po sekcji:
1<!DOCTYPE html>
2<html lang="pl">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <meta name="description" content="Odkryj tajemnice starożytnego Egiptu z Egipską Przygodą - najlepsza agencja turystyczna">
7 <title>Egipska Przygoda - Wycieczki do Starożytnego Egiptu</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="Główna nawigacja">
14 <div class="container">
15 <a href="#" class="logo">
16 <span class="logo-icon"></span>
17 <span class="logo-text">Egipska Przygoda</span>
18 </a>
19 <ul class="nav-list">
20 <li><a href="#features">Dlaczego my</a></li>
21 <li><a href="#tours">Wycieczki</a></li>
22 <li><a href="#testimonials">Opinie</a></li>
23 <li><a href="#pricing">Cennik</a></li>
24 <li><a href="#contact" class="btn-primary">Kontakt</a></li>
25 </ul>
26 </div>
27 </nav>
28 </header>
29
30 <!-- Hero Section -->
31 <section class="hero" id="hero">
32 <div class="hero-content">
33 <h1 class="hero-title">
34 Odkryj Tajemnice<br>
35 <span class="gradient-text">Starożytnego Egiptu</span>
36 </h1>
37 <p class="hero-subtitle">
38 Przenieś się w czasie o 5000 lat wstecz i poznaj cuda faraonów,
39 piramid i hieroglifów w niezapomnianej podróży życia!
40 </p>
41 <div class="hero-cta">
42 <a href="#tours" class="btn btn-large btn-primary">
43 Przeglądaj Wycieczki
44 </a>
45 <a href="#contact" class="btn btn-large btn-secondary">
46 Skontaktuj się z nami
47 </a>
48 </div>
49 </div>
50 <div class="hero-image">
51 <div class="pyramid-illustration"></div>
52 </div>
53 </section>
54
55 <!-- Features -->
56 <section class="features" id="features">
57 <div class="container">
58 <h2 class="section-title">Dlaczego Egipska Przygoda?</h2>
59 <p class="section-subtitle">
60 Jesteśmy liderami w organizacji wycieczek do Egiptu od 20 lat
61 </p>
62
63 <div class="features-grid">
64 <article class="feature-card">
65 <div class="feature-icon"></div>
66 <h3>Ekskluzywny Dostęp</h3>
67 <p>Dostęp do miejsc niedostępnych dla zwykłych turystów,
68 w tym prywatne zwiedzanie komnat piramid</p>
69 </article>
70
71 <article class="feature-card">
72 <div class="feature-icon"></div>
73 <h3>Eksperci Egiptologii</h3>
74 <p>Przewodnicy z tytułami naukowymi, pasjonaci historii
75 starożytnego Egiptu</p>
76 </article>
77
78 <article class="feature-card">
79 <div class="feature-icon"></div>
80 <h3>Luksusowe Hotele</h3>
81 <p>Noclegi w najlepszych hotelach 5*, z widokiem na Nil
82 i piramidy</p>
83 </article>
84
85 <article class="feature-card">
86 <div class="feature-icon"></div>
87 <h3>Autentyczna Kuchnia</h3>
88 <p>Degustacja tradycyjnych egipskich potraw przygotowanych
89 przez lokalnych szefów kuchni</p>
90 </article>
91 </div>
92 </div>
93 </section>
94
95 <!-- Tours Grid -->
96 <section class="tours" id="tours">
97 <div class="container">
98 <h2 class="section-title">Nasze Wycieczki</h2>
99 <p class="section-subtitle">
100 Wybierz wycieczkę dopasowaną do Twoich marzeń
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="Piramidy w Gizie">
107 <span class="tour-badge">Bestseller</span>
108 </div>
109 <div class="tour-content">
110 <h3>Klasyczny Egipt</h3>
111 <p class="tour-duration">⏱7 dni / 6 nocy</p>
112 <p class="tour-description">
113 Kairo, Giza, Luksor, Dolina Królów, rejs po Nilu
114 </p>
115 <ul class="tour-highlights">
116 <li>Wielka Piramida w Gizie</li>
117 <li>Muzeum Egipskie w Kairze</li>
118 <li>Świątynia Karnak</li>
119 <li>Rejs statkiem po Nilu</li>
120 </ul>
121 <div class="tour-footer">
122 <div class="tour-price">
123 <span class="price-from">od</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">Rezerwuj</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="Abu Simbel">
135 </div>
136 <div class="tour-content">
137 <h3>Egipskie Cuda</h3>
138 <p class="tour-duration">⏱10 dni / 9 nocy</p>
139 <p class="tour-description">
140 Wszystkie główne atrakcje + Abu Simbel i Alexandria
141 </p>
142 <ul class="tour-highlights">
143 <li>Wszystko z Klasycznego Egiptu</li>
144 <li>Świątynie Abu Simbel</li>
145 <li>Biblioteka Aleksandryjska</li>
146 <li>Oaza Siwa</li>
147 </ul>
148 <div class="tour-footer">
149 <div class="tour-price">
150 <span class="price-from">od</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">Rezerwuj</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="Nurkowanie w Morzu Czerwonym">
162 </div>
163 <div class="tour-content">
164 <h3>Egipt + Morze Czerwone</h3>
165 <p class="tour-duration">⏱12 dni / 11 nocy</p>
166 <p class="tour-description">
167 Historia + relaks i nurkowanie w Hurghadzie
168 </p>
169 <ul class="tour-highlights">
170 <li>Zwiedzanie zabytków</li>
171 <li>5 dni all inclusive w Hurghadzie</li>
172 <li>Nurkowanie z rafa koralowa</li>
173 <li>Safari na pustyni</li>
174 </ul>
175 <div class="tour-footer">
176 <div class="tour-price">
177 <span class="price-from">od</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">Rezerwuj</a>
182 </div>
183 </div>
184 </article>
185 </div>
186 </div>
187 </section>
188
189 <!-- Testimonials -->
190 <section class="testimonials" id="testimonials">
191 <div class="container">
192 <h2 class="section-title">Co Mówią Nasi Klienci</h2>
193
194 <div class="testimonials-grid">
195 <blockquote class="testimonial-card">
196 <div class="testimonial-rating"></div>
197 <p class="testimonial-text">
198 "Niesamowite przeżycie! Przewodnicy byli niezwykle kompetentni,
199 a organizacja wycieczki na najwyższym poziomie. Piramidy na żywo
200 robią wrażenie tysiąckrotnie większe niż na zdjęciach!"
201 </p>
202 <cite class="testimonial-author">
203 <strong>Anna Kowalska</strong>
204 <span>Warszawa</span>
205 </cite>
206 </blockquote>
207
208 <blockquote class="testimonial-card">
209 <div class="testimonial-rating"></div>
210 <p class="testimonial-text">
211 "Jako egiptolog byłem sceptyczny, ale ta wycieczka przekroczyła
212 moje oczekiwania. Dostęp do miejsc, których nie pokazują w
213 standardowych wycieczpach, był bezcenny!"
214 </p>
215 <cite class="testimonial-author">
216 <strong>Dr. Jan Nowak</strong>
217 <span>Kraków</span>
218 </cite>
219 </blockquote>
220
221 <blockquote class="testimonial-card">
222 <div class="testimonial-rating"></div>
223 <p class="testimonial-text">
224 "Podróż życia! Szczególnie zachwycił mnie rejs po Nilu i
225 zwiedzanie Doliny Królów. Wszystko było perfekcyjnie zorganizowane."
226 </p>
227 <cite class="testimonial-author">
228 <strong>Maria Wiśniewska</strong>
229 <span>Gdańsk</span>
230 </cite>
231 </blockquote>
232 </div>
233 </div>
234 </section>
235
236 <!-- Contact Form -->
237 <section class="contact" id="contact">
238 <div class="container">
239 <div class="contact-grid">
240 <div class="contact-info">
241 <h2>Zarezerwuj Swoją Przygodę</h2>
242 <p>Wypełnij formularz, a nasz konsultant skontaktuje się z Tobą
243 w ciągu 24 godzin!</p>
244
245 <div class="contact-details">
246 <div class="contact-item">
247 <span class="contact-icon"></span>
248 <div>
249 <strong>Telefon</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>kontakt@egipskaprzygoda.pl</p>
258 </div>
259 </div>
260 <div class="contact-item">
261 <span class="contact-icon"></span>
262 <div>
263 <strong>Adres</strong>
264 <p>ul. Piramid 123, 00-001 Warszawa</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">Imię i nazwisko</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">Telefon</label>
283 <input type="tel" id="phone" name="phone" required>
284 </div>
285
286 <div class="form-group">
287 <label for="tour">Wybierz wycieczkę</label>
288 <select id="tour" name="tour" required>
289 <option value="">Wybierz...</option>
290 <option value="classic">Klasyczny Egipt - 7 dni</option>
291 <option value="wonders">Egipskie Cuda - 10 dni</option>
292 <option value="red-sea">Egipt + Morze Czerwone - 12 dni</option>
293 </select>
294 </div>
295
296 <div class="form-group">
297 <label for="message">Wiadomość (opcjonalnie)</label>
298 <textarea id="message" name="message" rows="4"></textarea>
299 </div>
300
301 <button type="submit" class="btn btn-primary btn-large">
302 Wyślij zapytanie
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>Egipska Przygoda</h3>
315 <p>Odkrywamy tajemnice starożytnego Egiptu od 2004 roku</p>
316 <div class="social-links">
317 <a href="#" aria-label="Facebook"></a>
318 <a href="#" aria-label="Instagram"></a>
319 <a href="#" aria-label="Twitter"></a>
320 <a href="#" aria-label="YouTube"></a>
321 </div>
322 </div>
323
324 <div class="footer-section">
325 <h4>Wycieczki</h4>
326 <ul>
327 <li><a href="#">Klasyczny Egipt</a></li>
328 <li><a href="#">Egipskie Cuda</a></li>
329 <li><a href="#">Egipt + Morze Czerwone</a></li>
330 <li><a href="#">Wycieczki grupowe</a></li>
331 </ul>
332 </div>
333
334 <div class="footer-section">
335 <h4>Informacje</h4>
336 <ul>
337 <li><a href="#">O nas</a></li>
338 <li><a href="#">Regulamin</a></li>
339 <li><a href="#">Polityka prywatności</a></li>
340 <li><a href="#">FAQ</a></li>
341 </ul>
342 </div>
343
344 <div class="footer-section">
345 <h4>Kontakt</h4>
346 <ul>
347 <li>+48 123 456 789</li>
348 <li>kontakt@egipskaprzygoda.pl</li>
349 <li>ul. Piramid 123, Warszawa</li>
350 </ul>
351 </div>
352 </div>
353
354 <div class="footer-bottom">
355 <p>© 2024 Egipska Przygoda. Wszystkie prawa zastrzeżone.</p>
356 </div>
357 </div>
358 </footer>
359</body>
360</html>Każda sekcja ma własne id, dzięki czemu linki z nawigacji przewijają stronę do właściwego miejsca. Pola formularza są powiązane z etykietami przez for i id, a atrybut required blokuje wysłanie pustego pola. lang="pl" podpowiada czytnikom ekranu, w jakim języku czytać treść.
2. CSS - Kompletne style
Arkusz stylów dzielą komentarze na sekcje: reset i zmienne, nagłówek, hero, features, wycieczki i kolejne części strony. Paleta, odstępy i cienie żyją w zmiennych CSS na :root:
1/* ==========================================
2 CSS RESET & BASE STYLES
3 ========================================== */
4*, *::before, *::after {
5 margin: 0;
6 padding: 0;
7 box-sizing: border-box;
8}
9
10:root {
11 /* Colors - Egyptian theme */
12 --color-primary: #D4AF37; /* Gold */
13 --color-primary-dark: #B8941F;
14 --color-secondary: #2C3E50; /* Dark blue */
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 /* Spacing */
23 --spacing-xs: 0.5rem;
24 --spacing-sm: 1rem;
25 --spacing-md: 2rem;
26 --spacing-lg: 4rem;
27 --spacing-xl: 6rem;
28
29 /* Typography */
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 /* Layout */
39 --container-max-width: 1200px;
40 --border-radius: 12px;
41
42 /* Shadows */
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 UTILITIES
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/* Buttons */
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 HERO SECTION
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 FEATURES SECTION
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 dla feature cards */
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 TOURS SECTION
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 TESTIMONIALS SECTION
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 CONTACT SECTION
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 RESPONSIVE DESIGN
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}Zwróć uwagę na trzy miejsca. Siatka features jest kontenerem, więc karty zmieniają odstępy przez @container. Wyróżniona wycieczka zajmuje dwie kolumny, a poniżej 768px wraca do jednej. Zdjęcia wycieczek trzymają proporcje dzięki aspect-ratio: 16 / 9.
3. Oko Horusa: selektor :has()
Brakuje jeszcze jednego narzędzia. Pseudoklasa :has() wybiera element, jeśli zawiera określone dziecko, więc rodzic może reagować na to, co ma w środku, jak oko Horusa zaglądające do wnętrza komnaty:
1/* Karta z plakietką (np. Bestseller) dostaje złotą obwódkę */
2.tour-card:has(.tour-badge) {
3 outline: 3px solid var(--color-primary);
4}
5
6/* Etykieta pola, które użytkownik wypełnił błędnie */
7.form-group:has(input:user-invalid) label {
8 color: #c0392b;
9}
10
11/* Artykuł, którego bezpośrednim dzieckiem jest obraz */
12article:has(> img) {
13 border: 2px solid gold;
14}Obwódkę dostanie tylko karta z plakietką Bestseller, bo pozostałe jej nie mają. :user-invalid zadziała dopiero po interakcji z polem, więc pusty formularz nie świeci na czerwono od progu. Zapis > img oznacza bezpośrednie dziecko, a article:not(:has(img, video)) wybierze artykuły bez zdjęć i filmów. :has() działa we wszystkich głównych przeglądarkach od końca 2023 roku.
Wskazówki implementacji
1. Performance
Obrazy poniżej pierwszego ekranu ładuj leniwie, a kluczowe zasoby zapowiadaj przeglądarce wcześniej:
1<!-- Lazy loading dla obrazów -->
2<img src="image.jpg" alt="Description" loading="lazy">
3
4<!-- Preload dla kluczowych zasobów -->
5<link rel="preload" as="image" href="hero-image.jpg">Uważaj na jedno: obrazu w sekcji hero nie ładuj leniwie, bo widać go od razu i opóźniłbyś jego wyświetlenie. Właśnie dla niego jest preload.
2. Accessibility
Semantyczne znaczniki i etykiety ARIA pomagają czytnikom ekranu zrozumieć stronę:
1<!-- Używaj semantycznych znaczników -->
2<nav role="navigation" aria-label="Główna nawigacja">
3<main role="main">
4<footer role="contentinfo">
5
6<!-- Dodaj aria-labels tam gdzie potrzeba -->
7<button aria-label="Zamknij modal">×</button>Role w tym przykładzie są nadmiarowe, bo nav, main i footer mają je domyślnie. Zasada brzmi: nie dodawaj roli, którą element już ma. aria-label jest za to niezbędny przy przyciskach z samą ikoną.
3. SEO
Znaczniki meta opisują stronę wyszukiwarkom i serwisom społecznościowym:
1<!-- Meta tags dla SEO -->
2<meta name="description" content="Opis strony">
3<meta name="keywords" content="egipt, wycieczki, piramidy">
4<meta property="og:title" content="Egipska Przygoda">
5<meta property="og:description" content="Najlepsze wycieczki do Egiptu">
6<meta property="og:image" content="og-image.jpg">Opis z description często trafia do wyników wyszukiwania, a znaczniki Open Graph budują podgląd linku. keywords możesz pominąć, bo Google nie bierze go pod uwagę od 2009 roku.
Podsumowanie
Gratulacje, młody budowniczy! Stworzyłeś kompletną, profesjonalną stronę landing page, która łączy:
HTML semantyczny - dostępna struktura CSS Grid & Flexbox - zaawansowane layouty Container Queries - responsywne komponenty Modern CSS - najnowsze funkcje Animacje - płynne efekty Performance - optymalizacja
Podobnie jak budowa Wielkiej Piramidy w Gizie była kulminacją wiedzy starożytnych budowniczych, tak Twój projekt końcowy jest syntezą wszystkiego, czego nauczyłeś się w tym module! W zadaniu końcowym zbudujesz jeszcze egipski dashboard, w którym połączysz Container Queries, :has(), zagnieżdżanie, @layer, właściwości logiczne, clamp(), aspect-ratio, :is() i :where(), scroll-snap, color-mix(), @supports oraz accent-color. Moja rada: dodawaj je po jednej i po każdej sprawdzaj stronę w dwóch przeglądarkach. W piaskownicy poniżej znajdziesz egipski dashboard, na którym przećwiczysz te techniki.
Kod do tej lekcji: 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>Master Architecture Dashboard Totha</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">Master Architecture Dashboard Totha</h1>
15 <span class="eye-of-horus">𓂀</span>
16 </div>
17 <p class="header-subtitle">Summa całej mądrości architektury oprogramowania z Nekropolii w Gizie</p>
18 </header>
19
20 <!-- Clean Architecture Layers -->
21 <section class="pyramid-section">
22 <h2 class="section-title">
23 <span class="hieroglyph"></span>
24 Clean Architecture - Hierarchia Świątyni
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>Encje Biznesowe</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>Przypadki Użycia</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>Adaptery Interfejsów</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>Frameworki i Sterowniki</p>
52 <span class="layer-icon"></span>
53 </div>
54 </div>
55 </div>
56 </section>
57
58 <!-- Design Patterns Categories -->
59 <section class="papyrus-section">
60 <h2 class="section-title">
61 <span class="hieroglyph"></span>
62 Wzorce Projektowe - Zwoje Mądrości
63 </h2>
64 <div class="patterns-grid">
65 <article class="pattern-category pattern-category--creational">
66 <div class="pattern-header">
67 <h3>Creational Patterns</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>Structural Patterns</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>Behavioral Patterns</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>Architectural Patterns</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 <!-- Microservices Patterns -->
120 <section class="obelisk-section">
121 <h2 class="section-title">
122 <span class="hieroglyph"></span>
123 Mikrousługi - Obeliski Imperium
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>Brama Pałacu</p>
130 <div class="card-status status--active">Active</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>Mapa Imperium</p>
137 <div class="card-status status--active">Active</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>Strażnik Bramy</p>
144 <div class="card-status status--active">Active</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>Kroniki Wydarzeń</p>
151 <div class="card-status status--active">Active</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>History Królestwa</p>
158 <div class="card-status status--active">Active</div>
159 </div>
160
161 <div class="microservice-card microservice-card--cqrs">
162 <div class="card-icon"></div>
163 <h3>CQRS</h3>
164 <p>Podział Władzy</p>
165 <div class="card-status status--active">Active</div>
166 </div>
167 </div>
168 </section>
169
170 <!-- Performance Patterns -->
171 <section class="granary-section">
172 <h2 class="section-title">
173 <span class="hieroglyph"></span>
174 Performance - Magazyny Zbożowe
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>Optimization</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 <!-- Security Layers -->
241 <section class="fortress-section">
242 <h2 class="section-title">
243 <span class="hieroglyph"></span>
244 Security - Fortyfikacje Faraona
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>Uwierzytelnienie tożsamości</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>Kontrola dostępu i uprawnień</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>Walidacja i sanityzacja danych</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>Encryption danych</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>Logowanie i monitoring nopieczeństwa</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 <!-- Footer with blessing -->
315 <footer class="pharaoh-footer">
316 <div class="footer-content">
317 <p class="blessing">
318 <span class="ankh-symbol"></span>
319 Błogosławieństwo Totha - Boga Mądrości i Pisma
320 <span class="ankh-symbol"></span>
321 </p>
322 <p class="footer-quote">
323 "Architektura godna pyramids Gizy - trwała i monumentalna przez tysiąclecia"
324 </p>
325 </div>
326 </footer>
327 </div>
328</body>
329</html>Pamiętaj: To dopiero początek Twojej podróży! Te fundamenty pozwolą Ci budować coraz większe i bardziej zaawansowane projekty. Powodzenia, młody faraonie internetu!
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Co robi pseudo-klasa :has() w CSS?
Zadania praktyczne w grze
- Klikanie w kolejności
Ułóż składnię selektora :has() dla stylowania rodzica zawierającego obrazek:
- Edytor kodu
Stwórz: article:has(img) { border: 2px solid gold }, article:has(video) { background: #1a1a2e }, article:not(:has(img, video)) { opacity: 0.7 }. Testuj z różną zawartością - rodzic reaguje na dzieci!