- 1 Basisprincipes
- 2 Flexbox
- 3 Raster en subraster
- 4 Containerquery's
- 5 Native overlays, ankerpositionering en stapelcontexten
- 6 Overloop bijhouden en stabiliteit van de indeling
- 7 Mechanica van de viewport en distributie van tracks
- 8 rasterbanen (ook wel metselwerk genoemd)
1 Fundamentals
Gebruik waar mogelijk de lay-outengine van de browser voor betere prestaties. Gebruik intrinsieke grootte, logische eigenschappen en aspect-ratio voordat je hardgecodeerde dimensies of ingewikkelde mediaquery's gebruikt.
1.1 Welke lay-outmodus moet ik gebruiken?
Doorloop het beslissingsschema van boven naar beneden en stop bij de eerste overeenkomst. Indelingen kunnen in elkaar worden genest en elke beslissing is gebaseerd op de use case voor die container.
- Is het een eenvoudige rij OF kolom met items? Gebruik flexbox: 1D, content-first, content wordt verdeeld over één as.
- Moet een genest element worden uitgelijnd met de tracks van het bovenliggende raster? Gebruik subgrid: 2D, relatiegericht, neemt bovenliggende tracks over zodat kleinkinderen kunnen worden uitgelijnd met broers en zussen.
- Is het een complexe pagina- of componentstructuur met rijen EN kolommen? Gebruik grid: 2D, layout-first, je definieert het skelet en de content vult het.
- Is de content een lange tekst die moet worden opgesplitst in evenwichtige kolommen? Gebruik meerdere kolommen: 1D-stroom, krantenstijl.
- Zijn er items van verschillende hoogtes die strak moeten worden ingepakt? Gebruik vandaag nog grid met
grid-auto-flow: dense. Gebruik native masonry (ook wel 'grid lanes' genoemd) alleen als dit wordt geleverd in je Baseline-doel (zie §8). - Moet een element boven de pagina zweven en ruimtelijk gekoppeld blijven aan een trigger, zelfs over DOM-grenzen of stapelcontexten heen? Gebruik ankerpositionering:
anchor-nameop de trigger,position-anchorop de overlay (zie §5).
1.2 Werkprincipes
- Gebruik logische eigenschappen (
inline-size,block-size,margin-inline,padding-block,inset-inline-start) voor de afmetingen en afstand van de indeling. Ga naar CSS: Modern Architecture voor een volledige beschrijving. - Pas het mentale model content-first versus layout-first toe: flexbox als items de flow bepalen, grid als je eerst het skelet definieert.
- Gebruik de
place-*-afkortingen (place-content,place-items,place-self) om beide assen in één declaratie uit te lijnen. - Gebruik intrinsieke grootte (
min-content,max-content,fit-content()) en flexibele tracks (fr,minmax()) in plaats van vastewidth/height: minder mediaquery's, veerkrachtigere indelingen. - Gebruik
overflow: hiddenNIET om indelingproblemen te maskeren. Los ze op bij de bron. Bepaalde vervangen elementen zoals<canvas>,<img>,<svg>,<video>,<iframe>gebruiken bijvoorbeeld standaarddisplay: inline, wat in bepaalde lay-outs een gat kan toevoegen. In plaats van de ruimte te verkleinen metoverflow, verwijder je deze door een block display-outside-waarde toe te passen (bijvoorbeelddisplay: block,grid,flex, enzovoort). - Gebruik
aspect-ratioom ruimte te reserveren voor media en indelingsverschuivingen te voorkomen voordat componenten worden geladen.
.sidebar { inline-size: max-content; } /* Size to longest unbreakable token. */
.main-content { inline-size: fit-content; } /* Grow to available space, no further. */
.media { aspect-ratio: 16 / 9; inline-size: 100%; block-size: auto; }
body.centered { display: grid; place-content: center; min-block-size: 100dvb; }
Ga naar Berekenen met intrinsieke grootten voor
calc-size()en intrinsieke groottebepaling op basis van beperkingen.
2 Flexbox
Eendimensionale indeling: items lopen langs één hoofdas met uitlijning op de dwarsas. Gebruik dit voor navigatiebalken, werkbalken, itemrijen en elke verdeling met één rij of kolom.
Doen:
- Stel een context in met
display: flexen stel de hoofdas in metflex-direction(rowis de standaardinstelling). - Gebruik
flex-wrap: wrapals er een mogelijkheid is dat de tekst niet past.nowrapzonderoverflow: auto/hiddenloopt over op smalle viewports. - Laag
flex-wrap: balancenaflex-wrap: wrapop teruggelopen rijen van compacte items (zoals tag clouds, filterpillen, knoppengroepen of navigatielinks) om items gelijkmatig over regels te verdelen en te voorkomen dat er één item op de laatste rij staat. Browsers zonder ondersteuning vallen terug opwrap. - Gebruik de verkorte notatie
flex<grow> <shrink> <basis>(bijv.flex: 1 1 250px) voor items in plaats vanflex-grow/flex-shrink/flex-basisafzonderlijk in te stellen. - Gebruik
gap(of de volledigerow-gap/column-gap) voor de afstand tussen items in plaats van onderliggende marges. - Voeg het voorvoegsel
safetoe aan de positionele uitlijning (bijv.align-items: safe center) zodat content waarop kan worden gefocust niet wordt afgekapt als de container smaller is dan de content. - Duw één item naar het uiteinde van de hoofdas met
margin-inline-start: auto(ofmargin-block-start: auto). Dat is de standaard ontsnappingsroute. - Overschrijf de uitlijning op de dwarsas per item met
align-self. - Gebruik
align-itemsom alle items op de kruisas te centreren. Gebruikmargin: autovoor één item om het onafhankelijk op beide assen te centreren. Gebruikalign-contentalleen als de container wordt teruggebroken en extra ruimte tussen de rijen heeft. - Stel
min-inline-size: 0(ofmin-width: 0) in voor flex-items die lange, niet-afbreekbare content bevatten (URL's, code, lange tekenreeksen). Flex-items worden standaard niet kleiner dan hun contentgrootte, waardoor overloop ontstaat.
Wat u niet moet doen:
- Gebruik
justify-selfniet voor flex-items. Dit werkt alleen voor raster-, blok- en absoluut gepositioneerde lay-outs. Gebruik in plaats daarvan automatische marges. - Gebruik
orderofflex-direction: *-reverseniet om interactieve content opnieuw te ordenen. Ze wijzigen alleen de visuele volgorde. De DOM-volgorde bepaalt nog steeds de opeenvolgende focus, dus de tabstroom van het toetsenbord komt niet overeen met wat de gebruiker ziet. - Verwar
space-around(halve tussenruimte aan de uiteinden) niet metspace-evenly(gelijke tussenruimtes voor, tussen en na). - Vergeet niet dat de as wordt omgedraaid: bij
flex-direction: columnwordtjustify-contentuitgelijnd op de blokas enalign-itemsop de inline-as, het tegenovergestelde van de standaardinstelling. - Maak de container en de onderliggende elementen niet zo groot dat ze elkaar vullen. Dit is een veelvoorkomende oorzaak van overloop en onverwachte resultaten. Geef één zijde een definitieve grootte.
- Stel niet zowel
flex-basisalswidth/inline-sizein voor hetzelfde item.flex-basisheeft voorrang in een flex-context enwidthwordt genegeerd. Gebruikflex-basis(of de afkortingflex) als de enige bron van waarheid voor de grootte van flex-items.
.card-grid { display: flex; flex-flow: row wrap; gap: 1rem; }
.card-item { flex: 1 1 250px; } /* grow, shrink, basis */
.card-item-action { margin-inline-start: auto; } /* Push to main-axis end. */
.toolbar { display: flex; align-items: safe center; }
.tag-list { display: flex; flex-wrap: wrap; flex-wrap: balance; gap: 0.5rem; }
3 Raster en subraster
2-dimensionale indeling: definieer rijen EN kolommen expliciet of laat de engine ze afleiden. Met subgrid neemt een genest raster de tracks van de bovenliggende elementen over, zodat kleinkinderen worden uitgelijnd met broers en zussen.
Rasterfuncties kiezen:
- Weet je precies hoeveel kolommen je nodig hebt?
- Ja: gebruik expliciete tracks (
grid-template-columns: 200px 1fr,repeat(3, 1fr), enzovoort) - Hebben verschillende kolommen verschillende formaten nodig (zijbalk + hoofdgedeelte, koptekst die alles omvat)? → gebruik
grid-template-areasvoor benoemde, leesbare regio's - Zijn alle kolommen uniform of puur gepositioneerd op regelnummer? → gebruik
repeat(N, ...)of benoemde lijnen - Nee (responsief, onbekend aantal items): gebruik
repeat(auto-fit, minmax(min, 1fr)) - Moeten items op de laatste rij worden uitgerekt om de resterende ruimte te vullen? →
auto-fit - Moeten lege tracks op de laatste rij hun minimale grootte behouden (zodat spookslots in kolommen behouden blijven)? →
auto-fill
- Ja: gebruik expliciete tracks (
- Moet je een item op een specifieke locatie plaatsen?
- Ja: gebruik
grid-column: <start> / <end>ofgrid-area: <name> - Nee (deze beslaat meerdere tracks, de positie van de flow maakt niet uit): gebruik
grid-column: span <n>
- Ja: gebruik
- Moeten onderliggende elementen de trackgrootten van het bovenliggende raster overnemen (uitlijning met onregelmatige randen tussen broers en zussen)?
- Ja: gebruik een subraster op de betreffende as
- Is het aantal kinderen per cel variabel? → subgrid slechts één as; gebruik
grid-auto-rows/grid-auto-columnsvoor de andere - Is het aantal kinderen vastgesteld? → subgrid op beide assen is prima
- Nee: standaardraster, geen subraster nodig
Doen:
- Stel een context vast met
display: grid. - Gebruik
grid-template-areasvoor complexe lay-outs op paginaniveau. Gebiedsnamen zijn zelfdocumenterend en de declaratie kan worden uitgelijnd in rijen en kolommen voor een snelle leesbaarheid. - Gebruik
repeat(auto-fit, minmax(200px, 1fr))voor responsieve kaartrasters die gevulde tracks uitrekken om de rij te vullen, ofauto-fillom lege herhaalde tracks op hun minimale grootte te houden. - Gebruik
frvoor proportionele trackdistributie enminmax(min, max)voor flexibele maar begrensde tracks. - Plaats items met
grid-column: span <n>om de grootte over tracks te bepalen,grid-column: <start> / <end>om ze op specifieke regels te plaatsen ofgrid-area: <name>voor benoemde regio's. - Gebruik subgrid (
grid-template-columns: subgridofgrid-template-rows: subgrid) om het probleem van de 'rafelige rand' in kaartlijsten op te lossen. Interne elementen zoals titels, metadata en CTA's worden dan uitgelijnd tussen broers en zussen. - Koppel een subgrid-declaratie aan een voorgaande expliciete
grid-template-rows/-columns-declaratie als een fallback met dezelfde cascade voor oudere browsers.
Wat u niet moet doen:
- Verwacht niet dat de trackgrootte van
auto-fit/auto-fillafkomstig is van de itemcontent. Deze is afkomstig van het argumentrepeat()size. - Gebruik
grid-auto-flow: denseniet voor interactieve content. De items worden efficiënt ingepakt, maar visueel opnieuw geordend, waardoor de tabvolgorde van het toetsenbord in de DOM-volgorde wordt verbroken. - Pas geen subraster toe op beide assen als het aantal onderliggende elementen variabel is. Extra's komen in de laatste track terecht. Gebruik in plaats daarvan
grid-auto-rows/grid-auto-columnsvoor de impliciete as. - Verwar
justify-items/align-items(lijnt itemcontent uit binnen de track) niet metjustify-content/align-content(lijnt de rastertracks uit binnen de container). Als je de verkeerde gebruikt, heeft dit geen effect. - Gebruik
repeat(auto-fit/auto-fill, ...)niet zonder een bepaaldeinline-sizevoor de container. Indisplay: inline-gridof een flex-item zonder grootte heeft de container geen breedte om te verdelen, waardoor het aantal tracks onvoorspelbaar wordt.
3.1 Codevoorbeeld: grid en subgrid
Pagina-shell: <main class="page-layout"> bevat <header>, <aside>, een <section class="card-grid"> met <div class="card"> onderliggende elementen en <footer>.
/* Align grid-template-areas in rows and columns for readability. */
.page-layout {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-areas:
"header header header"
"sidebar main main"
"footer footer footer";
gap: 1.5rem;
}
header { grid-area: header; }
aside { grid-area: sidebar; }
footer { grid-area: footer; }
.card-grid {
grid-area: main;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
grid-template-rows: auto 1fr; /* title block, body block */
gap: 1rem;
}
.card {
grid-row: span 2;
display: grid;
/* Same-cascade fallback: ignored when subgrid is supported. */
grid-template-rows: auto 1fr;
grid-template-rows: subgrid;
}
4 Containerquery's
Voer een query uit voor de grootte (of berekende stijl) van een bovenliggende container in plaats van het kijkvenster. Mentaal model: containerquery's = componentcontext; mediaquery's = algemene pagina-indeling en gebruikersvoorkeuren (prefers-color-scheme, prefers-reduced-motion).
Doen:
- Stel een containmentcontext in met
container-type: inline-size(alleen breedtequery's) ofcontainer-type: size(beide assen) op een wrapper voordat de onderliggende elementen kunnen worden opgevraagd. - Geef containers een naam met
container-name(of de afkortingcontainer:container: card / inline-size) als geneste contexten kunnen botsen. - Neem containerquery-eenheden op bij de berekening van dynamische typen en tussenruimte:
cqi/cqb(logische inline/blok),cqw/cqh(fysiek),cqmin/cqmax. - Geef de container een definitieve
block-sizewanneercontainer-type: sizewordt gebruikt. Zonder deze worden afstammelingen samengevouwen omdat de container door de formaatbeperking de content negeert.
Wat u niet moet doen:
- Gebruik
block-sizeniet alscontainer-type-waarde. Deze is niet geldig. Gebruiksizevoor beide assen. - Verwacht niet dat de intrinsieke grootte van onderliggende elementen de container beïnvloedt nadat je
container-typehebt gedeclareerd. De container wordt berekend alsof deze geen onderliggende elementen heeft zodra de containment actief is. - Vertrouw niet op containerquery-eenheden in afstammelingen van een niet-gekwalificeerde voorouder. Ze vallen terug naar de kleine viewport (
svw/svh).
4.1 Codevoorbeeld: vloeiende typografie met containerquery-eenheden
.card-wrapper {
container: card / inline-size; /* shorthand for container-name / container-type */
}
@container card (inline-size > 400px) {
.content {
display: flex;
gap: 2rem;
}
}
.title {
/* Fluid type bound to the container width, not the viewport. */
font-size: clamp(1rem, 4cqi, 2rem);
}
Ga naar Size Aware Styling en Fluid Scaling voor componentgestuurde responsieve stijlpatronen.
5 Native overlays, ankerpositionering en stapelcontexten
Wanneer u elke overlay-primitief gebruikt:
- Gebruik
popovervoor tijdelijke, niet-modale UI (fly-outs, toasts, tooltips). Deze bevindt zich in de bovenste laag, dus je hebt geenz-index-beheer nodig. - Gebruik
<dialog>met.showModal()voor modale interacties die focusvastlegging en een inerte achtergrond vereisen. - Combineer
popoveren.showModal()niet in hetzelfde element. Dit zijn elkaar uitsluitende runtimestatussen.
Ankerpositionering (ruimtelijke indeling van overlays):
- Gebruik
position-area(ofanchor()voor insets) enanchor-size()om een overlay te positioneren en de grootte ervan te bepalen ten opzichte van de trigger. - Gebruik
position-try-fallbacks: flip-block(offlip-inline) om de browser de positie te laten aanpassen als de overlay de viewport overschrijdt. - Combineer geen fysieke en logische zoekwoorden in één
position-area-waarde. Kies één coördinatensysteem. - Voer een functiedetectie uit met
@supports (anchor-name: --x)en zorg voor een absolute-position-fallback.
Ga naar Declarative Dialog and Popover Control en Position Aware Tooltips voor volledige implementatiegegevens, polyfill-strategieën en de referentie voor de waarde
popover. Ga naar Viewport-Aware Dropdowns en Anchor Positioning Tab Underline voor ankerpositionering die wordt toegepast op dropdownmenu's, flyouts en tabbladindicatoren.
6 Overlooptracking en lay-outstabiliteit
Beheer lay-outverschuivingen, schuifbalken en knippen op een voorspelbare manier.
Doen:
- Gebruik
overflow: autozodat schuifbalken alleen verschijnen als de content daadwerkelijk overloopt. - Gebruik
overflow: clipom content te knippen zonder een scrollcontainer te maken. Kies voor overloop metoverflow-clip-margin. - Gebruik
scrollbar-gutter: stableom ruimte te reserveren voor scrollbalken en indelingsverschuivingen te voorkomen als de content groeit. - Gebruik
overscroll-behavior: contain(ofnone) voor geneste scrollgebieden (zoals zijbalken, modale vensters en lijsten) om te voorkomen dat scrollketens naar de pagina worden overgedragen, ofoverscroll-behavior: chainvoor tussenliggende scrollwrappers (zoals swipe-to-reveal-tracks) als overmatig scrollen moet worden doorgegeven aan een bovenliggende scroller zonder een lokale rubberband-bounce te activeren. Gebruik de triade
-webkit-line-clamp+display: -webkit-box+-webkit-box-orient: verticalvoor afkapping van meerdere regels. Ondanks het voorvoegsel is dit patroon volledig gespecificeerd en niet beëindigd. Declareer de afkortingline-clampzonder voorvoegsel ernaast. Browsers die deze nog niet ondersteunen, negeren de property zonder problemen. Wat u niet moet doen:Gebruik geen
overflow: scrollalsautovoldoende is.scrolldwingt scrollbalken af, zelfs als er niets is om doorheen te scrollen.Gebruik
overflow: hiddenniet als je alleen wilt knippen.hiddenmaakt een scrollcontainer die programmatisch kan worden gescrold.
.scrollable-list {
max-block-size: 400px;
overflow-y: auto;
scrollbar-gutter: stable; /* Reserve scrollbar space. */
overscroll-behavior: contain; /* No scroll chaining into the page. */
}
.snippet {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
line-clamp: 3; /* Ignored where unsupported. */
overflow: clip;
}
Ga naar Overflow Clipping Control voor meer informatie over
overflow: clipenoverflow-clip-margin. Ga naar Swipen om te tonen voor geneste scroll-overdracht metoverscroll-behavior: chainin de UI voor swipen om te tonen. Ga naar De kleur of dikte van een scrollbalk aanpassen, Donkere modus en Scrollbalk aanpassen aan voorkeuren voor hoog contrast voor de kleur, grootte en thema's van de scrollbalk.
7 Mechanica van de viewport en distributie van tracks
- Gebruik
dvh/dvwvoor containers met een mobiele indeling die rekening moeten houden met verschuivingen in de browser-UI (samenvouwen/uitvouwen van de URL-balk). - Gebruik
100vwniet voor een indeling op volledige breedte. De breedte van de scrollbalk wordt dan genegeerd en er ontstaat een horizontale overloop. Gebruik in plaats daarvan100%,100dvwof100svw.
Ga naar Responsief ontwerp voor het toegangspunt voor responsief ontwerp (dynamische viewport-eenheden, ruimte reserveren voor media, vloeiende typografie).
8 rasterbanen (ook wel metselwerk genoemd)
De specificatie is in ontwikkeling onder de naam 'grid lanes' (bijv. display: grid-lanes), terwijl eerdere experimentele prototypes grid-template-rows: masonry gebruikten.
Doen:
- Gebruik vandaag nog een raster met
grid-auto-flow: densevoor een compacte indeling en accepteer dat de DOM-volgorde misschien niet overeenkomt met de visuele volgorde. - Gebruik meerdere kolommen (
columns: 3; column-gap: 1rem) voor een metselwerkachtige flow met veel content als items documentfragmenten zijn in plaats van kaarten met een gelijk gewicht. - Behandel
grid-template-rows: masonryalleen als een progressive enhancement: voer een functieherkenning uit met@supports.
Wat u niet moet doen:
- Verzend
grid-template-rows: masonryniet als harde vereiste totdat je Baseline-doel is bijgewerkt.
.gallery { columns: 3 200px; column-gap: 1rem; }
.gallery > * { break-inside: avoid; margin-block-end: 1rem; }
@supports (grid-template-rows: masonry) {
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-template-rows: masonry;
gap: 1rem;
columns: unset;
}
}