HTML I CSS · INF.03 · MODUŁ 10
Grid, responsywność i projekt egzaminacyjny
Ostatni krok: łączysz wszystko w projekt, który działa na telefonie i na dużym ekranie.
Wprowadzenie
Flexbox układa elementy w jednej osi — w rzędzie albo w kolumnie. CSS Grid idzie o krok dalej: pozwala budować układ jednocześnie w wierszach i kolumnach. Galeria, katalog produktów czy cała strona z nagłówkiem, menu i stopką to naturalne zastosowania siatki.
Grid najlepiej sprawdza się tam, gdzie treść ma kilka kolumn o wspólnych odstępach. Definiujesz liczbę kolumn przez grid-template-columns, a odstępy przez gap. Zapis repeat(auto-fit, minmax(180px, 1fr)) sprawia, że przeglądarka sama dobiera liczbę kolumn do szerokości ekranu.
Responsywność to dostosowanie strony do telefonu, tabletu i dużego ekranu. Zaczyna się od <meta name="viewport"> w nagłówku dokumentu. Bez niego telefon renderuje stronę tak, jakby był szeroki na około 980 px, i wszystko wygląda jak pomniejszony desktop.
Do zmiany układu służą media queries, np. @media (max-width: 640px). W podejściu mobile-first piszemy najpierw style dla telefonu, a dopiero potem dodajemy układ dla większych ekranów przez min-width. Rozmiary tekstu i odstępów można płynnie ograniczać funkcją clamp().
Gdy układ ma wiersze i kolumny jednocześnie — wybierz Grid. Gdy wystarczy jeden rząd lub jedna kolumna — flexbox jest prostszy.
Najważniejsza własność
W Gridzie najważniejsza jest definicja kolumn, czyli grid-template-columns. Ona decyduje, ile kolumn powstanie i jak szerokie będą. Wiersze tworzą się automatycznie, chyba że ustawisz je osobno przez grid-template-rows.
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
@media (max-width: 640px) { grid-template-columns: 1fr; }
| Właściwość | Co robi |
|---|---|
display: grid | Włącza siatkę na elemencie-rodzicu. |
grid-template-columns | Ustala liczbę i szerokość kolumn, np. 200px 1fr 1fr. |
grid-template-rows | Ustala wysokość wierszy, np. 80px auto 60px. |
repeat(3, 1fr) | Powtarza definicję trzy razy — daje trzy równe kolumny. |
minmax(180px, 1fr) | Daje kolumnie minimum 180 px, a resztę miejsca rozdziela elastycznie. |
auto-fit | Tworzy tyle kolumn, ile zmieści się w dostępnej szerokości. |
gap | Dodaje odstępy między kolumnami i wierszami. |
grid-template-areas | Nazywa obszary siatki, np. "header header" "nav main" "footer footer". |
Media query to reguła warunkowa. Zapis @media (max-width: 640px) oznacza: zastosuj te style, gdy ekran ma najwyżej 640 px szerokości. Zapis @media (min-width: 640px) działa odwrotnie — od 640 px w górę. W podejściu mobile-first częściej używamy min-width, bo style bazowe są już przystosowane do telefonu.
Do responsywnych obrazów wystarczy max-width: 100%; height: auto;. Obraz nigdy nie wyjdzie wtedy poza kontener i zachowa proporcje. Do płynnego rozmiaru tekstu służy clamp(minimum, wartość płynna, maksimum), np. clamp(1.5rem, 4vw, 2.5rem).
Najpierw ustaw układ jednokolumnowy dla telefonu, a w @media (min-width: 700px) dodaj tylko to, co się zmienia: liczbę kolumn i wielkość odstępów.
Most: od reguły do gotowej siatki
Uzupełnij siatkę i oceń kod
Dwie aktywności prowadzą od składni do krytycznego czytania. Najpierw uzupełnij brakujące fragmenty, potem sprawdź, które linie responsywnego CSS są błędne.
Galeria, która sama dobiera kolumny
uzupełnianie lukUzupełnij cztery luki. Galeria ma tworzyć kolumny o minimalnej szerokości 180 px, a na telefonie przechodzić do jednej kolumny.
.galeria {
display: ;
grid-template-columns: repeat(
,
minmax(180px, 1fr)
);
: 16px;
}
@media (max-width: 640px) {
.galeria {
grid-template-columns: ;
}
}
display: grid włącza siatkę. auto-fit dobiera liczbę kolumn do szerokości, a minmax(180px, 1fr) pilnuje minimalnego rozmiaru. gap tworzy odstępy, a w media query jedna kolumna 1fr układa kafle pionowo.
Oceń responsywny kod
code reviewOceń każdą linię: OK, błąd logiczny, brak dostępności albo zła praktyka. W dwie linie wkradł się problem.
-
Stała szerokość 900 px wypycha obraz poza telefon. Powinno być
max-width: 100%; height: auto;. -
Poprawna, responsywna siatka: liczba kolumn wynika z szerokości ekranu.
-
Na wąskim ekranie karty układają się jedna pod drugą. To poprawne użycie media query.
-
Stała szerokość całej strony blokuje responsywność i powoduje poziomy pasek przewijania. Lepiej zostawić szerokość domyślną i ograniczać ją przez
max-width. -
clamp()ogranicza rozmiar tekstu z obu stron, więc nagłówek nie będzie ani za mały, ani za duży.
Przykłady
Od samej galerii, przez tę samą stronę na dwóch ekranach, do pełnego projektu egzaminacyjnego. Każdy przykład pokazuje kod i prawdziwy zrzut z przeglądarki.
Sześć zdjęć ma utworzyć siatkę, która sama dobiera liczbę kolumn. Każda kolumna ma mieć co najmniej 180 px, a wolne miejsce ma być rozdzielone równo.
<div class="galeria">
<figure>
<img src="../assets/gory.svg" alt="Górski pejzaż ze słońcem">
<figcaption>Górski szlak</figcaption>
</figure>
<figure>
<img src="../assets/morze.svg" alt="Błękitne morze i plaża">
<figcaption>Bałtyk latem</figcaption>
</figure>
<figure>
<img src="../assets/las.svg" alt="Zielony las z drzewami">
<figcaption>Las o świcie</figcaption>
</figure>
<figure>
<img src="../assets/miasto.svg" alt="Miasto o zachodzie słońca">
<figcaption>Miasto nocą</figcaption>
</figure>
<figure>
<img src="../assets/gory.svg" alt="Szczyty górskie ze śniegiem">
<figcaption>Zimowe szczyty</figcaption>
</figure>
<figure>
<img src="../assets/morze.svg" alt="Spokojne morze o zachodzie">
<figcaption>Port o zachodzie</figcaption>
</figure>
</div>
.galeria {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 16px;
max-width: 720px;
margin: 22px auto 0;
}
.galeria figure {
margin: 0;
overflow: hidden;
border: 1px solid #dbe3ef;
border-radius: 14px;
background: #fff;
}
.galeria img {
display: block;
width: 100%;
height: 120px;
object-fit: cover;
}
.galeria figcaption {
padding: 10px 12px;
font-size: 13px;
font-weight: 600;
}
repeat(auto-fit, minmax(180px, 1fr)): trzy kolumny, równe odstępy i jednakowa wysokość zdjęć.display: gridzamienia kontener w siatkę, a jego dzieci stają się komórkami.auto-fittworzy tyle kolumn, ile zmieści się w dostępnej szerokości.minmax(180px, 1fr)pilnuje, żeby kolumna nie była węższa niż 180 px, i dzieli resztę miejsca po równo.gap: 16pxdodaje odstępy bez marginesów na pojedynczych elementach.
Wynik: sześć zdjęć w trzech kolumnach; po zwężeniu okna liczba kolumn zmniejsza się automatycznie.
Nagłówek, sekcja powitalna i trzy karty usług mają działać na obu ekranach. Na telefonie nawigacja i karty mają ułożyć się pionowo, a na desktopie pozostać w rzędach.
<header class="naglowek">
<strong>Studio Web</strong>
<nav>
<a href="#">Oferta</a>
<a href="#">Realizacje</a>
<a href="#">Kontakt</a>
</nav>
</header>
<section class="hero">
<h2>Strony, które działają na każdym ekranie</h2>
<p>Projektujemy od telefonu, a potem rozszerzamy układ.</p>
</section>
<section class="uslugi">
<article class="usluga">
<h3>Projekt strony</h3>
<p>Układ, kolory i typografia.</p>
</article>
<article class="usluga">
<h3>Wdrożenie</h3>
<p>Semantyczny HTML i czysty CSS.</p>
</article>
<article class="usluga">
<h3>Responsywność</h3>
<p>Telefon, tablet i desktop.</p>
</article>
</section>
.naglowek {
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
padding: 16px 20px;
background: #0f172a;
color: #fff;
}
.naglowek nav { display: flex; gap: 14px; }
.hero h2 {
margin: 0 0 10px;
font-size: clamp(26px, 5vw, 38px);
}
.uslugi {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 14px;
}
@media (max-width: 640px) {
.naglowek {
flex-direction: column;
align-items: flex-start;
}
.uslugi { grid-template-columns: 1fr; }
.hero { padding: 22px 18px; }
}
- Style bazowe ustawiają nagłówek i karty w rzędzie — to widok desktopowy.
@media (max-width: 640px)uruchamia się dopiero na wąskim ekranie.- W środku media query nawigacja dostaje
flex-direction: column, a siatka usług jedną kolumnę. - Pliki demo różnią się tylko klasą szerokości (
.demoi.demo.mobile). Reguły CSS są identyczne.
Wynik: jedna strona, dwa układy. Na telefonie nic nie wychodzi poza ekran i nie trzeba przewijać w poziomie.
Projekt egzaminacyjny: nagłówek z logo i telefonem, menu, sekcja powitalna, sześć usług w Gridzie, galeria czterech zdjęć i stopka w trzech kolumnach. Całość ma 960 px szerokości.
<div class="projekt">
<header class="naglowek">
<div>
<p class="firma">Warsztat <span>Podróży</span></p>
<p class="haslo">Fotografia · góry · morze</p>
</div>
<div class="telefon">Zadzwoń<strong>500 600 700</strong></div>
</header>
<nav class="menu">
<a href="#uslugi">Usługi</a>
<a href="#galeria">Galeria</a>
<a href="#kontakt">Kontakt</a>
</nav>
<section class="hero">
<p class="kicker">Od 1998 roku</p>
<h1>Naucz się podróżować świadomie</h1>
<p>Warsztaty fotograficzne, wyjazdy w góry i weekendy nad morzem.</p>
<a class="cta" href="#kontakt">Zarezerwuj miejsce</a>
</section>
<section class="sekcja" id="uslugi">
<h2>Nasze usługi</h2>
<div class="uslugi">
<article class="usluga">
<span class="nr">01</span>
<h3>Warsztaty fotograficzne</h3>
<p>Kompozycja, światło i ustawienia aparatu.</p>
</article>
<article class="usluga">
<span class="nr">02</span>
<h3>Wyjazdy w góry</h3>
<p>Trzy dni z planem zdjęciowym i przewodnikiem.</p>
</article>
<!-- cztery kolejne karty usług -->
</div>
</section>
<section class="sekcja" id="galeria">
<h2>Warsztat w praktyce</h2>
<div class="galeria">
<img src="../assets/gory.svg" alt="Górski pejzaż">
<img src="../assets/morze.svg" alt="Morze i plaża">
<img src="../assets/las.svg" alt="Zielony las">
<img src="../assets/miasto.svg" alt="Miasto o zachodzie słońca">
</div>
</section>
<footer class="stopka" id="kontakt">
<div><h3>Kontakt</h3><p>ul. Wędrowna 12<br>500 600 700</p></div>
<div><h3>Godziny</h3><p>pon.–pt. 9:00–17:00</p></div>
<div><h3>Napisz</h3><p>biuro@warsztat-podrozy.pl</p></div>
</footer>
</div>
.projekt { width: 960px; max-width: 100%; padding: 0; overflow: hidden; }
.naglowek {
display: flex;
justify-content: space-between;
align-items: center;
gap: 24px;
padding: 22px 40px;
background: #0f172a;
color: #fff;
}
.menu { display: flex; gap: 26px; padding: 14px 40px; background: #1e293b; }
.hero {
padding: 54px 40px 58px;
background:
linear-gradient(90deg, rgba(15, 23, 42, .95), rgba(15, 23, 42, .35)),
url("../assets/gory.svg") center / cover;
color: #fff;
}
.hero h1 { margin: 0 0 12px; font-size: 40px; line-height: 1.15; }
.sekcja { padding: 40px; }
.uslugi { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.usluga { padding: 18px; border: 1px solid #e2e8f0; border-radius: 12px; }
.galeria { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.galeria img { width: 100%; height: 120px; object-fit: cover; border-radius: 10px; }
.stopka {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
padding: 30px 40px;
background: #0f172a;
color: #cbd5e1;
}
@media (max-width: 720px) {
.naglowek { flex-direction: column; align-items: flex-start; }
.uslugi { grid-template-columns: 1fr; }
.galeria { grid-template-columns: repeat(2, 1fr); }
.stopka { grid-template-columns: 1fr; }
}
- Nagłówek i stopka korzystają z flexboxa, bo wystarczy jeden rząd lub jedna kolumna.
- Sekcja usług używa
repeat(3, 1fr), bo zawsze mają być trzy równe kolumny na desktopie. - Galeria używa
repeat(4, 1fr), a na telefonie przechodzi na dwie kolumny. - Media query zmienia tylko to, co trzeba: kierunek nagłówka i liczbę kolumn. Reszta stylów zostaje wspólna.
Wynik: kompletna strona projektu z czytelnym podziałem na sekcje i układem, który nadal działa po zwężeniu okna.
Most: policz kolumny przed uruchomieniem
Przewidź wynik siatki
Zanim otworzysz przeglądarkę, policz, ile kolumn powstanie. To najczęstsze pytanie przy czytaniu kodu z auto-fit i minmax.
Ile kolumn w galerii?
przewidywanieKontener ma 660 px szerokości. Ile kolumn utworzy ta siatka?
.galeria {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
width: 660px;
}
Trzy kolumny po 200 px zajmują 600 px, a dwa odstępy po 20 px — 40 px. Razem 640 px, więc zostaje jeszcze 20 px. Cztery kolumny wymagałyby 860 px, dlatego mieszczą się tylko trzy.
Spróbuj samodzielnie
Cztery zadania prowadzą od pojedynczej reguły do pełnej sekcji projektu. W każdym rozwiązaniu zwróć uwagę nie tylko na wynik, ale i na kolejność decyzji.
-
Galeria ma mieć kolumny o minimalnej szerokości 200 px i same dopasowywać się do ekranu. Napisz regułę dla kontenera i wyjaśnij, co robi każda część zapisu.
Pokaż rozwiązanie
.galeria { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; }display: gridwłącza siatkę.repeatpowtarza definicję kolumny.auto-fitdobiera liczbę powtórzeń do miejsca, aminmax(200px, 1fr)mówi, że kolumna ma co najmniej 200 px i może rosnąć.gapdodaje odstępy. -
Masz stronę z nagłówkiem
.nagloweki sekcją kart.karty. Napisz style mobile-first: na telefonie nagłówek ma być w kolumnie, a karty jedna pod drugą; od 700 px nagłówek ma być w rzędzie, a karty w trzech kolumnach.Pokaż rozwiązanie
.naglowek { display: flex; flex-direction: column; gap: 10px; } .karty { display: grid; grid-template-columns: 1fr; gap: 16px; } @media (min-width: 700px) { .naglowek { flex-direction: row; justify-content: space-between; } .karty { grid-template-columns: repeat(3, 1fr); } }Style bazowe opisują telefon, więc nie trzeba ich później nadpisywać. Media query z
min-widthdodaje tylko układ dla szerszego ekranu. -
Uczeń twierdzi, że strona jest responsywna, ale na telefonie pojawia się poziomy pasek przewijania, a obraz wychodzi poza kartę. Wskaż dwie przyczyny i popraw kod.
<meta charset="utf-8"> <title>Moja strona</title> .kontener { width: 1100px; } .kontener img { width: 800px; }Pokaż rozwiązanie
Brakuje
<meta name="viewport">, więc telefon nie dopasowuje strony do ekranu. Dodatkowo stałe szerokości 1100 px i 800 px są szersze niż ekran telefonu.<meta name="viewport" content="width=device-width, initial-scale=1"> .kontener { width: 100%; max-width: 1100px; margin: 0 auto; } .kontener img { max-width: 100%; height: auto; }max-widthpozwala elementowi zwężyć się na telefonie, ale nie rozpycha go ponad 1100 px na dużym ekranie. Obraz zmax-width: 100%nigdy nie wyjdzie poza rodzica. -
Zbuduj sekcję usług: nagłówek
h2, krótki opis i trzy karty w Gridzie. Każda karta ma mieć numer, tytuł i opis. Na telefonie karty mają ułożyć się pionowo. Napisz HTML i CSS z media query.Pokaż rozwiązanie
<section class="sekcja"> <h2>Nasze usługi</h2> <p class="lead">Wybierz formę dla siebie.</p> <div class="uslugi"> <article class="usluga"> <span class="nr">01</span> <h3>Warsztaty</h3> <p>Praktyka w małej grupie.</p> </article> <article class="usluga"> <span class="nr">02</span> <h3>Wyjazdy</h3> <p>Trzy dni w górach.</p> </article> <article class="usluga"> <span class="nr">03</span> <h3>Online</h3> <p>Szkolenia bez wychodzenia z domu.</p> </article> </div> </section>.uslugi { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; } .usluga { padding: 18px; border: 1px solid #e2e8f0; border-radius: 12px; } @media (max-width: 640px) { .uslugi { grid-template-columns: 1fr; } }Desktop ma trzy równe kolumny. Media query zmienia tylko definicję kolumn, więc karty same ustawią się pionowo. Nie trzeba zmieniać HTML ani dodawać nowych klas.
Grid buduje układ w dwóch wymiarach: grid-template-columns ustala kolumny, repeat powtarza definicję, a minmax i auto-fit pozwalają siatce dopasować się do ekranu. gap pilnuje odstępów, a grid-template-areas nazywa większe obszary strony. Responsywność zaczyna się od meta viewport, a media queries zmieniają układ przy wybranej szerokości. Mobile-first oznacza style dla telefonu jako bazę i min-width dla większych ekranów. clamp() ogranicza rozmiary, a max-width: 100% chroni obrazy przed wychodzeniem poza kontener.