Før CSS Grid betydde bygging av en ordentlig sidelayout floats, clearfixes, negative marginer og en bønn om at kolonnene dine ikke kollapset. Flexbox hjalp, men det er fundamentalt endimensjonalt — du får en rad eller en kolonne, ikke begge. CSS Grid er det første layoutsystemet som er innebygd i CSS og faktisk er designet for todimensjonale layouter. Utfordringen er ikke nettleserstøtte — den har vært universell siden 2017. Utfordringen er at API-et har mye overflate og navngivingen er inkonsekvent nok til å være forvirrende hvis du ikke har den underliggende modellen klar. Denne artikkelen fokuserer på delene du vil bruke i virkelig arbeid.

Rader, kolonner og rutenettbeholderen

Alt starter med display: grid på beholderen. Når det er satt, blir de direkte barna rutenetttelementer automatisk — ingen klasse trengs på dem. Du definerer kolonne- og radspor med grid-template-columns og grid-template-rows.

Nøkkelenheten å forstå først er fr — brøkenheten. Den representerer en brøkdel av den tilgjengelige plassen i rutenettbeholderen etter at spor med fast størrelse er tatt hensyn til. Én 1fr betyr "ta én andel av hva enn som er igjen." To kolonner med 1fr 1fr betyr like halvdeler. 2fr 1fr betyr at den første kolonnen får dobbelt så mye som den andre. Dette er enheten som gjør flytende rutenett naturlige.

css
/* 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, spor) er en forkortelse slik at du ikke trenger å skrive den samme sporstørrelsen fem ganger. Det fungerer også med blandede verdier: repeat(3, 1fr 2fr) gir deg et 6-kolonne rutenett som alternerer smale og brede spor. For rader er auto nesten alltid det du ønsker — rader skaleres etter innholdet med mindre du eksplisitt trenger en fast høyde.

html
<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>

gap-egenskapen

Før Grid betydde å legge til avstand mellom layoutbarn marginer — og marginer betydde å huske å nullstille det siste barnet, håndtere kollaps, og legge til negative marginer på beholderen for å kompensere. gap løser dette rent. Det legger til plass mellom spor kun, aldri på de ytre kantene av rutenettet.

css
/* 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;
}
Merk: gap fungerer også i Flexbox (det erstattet det gamle grid-gap-aliaset). Hvis du allerede bruker Flexbox for en komponent og bare trenger avstand mellom elementer, kan du bruke gap der også uten å bytte til Grid.

Plassering av elementer eksplisitt

Som standard flyter rutenettselementer inn i den neste tilgjengelige cellen i kilderekkefølge. Men Grids virkelige kraft er at du kan plassere elementer hvor som helst — og til og med få dem til å spanne over flere spor. Egenskapene er grid-column og grid-row, som refererer til rutenettlinjer (linjene mellom spor, nummerert fra 1).

css
/* Dashboard where the first card spans 2 columns */
.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;
}

Linjenumre starter på 1 for venstre/øverste kant og går opp til n+1 der n er antall spor. Negative linjenumre teller fra høyre/bunn: grid-column: 1 / -1 strekker et element over hele bredden av rutenettet uavhengig av hvor mange kolonner det er — et nyttig triks for elementer i full bredde inne i en flerkolonne-beholder.

grid-template-areas — gi layouten din navn

Dette er funksjonen som gjør Grid genuint flott for layouter på sidenivå, og det er det mest lesbare API-et i all CSS. I stedet for å plassere elementer etter linjenumre, tegner du layouten som ASCII-kunst i CSS, og forteller deretter hvert element hvilket navngitt område det okkuperer. grid-template-areas-spesifikasjonen krever at hver rad har samme antall celler, og hvert navngitt område må være rektangulært — ingen L-former.

