Kurs HTML i CSS · Moduł 4: Flexbox
Gap property - nowoczesne odstępy w Flexbox
W tej lekcji7
Witaj ponownie! Trzy karty w rzędzie potrzebują odstępów między sobą, ale nie przy krawędziach kontenera. Przez lata robiliśmy to marginesami i sztuczkami z ujemnymi wartościami. Egipscy skrybowie też pilnowali odstępów między hieroglifami, bo od nich zależała czytelność. Poznaj właściwość gap, nowoczesny i czysty sposób na odstępy we Flexboksie.
Czym jest Gap?
Gap to nowoczesna właściwość CSS, szeroko wspierana od 2021 roku, która tworzy odstępy między elementami flex bez używania margin.
Przed gap - stary sposób
Dawniej każdy element dostawał margines ze wszystkich stron, a kontener ujemny margines, który zjadał nadmiar przy krawędziach:
/* Stary sposób - skomplikowany i problematyczny */
.container {
display: flex;
margin: -10px; /* Negatywny margin na kontener */
}
.item {
margin: 10px; /* Margin na każdy element */
}
/* Problem: Margin jest też na zewnętrznych krawędziach! */Działa, ale trzeba pilnować dwóch miejsc naraz, bo marginesy elementów lądują też na zewnętrznych krawędziach.
Z gap - nowy sposób
Z gap cały odstęp opisuje jedna deklaracja na kontenerze, a elementy nie potrzebują żadnych marginesów:
/* Nowy sposób - czysty i prosty */
.container {
display: flex;
gap: 20px; /* Odstęp tylko MIĘDZY elementami */
}
.item {
/* Nie potrzeba marginu! */
}
/* Gap NIE dodaje odstępu na zewnętrznych krawędziach! */Gap dodaje odstęp tylko między elementami, nigdy przy krawędziach kontenera. To jak odstępy między hieroglifami wykute przez mistrza: precyzyjne, równomierne i bez zbędnych dodatkowych przestrzeni.
Składnia Gap
Gap przyjmuje jedną albo dwie wartości, a zamiast skrótu możesz użyć dwóch osobnych właściwości:
/* Jeden wymiar - taki sam odstęp w poziomie i pionie */
.container {
gap: 20px;
}
/* Dwa wymiary - row-gap i column-gap */
.container {
gap: 20px 40px; /* 20px w pionie, 40px w poziomie */
}
/* Lub osobno */
.container {
row-gap: 20px;
column-gap: 40px;
}Przy dwóch wartościach pierwsza to odstęp między wierszami, a druga między kolumnami, więc gap: 20px 40px znaczy to samo co row-gap: 20px i column-gap: 40px.
Gap w różnych kierunkach Flexbox
Gap działa w każdym kierunku osi głównej, a przy zawijaniu także między liniami:
/* flex-direction: row (default) */
.container-row {
display: flex;
flex-direction: row;
gap: 20px; /* Odstęp między elementami w poziomie */
}
/* flex-direction: column */
.container-column {
display: flex;
flex-direction: column;
gap: 20px; /* Odstęp między elementami w pionie */
}
/* flex-wrap: wrap */
.container-wrap {
display: flex;
flex-wrap: wrap;
gap: 20px; /* Odstęp między wierszami I kolumnami */
}W rzędzie gap odsuwa elementy w poziomie, w kolumnie w pionie, a z flex-wrap: wrap rozdziela zarówno elementy, jak i kolejne rzędy.
Praktyczne przykłady
1. Card grid
Pierwszy przykład to siatka sześciu kart w trzech kolumnach:
<div class="card-grid">
<div class="card">Card 1</div>
<div class="card">Card 2</div>
<div class="card">Card 3</div>
<div class="card">Card 4</div>
<div class="card">Card 5</div>
<div class="card">Card 6</div>
</div>Szerokość każdej karty pomniejszamy o jej udział w odstępach, bo gap też zajmuje miejsce w rzędzie:
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 20px; /* Idealny odstęp między cards */
}
.card {
flex: 0 0 calc(33.333% - 14px); /* 3 kolumny */
/* calc uwzględnia gap: (40px total gap / 3 columns) ≈ 14px */
padding: 20px;
background: #3498db;
color: white;
border-radius: 8px;
}
@media (max-width: 768px) {
.card {
flex: 0 0 calc(50% - 10px); /* 2 kolumny na tablet */
}
}
@media (max-width: 480px) {
.card {
flex: 0 0 100%; /* 1 kolumna na mobile */
}
}Trzy karty to dwa odstępy po 20 pikseli, czyli około 13,3 piksela na kartę, a kod zaokrągla to do 14. Te obliczenia zakładają box-sizing: border-box, bo inaczej wypełnienie kart doliczy się do szerokości i trzecia karta spadnie do nowego rzędu.
Matematyka gap dla multi-column:
- 3 kolumny z gap 20px:
calc(33.333% - 13.333px) - 4 kolumny z gap 20px:
calc(25% - 15px) - Formuła:
calc(100% / n_columns - gap * (n_columns - 1) / n_columns)
2. Button group
Drugi przykład to grupa przycisków dosuniętych do prawej krawędzi:
<div class="button-group">
<button class="btn">Cancel</button>
<button class="btn btn-primary">Save</button>
<button class="btn btn-danger">Delete</button>
</div>Kontener dostaje gap: 10px i justify-content: flex-end, a same przyciski nie mają żadnych marginesów:
.button-group {
display: flex;
gap: 10px; /* Równy odstęp między przyciskami */
justify-content: flex-end;
}
.btn {
padding: 10px 20px;
border: 1px solid #ddd;
background: white;
cursor: pointer;
border-radius: 4px;
}
.btn-primary {
background: #3498db;
color: white;
}
.btn-danger {
background: #e74c3c;
color: white;
}Przyciski stoją w równych odstępach przy prawej krawędzi, a gdy dodasz czwarty, odstęp pojawi się sam.
3. Form layout
Trzeci przykład to formularz, w którym imię i nazwisko stoją w jednym wierszu:
<form class="form">
<div class="form-row">
<input type="text" placeholder="Imię">
<input type="text" placeholder="Nazwisko">
</div>
<input type="email" placeholder="Email">
<textarea placeholder="Wiadomość"></textarea>
<button type="submit">Send</button>
</form>Formularz jest kolumną z odstępem 15 pikseli, a wiersz z imieniem i nazwiskiem osobnym kontenerem flex z takim samym gap:
.form {
display: flex;
flex-direction: column;
gap: 15px; /* Odstęp między polami formularza */
max-width: 500px;
}
.form-row {
display: flex;
gap: 15px; /* Odstęp między polami w wierszu */
}
.form input,
.form textarea {
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-family: inherit;
}
.form-row input {
flex: 1; /* Równa szerokość */
}
.form textarea {
min-height: 100px;
resize: vertical;
}
.form button {
padding: 12px;
background: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
align-self: flex-start;
}Pola w wierszu dzielą szerokość po równo dzięki flex: 1, a przycisk z align-self: flex-start nie rozciąga się na całą szerokość.
4. Navigation bar
Czwarty przykład to pasek nawigacji z logo, menu i przyciskami:
<nav class="navbar">
<div class="nav-brand">Logo</div>
<ul class="nav-menu">
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Services</a></li>
<li><a href="#">Contact</a></li>
</ul>
<div class="nav-actions">
<button>Login</button>
<button class="btn-primary">Sign Up</button>
</div>
</nav>Gap pracuje tu na trzech poziomach: między sekcjami paska, między linkami i między przyciskami:
.navbar {
display: flex;
align-items: center;
padding: 15px 30px;
background: #2c3e50;
gap: 30px; /* Odstęp między głównymi sekcjami */
}
.nav-brand {
font-size: 1.5em;
font-weight: bold;
color: goldenrod;
}
.nav-menu {
display: flex;
list-style: none;
margin: 0;
padding: 0;
gap: 20px; /* Odstęp między linkami */
flex: 1; /* Zajmij środek */
}
.nav-menu a {
color: white;
text-decoration: none;
transition: color 0.3s;
}
.nav-menu a:hover {
color: goldenrod;
}
.nav-actions {
display: flex;
gap: 10px; /* Odstęp między przyciskami */
}
.nav-actions button {
padding: 8px 16px;
border: 1px solid white;
background: transparent;
color: white;
cursor: pointer;
border-radius: 4px;
}
.nav-actions .btn-primary {
background: goldenrod;
border-color: goldenrod;
color: #2c3e50;
}Menu z flex: 1 zajmuje środek. Gdy pasek ma tylko logo i menu, wystarczy justify-content: space-between na kontenerze: logo trafi do lewej krawędzi, menu do prawej, a align-items: center wyrówna je w pionie.
5. Dashboard layout
Piąty przykład łączy wszystko: panel z menu bocznym, nagłówkiem i widżetami:
<div class="dashboard">
<aside class="sidebar">
<div class="sidebar-item">Dashboard</div>
<div class="sidebar-item">Analytics</div>
<div class="sidebar-item">Settings</div>
</aside>
<main class="dashboard-main">
<header class="dashboard-header">Header</header>
<div class="dashboard-content">
<div class="widget">Widget 1</div>
<div class="widget">Widget 2</div>
<div class="widget">Widget 3</div>
<div class="widget">Widget 4</div>
</div>
</main>
</div>Każdy poziom układu ma własny gap, a widżety tworzą dwie kolumny:
.dashboard {
display: flex;
gap: 20px; /* Odstęp między sidebar i main */
height: 100vh;
padding: 20px;
}
.sidebar {
display: flex;
flex-direction: column;
gap: 10px; /* Odstęp między items */
flex: 0 0 250px;
background: #34495e;
padding: 20px;
border-radius: 8px;
}
.sidebar-item {
padding: 12px;
background: #2c3e50;
color: white;
border-radius: 4px;
cursor: pointer;
}
.dashboard-main {
flex: 1;
display: flex;
flex-direction: column;
gap: 20px; /* Odstęp między header i content */
}
.dashboard-header {
flex: 0 0 auto;
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.dashboard-content {
flex: 1;
display: flex;
flex-wrap: wrap;
gap: 20px; /* Odstęp między widgets */
}
.widget {
flex: 0 0 calc(50% - 10px); /* 2 kolumny */
min-height: 200px;
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}Żaden element nie ma marginesu, a wszystkie odstępy są równe, bo dwa widżety z calc(50% - 10px) dzielą jeden odstęp po połowie.
Gap vs Margin - kiedy używać czego?
Używaj Gap:
- Odstępy między elementami w kontenerze flex/grid
- Równomierne odstępy bez komplikacji
- Gdy nie potrzebujesz odstępów zewnętrznych
- Responsive layouts
Używaj Margin:
- Odstępy zewnętrzne (od kontenera)
- Asymetryczne odstępy
- Gdy potrzebujesz
margin: autodo centrowania - Gdy musisz wspierać stare przeglądarki (pre-2021)
W praktyce oba narzędzia pracują razem: gap i padding działają wewnątrz kontenera, a margin na zewnątrz:
/* Gap dla wewnętrznych odstępów */
.container {
display: flex;
gap: 20px;
padding: 20px; /* Odstęp wewnętrzny od krawędzi */
}
/* Margin dla zewnętrznych odstępów */
.container {
margin: 40px auto; /* Wyśrodkowanie kontenera */
}
/* Kombinacja */
.card {
/* Gap usuwa potrzebę margin między items */
/* ale margin-top może być używany dla konkretnego celu */
margin-top: auto; /* Przesuń na dół w flex column */
}margin: 40px auto nadal centruje kontener, a margin-top: auto spycha element na dół kolumny flex. Moja rada: odstępy między elementami zawsze zostawiaj gapowi, a margines trzymaj do zadań specjalnych.
Responsywny gap
Na małym ekranie duże odstępy marnują miejsce, więc gap warto zmniejszać w media queries albo przez zmienne CSS:
.container {
display: flex;
flex-wrap: wrap;
gap: 30px;
}
@media (max-width: 1024px) {
.container {
gap: 20px; /* Mniejszy gap na tablecie */
}
}
@media (max-width: 768px) {
.container {
gap: 15px; /* Jeszcze mniejszy na mobile */
}
}
/* Lub z CSS variables */
:root {
--gap-large: 30px;
--gap-medium: 20px;
--gap-small: 15px;
}
.container {
gap: var(--gap-large);
}
@media (max-width: 768px) {
:root {
--gap-large: var(--gap-medium);
}
}Druga wersja zmienia tylko wartość zmiennej --gap-large, a każda reguła, która jej używa, dostosuje się sama.
Wsparcie przeglądarek
Gap we Flexboksie jest wspierany od:
- Chrome 84+ (2020)
- Firefox 63+ (2018)
- Safari 14.1+ (2021)
- Edge 84+ (2020)
Jeśli musisz obsłużyć starsze przeglądarki, zacznij od marginesów i nadpisz je tam, gdzie gap jest dostępny:
.container {
display: flex;
/* Fallback z margin */
margin: -10px;
}
.item {
margin: 10px;
}
/* Modern browsers override */
@supports (gap: 20px) {
.container {
gap: 20px;
margin: 0;
}
.item {
margin: 0;
}
}Uwaga na pułapkę: @supports (gap: 20px) sprawdza tylko, czy przeglądarka zna właściwość gap. Starsze wersje Chrome i Safari znały ją już z CSS Grid, choć we Flexboksie jeszcze jej nie obsługiwały, więc test mógł skłamać. Dziś taki fallback rzadko jest potrzebny.
Podsumowanie
Gap to nowoczesny i czysty sposób na odstępy, który działa we Flexboksie i w Gridzie. Dodaje odstęp tylko między elementami, przyjmuje jedną wartość albo dwie, ma prostszą składnię niż marginesy i ułatwia obliczanie szerokości. Sprawdza się w siatkach kart, grupach przycisków, formularzach, nawigacjach i dashboardach. W następnej lekcji zajmiemy się dostępnością.
W edytorze poniżej porównasz oba podejścia: pierwszy przykład trzyma odstępy marginesami, drugi korzysta z gap: 10px, a trzeci ustawia osobno row-gap i column-gap.
Kod do tej lekcji: index.html
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Flexbox Gap Property</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Gap w Flexbox</h1>
<h2>Bez gap (stary sposób z margin)</h2>
<div class="container-old">
<div class="box">A</div>
<div class="box">B</div>
<div class="box">C</div>
<div class="box">D</div>
</div>
<h2>Z gap (nowoczesny sposób)</h2>
<div class="container-gap">
<div class="box">A</div>
<div class="box">B</div>
<div class="box">C</div>
<div class="box">D</div>
</div>
<h2>Row-gap i column-gap osobno</h2>
<div class="container-mixed">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
<div class="box">6</div>
</div>
</body>
</html>Pamiętaj: gap to idealne odstępy między hieroglifami na egipskim papirusie, precyzyjne i równomierne, a do tego ustawiane jedną deklaracją, bez komplikacji.
Hasła z dokumentacji w tej lekcji
Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Do czego służy właściwość gap w kontenerze Flexbox?
Zadania praktyczne w grze
- Edytor kodu
Stwórz pasek nawigacji z logo po lewej i 4 linkami menu po prawej, używając Flexbox z justify-content: space-between i align-items: center.