Document flow en CSS positionering

Voor je met CSS-positionering werkt, moet je eerst begrijpen hoe elementen zich standaard gedragen in een webpagina. Browsers plaatsen HTML-elementen namelijk volgens de normale document flow.

Pas wanneer je die standaardflow begrijpt, wordt duidelijk wat position: relative, absolute, fixed en sticky precies veranderen.

Wat is document flow?

De document flow is de gewone volgorde waarin de browser HTML-elementen op de pagina zet. Block-level elementen worden onder elkaar geplaatst. Inline elementen staan op dezelfde regel, zolang er voldoende plaats is.

Block-level elementen

Elementen zoals <div>, <p>, <section> en <h1> zijn block-level elementen. Ze starten normaal op een nieuwe regel en nemen standaard de volledige breedte in van hun parent element.

<div>Eerste div-element</div>
<div>Tweede div-element</div>
Eerste div-element
Tweede div-element

Inline elementen

Inline elementen zoals <span> en <a> blijven standaard op dezelfde regel staan en nemen enkel de ruimte in die ze nodig hebben.

<span>Inline element</span>
<span>Nog een inline element</span>
Inline element Nog een inline element

Opgelet: inline elementen reageren standaard niet zoals block-level elementen op width en height.

Nesting van elementen

HTML-elementen kunnen ook in elkaar genest worden. Een parent element kan één of meerdere child-elementen bevatten.

<div class="flow-nested">
  <p>Dit is een paragraaf binnen een div.</p>
</div>

Dit is een paragraaf binnen een div.

Waarom is document flow zo belangrijk?

Zodra je elementen gaat positioneren met CSS, kan je ze visueel verplaatsen of zelfs volledig uit de normale document flow halen. Daardoor kunnen elementen elkaar overlappen of onverwacht reageren.

Wie document flow niet begrijpt, begrijpt meestal ook niet waarom een element plots “verkeerd” staat.


De CSS-eigenschap position

Met de CSS-eigenschap position bepaal je hoe een element zich gedraagt ten opzichte van de normale document flow en ten opzichte van andere elementen.

De belangrijkste waarden zijn:

  • static
  • relative
  • absolute
  • fixed
  • sticky

Opgelet: zodra een element uit de normale document flow wordt gehaald, reageren andere elementen daar niet meer op. Dat is vaak de oorzaak van overlap of onverwachte layoutproblemen.

position: static

static is de standaardwaarde. Een element met position: static volgt gewoon de normale document flow.

De eigenschappen top, right, bottom, left en z-index hebben bij een statisch element geen effect.

.element {
  position: static;
  top: 20px; /* heeft geen effect */
}

In echte projecten schrijf je zelden expliciet position: static, omdat dit al de standaardinstelling is.

position: relative

Een relatief gepositioneerd element blijft deel uitmaken van de normale document flow, maar kan visueel verschoven worden ten opzichte van zijn oorspronkelijke plaats.

Dat betekent:

  • het element blijft zijn oorspronkelijke ruimte behouden;
  • de rest van de layout blijft dus rekenen met de oude plaats;
  • alleen de visuele weergave verschuift.

De eigenschappen top, right, bottom en left verplaatsen het element ten opzichte van zijn oorspronkelijke positie.

<div class="relative-demo">Ik ben relatief gepositioneerd</div>
.relative-demo {
  position: relative;
  top: 10px;
  left: 20px;
  background-color: lightblue;
  padding: 0.75rem;
}
Ik ben relatief gepositioneerd

Een relatief gepositioneerd element is ook belangrijk omdat het vaak dient als referentiepunt voor een absoluut gepositioneerd child-element.

position: absolute

Een absoluut gepositioneerd element wordt volledig uit de normale document flow gehaald. Andere elementen doen dus alsof het er niet meer is.

Een absoluut element positioneert zich ten opzichte van het dichtstbijzijnde parent element dat zelf een position heeft anders dan static.

In de praktijk is dat vaak een parent met position: relative.

Als er geen gepositioneerde parent bestaat, positioneert het element zich ten opzichte van de viewport.

<div class="absolute-stage">
  Parent element
  <div class="absolute-demo">Absolute</div>
</div>
.absolute-stage {
  position: relative;
  height: 160px;
  border: 1px solid #ccc;
  padding: 1rem;
}

.absolute-demo {
  position: absolute;
  top: 20px;
  right: 20px;
  background-color: lightgreen;
  padding: 0.75rem;
}
Parent element
Absolute

