CSS Box Model i Layout — Flexbox vs Grid w praktyce

Zrozum Box Model, pozycjonowanie, Flexbox i Grid bez skrótów myślowych. Praktyczne przykłady, porównania i typowe pułapki w nowoczesnym CSS.

Opublikowano

5 października 2025 12:18

Czytanie

6 min czytania

Aktualizacja

15 kwietnia 2026 11:52

Każdy element HTML to prostokątne pudełko. Zrozumienie jak te pudełka się zachowują, jak je układać i pozycjonować — to fundament CSS.

W tym artykule przejdziemy przez Box Model, positioning, a potem głęboko wejdziemy w Flexbox i Grid. Na końcu będziesz wiedzieć, kiedy użyć którego.

Krótka odpowiedź: CSS Box Model opisuje każdy element HTML jako prostokąt złożony z treści, paddingu, obramowania i marginesu — box-sizing: border-box sprawia, że width i height obejmują padding i border, co ułatwia obliczenia. Flexbox służy do układów jednowymiarowych (wiersz lub kolumna) i dobrze sprawdza się w nawigacji czy wyrównaniu elementów, natomiast Grid to system dwuwymiarowy idealny do złożonych siatek i layoutów całych stron. Oba mechanizmy można łączyć.

Box Model — anatomia elementu

Każdy element składa się z czterech warstw:

Code
┌─────────────────────────────────────────┐
│                 MARGIN                  │
│  ┌───────────────────────────────────┐  │
│  │              BORDER               │  │
│  │  ┌─────────────────────────────┐  │  │
│  │  │           PADDING           │  │  │
│  │  │  ┌───────────────────────┐  │  │  │
│  │  │  │                       │  │  │  │
│  │  │  │       CONTENT         │  │  │  │
│  │  │  │                       │  │  │  │
│  │  │  └───────────────────────┘  │  │  │
│  │  └─────────────────────────────┘  │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘
  • Content — treść elementu (tekst, obrazy)
  • Padding — przestrzeń wewnątrz elementu (wokół contentu)
  • Border — obramowanie
  • Margin — przestrzeń na zewnątrz elementu
Code
.box {
  width: 200px;
  height: 100px;
  padding: 20px;
  border: 5px solid black;
  margin: 10px;
}

Obliczanie rozmiaru: content-box vs border-box

Domyślnie (content-box), width i height dotyczą tylko contentu:

Code
.box {
  width: 200px;
  padding: 20px;
  border: 5px solid black;
}
/* Rzeczywista szerokość: 200 + 20*2 + 5*2 = 250px */

Z border-box, width zawiera padding i border:

Code
.box {
  box-sizing: border-box;
  width: 200px;
  padding: 20px;
  border: 5px solid black;
}
/* Rzeczywista szerokość: 200px (content się skurczy) */

Best practice — zawsze używaj border-box:

Code
*, *::before, *::after {
  box-sizing: border-box;
}

Skróty padding i margin

Code
/* Wszystkie strony */
padding: 10px;
 
/* Góra/dół, lewo/prawo */
padding: 10px 20px;
 
/* Góra, lewo/prawo, dół */
padding: 10px 20px 30px;
 
/* Góra, prawo, dół, lewo (zegar) */
padding: 10px 20px 30px 40px;
 
/* Poszczególne strony */
padding-top: 10px;
padding-right: 20px;
padding-bottom: 30px;
padding-left: 40px;

Margin collapse

Marginesy pionowe sąsiednich elementów się łączą (collapse):

Code
.box1 { margin-bottom: 30px; }
.box2 { margin-top: 20px; }
/* Odstęp między nimi: 30px (nie 50px!) */

Większy margin "wygrywa". To nie bug — to feature.

Display — jak element się zachowuje

display: block

  • Zajmuje całą dostępną szerokość
  • Zaczyna od nowej linii
  • Respektuje width/height, margin, padding
Code
<div>Block 1</div>
<div>Block 2</div>
<!-- Każdy div w osobnej linii -->

Elementy block: <div>, <p>, <h1>, <section>, <article>...

display: inline

  • Zajmuje tylko tyle miejsca, ile potrzebuje
  • Nie zaczyna nowej linii
  • Ignoruje width/height, margin-top/bottom
Code
<span>Inline 1</span>
<span>Inline 2</span>
<!-- Oba span w jednej linii -->

Elementy inline: <span>, <a>, <strong>, <em>, <img>...

display: inline-block

Hybryda — inline, ale respektuje width/height/margin:

Code
.badge {
  display: inline-block;
  width: 100px;
  padding: 10px;
  margin: 5px;
}

display: none vs visibility: hidden

Code
.hidden { display: none; }       /* Usunięty z layoutu */
.invisible { visibility: hidden; } /* Niewidoczny, ale zajmuje miejsce */