css
/* 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; }
html
<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>

Et punktum (.) i malstrengen etterlater en celle bevisst tom. Du kan også stable samme navn over flere celler for å spenne dem — det er nøyaktig hva "header header" gjør ovenfor. Når du endrer størrelse eller omstrukturerer layouten for ulike visningspunkter, oppdaterer du malstrengen på ett sted i stedet for å jakte på tvers av flere grid-column-deklarasjoner.

auto-fill vs auto-fit med minmax()

Mønsteret bak responsive rutenett med null mediespørringer er ett av Grids mest praktiske triks: repeat(auto-fill, minmax(250px, 1fr)). Bryt det ned — minmax(250px, 1fr) sier at hver kolonne skal være minst 250px bred, men utvide seg for å fylle tilgjengelig plass. auto-fill forteller Grid å opprette så mange kolonner som vil passe i beholderen gitt den begrensningen. Resultatet er et rutenett som automatisk får og mister kolonner etter hvert som visningsporten endres.

css
/* 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;
}

Forskjellen mellom auto-fill og auto-fit er subtil men betyr noe når det er færre elementer enn kolonner. Med auto-fill reserverer Grid plass for de potensielle sporene selv om de er tomme — elementer forblir på minimumbredden sin. Med auto-fit kollapser tomme spor til null og elementene utvider seg for å fylle raden. For produktrutenett og gallerier der du vil at elementer skal bruke tilgjengelig plass, ser auto-fit vanligvis bedre ut. For rutenett der konsekvent elementbredde betyr mer enn å fylle beholderen, er auto-fill det riktige valget.

Nettleserstøtte: auto-fill og auto-fit med minmax() har utmerket støtte — 97%+ globalt ifølge caniuse. Du kan bruke dette mønsteret i dag uten en reserveløsning i ethvert moderne prosjekt.

Justering i Grid

Grid bruker samme Box Alignment-modul som Flexbox, så egenskapsnavnene mapper direkte hvis du allerede kan Flexbox. Den viktigste mentale modellen: justify opererer på den innebygde aksen (horisontal i de fleste skrivemodi), align opererer på blokk-aksen (vertikal).

  • justify-items / align-items — standard justering for alle elementer i sin rutenettcelle. Standard er stretch, noe som er grunnen til at rutenettselementer fyller cellen sin automatisk.
  • justify-content / align-content — distribuerer sporene selv innenfor beholderen når rutenettet er mindre enn beholderen (ligner Flexbox justify-content på flex-beholderen).
  • justify-self / align-self — overstyrer justeringen for ett enkelt element. Nyttig når ett element trenger å sentreres mens andre strekker seg.
  • Verdier: start, end, center, stretch, space-between, space-around, space-evenly (siste tre kun for *-content).
css
/* 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;
}

Hvis du kommer fra Flexbox, er den viktigste forskjellen at Grid-justering opererer på et todimensjonalt rom — du kan kontrollere begge akser simultant. I Flexbox gjelder kryss-akse-justeringen en hel rad eller kolonne med elementer; i Grid har hvert element sin egen celle, så justify-self og align-self gir deg kontroll per element uten behov for innpakningselementer.

Virkelig layout: komplett dashbord-skall

Her er en komplett CSS Grid-sidestruktur for en dashbord-applikasjon — topptekst, sammenleggbar navigasjonssidepanel, hovedinnholdsområde med eget internt rutenett, og bunntekst. Dette er mønsteret du vil bruke som det ytterste layout-skallet for et SaaS-dashbord eller adminpanel.

css
/* ── 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";
  }
}
html
<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 &mdash; <a href="/status">System Status</a></p>
  </footer>
</div>

Noen ting verdt å merke seg i dette eksempelet. Bunnteksten er plassert i sidenav-kolonnen på store skjermer ved hjelp av malområdedefinisjonen, noe som holder den visuelt separert fra det rullende hovedinnholdsområdet. 1fr på rader og overflow-y: auto på innholdsruten betyr at innholdsområdet ruller uavhengig — topplisten og sidepanelet forblir faste på plass, noe som er standard dashbord-UX. @media-spørringen nederst omordner rutenettområdene for mobil uten å berøre HTML i det hele tatt.

Oppsummering

CSS Grids læringskurve kommer mest fra navngivingen — linjer, spor, områder, fr-enheter — men når modellen klikker, er den bemerkelsesverdig direkte. For virkelige layouter: bruk grid-template-areas for sideskall (det leser som en wireframe), bruk repeat(auto-fit, minmax()) for responsive kortrutenett, og bruk eksplisitt grid-column: span N for elementer som bryter standardflyten. Du trenger ikke alt av Grids API for 90% av virkelig arbeid — disse mønstrene dekker det meste av det.

For videre lesing: CSS-Tricks Complete Guide to Grid er den beste enkeltsidesreferansen; MDNs CSS Grid-dokumentasjon går i dybden på hver egenskap; og W3C CSS Grid Level 1-spesifikasjonen er overraskende lesbar hvis du vil forstå den eksakte oppførselen til kanttilfeller. Nettleserkompabilitet er utmerket — sjekk caniuse.com/css-grid for gjeldende tall. Hvis du jobber med et levende prosjekt og vil rydde opp i CSS-en din, vil CSS Formatter på dette nettstedet rydde opp og forskjønne stilarkene dine.