Dit gebruik je bijvoorbeeld voor:

  • badges op cards,
  • knoppen of iconen op afbeeldingen,
  • decoratieve elementen die niet mee in de gewone flow moeten zitten.

position: fixed

Een fixed element wordt gepositioneerd ten opzichte van de viewport. Dat betekent dat het op dezelfde plaats blijft staan, ook wanneer je scrollt.

<div class="fixed-demo">Ik blijf staan</div>
.fixed-demo {
  position: fixed;
  bottom: 10px;
  left: 10px;
  background-color: lightcoral;
  padding: 0.5rem 0.75rem;
}
Ik blijf staan

Veelgebruikte toepassingen zijn:

  • een knop “terug naar boven”,
  • een zwevende chatknop,
  • een vaste navigatiebalk of cookie-banner.

Omdat een fixed element boven de rest kan zweven, gebruik je deze techniek best doordacht.

position: sticky

Sticky is een combinatie van relative en fixed. Het element gedraagt zich eerst alsof het gewoon in de flow zit, maar blijft “plakken” zodra een bepaalde drempel bereikt wordt.

In dit voorbeeld staat het sticky element niet meteen bovenaan. Tijdens het scrollen schuift het eerst mee omhoog, en zodra het de ingestelde grens bereikt, blijft het staan.

<div class="sticky-scrollbox">
  <p>Scroll naar beneden…</p>
  <p>Nog meer content…</p>
  <div class="sticky-demo">Ik ben sticky</div>
  <p>Nog meer content…</p>
  <p>Nog meer content…</p>
  <p>Nog meer content…</p>
  <p>Nog meer content…</p>
  <p>Nog meer content…</p>
</div>
.sticky-scrollbox {
  height: 260px;
  overflow: auto;
  border: 1px solid #ccc;
  padding: 1rem;
}

.sticky-demo {
  position: sticky;
  top: 1rem;
  background-color: lightpink;
  padding: 0.75rem;
}

Scroll naar beneden…

Nog meer content…

Ik ben sticky

Nog meer content…

Nog meer content…

Nog meer content…

Nog meer content…

Nog meer content…

Nog meer content…

Nog meer content…

Sticky werkt alleen als:

  • je een waarde opgeeft zoals top: 1rem,
  • er effectief gescrold kan worden,
  • het element zich in een container bevindt waarin het gedrag zichtbaar wordt.

De eigenschappen top, right, bottom en left

Deze eigenschappen bepalen de afstand tot een rand. Ze werken niet bij position: static, maar wel bij andere positioneringsmethodes.

  • top: afstand tot de bovenkant
  • right: afstand tot de rechterkant
  • bottom: afstand tot de onderkant
  • left: afstand tot de linkerkant

Bij relative verschuiven ze het element ten opzichte van zijn oude plaats. Bij absolute, fixed en sticky bepalen ze waar het element komt te staan.

Besluit over position

Met position bepaal je of een element:

  • gewoon in de flow blijft (static),
  • licht verschoven wordt maar zijn plaats behoudt (relative),
  • uit de flow gehaald wordt (absolute),
  • vast aan de viewport blijft hangen (fixed),
  • of pas blijft plakken vanaf een bepaalde drempel (sticky).

De CSS-eigenschap z-index

Zodra elementen elkaar overlappen, wordt het belangrijk om te bepalen welk element visueel bovenaan ligt. Daarvoor gebruik je z-index.

Je kan z-index zien als de volgorde op de denkbeeldige Z-as: niet van links naar rechts, niet van boven naar onder, maar van voor naar achter.

Belangrijk: z-index werkt alleen op elementen met een positionering anders dan static.

Basisvoorbeeld

In dit voorbeeld overlappen twee absoluut gepositioneerde elementen. Het element met de hoogste z-index ligt bovenaan.

<div class="z-stage">
  <div class="z-box z-behind">z-index: 1</div>
  <div class="z-box z-front">z-index: 2</div>
</div>
.z-stage {
  position: relative;
  width: 240px;
  height: 180px;
  border: 1px solid #ccc;
}

.z-box {
  position: absolute;
  width: 130px;
  height: 130px;
}

.z-behind {
  z-index: 1;
}

.z-front {
  z-index: 2;
  top: 30px;
  left: 50px;
}
z-index: 1
z-index: 2

Wat is een stacking context?

Een stacking context is een soort “groep” of “laag” waarin elementen met elkaar vergeleken worden. Alleen elementen binnen dezelfde stacking context kunnen rechtstreeks met elkaar vergeleken worden via z-index.