Positioning

position: static (domyślne)

Element w normalnym flow dokumentu. top, right, bottom, left nie działają.

position: relative

Element w normalnym flow, ale można go przesunąć względem oryginalnej pozycji:

Code
.box {
  position: relative;
  top: 20px;   /* przesuń 20px w dół */
  left: 10px;  /* przesuń 10px w prawo */
}
/* Oryginalne miejsce zostaje "zarezerwowane" */

position: absolute

Element wyjęty z flow, pozycjonowany względem najbliższego rodzica z position innym niż static:

Code
.parent {
  position: relative;
}
 
.child {
  position: absolute;
  top: 0;
  right: 0;
  /* W prawym górnym rogu rodzica */
}

position: fixed

Pozycjonowany względem viewport (okna przeglądarki):

Code
.navbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  /* Zawsze na górze ekranu */
}

position: sticky

Hybryda relative/fixed — "przykleja się" po przewinięciu:

Code
.sticky-header {
  position: sticky;
  top: 0;
  /* Normalny flow, ale przykleja się do góry po scrollu */
}

z-index

Kontroluje nakładanie się elementów. Najczęściej używasz go razem z pozycjonowaniem, ale działa też np. na flex i grid items:

Code
.box1 { position: relative; z-index: 1; }
.box2 { position: relative; z-index: 2; } /* Na wierzchu */

Flexbox — układ jednowymiarowy

Flexbox to system layoutu dla jednego wymiaru (wiersz LUB kolumna).

Podstawy

Code
.container {
  display: flex;
}

Od teraz dzieci .container to flex items.

Kierunek: flex-direction

Code
.container {
  display: flex;
  flex-direction: row;            /* → domyślne */
  flex-direction: row-reverse;    /* ← */
  flex-direction: column;         /* ↓ */
  flex-direction: column-reverse; /* ↑ */
}

Oś główna i poprzeczna

Code
flex-direction: row
┌─────────────────────────────────────┐
│  ═══════════ main axis ══════════▶  │
│  ║                                  │
│  ║ cross                            │
│  ║ axis                             │
│  ▼                                  │
└─────────────────────────────────────┘

flex-direction: column
┌──────────────────┐
│  ║  cross axis ▶ │
│  ║               │
│  ║ main          │
│  ║ axis          │
│  ▼               │
└──────────────────┘

justify-content — oś główna

Code
.container {
  display: flex;
  justify-content: flex-start;    /* domyślne — na początku */
  justify-content: flex-end;      /* na końcu */
  justify-content: center;        /* wyśrodkowane */
  justify-content: space-between; /* równe odstępy, bez na brzegach */
  justify-content: space-around;  /* równe odstępy, połowa na brzegach */
  justify-content: space-evenly;  /* równe odstępy wszędzie */
}
Code
space-between:  [A]     [B]     [C]
space-around:    [A]   [B]   [C]  
space-evenly:   [A]   [B]   [C]

align-items — oś poprzeczna

Code
.container {
  display: flex;
  align-items: stretch;    /* domyślne — rozciągnij */
  align-items: flex-start; /* góra */
  align-items: flex-end;   /* dół */
  align-items: center;     /* środek */
  align-items: baseline;   /* linia bazowa tekstu */
}

Centrowanie idealne

Code
.container {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100dvh;
}
/* Dziecko wyśrodkowane w pionie i poziomie */

flex-wrap — zawijanie

Code
.container {
  display: flex;
  flex-wrap: nowrap;  /* domyślne — jedna linia */
  flex-wrap: wrap;    /* zawijaj do nowych linii */
  flex-wrap: wrap-reverse;
}

gap — odstępy między elementami

Code
.container {
  display: flex;
  gap: 20px;           /* odstęp między wszystkimi */
  gap: 20px 10px;      /* row-gap column-gap */
  row-gap: 20px;
  column-gap: 10px;
}

Do budowania odstępów w nowoczesnych layoutach zwykle lepiej używać gap niż kombinacji marginów na dzieciach.

Właściwości flex items

Code
.item {
  flex-grow: 1;    /* jak bardzo może rosnąć (0 = nie rośnie) */
  flex-shrink: 1;  /* jak bardzo może się kurczyć */
  flex-basis: 200px; /* bazowy rozmiar przed grow/shrink */
  
  /* Skrót */
  flex: 1;         /* flex-grow: 1, flex-shrink: 1, flex-basis: 0 */
  flex: 0 0 200px; /* nie rośnie, nie kurczy się, 200px */
  
  /* Nadpisanie align-items dla tego elementu */
  align-self: center;
  
  /* Kolejność */
  order: 2;  /* domyślnie 0, wyższe = później */
}

Przykład: nawigacja z Flexbox

