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. Atrybut lang informuje 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>&copy; 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?

  1. SEO: Google lepiej rozumie strukturę strony i wyżej ją pozycjonuje.
  2. Dostępność (a11y): Czytniki ekranowe mogą powiedzieć: “Nawigacja — 3 linki”, “Artykuł — Tytuł artykułu”.
  3. 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 required nie jest puste,
  • type="email" zawiera @,
  • min/max jest w zakresie,
  • pattern pasuje do wyrażenia regularnego,
  • minlength jest 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:

  1. Content — sama treść (tekst, obraz)
  2. Padding — wewnętrzny odstęp (między treścią a ramką)
  3. Border — obramowanie
  4. 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).