HTML5 i CSS – Budowanie szkieletu i skóry aplikacji
1. Od dokumentu do aplikacji: Ewolucja HTML
Na Wykładzie 1 poznaliśmy, że Frontend to kod działający w przeglądarce użytkownika. Widzieliśmy prosty szkielet HTML i wiemy, że przeglądarka rozumie trzy języki: HTML, CSS i JavaScript.
Teraz pora zagłębić się w pierwszy z nich. HTML (HyperText Markup Language) przeszedł długą drogę:
- HTML 1.0 (1993): Proste dokumenty tekstowe z linkami. Internet jako cyfrowa biblioteka.
- HTML 4.01 (1999): Tabele, formularze, ramki. Strony zaczęły wyglądać jak “aplikacje”, ale kod był brzydki i chaotyczny (
<font>,<center>, tabele do layoutu). - XHTML (2000): Próba narzucenia ścisłych reguł XML na HTML. Każdy niezamknięty tag był błędem. Zbyt surowe podejście.
- HTML5 (2014–dziś): Rewolucja. Nowe elementy semantyczne, wbudowane multimedia, API do geolokalizacji, rysowania (Canvas), przechowywania danych (Web Storage). HTML5 to nie tylko język znaczników — to cała platforma do budowy aplikacji.
Kluczowa zmiana filozofii: HTML5 nie wymusza na programiście perfekcyjnej składni (jak XHTML), ale zachęca do pisania kodu semantycznego — takiego, który swoją strukturą mówi przeglądarce i wyszukiwarce, czym jest dany fragment treści.
2. Anatomia dokumentu HTML5
Każdy plik HTML zaczyna się od tego samego szkieletu. Przypomnijmy go z Wykładu 1 i omówmy dokładnie:
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Moja aplikacja</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Witaj na stronie!</h1>
<p>To jest akapit tekstu.</p>
</body>
</html>Co robi każda linia?
<!DOCTYPE html>— Deklaracja typu dokumentu. Mówi przeglądarce: “Traktuj mnie jak HTML5”. Bez tego przeglądarka wchodzi w tzw. quirks mode i renderuje stronę nieprzewidywalnie.<html lang="pl">— Element główny. Atrybutlanginformuje wyszukiwarki i czytniki ekranowe, w jakim języku jest treść.<meta charset="UTF-8">— Kodowanie znaków. Bez tego polskie litery (ą, ę, ś) mogą wyświetlić się jako “krzaczki”.<meta name="viewport" ...>— Kluczowa linia dla urządzeń mobilnych. Bez niej telefon wyświetli stronę jakby miał ekran desktopowy i zmniejszy ją do rozmiaru znaczka pocztowego.<head>— Sekcja niewidoczna dla użytkownika. Metadane, tytuł w zakładce, linki do arkuszy CSS i skryptów.<body>— Wszystko, co użytkownik widzi na ekranie.
3. Elementy semantyczne HTML5: Kod, który ma znaczenie
W starym HTML cała strona składała się z <div> (kontener blokowy) i <span> (kontener liniowy). Kod wyglądał tak:
<!-- ❌ Stare podejście: "Divitis" — choroba nadmiarowych divów -->
<div id="header">
<div id="nav">...</div>
</div>
<div id="content">
<div class="post">...</div>
<div id="sidebar">...</div>
</div>
<div id="footer">...</div>Problem? Przeglądarka, wyszukiwarka Google i czytnik ekranowy dla osoby niewidomej widzą jedynie stos anonimowych “pudełek” (div). Nie wiedzą, co jest menu, co artykułem, a co stopką.
HTML5 wprowadził elementy semantyczne — tagi, które opisują swoją rolę:
<!-- ✅ HTML5: Kod, który mówi, czym jest -->
<header>
<nav>
<a href="/">Start</a>
<a href="/about">O nas</a>
<a href="/contact">Kontakt</a>
</nav>
</header>
<main>
<article>
<h1>Tytuł artykułu</h1>
<section>
<h2>Sekcja pierwsza</h2>
<p>Treść sekcji...</p>
</section>
<section>
<h2>Sekcja druga</h2>
<p>Kolejna treść...</p>
</section>
</article>
<aside>
<h3>Powiązane artykuły</h3>
<ul>
<li><a href="/art2">Inny artykuł</a></li>
</ul>
</aside>
</main>
<footer>
<p>© 2026 Moja Firma</p>
</footer>Każdy z tych tagów ma konkretne znaczenie:
| Tag | Rola | Uwagi |
|---|---|---|
<header> |
Nagłówek strony lub sekcji | Logo, nawigacja, tytuł |
<nav> |
Blok nawigacyjny | Menu główne, breadcrumbs |
<main> |
Główna treść strony | Tylko jeden na stronę |
<article> |
Samodzielna treść | Post, artykuł, komentarz |
<section> |
Sekcja tematyczna | Grupuje powiązaną treść |
<aside> |
Treść poboczna | Sidebar, reklamy, linki |
<footer> |
Stopka | Copyright, kontakt, linki |
Dlaczego to ważne?
- SEO: Google lepiej rozumie strukturę strony i wyżej ją pozycjonuje.
- Dostępność (a11y): Czytniki ekranowe mogą powiedzieć: “Nawigacja — 3 linki”, “Artykuł — Tytuł artykułu”.
- Czytelność kodu: Programista od razu widzi architekturę strony, bez czytania klas CSS.
4. Formularze HTML5: Walidacja za darmo
Na Wykładzie 1 widzieliśmy, jak JavaScript wysyła dane przez fetch. Ale zanim dane dotrą do serwera, użytkownik musi je gdzieś wpisać. Do tego służą formularze.
HTML5 drastycznie ulepszyło formularze, dodając nowe typy pól i wbudowaną walidację — bez jednej linii JavaScriptu:
<form action="/api/rejestracja" method="POST">
<!-- Pole tekstowe z walidacją e-mail -->
<label for="email">E-mail:</label>
<input type="email" id="email" name="email"
required placeholder="jan@example.com">
<!-- Pole numeryczne z zakresem -->
<label for="wiek">Wiek:</label>
<input type="number" id="wiek" name="wiek"
min="18" max="120" required>
<!-- Hasło z minimalną długością -->
<label for="haslo">Hasło:</label>
<input type="password" id="haslo" name="haslo"
minlength="8" required>
<!-- Wybór daty -->
<label for="data">Data urodzenia:</label>
<input type="date" id="data" name="data_urodzenia">
<!-- Pole z wyrażeniem regularnym -->
<label for="kod">Kod pocztowy:</label>
<input type="text" id="kod" name="kod_pocztowy"
pattern="[0-9]{2}-[0-9]{3}" placeholder="00-000"
title="Format: 00-000">
<!-- Suwak -->
<label for="ocena">Ocena (1-10):</label>
<input type="range" id="ocena" name="ocena" min="1" max="10">
<button type="submit">Zarejestruj się</button>
</form>Przeglądarka sama sprawdzi, czy:
- pole
requirednie jest puste, type="email"zawiera@,min/maxjest w zakresie,patternpasuje do wyrażenia regularnego,minlengthjest spełnione.
Użytkownik zobaczy komunikat błędu w swoim języku — i to bez JavaScriptu.
Ważna uwaga z Wykładu 1: Walidacja po stronie klienta (HTML/JS) to wygoda dla użytkownika. Walidacja po stronie serwera to bezpieczeństwo. Klientowi nigdy nie ufamy — ktoś może obejść HTML i wysłać dowolne dane bezpośrednio do naszego API (np. przez Postman).
5. Multimedia bez wtyczek
Pamiętacie Adobe Flash? Kiedyś bez niego nie dało się odtworzyć filmiku w przeglądarce. Flash był ciężki, dziurawy jak ser szwajcarski (luki bezpieczeństwa) i nie działał na iPhone’ach. HTML5 rozwiązało ten problem raz na zawsze:
<!-- Audio -->
<audio controls>
<source src="podcast.mp3" type="audio/mpeg">
<source src="podcast.ogg" type="audio/ogg">
Twoja przeglądarka nie wspiera odtwarzania audio.
</audio>
<!-- Video -->
<video controls width="640" poster="miniatura.jpg">
<source src="wyklad.mp4" type="video/mp4">
<source src="wyklad.webm" type="video/webm">
Twoja przeglądarka nie wspiera odtwarzania wideo.
</video>Dwie linie kodu — pełny odtwarzacz multimedialny z przyciskami play/pause, paskiem postępu i regulacją głośności. Atrybut poster wyświetla miniaturę przed odtworzeniem.
Dlaczego wiele elementów <source>? Nie każda przeglądarka obsługuje każdy format. Przeglądarka sama wybierze pierwszy format, który potrafi odtworzyć.
6. Web Storage: Dane w przeglądarce
Na Wykładzie 1 mówiliśmy, że serwer jest bezstanowy (Statelessness w REST). Ale co jeśli Frontend chce zapamiętać coś lokalnie — np. motyw ciemny/jasny albo zawartość koszyka przed zalogowaniem?
// localStorage — dane przetrwają zamknięcie przeglądarki
localStorage.setItem('motyw', 'ciemny');
const motyw = localStorage.getItem('motyw'); // 'ciemny'
localStorage.removeItem('motyw');
// sessionStorage — dane znikają po zamknięciu karty
sessionStorage.setItem('koszyk', JSON.stringify(['Laptop', 'Myszka']));
const koszyk = JSON.parse(sessionStorage.getItem('koszyk'));Ograniczenia: Dane przechowywane wyłącznie w przeglądarce użytkownika (ok. 5–10 MB). Nie zastąpią bazy danych na serwerze. Nigdy nie przechowuj tu haseł ani tokenów sesji (podatność na ataki XSS).
7. CSS: Od chaosu do systemu
Wiemy już, że HTML definiuje co jest na stronie. CSS (Cascading Style Sheets) definiuje jak to wygląda. Ale CSS to nie tylko “zmień kolor na czerwony” — to potężny system layoutu.
Trzy sposoby dołączania CSS
<!-- 1. Inline — bezpośrednio w elemencie (❌ unikaj!) -->
<p style="color: red; font-size: 18px;">Tekst</p>
<!-- 2. Wewnętrzny — w sekcji <head> (dopuszczalny dla małych stron) -->
<head>
<style>
p { color: red; }
</style>
</head>
<!-- 3. Zewnętrzny plik — ✅ najlepsza praktyka -->
<head>
<link rel="stylesheet" href="style.css">
</head>Zewnętrzny plik CSS to standard w profesjonalnych projektach. Jeden plik stylów obsługuje setki stron HTML. Zmiana koloru w jednym miejscu aktualizuje cały serwis.
Selektory — targetowanie elementów
Selektor to “celownik”, który mówi CSS: “Zastosuj te style do TYCH elementów”.
/* Selektor elementu — wszystkie paragrafy */
p {
line-height: 1.6;
}
/* Selektor klasy — elementy z class="karta" */
.karta {
padding: 20px;
border: 1px solid #ddd;
}
/* Selektor ID — jeden konkretny element */
#logo {
width: 150px;
}
/* Selektor potomka — linki WEWNĄTRZ nav */
nav a {
text-decoration: none;
color: #333;
}
/* Pseudoklasa — stan po najechaniu myszką */
a:hover {
color: #0066cc;
text-decoration: underline;
}
/* Pseudoklasa — pierwszy element listy */
li:first-child {
font-weight: bold;
}
/* Selektor atrybutu */
input[type="email"] {
border: 2px solid #0066cc;
}Specyficzność: Kto wygrywa?
Co jeśli dwie reguły CSS mówią co innego o tym samym elemencie? Wygrywa ta z wyższą specyficznością. Można ją policzyć jak punkty:
| Selektor | Punkty | Przykład |
|---|---|---|
!important |
∞ (nadpisuje wszystko) | color: red !important |
| Inline style | 1000 | style="color:red" |
Selektor ID # |
100 | #header |
Klasa . / pseudoklasa |
10 | .karta, :hover |
| Element / pseudoelement | 1 | p, ::before |
/* Specyficzność: 1 (element) */
p { color: blue; }
/* Specyficzność: 10 (klasa) — WYGRYWA nad elementem */
.wazny { color: red; }
/* Specyficzność: 100 (ID) — WYGRYWA nad klasą */
#naglowek { color: green; }Praktyczna zasada: Unikaj !important i selektorów ID w CSS. Używaj klas — dają wystarczającą specyficzność i są reużywalne.
8. Box Model: Fundament layoutu
Każdy element HTML to prostokąt (nawet okrągły przycisk!). Ten prostokąt składa się z czterech warstw — od środka na zewnątrz:
- Content — sama treść (tekst, obraz)
- Padding — wewnętrzny odstęp (między treścią a ramką)
- Border — obramowanie
- Margin — zewnętrzny odstęp (między tym elementem a sąsiadami)
.karta {
/* Treść: 300px szerokości */
width: 300px;
/* Wewnętrzny odstęp: 20px z każdej strony */
padding: 20px;
/* Ramka: 2px */
border: 2px solid #333;
/* Zewnętrzny odstęp: 15px */
margin: 15px;
}Pytanie: Ile pikseli zajmuje ta karta na ekranie?
Domyślnie: 300 (content) + 20*2 (padding) + 2*2 (border) = 344px (margin się nie wlicza w rozmiar elementu, ale wpływa na odległość od sąsiadów).
To nieczytelne. Dlatego w każdym profesjonalnym projekcie dodaje się:
/* ✅ Zmiana modelu obliczania: width OBEJMUJE padding i border */
*, *::before, *::after {
box-sizing: border-box;
}Z box-sizing: border-box karta ma width: 300px i tyle zajmuje na ekranie — padding i border są wliczone w te 300px.
9. Flexbox: Jednowymiarowy układ
Przed Flexboxem centrowanie elementu w pionie wymagało “hacków” CSS (np. display: table-cell albo position: absolute z transform). Flexbox rozwiązuje to jedną linią.
Flexbox działa w jednym wymiarze — albo układasz elementy w wierszu (oś X), albo w kolumnie (oś Y).
/* Kontener — aktywuje Flexbox */
.navbar {
display: flex;
justify-content: space-between; /* Rozmieszczenie wzdłuż osi głównej */
align-items: center; /* Wyrównanie wzdłuż osi poprzecznej */
gap: 20px; /* Odstępy między elementami */
padding: 10px 20px;
}<nav class="navbar">
<a href="/" class="logo">MojaApp</a>
<div class="menu">
<a href="/about">O nas</a>
<a href="/contact">Kontakt</a>
</div>
<button>Zaloguj</button>
</nav>Najważniejsze właściwości kontenera flex:
| Właściwość | Co robi | Popularne wartości |
|---|---|---|
justify-content |
Rozmieszczenie na osi głównej | flex-start, center, space-between, space-around |
align-items |
Wyrównanie na osi poprzecznej | flex-start, center, stretch |
flex-direction |
Kierunek osi głównej | row (domyślny), column |
flex-wrap |
Zawijanie do nowej linii | nowrap (domyślny), wrap |
gap |
Odstępy między elementami | np. 20px, 1rem |
Właściwości elementów wewnątrz flex:
.element {
flex: 1; /* Rośnij, aby wypełnić dostępną przestrzeń */
min-width: 250px; /* Ale nie mniejszy niż 250px */
}Klasyczny przykład: Centrowanie w pionie i poziomie
.center-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh; /* Cała wysokość ekranu */
}Trzy linie CSS — element idealnie wycentrowany. Przed Flexboxem wymagało to 10+ linii hacków.
10. CSS Grid: Dwuwymiarowy układ
Flexbox to układ jednowymiarowy (wiersz LUB kolumna). CSS Grid to pełny dwuwymiarowy system — definiujesz zarówno wiersze, jak i kolumny jednocześnie.
.layout {
display: grid;
grid-template-columns: 250px 1fr 200px; /* 3 kolumny: sidebar, main, aside */
grid-template-rows: auto 1fr auto; /* 3 wiersze: header, content, footer */
gap: 20px;
min-height: 100vh;
}
.header { grid-column: 1 / -1; } /* Od pierwszej do ostatniej kolumny */
.footer { grid-column: 1 / -1; }
.sidebar { grid-row: 2; }
.main { grid-row: 2; }
.aside { grid-row: 2; }<div class="layout">
<header class="header">Nagłówek</header>
<nav class="sidebar">Menu</nav>
<main class="main">Treść główna</main>
<aside class="aside">Panel boczny</aside>
<footer class="footer">Stopka</footer>
</div>Bez float, bez position: absolute, bez hacków — czytelny, dwuwymiarowy układ strony.
Kiedy Flexbox, a kiedy Grid?
- Flexbox: Nawigacja, pasek narzędzi, lista kart, wyrównanie elementów w jednej linii.
- Grid: Cały layout strony, galerie zdjęć, dashboard z widgetami — wszędzie, gdzie myślisz “wiersze I kolumny”.
11. Responsywność: Media Queries
Twoja aplikacja musi działać na ekranie 6-calowego telefonu i 27-calowego monitora. Podejście “Mobile First” oznacza: piszesz style dla telefonu jako bazowe, a potem za pomocą Media Queries dodajesz reguły dla większych ekranów.
/* ===== BAZOWE STYLE (MOBILE) ===== */
.grid-produktow {
display: grid;
grid-template-columns: 1fr; /* 1 kolumna na telefonie */
gap: 15px;
padding: 10px;
}
/* ===== TABLET (768px i więcej) ===== */
@media (min-width: 768px) {
.grid-produktow {
grid-template-columns: repeat(2, 1fr); /* 2 kolumny */
gap: 20px;
padding: 20px;
}
}
/* ===== DESKTOP (1024px i więcej) ===== */
@media (min-width: 1024px) {
.grid-produktow {
grid-template-columns: repeat(3, 1fr); /* 3 kolumny */
max-width: 1200px;
margin: 0 auto; /* Wyśrodkuj na szerokim ekranie */
}
}
/* ===== DUŻY EKRAN (1440px i więcej) ===== */
@media (min-width: 1440px) {
.grid-produktow {
grid-template-columns: repeat(4, 1fr); /* 4 kolumny */
}
}To samo siatka produktów — 1 kolumna na telefonie, 2 na tablecie, 3 na desktopie, 4 na dużym monitorze. Bez JavaScriptu, bez osobnych stron “m.example.com”.
Jednostki responsywne
Zamiast sztywnych pikseli, używaj jednostek względnych:
| Jednostka | Odniesienie | Przykład użycia |
|---|---|---|
rem |
Rozmiar fontu <html> (domyślnie 16px) |
Fonty, padding, margin |
em |
Rozmiar fontu rodzica | Odstępy wewnątrz komponentu |
% |
Rozmiar rodzica | Szerokości, wysokości |
vw / vh |
1% szerokości / wysokości okna | Sekcje pełnoekranowe |
fr |
Ułamek wolnej przestrzeni (Grid) | Kolumny/wiersze Grid |
/* ❌ Sztywne piksele — nie skaluje się */
h1 { font-size: 32px; }
/* ✅ Jednostki względne — skaluje się z ustawieniami użytkownika */
h1 { font-size: 2rem; } /* 2 × 16px = 32px domyślnie */12. Praktyczny przykład: Karta produktu
Złóżmy wszystko razem — semantyczny HTML5 + nowoczesny CSS:
<article class="karta-produktu">
<img src="laptop.jpg" alt="Laptop ThinkPad X1 Carbon">
<div class="karta-tresc">
<h3>ThinkPad X1 Carbon</h3>
<p class="cena">5 499 zł</p>
<p class="opis">Lekki laptop biznesowy z 14" ekranem IPS.</p>
<button class="btn-kup">Dodaj do koszyka</button>
</div>
</article>.karta-produktu {
display: flex;
flex-direction: column;
border: 1px solid #e0e0e0;
border-radius: 8px;
overflow: hidden; /* Obcina rogi obrazka */
transition: transform 0.2s; /* Płynna animacja */
}
.karta-produktu:hover {
transform: translateY(-4px); /* Unosi się po najechaniu */
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
.karta-produktu img {
width: 100%;
height: 200px;
object-fit: cover; /* Przycina obraz proporcjonalnie */
}
.karta-tresc {
padding: 16px;
display: flex;
flex-direction: column;
flex: 1; /* Rozciąga się, by karty miały równą wysokość */
}
.cena {
font-size: 1.5rem;
font-weight: bold;
color: #0066cc;
}
.btn-kup {
margin-top: auto; /* Przycisk zawsze na dole karty */
padding: 12px;
background: #0066cc;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
transition: background 0.2s;
}
.btn-kup:hover {
background: #0052a3;
}Efekt: responsywna karta z animacją hover, wyrównanymi przyciskami i semantycznym HTML-em. Przycisk “Dodaj do koszyka” zawsze przylega do dołu karty dzięki margin-top: auto w Flexboxie.
Podsumowanie Wykładu 3
| Temat | Co zapamiętać |
|---|---|
| HTML5 | <!DOCTYPE html>, charset, viewport — trzy obowiązkowe elementy |
| Semantyka | header, nav, main, article, section, aside, footer zamiast div |
| Formularze | type="email", required, pattern — walidacja bez JS |
| CSS Selektory | Klasy . (10 pkt) > elementy (1 pkt). Unikaj !important |
| Box Model | Zawsze box-sizing: border-box |
| Flexbox | Jednowymiarowy: display: flex, justify-content, align-items |
| Grid | Dwuwymiarowy: display: grid, grid-template-columns, grid-template-rows |
| Responsywność | Mobile First + @media (min-width: ...) + jednostki rem/fr/vw |
Zadanie do przećwiczenia: Stwórz stronę portfolio (strona główna z nawigacją, sekcja “O mnie”, siatka projektów jako karty, formularz kontaktowy, stopka). Użyj wyłącznie HTML5 semantycznego + CSS Grid/Flexbox. Strona musi być responsywna (1 kolumna na mobile, 2–3 na desktopie).