Code
.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem 2rem;
}
 
.nav-links {
  display: flex;
  gap: 2rem;
}
Code
<nav class="navbar">
  <a href="/" class="logo">Logo</a>
  <ul class="nav-links">
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>

Grid — układ dwuwymiarowy

CSS Grid to system layoutu dla dwóch wymiarów (wiersze I kolumny).

Podstawy

Code
.container {
  display: grid;
  grid-template-columns: 200px 200px 200px;
  grid-template-rows: 100px 100px;
}

Tworzy siatkę 3×2.

Jednostka fr (fraction)

Code
.container {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  /* Pierwsza i trzecia kolumna: 1 część, środkowa: 2 części */
}

repeat() — powtórzenia

Code
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);      /* 3 równe kolumny */
  grid-template-columns: repeat(4, 100px);    /* 4 kolumny po 100px */
  grid-template-columns: repeat(3, 1fr 2fr);  /* 1fr 2fr 1fr 2fr 1fr 2fr */
}

minmax() — elastyczne rozmiary

Code
.container {
  display: grid;
  grid-template-columns: repeat(3, minmax(200px, 1fr));
  /* Min 200px, max równy podział */
}

auto-fit i auto-fill

Code
.container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  /* Tworzy tyle ścieżek ile się zmieści, także pustych */
}
 
.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  /* Podobnie, ale puste ścieżki się zwijają i istniejące elementy się rozszerzają */
}

gap w Grid

Code
.container {
  display: grid;
  gap: 20px;           /* równe odstępy */
  gap: 20px 10px;      /* row-gap column-gap */
}

Pozycjonowanie elementów w Grid

Code
.item {
  grid-column: 1 / 3;     /* od linii 1 do linii 3 (zajmuje 2 kolumny) */
  grid-row: 1 / 2;        /* od linii 1 do linii 2 */
  
  /* Skróty */
  grid-column: span 2;    /* zajmij 2 kolumny */
  grid-row: span 3;       /* zajmij 3 wiersze */
  
  /* Pozycja + rozpiętość */
  grid-area: 1 / 1 / 3 / 4;  /* row-start / col-start / row-end / col-end */
}

Nazwy obszarów (grid-template-areas)

Code
.container {
  display: grid;
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header header"
    "sidebar main aside"
    "footer footer footer";
  gap: 20px;
}
 
.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.aside   { grid-area: aside; }
.footer  { grid-area: footer; }
Code
<div class="container">
  <header class="header">Header</header>
  <nav class="sidebar">Sidebar</nav>
  <main class="main">Main content</main>
  <aside class="aside">Aside</aside>
  <footer class="footer">Footer</footer>
</div>

justify-items i align-items w Grid

Code
.container {
  display: grid;
  /* Wyrównanie zawartości w komórkach */
  justify-items: start | end | center | stretch;  /* oś X */
  align-items: start | end | center | stretch;    /* oś Y */
  place-items: center center;  /* skrót: align justify */
}

justify-content i align-content w Grid

Code
.container {
  display: grid;
  /* Wyrównanie całej siatki w kontenerze */
  justify-content: start | end | center | space-between | space-around;
  align-content: start | end | center | space-between | space-around;
}

Flexbox vs Grid — kiedy który?

ScenariuszFlexboxGrid
Nawigacja (pozioma)⚠️
Lista kart (responsywna)⚠️
Centrowanie elementu
Layout strony (header/main/footer)⚠️
Wyrównanie w jednej linii⚠️
Złożona siatka
Nieznana liczba elementów

Zasada ogólna:

  • Flexbox — gdy układ zależy od contentu (content-out)
  • Grid — gdy masz zdefiniowaną siatkę (layout-in)

Mogą współpracować!

Code
/* Grid dla głównego layoutu */
.page {
  display: grid;
  grid-template-areas:
    "header"
    "main"
    "footer";
}
 
/* Flexbox dla nawigacji w headerze */
.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
 
/* Grid dla siatki kart */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
}
 
/* Flexbox wewnątrz karty */
.card {
  display: flex;
  flex-direction: column;
}
.card-footer {
  margin-top: auto; /* przesuń na dół */
}

FAQ

Czym różni się Flexbox od Grid i kiedy używać którego?

Flexbox to system jednowymiarowy — układa elementy w jednej osi (wiersz lub kolumna) i dobrze sprawdza się w komponentach takich jak nawigacja, pasek przycisków czy wyrównanie elementów w linii. Grid to system dwuwymiarowy, który kontroluje jednocześnie wiersze i kolumny — idealny do layoutu całej strony, siatek kart czy złożonych widoków. W praktyce oba mechanizmy często współpracują w jednym projekcie.

Co to jest box-sizing: border-box i dlaczego warto go używać?

