Przed CSS Grid budowanie właściwego układu strony oznaczało elementy pływające, clearfiksy, ujemne marginesy i modlitwę, że kolumny się nie zawalą. Flexbox pomógł, ale jest fundamentalnie jednowymiarowy — masz wiersz lub kolumnę, nie oba jednocześnie. CSS Grid to pierwszy system układu natywny dla CSS, który jest naprawdę zaprojektowany dla układów dwuwymiarowych. Wyzwaniem nie jest obsługa przeglądarek — jest ona powszechna od 2017 roku. Wyzwaniem jest to, że API ma duży obszar powierzchni, a nazewnictwo jest na tyle niespójne, że może być mylące bez jasnego modelu bazowego. Ten artykuł skupia się na częściach, których będziesz używać w prawdziwej pracy.
Wiersze, kolumny i kontener siatki
Wszystko zaczyna się od display: grid na kontenerze. Po ustawieniu bezpośrednie dzieci automatycznie stają się elementami siatki — nie jest potrzebna żadna klasa. Kolumny i wiersze definiujesz za pomocą grid-template-columns i grid-template-rows.
Kluczową jednostką do zrozumienia jako pierwszą jest fr — jednostka ułamkowa. Reprezentuje ułamek dostępnej przestrzeni w kontenerze siatki po uwzględnieniu ścieżek o stałym rozmiarze. Jedna 1fr oznacza „weź jeden udział z pozostałej przestrzeni". Dwie kolumny 1fr 1fr oznaczają równe połowy. 2fr 1fr oznacza, że pierwsza kolumna dostaje dwa razy więcej niż druga. Ta jednostka sprawia, że płynne siatki czują się naturalne.
/* 3-column blog card layout */
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto;
gap: 1.5rem;
}
/* repeat(3, 1fr) is shorthand for: 1fr 1fr 1fr */
/* auto rows size to their content */repeat(n, track) to skrót, dzięki któremu nie musisz pisać tego samego rozmiaru ścieżki pięć razy. Działa również z mieszanymi wartościami: repeat(3, 1fr 2fr) tworzy 6-kolumnową siatkę z naprzemiennie wąskimi i szerokimi ścieżkami. W przypadku wierszy auto jest prawie zawsze tym, czego chcesz — wiersze dopasowują rozmiar do zawartości, chyba że wyraźnie potrzebujesz stałej wysokości.
<div class="card-grid">
<article class="card">
<img src="/posts/grid-post-1.jpg" alt="CSS Grid post 1">
<h2>Getting Started with Grid</h2>
<p>A practical intro for developers moving from Flexbox.</p>
</article>
<article class="card">
<img src="/posts/grid-post-2.jpg" alt="CSS Grid post 2">
<h2>Grid vs Flexbox</h2>
<p>Which layout tool to reach for, and when.</p>
</article>
<article class="card">
<img src="/posts/grid-post-3.jpg" alt="CSS Grid post 3">
<h2>Responsive Grids Without Media Queries</h2>
<p>Using minmax() to handle reflow automatically.</p>
</article>
</div>Właściwość gap
Przed Gridem dodawanie odstępów między dziećmi układu oznaczało marginesy — a marginesy oznaczały pamiętanie o resetowaniu ostatniego dziecka, radzenie sobie ze zwijaniem i dodawanie ujemnych marginesów na kontenerze, aby to skompensować. gap rozwiązuje to w czysty sposób. Dodaje przestrzeń tylko między ścieżkami, nigdy na zewnętrznych krawędziach siatki.
/* Uniform gap between all rows and columns */
.card-grid {
gap: 1.5rem;
}
/* Different row and column gaps */
.dashboard {
row-gap: 2rem;
column-gap: 1rem;
}
/* gap is shorthand: row-gap then column-gap */
.dashboard {
gap: 2rem 1rem;
}gap działa również we Flexboksie (zastąpił stary alias grid-gap). Jeśli już używasz Flexboksa dla komponentu i potrzebujesz tylko odstępów między elementami, możesz użyć gap tam również bez przełączania się na Grid.Jawne rozmieszczanie elementów
Domyślnie elementy siatki przepływają do następnej dostępnej komórki w kolejności źródłowej. Ale prawdziwa moc Grida polega na tym, że możesz umieszczać elementy w dowolnym miejscu — a nawet sprawiać, że będą obejmować wiele ścieżek. Właściwościami są grid-column i grid-row, które odwołują się do linii siatki (linie między ścieżkami, numerowane od 1).
/* Dashboard gdzie pierwsza karta obejmuje 2 kolumny */
.dashboard {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
}
.card--featured {
grid-column: 1 / 3; /* start at line 1, end at line 3 */
}
/* span keyword: simpler when you don't care about exact position */
.card--featured {
grid-column: span 2; /* occupy 2 column tracks from wherever it lands */
}
/* Spanning rows too */
.card--tall {
grid-row: span 2; /* occupy 2 row tracks */
}
/* Explicit positioning: place a panel in column 3-5, row 1-2 */
.sidebar {
grid-column: 3 / 5;
grid-row: 1 / 2;
}Numery linii zaczynają się od 1 dla lewej/górnej krawędzi i idą do n+1, gdzie n to liczba ścieżek. Ujemne numery linii liczą się od prawej/dolnej strony: grid-column: 1 / -1 rozciąga element na pełną szerokość siatki niezależnie od liczby kolumn — przydatna sztuczka dla elementów pełnej szerokości wewnątrz kontenera wielokolumnowego.
grid-template-areas — nazwij swój układ
To jest funkcja, która sprawia, że Grid jest naprawdę świetny dla układów na poziomie strony, i jest to najbardziej czytelne API w całym CSS. Zamiast rozmieszczać elementy według numerów linii, rysujesz układ jako grafikę ASCII w CSS, a następnie mówisz każdemu elementowi, który nazwany obszar zajmuje. Specyfikacja grid-template-areas wymaga, aby każdy wiersz miał tę samą liczbę komórek, a każdy nazwany obszar musi być prostokątny — bez kształtów L.
/* Full page layout: header, sidebar, main, footer */
.page-shell {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 60px 1fr 50px;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100vh;
gap: 0;
}
.page-header { grid-area: header; }
.page-sidebar { grid-area: sidebar; }
.page-main { grid-area: main; }
.page-footer { grid-area: footer; }<div class="page-shell">
<header class="page-header">Site header / nav</header>
<nav class="page-sidebar">Sidebar navigation</nav>
<main class="page-main">Primary content</main>
<footer class="page-footer">Footer</footer>
</div>Kropka (.) w ciągu szablonu pozostawia komórkę celowo pustą. Możesz również układać tę samą nazwę w wielu komórkach, aby je obejmować — dokładnie to robi "header header" powyżej. Gdy zmieniasz rozmiar lub restrukturyzujesz układ dla różnych punktów widzenia, aktualizujesz ciąg szablonu w jednym miejscu, zamiast szukać po wielu deklaracjach grid-column.
auto-fill kontra auto-fit z minmax()
Wzorzec stojący za responsywnymi siatkami bez zapytań mediów to jedna z najbardziej praktycznych sztuczek Grida: repeat(auto-fill, minmax(250px, 1fr)). Rozbijmy to — minmax(250px, 1fr) mówi, że każda kolumna powinna mieć co najmniej 250px szerokości, ale rozszerzać się, aby wypełnić dostępną przestrzeń. auto-fill mówi Gridowi, aby tworzył tyle kolumn, ile się zmieści w kontenerze przy tym ograniczeniu. Wynikiem jest siatka, która automatycznie zyskuje i traci kolumny w miarę zmiany widoku.
/* Product card grid — reflows automatically, no media queries */
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 1.5rem;
}
/* auto-fit collapses empty tracks — items stretch to fill the container */
.product-grid--stretch {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 1.5rem;
}Różnica między auto-fill i auto-fit jest subtelna, ale ma znaczenie, gdy jest mniej elementów niż kolumn. Z auto-fill Grid rezerwuje miejsce dla potencjalnych ścieżek nawet jeśli są puste — elementy pozostają przy minimalnej szerokości. Z auto-fit puste ścieżki zwijają się do zera, a elementy rozszerzają się, aby wypełnić wiersz. W przypadku siatek produktów i galerii, gdzie chcesz, aby elementy zajmowały dostępną przestrzeń, auto-fit zazwyczaj wygląda lepiej. W przypadku siatek, gdzie ważniejsza jest spójna szerokość elementów, właściwym wyborem jest auto-fill.
auto-fill i auto-fit z minmax() mają doskonałą obsługę — 97%+ globalnie według caniuse. Możesz używać tego wzorca już dziś bez fallbacku w każdym nowoczesnym projekcie.Wyrównanie w Gridzie
Grid używa tego samego modułu Box Alignment co Flexbox, więc nazwy właściwości mapują się bezpośrednio, jeśli znasz już Flexbox. Kluczowy model mentalny: justify działa na osi inline (poziomej w większości trybów pisania), align działa na osi blokowej (pionowej).
justify-items/align-items— domyślne wyrównanie dla wszystkich elementów w ich komórce siatki. Domyślnie jeststretch, dlatego elementy siatki automatycznie wypełniają swoją komórkę.justify-content/align-content— rozdziela same ścieżki w kontenerze, gdy siatka jest mniejsza niż kontener (podobnie dojustify-contentwe Flexboksie na kontenerze flex).justify-self/align-self— nadpisuje wyrównanie dla pojedynczego elementu. Przydatne, gdy jeden element musi być wyśrodkowany, podczas gdy inne są rozciągnięte.- Wartości:
start,end,center,stretch,space-between,space-around,space-evenly(ostatnie trzy tylko dla*-content).
/* Center all card content within its cell */
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
align-items: start; /* cards align to their cell top, not stretched */
gap: 1.5rem;
}
/* Center a single call-to-action card both ways */
.card--cta {
justify-self: center;
align-self: center;
}
/* Distribute tracks with space between — useful for nav bars */
.nav-grid {
display: grid;
grid-template-columns: auto auto auto;
justify-content: space-between;
}Jeśli przychodzisz z Flexboksa, główna różnica polega na tym, że wyrównanie w Gridzie działa w przestrzeni dwuwymiarowej — możesz kontrolować obie osie jednocześnie. We Flexboksie wyrównanie osi poprzecznej dotyczy całego wiersza lub kolumny elementów; w Gridzie każdy element ma własną komórkę, więc justify-self i align-self dają kontrolę na poziomie elementu bez potrzeby używania elementów opakowujących.
Prawdziwy układ: kompletna powłoka dashboardu
Oto kompletna struktura strony CSS Grid dla aplikacji dashboardu — nagłówek, zwijanv pasek nawigacyjny, główny obszar treści z własną wewnętrzną siatką i stopka. To wzorzec, którego użyłbyś jako zewnętrzną powłokę układu dla dashboardu SaaS lub panelu administracyjnego.
/* ── Dashboard shell ──────────────────────────────────── */
.dashboard-shell {
display: grid;
grid-template-columns: 220px 1fr;
grid-template-rows: 56px 1fr 44px;
grid-template-areas:
"topbar topbar"
"sidenav content"
"sidenav footer";
min-height: 100vh;
}
.dashboard-topbar { grid-area: topbar; background: #1e293b; }
.dashboard-sidenav { grid-area: sidenav; background: #0f172a; overflow-y: auto; }
.dashboard-content { grid-area: content; padding: 1.5rem; overflow-y: auto; }
.dashboard-footer { grid-area: footer; background: #f8fafc; border-top: 1px solid #e2e8f0; }
/* ── Collapsed sidebar variant ────────────────────────── */
.dashboard-shell--collapsed {
grid-template-columns: 56px 1fr;
}
/* ── Content area — internal card grid ─────────────────── */
.dashboard-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
grid-template-rows: auto;
align-content: start;
gap: 1.25rem;
}
/* Stat cards take one column each; the main chart spans all */
.stat-card { /* auto-placed, one column */ }
.main-chart { grid-column: 1 / -1; } /* always full width */
/* ── Responsive: collapse to single-column on small screens */
@media (max-width: 768px) {
.dashboard-shell {
grid-template-columns: 1fr;
grid-template-rows: 56px 1fr auto 44px;
grid-template-areas:
"topbar"
"content"
"sidenav"
"footer";
}
}<div class="dashboard-shell">
<header class="dashboard-topbar">
<a href="/dashboard" class="brand">AppName</a>
<nav class="topbar-actions">
<button class="btn-icon" aria-label="Notifications">
<span class="icon-bell"></span>
</button>
<img src="/avatar/user.png" class="user-avatar" alt="User menu">
</nav>
</header>
<nav class="dashboard-sidenav">
<ul>
<li><a href="/dashboard">Overview</a></li>
<li><a href="/dashboard/analytics">Analytics</a></li>
<li><a href="/dashboard/users">Users</a></li>
<li><a href="/dashboard/settings">Settings</a></li>
</ul>
</nav>
<main class="dashboard-content">
<div class="stat-card">Monthly Revenue</div>
<div class="stat-card">Active Users</div>
<div class="stat-card">Churn Rate</div>
<div class="main-chart">Revenue Chart (full width)</div>
</main>
<footer class="dashboard-footer">
<p>AppName v2.4.1 — <a href="/status">System Status</a></p>
</footer>
</div>Kilka rzeczy wartych odnotowania w tym przykładzie. Stopka jest umieszczona w kolumnie sidenav na dużych ekranach dzięki definicji obszaru szablonu, co utrzymuje ją wizualnie oddzieloną od przewijającego się obszaru głównej treści. 1fr na wierszach i overflow-y: auto na panelu treści oznacza, że obszar treści przewija się niezależnie — topbar i sidenav pozostają na miejscu, co jest standardowym UX dashboardu. Zapytanie @media na dole zmienia kolejność obszarów siatki dla urządzeń mobilnych bez dotykania HTML.
Podsumowanie
Krzywa uczenia się CSS Grid pochodzi głównie z nazewnictwa — linie, ścieżki, obszary, jednostki fr — ale gdy model zaskoczy, jest niezwykle bezpośredni. W przypadku prawdziwych układów: używaj grid-template-areas dla powłok stron (czyta się jak makieta), używaj repeat(auto-fit, minmax()) dla responsywnych siatek kart i używaj jawnego grid-column: span N dla elementów, które przełamują standardowy przepływ. Nie potrzebujesz całego API Grida do 90% prawdziwej pracy — te wzorce obejmują większość z tego.
Do dalszego czytania: Kompletny przewodnik CSS-Tricks po Gridzie to najlepsza jednostronicowa referencja; dokumentacja MDN CSS Grid zagłębia się w każdą właściwość; a specyfikacja W3C CSS Grid Level 1 jest zaskakująco czytelna, jeśli chcesz zrozumieć dokładne zachowanie przypadków brzegowych. Zgodność przeglądarek jest doskonała — sprawdź caniuse.com/css-grid dla aktualnych liczb. Jeśli pracujesz nad projektem na żywo i chcesz wyczyścić swój CSS, Formater CSS na tej stronie porządkuje i upiększa arkusze stylów.