Met andere woorden: stacking contexts bepalen welke z-index waarden met elkaar vergeleken worden.

Een element met z-index: 999 kan niet zomaar boven een element uit een andere stacking context springen.

Hoe ontstaat een stacking context?

Een stacking context ontstaat onder andere door:

  • een gepositioneerd element met een eigen z-index,
  • opacity kleiner dan 1,
  • transform,
  • en enkele andere speciale eigenschappen.

Veelgemaakte fouten met z-index

  • z-index gebruiken op een element zonder positionering,
  • steeds grotere getallen gebruiken zonder het echte probleem te begrijpen,
  • vergeten dat een parent element een nieuwe stacking context kan maken.

Tip: probeer z-index-problemen eerst te begrijpen via structuur en positionering, niet door zomaar 9999 te gebruiken.


Verdieping (optioneel): waarom z-index soms “niet werkt”

Soms lijkt het alsof z-index geen enkel effect heeft, zelfs wanneer je een heel hoge waarde gebruikt. In de meeste gevallen ligt dat niet aan z-index zelf, maar aan het feit dat elementen zich in verschillende stacking contexts bevinden.

Alleen elementen die zich binnen dezelfde stacking context bevinden, kunnen met elkaar vergeleken worden via z-index. Zodra een parent element een nieuwe stacking context creëert, begint de stapelvolgorde daar opnieuw.

<div class="stacking-demo">
  <div class="stacking-col">
    <h3 class="h6">Context A (normaal)</h3>
    <div class="stacking-stage">
      <div class="stacking-bg">Achtergrond</div>
      <div class="stacking-label">LABEL (z-index: 999)</div>
    </div>
  </div>

  <div class="stacking-col stacking-context">
    <h3 class="h6">Context B (nieuwe stacking context)</h3>
    <div class="stacking-stage">
      <div class="stacking-card">Kaart</div>
      <div class="stacking-label">LABEL (z-index: 999)</div>
    </div>
  </div>
</div>
.stacking-context {
  /* Deze eigenschap creëert een nieuwe stacking context */
  transform: translateY(0);
}

Context A (normaal)

Achtergrond
LABEL (z-index: 999)

Context B (nieuwe stacking context)

Kaart met transform
LABEL (z-index: 999)

Samengevat: z-index werkt altijd correct, maar alleen binnen dezelfde stacking context. Eigenschappen zoals transform kunnen onzichtbaar een nieuwe context creëren en zo de verwachte volgorde doorbreken.

Besluit over z-index

z-index bepaalt welke elementen boven of onder elkaar liggen, maar werkt enkel in combinatie met positionering en binnen dezelfde stacking context.

Wie begrijpt hoe stacking contexts werken, zal veel sneller begrijpen waarom een element soms niet bovenaan komt, zelfs met een hoge z-index.


Overzicht — CSS positionering (cheatsheet)

Gebruik dit schema als snelle geheugensteun bij oefeningen.

Belangrijk: positionering bepaalt waar een element staat, maar ook of het nog deel uitmaakt van de normale document flow.

De 5 position-waarden

  • static → standaard gedrag (in de flow, geen controle met top/left)
  • relative → blijft in de flow, maar kan visueel verschuiven
  • absolute → uit de flow, kijkt naar eerste parent met position
  • fixed → uit de flow, positie t.o.v. viewport
  • sticky → eerst relative, wordt fixed tijdens scroll

De gouden regels

  • absolute → zoekt de eerste parent met position: relative
  • geen parent met position? → dan wordt <body> gebruikt
  • z-index → werkt alleen op gepositioneerde elementen
  • hogere z-index → ligt boven andere elementen (binnen dezelfde context)
  • nieuwe stacking context → ontstaat o.a. door transform, opacity, filter

Flow vs out-of-flow

  • In flow: static, relative
  • Out of flow: absolute, fixed
  • Hybrid: sticky

Typische fouten (zeer belangrijk)

  • Absolute werkt niet? → parent heeft geen position: relative
  • z-index werkt niet? → element heeft geen position
  • Sticky werkt niet?
    • geen top ingesteld
    • parent heeft overflow: hidden of auto
    • container is te klein om te scrollen

Snelle check

Vraag jezelf altijd af:
  • → Zit mijn element nog in de flow?
  • → Waarop baseert mijn positionering zich?
  • → Is er een parent met position: relative?
  • → Zit mijn element in dezelfde stacking context?
Terug naar boven