Domyślnie CSS oblicza width i height tylko dla contentu — padding i border są dodawane do zadeklarowanego rozmiaru. Z border-box zadeklarowana szerokość obejmuje padding i border, co sprawia, że rozmiary elementów są bardziej przewidywalne. Standardowa praktyka to ustawienie *, *::before, *::after { box-sizing: border-box; } globalnie.

Czym jest margin collapse i jak go uniknąć?

Margin collapse to zjawisko, w którym sąsiednie pionowe marginesy elementów blokowych łączą się w jeden większy margines zamiast sumować się. Zwycięża większy z dwóch marginesów. Można go uniknąć stosując Flexbox lub Grid jako kontener (dzieci kontenerów flex/grid nie podlegają margin collapse), dodając padding lub border do rodzica, albo używając overflow: hidden.

Jak wyśrodkować element w pionie i poziomie w CSS?

Najprościej z Flexbox: ustaw display: flex; justify-content: center; align-items: center; na kontenerze. Działa zarówno dla dzieci znanych, jak i nieznanych rozmiarów. Alternatywnie z Grid: display: grid; place-items: center;. Oba sposoby są nowoczesne i dobrze wspierane przez przeglądarki.

Czym jest position: sticky i jak go używać?

position: sticky to hybryda relative i fixed — element zachowuje się jak relatywny w normalnym przepływie dokumentu, ale "przykleja się" do określonej pozycji podczas scrollowania. Wymaga podania wartości top, right, bottom lub left i działa tylko w obrębie swojego kontenera rodzica. Przydatne do nagłówków tabel, paneli bocznych czy pasków nawigacji wewnątrz sekcji.

Jak działają auto-fit i auto-fill w CSS Grid?

Oba słowa kluczowe w repeat() automatycznie tworzą tyle kolumn (lub wierszy) ile się zmieści. auto-fill tworzy ślady nawet jeśli nie ma wystarczająco elementów (możliwe puste kolumny), natomiast auto-fit zwija puste ślady i rozciąga istniejące elementy. Najczęściej używa się ich razem z minmax() do tworzenia responsywnych siatek bez media queries: grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)).

Czy można łączyć Flexbox i Grid w jednym projekcie?

Tak i jest to zalecane podejście. Grid dobrze nadaje się do makrolayoutu strony (header, sidebar, main, footer), natomiast Flexbox sprawdza się wewnątrz komponentów. Przykładowo: Grid definiuje układ strony, Flexbox wyrównuje elementy w nawigacji, a Grid ponownie tworzy siatkę kart produktów — każde narzędzie tam, gdzie ma sens.

Podsumowanie

PojęcieOpis
Box Modelcontent + padding + border + margin
box-sizing: border-boxwidth zawiera padding i border
display: blockpełna szerokość, nowa linia
display: inlinetylko potrzebna szerokość
position: relativeprzesunięcie względem siebie
position: absolutewzględem rodzica z position
Flexbox1D layout (wiersz LUB kolumna)
Grid2D layout (wiersze I kolumny)

Box Model i positioning to fundamenty. Flexbox i Grid to narzędzia, które rozwiązują 99% problemów z layoutem. Jeśli korzystasz z Tailwind CSS, to jego klasy utility — flex, grid, gap — to właśnie te mechanizmy pod spodem.

Źródła i dokumentacja


Chcesz więcej o CSS? Sprawdź artykuł o responsywnym designie — kolejny krok do profesjonalnych stron.

Pracuję z tym zawodowo.

Jeśli chcesz przełożyć ten temat na lepszą architekturę frontendu, uporządkować React lub Next.js i podnieść jakość pracy zespołu, skontaktuj się ze mną. Pomagam zamieniać wiedzę z artykułów w praktyczne decyzje technologiczne.

O autorze

Maciej Sala

Maciej Sala — project manager i frontendowiec z doświadczeniem w marketingu internetowym. Na co dzień pracuję z Reactem, Next.js i TypeScriptem, łącząc perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat związany z branżą gier wideo jako project manager i game designer.

Absolwent historii na Uniwersytecie Jagiellońskim i studiów podyplomowych z marketingu internetowego na Akademii Górniczo-Hutniczej w Krakowie. Poza pracą trenuje na siłowni, maluje figurki i realizuje własne projekty.

Biblioteka wiedzy

Czytaj dalej

Zobacz więcej wpisów
Astro.js vs Next.js — które narzędzie wybrać w 2026 roku?

Astro.js vs Next.js — które narzędzie wybrać w 2026 roku?

Fachowe porównanie Astro.js i Next.js z perspektywy developera pracującego na co dzień w Next.js. Architektura, wydajność, SEO, DX, koszty i konkretne use case — z benchmarkami i przykładami kodu.

Maciej Sala

Maciej Sala

Founder Strivelab