PX vs REM vs EM: de complete gids voor CSS-eenheden
Hier is het korte antwoord op px vs rem vs em, nog vóór enige uitleg. Gebruik rem voor vrijwel alle maatvoering (lettergroottes, padding, marges, gaps, border-radius en breakpoints), omdat het meeschaalt met de lettergrootte-instelling van de browser van de lezer. Gebruik px voor de paar dingen die nooit mogen meeschalen, zoals een 1px-rand of een precieze schaduwverschuiving. Gebruik em voor het zeldzame lokale geval waarin een waarde mee moet groeien met de eigen lettergrootte van het element, zoals knop-padding die de tekst van de knop volgt.
Die regel dekt 90% van de keuzes. De andere 10% is waar het misgaat: de em-rekenkunde die zich opstapelt en iedereen de eerste keer verrast, de media-query-bug die layouts breekt bij inzoomen, en de gevallen waarin px juist de toegankelijkere keuze is. Hieronder staat voor elk van die drie werkende CSS, plus een spiekbriefje per eigenschap dat je open kunt houden terwijl je styles schrijft.
Wat px, rem en em eigenlijk betekenen
Het verschil tussen de drie eenheden zit in hun referentiepunt.
px is een absolute eenheid. Eén px is één CSS-pixel, en die blijft die grootte ongeacht wat eromheen staat. border: 1px solid is één pixel, punt uit. Het addertje is dat “absoluut” ook betekent dat het de voorkeuren van de gebruiker negeert. Verderop staat waarom dat ertoe doet.
rem is relatief ten opzichte van de lettergrootte van het root-element. De root is <html>, en browsers stellen die lettergrootte standaard in op 16px. Dus 1rem is gelijk aan 16px in een standaardopstelling, overal op de pagina, ongeacht de nesting. Die consistentie is precies de aantrekkingskracht: één ankerwaarde, geen verrassingen.
em is relatief ten opzichte van de lettergrootte van het huidige element (of de ouder, voor andere eigenschappen dan font-size). Omdat die referentie verandert naarmate je elementen nest, verschuiven em-waarden afhankelijk van de context. Dezelfde 1.5em kan op de ene plek 24px worden en op de andere 30px.
Het anker om uit je hoofd te leren is 16px = 1rem. Als je verder niets onthoudt, onthoud dan dit. Wanneer je een specifieke waarde moet omrekenen, doet de px-naar-rem-converter de deling tegen elke basis die je kiest.
px vs rem vs em in één oogopslag
| Eenheid | Relatief ten opzichte van | Schaalt mee met gebruikerslettergrootte? | Typisch gebruik | Gedrag bij nesting |
|---|---|---|---|---|
px | Niets (absoluut) | Nee | Randen, schaduwverschuivingen, haarlijnen | Altijd dezelfde grootte |
rem | Lettergrootte van root <html> | Ja | Lettergrootte, witruimte, breakpoints | Altijd dezelfde grootte |
em | Lettergrootte van huidige element | Ja | Lokale waarden gekoppeld aan een component | Stapelt op — kan afdrijven |
De twee kolommen die de meeste discussies beslechten zijn “schaalt mee met gebruikerslettergrootte” en “gedrag bij nesting.” rem wint op beide: het respecteert de voorkeur van de lezer én blijft voorspelbaar. em deelt het eerste voordeel maar levert het tweede in.
Hoe elke eenheid wordt berekend
De rekensom zelf is eenvoudig. Wat mensen in de war brengt, is door welk getal je deelt of vermenigvuldigt.
rem gebruikt de root-lettergrootte:
rem = px ÷ root-font-size
Bij de standaard 16px-root is 24px ÷ 16 = 1.5rem. Om terug te gaan vermenigvuldig je: 1.5rem × 16 = 24px. Elke rem op de pagina gebruikt diezelfde 16 (of wat je de root ook geeft), wat precies de reden is dat rem voorspelbaar is.
em gebruikt de eigen lettergrootte van het huidige element:
em = px ÷ current-element-font-size
Als de font-size van een element 20px is, dan is 1em op dat element 20px, 0.5em is 10px, en een 1.5em-padding is 30px. Verander de lettergrootte van het element en elke em-waarde die eraan hangt verandert mee. Die lokale koppeling is het hele punt van em, en meteen ook de valkuil.
De em-stapelvalkuil
Wanneer je elementen nest die allemaal em gebruiken voor de lettergrootte, vermenigvuldigen de waarden zich naar beneden door de boom. Elk niveau erft de berekende lettergrootte van zijn ouder en past daar zijn eigen em-factor bovenop toe.
.menu { font-size: 1.2em; } /* ouder is 16px → 19.2px */
.menu .item { font-size: 1.2em; } /* ouder is 19.2px → 23.04px */
.menu .item .sub { font-size: 1.2em; } /* ouder is 23.04px → 27.648px */
Elk niveau is “120% van zijn ouder,” wat onschuldig klinkt. Maar omdat de ouder al gegroeid was, is het derde niveau 1.2 × 1.2 × 1.2 = 1.728em ten opzichte van de oorspronkelijke 16px, zo’n 27.6px, niet de 19.2px die je op het oog uit de losse regel zou aflezen. Nest een lijst in een lijst in een component en de tekst zwelt op een manier die lastig te traceren is.
rem omzeilt dit volledig. 1.2rem is 19.2px, of het nu bovenaan het document staat of twaalf niveaus diep, omdat het altijd tegen de root meet, nooit tegen de ouder. Wanneer een waarde uitkomt op een grootte die je niet had verwacht, is de eerste vraag of het em is (ouder-relatief, stapelt op) of rem (root-relatief, stabiel). Als je een verdwaalde rem aan het uitpluizen bent en snel zijn pixelgrootte wilt zien, lost de rem-naar-px-converter hem direct op.
Wanneer rem te gebruiken
Grijp standaard naar rem. Het is de juiste eenheid voor lettergroottes, padding, marges, gaps, border-radius en media-query-breakpoints: alles wat mee moet schalen wanneer een lezer zijn tekstgrootte aanpast.
Die laatste zinsnede is het toegankelijkheidsargument, en het is niet hypothetisch. De enquêtes van WebAIM onder screenreader- en slechtziende gebruikers vinden telkens weer dat een groot deel van de gebruikers de standaardlettergrootte van hun browser of besturingssysteem aanpast, velen van hen ruim boven de standaard 16px. Een layout opgemaakt in rem eert die verandering: zet de standaard op 20px en elke rem-gebaseerde waarde groeit evenredig mee. Een layout opgemaakt in px negeert het volledig. De tekst blijft vastgepind op zijn hardgecodeerde grootte, hoezeer de lezer hem ook groter nodig heeft.
:root {
font-size: 16px; /* 1rem = 16px */
}
h1 { font-size: 2rem; } /* 32px, schaalt mee met gebruikersvoorkeur */
p { font-size: 1rem; } /* 16px */
.card { padding: 1.5rem; } /* 24px */
.card { border-radius: 0.5rem; } /* 8px */
Omdat elke waarde hier verankerd is aan dezelfde root, herschaalt één wijziging aan de root-lettergrootte de hele interface in verhouding. Dat is ook wat een designsysteem samenhangend houdt: witruimte en typografie bewegen samen in plaats van uit elkaar te drijven.
De 62.5%-truc
Er bestaat een populaire kortere weg om rem-rekenkunde triviaal te maken. Zet de root-lettergrootte op 62.5%, wat neerkomt op 62.5% × 16px = 10px:
html {
font-size: 62.5%; /* nu 1rem = 10px */
}
body {
font-size: 1.6rem; /* herstel leesbare body-tekst van 16px */
}
h1 { font-size: 2.4rem; } /* 24px */
p { font-size: 1.6rem; } /* 16px */
Met een 10px-root klapt het hoofdrekenen in elkaar tot “deel de pixelwaarde door 10”: 24px → 2.4rem, 12px → 1.2rem. Het enige addertje is dat je een leesbare body-grootte herstelt met body { font-size: 1.6rem }, want een kale 10px-basis laat de standaardtekst veel te klein. Het gebruik van 62.5% als percentage in plaats van 10px houdt het relatief, zodat een lezer die zijn browserstandaard opschaalt nog steeds evenredige groei krijgt. Als je deze basis aanhoudt, zet dan de root-lettergrootte van de converter op 10 zodat hij overeenkomt met je stylesheet.
Wanneer em te gebruiken
Gebruik em wanneer je wilt dat een waarde meeschaalt met de eigen lettergrootte van het element, niet met de root. Het klassieke geval is een knop:
.btn {
font-size: 1rem; /* gemeten tegen de root */
padding: 0.75em 1.5em; /* padding volgt de tekst van de knop */
}
.btn--large {
font-size: 1.25rem; /* één wijziging herschaalt alles */
}
Omdat de padding in em staat, herschaalt de .btn--large-modifier de tekst én de padding samen vanuit één enkele declaratie, zodat de knop op elke grootte evenredig blijft. Dezelfde logica geldt voor een icoon dat in em is opgemaakt zodat het past bij de tekstregel waarin het staat, of letter-spacing die mee moet groeien met de letter.
De strategie die in de praktijk werkt is rem voor het globale skelet, em voor lokale verhoudingen. Stel de lettergrootte in rem in zodat die luistert naar de root en de voorkeur van de gebruiker; stel de handvol waarden die dat element moeten volgen in em. Houd em alleen weg van alles wat diep nest, anders sluipt de stapelvalkuil van zonet er weer in.
Wanneer px te gebruiken
Sommige waarden horen echt niet mee te schalen, en px is daar juist voor: een 1px-haarlijnrand, een precieze box-shadow-verschuiving, een 2px-focusring. Dit zijn weergavedetails, geen inhoud. Een rand die “meeschaalt” naar 1.25px wanneer de gebruiker zijn tekst vergroot, wint daar niets mee en kan als een wazige lijn renderen. px houdt hem scherp.
.divider { border-bottom: 1px solid; } /* moet 1px blijven */
.card { box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /* vaste verschuiving */
.input:focus { outline: 2px solid; } /* scherpe focusring */
De verrassende nuance: wanneer px toegankelijker is
De meeste “gebruik altijd rem”-adviezen slaan dit over. De toegankelijke keuze is niet “overal rem.” Het is “schaal wat moet schalen, fixeer wat niet moet schalen.”
Een 1px-rand is een vast detail. Het dwingen in rem zodat het groeit wanneer de gebruiker tekst vergroot, helpt de leesbaarheid niet; het maakt een haarlijn alleen maar wazig. Voor deze eigenschappen is px de toegankelijkere keuze, juist omdat het op zijn plek blijft.
De fout die mensen werkelijk maken is het omgekeerde: px gebruiken voor de dingen die wel zouden moeten reageren, zoals lettergroottes en breakpoints. Daar schaadt px de toegankelijkheid. De regel gaat dus niet over de eenheid, maar over de eigenschap. Vraag je af of de waarde inhoud is waar de lezer mee omgaat (schaal hem met rem) of een vast weergavedetail (zet hem vast met px). De eenheid volgt uit het antwoord.
De media-query-valkuil
Deze breekt echte layouts en bijna geen enkele gids waarschuwt ervoor. Media-query-breakpoints geschreven in px reageren niet op de lettergrootte-zoom van de browser zoals je zou hopen.
Stel je een breakpoint voor op width: 600px waar een sidebar inklapt. Een gebruiker met verminderd zicht zet zijn browserstandaard op 24px om comfortabel te lezen. Je inhoud heeft nu meer horizontale ruimte nodig, want de grotere tekst wil eerder herschikken. Maar een px-breakpoint weet niet dat de tekst groeide; hij klapt nog steeds om op precies 600px viewport, dus de layout schakelt op het verkeerde moment en inhoud raakt opgepropt of overlapt.
Vergelijk de twee aanpakken:
/* px-breakpoint — negeert de lettergrootte-voorkeur van de gebruiker */
@media (min-width: 600px) {
.sidebar { display: block; }
}
/* em/rem-breakpoint — reageert op de lettergrootte van de gebruiker */
@media (min-width: 37.5em) {
.sidebar { display: block; }
}
37.5em is 600px bij de standaard 16px (600 ÷ 16 = 37.5). Het verschil zit in het gedrag: wanneer de gebruiker zijn standaardlettergrootte verdubbelt, verdubbelt de em-breakpoint feitelijk mee, dus de layout schakelt bij een viewportbreedte die evenredig is aan de tekst, precies wanneer de inhoud het nodig heeft. De px-breakpoint blijft bevroren.
Eén eigenaardigheid die het waard is te weten: in media-query-voorwaarden meten em en rem allebei tegen de standaardlettergrootte van de browser, niet tegen een html-override, dus gedragen ze zich daar identiek. Beide eenheden verhelpen de bug; px veroorzaakt hem.
De beslissingstabel per eigenschap
Twijfel je, dan beantwoordt deze tabel het sneller dan de logica elke keer opnieuw afleiden.
| Eigenschap | Aanbevolen eenheid | Waarom |
|---|---|---|
font-size | rem | Schaalt mee met de lettergrootte-voorkeur van de gebruiker |
padding / margin | rem | Witruimte schaalt samen met de tekst |
border | px | Haarlijnen moeten scherp en vast blijven |
box-shadow-verschuiving | px | Een precies weergavedetail, geen inhoud |
border-radius | rem | Houdt de afronding van hoeken evenredig met de schaal |
| media query | em / rem | Breakpoints moeten reageren op lettergrootte-zoom |
width / max-width | rem (vaak ch voor tekst) | Schaalbare layoutbreedtes; ch begrenst de regellengte |
line-height | eenheidloos | Een eenheidloze vermenigvuldiger erft correct |
De line-height-rij verdient een opmerking omdat het een veelvoorkomende bug is. Schrijf altijd line-height: 1.5, zonder eenheid. Een eenheidloze waarde is een vermenigvuldiger die elk element tegen zijn eigen lettergrootte berekent, zodat geneste elementen leesbaar blijven. Schrijf in plaats daarvan line-height: 1.5em of 24px en de berekende lengte wordt geërfd, wat betekent dat een kind met een grotere lettergrootte de regelhoogte van de ouder houdt en zijn tekst begint te botsen. Eenheidloos voorkomt het hele probleem.
Omrekenen tussen px en rem
De rekenkunde is klein genoeg om uit je hoofd te doen zodra je het anker vasthoudt: 16px = 1rem. Deel door 16 om naar rem te gaan, vermenigvuldig met 16 om terug te gaan naar px.
| px | rem (16px-basis) |
|---|---|
| 8px | 0.5rem |
| 12px | 0.75rem |
| 16px | 1rem |
| 24px | 1.5rem |
| 32px | 2rem |
Als je de 62.5%-truc gebruikt, wordt de basis 10px en is de rekenkunde nog eenvoudiger: deel of vermenigvuldig gewoon met 10, dus 24px = 2.4rem. De enige regel is om altijd om te rekenen tegen de basis die je stylesheet daadwerkelijk instelt.
Voor al het andere, zoals vreemde waarden, een aangepaste root, of het batchgewijs omrekenen van een Figma-export, sla het hoofdrekenen over en gebruik de px-naar-rem-converter of de rem-naar-px-converter. Beide laten je elke root-lettergrootte instellen en in beide richtingen in realtime omrekenen. En als je daarna een stylesheet vol gemengde eenheden aan het opruimen bent, normaliseert de CSS-formatter de witruimte en inspringing voor je.
Veelgemaakte fouten
Een paar patronen veroorzaken de meeste eenheidsellende:
De root-lettergrootte in px instellen. Het schrijven van html { font-size: 16px } (in plaats van de standaard laten staan of 100% / een percentage gebruiken) overschrijft de lettergrootte-voorkeur van de browser van de gebruiker botweg. Rem-waarden berekenen zich er nog steeds tegen, maar de lezer kan de hele pagina niet meer schalen. Laat de root op de standaard staan, of gebruik een percentage.
px en rem mengen zonder systeem. Sommige lettergroottes in px, sommige in rem, witruimte verdeeld over beide: het resultaat is een layout die ongelijk schaalt wanneer een gebruiker zijn tekst aanpast. Kies rem als standaard en reserveer px voor de bewuste uitzonderingen in de beslissingstabel.
em gebruiken voor globale witruimte. Em op breed geneste containers brengt de stapelvalkuil terug, dus een padding diep in de boom komt uit op iets wat niemand bedoeld had. Houd globale witruimte in rem; bewaar em voor lokale, component-gebonden waarden.
line-height een eenheid geven. Zoals hierboven besproken wordt line-height: 24px of 1.5em geërfd als een berekende lengte en breekt het op elementen met andere lettergroottes. Gebruik altijd een eenheidloze vermenigvuldiger.
FAQ
Is rem beter dan px?
Voor de meeste maatvoering wel: rem is beter dan px omdat het meeschaalt met de lettergrootte-voorkeur van de browser van de gebruiker, die px negeert. Maar “beter” hangt af van de eigenschap: px is de juiste keuze voor vaste details zoals 1px-randen en schaduwverschuivingen die scherp moeten blijven. Gebruik rem voor inhoudsmaatvoering, px voor weergavedetails.
Hoeveel pixels is 1rem?
1rem is gelijk aan de root-lettergrootte in pixels, wat standaard 16px is in vrijwel alle browsers. Dus 1rem = 16px, 1.5rem = 24px en 2rem = 32px in een standaardopstelling. Als een stylesheet html { font-size } overschrijft, bijvoorbeeld naar 10px via de 62.5%-truc, dan is 1rem in plaats daarvan gelijk aan die waarde.
Moet ik rem of em gebruiken voor font-size?
Gebruik in vrijwel elk geval rem voor font-size. Rem meet tegen de root, dus het blijft voorspelbaar hoe diep een element ook genest is. Em meet tegen de lettergrootte van de ouder, wat zich naar beneden door de boom opstapelt en geneste tekst onverwacht doet opzwellen. Reserveer em voor lokale waarden gekoppeld aan één enkel component.
Wanneer moet ik px gebruiken in plaats van rem?
Gebruik px voor waarden die niet mee moeten schalen met de lettergrootte van de gebruiker: 1px-randen, precieze box-shadow-verschuivingen, focusring-outlines en andere vaste weergavedetails. Dit zijn scherpe ontwerpdetails in plaats van inhoud, dus ze in px vastzetten is de toegankelijkere keuze. Alles wat met inhoud te maken heeft, moet nog steeds rem gebruiken.
Waarom breken media-queries wanneer ik px gebruik?
Media-query-breakpoints in px reageren niet op de lettergrootte-zoom van de browser. Wanneer een gebruiker zijn standaardletter vergroot, heeft zijn inhoud meer ruimte nodig, maar een px-breakpoint klapt nog steeds om bij dezelfde viewportbreedte, dus de layout schakelt op het verkeerde moment. Gebruik em- of rem-breakpoints, die meeschalen met de lettergrootte van de gebruiker.
Wat is de 62.5%-lettergroottetruc?
De 62.5%-truc zet html { font-size: 62.5% }, waardoor de root-lettergrootte 10px wordt (62,5% van 16). Met een 10px-basis wordt rem-rekenkunde “deel door 10”: 24px = 2.4rem, 12px = 1.2rem. Ontwikkelaars zetten vervolgens body { font-size: 1.6rem } om leesbare body-tekst van 16px te herstellen.
Is het oké om px, rem en em te mengen?
Ja, px, rem en em mengen is correct wanneer elk de eigenschap volgt die erbij past: rem voor typografie en witruimte, px voor vaste details, em voor lokale, component-gebonden waarden. Wat problemen veroorzaakt, is ze mengen zonder systeem, bijvoorbeeld sommige lettergroottes in px en sommige in rem. Kies rem als standaard en behandel px en em als bewuste uitzonderingen.
Welke eenheid moet ik gebruiken voor padding en margin?
Gebruik rem voor padding en margin zodat witruimte samen met de tekst schaalt wanneer de gebruiker zijn lettergrootte aanpast. Dit houdt een layout evenredig en toegankelijk. Reserveer em voor padding die de eigen lettergrootte van een element moet volgen, zoals een knop waarvan de padding meegroeit met de tekst, en vermijd em op diep geneste containers waar het zich opstapelt.