Kurs HTML i CSS · Moduł 4: Flexbox

Gap property - nowoczesne odstępy w Flexbox

10 min czytania ·
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: auto do 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. 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.

Przydatne artykuły