Lengte-eenheden in css (inclusief calc() en procenten)

Skip links en inhoudsopgave

www.css-voorbeelden.nl

Als je dit artikel afdrukt, wordt automatisch een printvriendelijke versie afgedrukt.
Dit artikel is van toepassing op alle browsers op alle systemen.
Links in dit artikel, vooral links naar andere sites, kunnen verouderd zijn. Op de pagina met links vind je steeds de meest recente links.
Tekst die tussen << en >> staat, is een link naar internet.
Tekst die omgeven is door een lijn (meestal een stippellijn), verwijst naar een andere plaats binnen het artikel. Op de site zijn dit ankers, maar dat werkt nou eenmaal niet op papier.
Als dit artikel wordt geprint, wordt een aantal voorbeelden niet juist afgedrukt. Bij veel voorbeelden worden relatieve eenheden gebruikt, juist om het 'relatieve' te laten zien. En dat werkt niet bij printen. Je kunt alle relatieve maten bij de voorbeelden wel in pt gaan omzetten, maar het gaat nou juist om het 'relatieve'. En dat werkt alleen op een scherm. Ook worden - afhankelijk van de instellingen van de printer - achtergronden, waaronder gradiënts, niet geprint.
Laatst herzien op .

Over welke eenheden gaat dit artikel?

Dit artikel heeft betrekking op de lengte-eenheden uit de specificatie CSS Values and Units Module Level 3 (CSS waarden en eenheden module level 3). Er bestaat al een ontwerp voor level 4, maar dat is nog (lang) niet klaar voor gebruik.

fr, de 'fractional unit'. wordt hier niet besproken (en staat ook niet in de specificatie Values and Units). De fr wordt alleen bij lay-outen met behulp van grid gebruikt. Alle andere hier besproken eenheden zijn niet aan één stukje specifieke css verbonden, maar kunnen op meerdere plaatsen worden gebruikt.

Het kan natuurlijk dat er in de toekomst nog andere eenheden bijkomen. Aangezien de astroloog van dienst met de bezem op vliegvakantie is en de enige glazen bol zojuist is gebroken, worden ook deze hier niet besproken.

De lengte-eenheden zijn onder te verdelen in drie grote groepen:

Daarnaast is er nog de eenheid procent, wat eigenlijk geen echte zelfstandige eenheid is. Hetzelfde geldt voor calc(), waarmee berekeningen kunnen worden uitgevoerd.

De meer algemene eigenschappen van groepen eenheden staan steeds bovenaan elk groepje. Bij de afzonderlijke eenheden staan meer specifieke eigenschappen.

Hoe betrouwbaar zijn lengte-eenheden?

Daarop is een simpel antwoord mogelijk: helemaal niet. Als een kind zegt: "Mijn mama heeft lekker een grotere centimeter dan de jouwe", dan kan dat kind volkomen gelijk hebben.

Die onbetrouwbaarheid zit ingebakken in de manier, waarop de lengte-eenheden in de specificatie zijn gedefinieerd. Uiteindelijk zijn alle lengte-eenheden gebaseerd op de pixel. De grootte van die pixel varieert. Daarmee variëren alle op de pixel gebaseerde eenheden ook. Inclusief centimeters, inches, en dergelijke. Over de grootte van de pixel is meer te vinden bij De grootte van een pixel.

(Met 'pixel' wordt hier de in css gebruikte 'css-pixel' bedoeld. Daarnaast heb je nog de 'schermpixel' (in het Engels 'device pixel'). Normaal genomen heb je weinig met de schermpixel te maken. Over die twee soorten pixels is meer te vinden bij css-pixels en schermpixels. Als het in dit artikel over px of 'pixel' gaat wordt de 'css-pixel' bedoeld, tenzij anders vermeld.)

Als je in css maten in bijvoorbeeld centimeters opgeeft, is er dus geen enkele garantie dat de weergave ook echt in centimeters is. Hoogstwaarschijnlijk is 'n centimeter op het scherm (veel) langer of korter dan een centimeter op een liniaal. Hetzelfde geldt voor eenheden als inches en millimeters. Bij printen zouden absolute maten wel correct moeten zijn, maar ook bij printen wijken ze meestal (fors) af.

De onderlinge verhoudingen van de eenheden zijn wel intact gebleven: 'n centimeter is tien keer zo lang als 'n millimeter, en 'n inch is 2,54 keer zo lang als 'n centimeter.

In de praktijk maakt dit allemaal weinig uit, zelfs niet bij printen. Je hebt hoe dan ook weinig controle over de uiteindelijke maten, als je 'n website maakt. En ook bij printen levert het geen echte problemen op. Als je je er maar bewust van bent dat een centimeter niet altijd een centimeter is. (Meer over printen is te vinden bij Printen.)

Schrijfwijze in dit artikel

Decimale komma of punt

In de waarde van een eenheid staan vaak decimalen. Omdat css op het Engels is gebaseerd, worden decimalen met een punt aangegeven. In het Nederlands geef je een decimaal echter met een komma aan. Als je alleen over css schrijft, is dat geen probleem. Als je alleen Nederlandstalige artikelen schrijft, is dat geen probleem. Als je een Nederlandstalig artikel over css schrijft, heb je 'n probleem.

In dit artikel wordt in daadwerkelijke css de punt gebruikt. In de gewone schrijftaal wordt de komma gebruikt. 1.5em is hetzelfde als 1,5 em. Dat is mogelijk enigszins verwarrend, maar het zou ook verwarrend zijn, als overal de punt wordt gebruikt. Want daarmee geef je in het Nederlands duizendtallen en dergelijke aan, dus dat schiet ook niet op.

In de css zelf moet echter overal, zonder uitzondering, een punt worden gebruikt om decimalen aan te geven.

Spatie of geen spatie

In het Nederlands staat, behalve bij procenten, een spatie tussen de eenheid en de waarde. In css mag geen spatie tussen eenheid en waarde staan. In daadwerkelijke css ontbreekt de spatie, in gewone schrijftaal wordt de spatie wel gebruikt. Hiervoor geldt hetzelfde als bij komma of punt: 1.5em is hetzelfde als 1,5 em.

Viewport, browservenster, venster, scherm, pfff

In dit artikel wordt meestal 'browservenster', 'venster van de browser' of kortweg 'venster' gebruikt voor bovenstaande woorden. Om schrijfkramp te voorkomen.

In een 'venster' wordt iets weergegeven. Dat kan het venster van de browser zijn, maar ook een tekst in LibreOffice, of het venster dat bij een spelletje hoort.

Het 'scherm' is het ding dat op je teen kan vallen. En dan pijn doet.

'Viewport' en 'browservenster' zijn, wat betreft dit artikel, hetzelfde. Het browservenster is het venster dat door de browser wordt gebruikt. Op de desktop kan de grootte hiervan worden veranderd, zodat je bijvoorbeeld meerdere programma's naast elkaar weer kunt geven.

Op mobiele apparaten is de viewport normaal genomen even groot als browservenster en scherm, omdat de browser normaal genomen het hele scherm vult.

Hierbij wordt er wel van uitgegaan dat de pagina geschikt is gemaakt voor weergave op mobiele apparaten (responsief is gemaakt). In de <head> moet iets staan als:

<meta name="viewport" content="width=device-width, initial-scale=1">

Hierdoor wordt de viewport even breed als het scherm van het mobiele apparaat. Als deze code ontbreekt, zal elk apparaat de pagina op z'n eigen wijze aanpassen voor een zo goed mogelijke weergave. Die meestal niet zo goed zal zijn...

Level 3??? Waar is css3 gebleven???

Om maar gelijk een wijd verbreid misverstand uit de wereld te helpen: css3 bestaat helemaal niet. Na css 2.1 is de css-specificatie gesplitst in een groot aantal afzonderlijke specificaties, die 'module' heten. Als de eigenschappen uit een bepaalde module al langere tijd bestaan, kan die module al op level 4 zitten. Nieuwe eigenschappen zitten vaak in een module die nog op level 1 zit.

Bij het ontwikkelen van de opvolger van css 2.1 is jarenlang gesproken over css3. Pas gaandeweg het ontwikkelen van css3 is besloten de specificatie te splitsen. Omdat iedereen het jarenlang over css3 heeft gehad, wordt die naam nog steeds gebruikt. Het is een synoniem geworden voor alles na css 2.1, ongeacht op welk level dat zit.

Getest in

De tekst in dit artikel is gecontroleerd in de hieronder staande browsers en systemen. De gebruikte browser is steeds de laatste versie, die nog geïnstalleerd kan worden op het betreffende systeem (op de datum dat dit artikel is geschreven).

Alle voorbeelden werken in de hieronder staande browsers en systemen, tenzij bij het voorbeeld staat dat dit niet werkt in 'n bepaalde browser en/of systeem.

Als wordt geschreven dat iets ergens wel of niet werkt, is dat alleen gecontroleerd in bovenstaande browsers en systemen. Er is nogal uitgebreid getest, dus iets wat in alle bovenstaande browsers en systemen werkt, zal vermoedelijk ook in niet-geteste browsers en systemen werken.

Bij twijfel of iets (inmiddels) werkt of niet, kun je daar op internet naar zoeken. Bij links onderaan de pagina staat een aantal links naar sites met browser support.

In Internet Explorer 10 en eerder wordt niet meer getest. Veel van de op deze pagina besproken zaken werken niet in deze browsers. Op alle nog ondersteunde systemen kan met nieuwere browsers worden gewerkt. Er is dus geen enkele reden om nog in deze onveilige browsers te testen.

In versies van Windows voor Windows 7 wordt ook niet meer getest. Deze systemen worden niet meer ondersteund door Microsoft. Het is daarom volslagen onverantwoord deze oudere versies nog op internet te gebruiken.

Browser support

Bij de betreffende eenheden is te zien, welke eenheden in de door jou op dit moment gebruikte browser werken. Hier staat de ondersteuning in alle geteste browsers, zoals die was op het moment van schrijven.

Eenheden die in alle geteste browsers en systemen werken

  • em: gebaseerd op lettergrootte van het element.
  • rem: 'root em', gebaseerd op lettergrootte van het <html>-element.
  • ex: x-hoogte van font in het element.
  • px: pixel. Meerdere betekenissen, waarover meer bij px.
  • cm: centimeter. Heeft weinig te maken met de 'normale' centimeter.
  • mm: millimeter. Heeft weinig te maken met de 'normale' millimeter.
  • in: inch. Heeft weinig te maken met de 'normale' inch.
  • pt: point. Gebruikt voor printen.
  • pc: pica. Geen flauw idee waar dit ooit voor gebruikt zou kunnen worden. Een zesde van een inch.

Eenheden die niet in alle geteste browsers en systemen werken

Soms is het geen probleem, als een browser een eenheid niet kent, maar soms zal daar een oplossing voor moeten worden gevonden. Bij de afzonderlijke eenheden worden die oplossingen, als ze er zijn, beschreven.

  • ch: breedte van het cijfer '0' van het font in het element.
    Werkt op Android niet in Android browser en UC browser; Opera Mini werkt pas op Android 4.4.2; Dolphin werkt pas op Android 6.
  • vw: 1% van de breedte van het venster van de browser.
    Werkt op Android niet in UC browser; Android browser werkt soms vanaf Android 4.4.2; Opera Mini werkt pas vanaf Android 4.4.2.
  • vh: 1% van de hoogte van het venster van de browser. Ondersteuning als vw gelijk hierboven.
  • vmin: 1% van de breedte of hoogte van het venster van de browser, afhankelijk van welke het kleinste is. Ondersteuning als iets hierboven bij vw.
  • vmax: 1% van de breedte of hoogte van het venster van de browser, afhankelijk van welke het grootste is.
    Werkt niet in Internet Explorer en Edge.
    Werkt in UC browser niet op Android en Windows Phone 8.1.
    Op Android werkt Android browser soms vanaf Android 4.4.2. Opera Mini werkt pas vanaf Android 4.4.2. Dolphin werkt pas vanaf Android 6.
  • q: kwart millimeter. Heeft weinig te maken met de 'normale' millimeter.
    Werkt alleen in Firefox, maar dan wel in alle versies.

Bij twijfel of iets (inmiddels) werkt of niet, kun je daar op internet naar zoeken. Bij links onderaan de pagina staat een aantal links naar sites met browser support.

Door deze browser ondersteunde lengte-eenheden

Hieronder is te zien, welke relatieve en absolute lengte-eenheden de op dit moment gebruikte browser ondersteunt. Als de browser een bepaalde eenheid niet ondersteunt, kun je er (uiteraard) ook geen dingen mee uitproberen in deze browser.

em

rem

ex

ch

vw

vh

vmin

vmax

px

cm

mm

q

in

pt

pc

%

calc()

Bugs

Er zitten opvallend weinig ernstige bugs in eenheden. Voor zover bugs echt relevant zijn, worden ze bij de betreffende eenheid vermeld. Overigens is dat overzicht waarschijnlijk niet volledig, omdat het extreem lastig is om te zoeken naar bugs in eenheden. Als je zoekt naar bijvoorbeeld 'em', blijkt zo ongeveer elk Engels woord deze letters te bevatten. En niet alle pagina's waar je naar bugs kunt zoeken, geven de mogelijkheid op bijvoorbeeld het losse woord 'em' te zoeken.

Niet elke browser heeft een plaats, waar je gericht naar bugs kunt zoeken. Of, zoals bij Opera Mini, ze veronderstellen dat je bereid bent en de tijd hebt om tientallen pagina's in het forum te lezen, of jouw bug daar toevallig al bij staat. Om deze redenen is alleen in Firefox, Safari, Google Chrome en Edge gericht naar bugs gezocht. Omdat echter (vrijwel) alle andere browsers de weergavemachine van een van deze browsers gebruiken, geeft dit toch een redelijke dekking.

Er worden ook nogal wat bugs gemeld, die zich alleen in extreem uitzonderlijke situaties voordoen. Zoals bij Google Chrome een breedte in ex die alleen bij gebruik van een bepaalde lettersoort veel te smal is. Dat soort bugs zijn weggelaten. (In het betreffende geval trouwens vermoedelijk geen bug in Chrome, maar verkeerde ex-informatie in het betreffende font.)

Het melden van dit soort exotische bugs blijft trouwens belangrijk, maar het voert te ver om ze hier op te nemen. Dan zie je door de bomen het bos niet meer, en er is al zoveel bos verdwenen in Nederland.

Het verschil tussen zoomen en lettergrootte (font-size) veranderen

Zoomen en het veranderen van de lettergrootte worden vaak met elkaar verward, maar het zijn twee totaal verschillende dingen. Inzoomen (vergroten) kan in álle browsers, uitzoomen (verkleinen) in een aantal. De lettergrootte kan niet in alle browsers worden veranderd.

Bij zoomen verandert álles op de pagina van grootte: borders, afbeeldingen, tekst, echt álles. Als de pagina hierdoor breder wordt dan het venster van de browser, moet je hierdoor in de meeste mobiele browsers horizontaal scrollen om alles te kunnen zien. Bij het lezen van tekst is dit uiteraard bijzonder lastig. Bij zoomen kan ook de lay-out van de pagina (ingrijpend) veranderen.

In veel browsers kun je de standaardlettergrootte aanpassen. Hierbij wordt alleen de grootte van de tekst veranderd. De lay-out blijft in principe ongewijzigd. De lettergrootte kan alleen worden aangepast, als voor de lettergrootte een font-relatieve eenheid (met name em en rem of procent) is gebruikt.

Voor slechtziende mensen is het belangrijk dat de lettergrootte kan worden aangepast, zonder dat ze - zoals bij inzoomen het geval is - horizontaal moeten scrollen om alles te kunnen zien. Daarom is het belangrijk dat, vooral bij lettergroottes, de juiste eenheid wordt gebruikt. En dat wordt getest op zowel zoomen als het veranderen van lettergrootte.

Eenheid en waarde

Eenheid en waarde (in het Engels 'unit' en 'value') worden vaak door elkaar heen gebruikt. Dat is meestal geen enkel probleem, omdat wel duidelijk is wat wordt bedoeld. In een artikel over eenheden is het wel handig om een duidelijk onderscheid te maken.

De eenheid is de naam van de eenheid, zoals px (pixel), cm (centimeter), enzovoort.

De waarde geeft de grootte van de eenheid of het aantal eenheden aan.

In 10px is '10' de waarde en 'px' de eenheid. In -20% is '-20' de waarde en '%' de eenheid.

Standaardwaarde (initial)

Heel veel eigenschappen hebben een standaardwaarde (de specificatie noemt dit 'initial'). Dit is de waarde die de eigenschap heeft, als nog niets is opgegeven. Bij border-width is dit bijvoorbeeld 'medium', wat meestal hetzelfde is als 2 px. Deze standaardwaarde verschilt per eigenschap. Als voor een eigenschap geen waarde wordt opgegeven, wordt in principe deze standaardwaarde gebruikt. Zodra in de css een waarde wordt opgegeven, vervangt deze de standaardwaarde.

Elke browser heeft een eigen stylesheet (de 'default style' of 'default stylesheet'), waarin allerlei waarden worden opgegeven. Zo heeft een <p> bijvoorbeeld een marge aan boven- en onderkant. Zodra in de css een waarde wordt opgegeven, overrulet deze altijd de waarde uit de stylesheet van de browser. Vaak is in de specificatie een standaardwaarde opgegeven. Vooral als dat niet het geval is, willen browsers nog wel eens afwijkende standaardwaardes hebben.

Een gebruiker kan ook zelf eigenschappen opgeven in een zogenaamde 'user style'. Ook deze overrulen de stylesheet van de browser. Maar de bij een pagina opgegeven stijl overrulet weer de door de gebruiker opgegeven stijl. Tenzij de gebruiker het sleutelwoord !important heeft gebruikt, want dat gaat boven alles.

De volgorde is dus (met de winnaar als laatste): stylesheet van de browser, stylesheet van de gebruiker, stylesheet van de site, stylesheet van de gebruiker met !important.

De gebruiker kan dit hebben gedaan om bijvoorbeeld kleuren aan te passen voor meer contrast, of om de lettergrootte te veranderen. Dit is één van de redenen dat je nooit op alleen css kunt vertrouwen om iets over te brengen: je weet niet, wat een gebruiker eventueel heeft aangepast.

Onderaan de pagina staan links naar sites, waarop bij de verschillende eigenschappen de standaardwaarde kan worden opgezocht. Dit is maar heel beperkt in dit artikel opgenomen, omdat er zo veel eigenschappen zijn. Bovendien komen er voortdurend eigenschappen bij, dus een lijst hiervan zou ook heel snel verouderen.

Standaardeenheid

Alleen al voor de lengte bestaan in css vijftien verschillende eenheden. Gelukkig wordt het grootste deel daarvan nooit gebruikt. Een eenheid als pc (pica) heb ik nog nooit waar dan ook gebruikt zien worden, ook niet voor printen. Bij deze eenheden komt dan nog de eenheid procent. Maar dat is eigenlijk geen eigen eenheid, omdat de uiteindelijk gebruikte eenheid bij procenten altijd een van de andere eenheden is.

Met eenheden kan worden gerekend met behulp van calc(). In principe kunnen dat zelfs vrij ingewikkelde berekeningen zijn. 80% van de breedte van het browservenster, vermindert met 10% van de hoogte van het venster, verminderd met 3 rem, vermeerderd met 3 px. En de uitkomst daarvan gedeeld door 1,3. Degene die zoiets bedenkt, moet dringend op een geheel verzorgde cruise op kosten van de baas, maar het kán in principe.

Uit de hogere wiskunde is bekend dat je geen eieren van appels kunt aftrekken. Dat geldt ook voor css. Je kunt geen em van vh aftrekken. Eerst moet je em en vh omrekenen naar een gemeenschappelijke eenheid. De specificatie noemt dit de 'canonical unit'. Op deze pagina wordt de term standaardeenheid gebruikt. ('canonical' vertaald naar het Nederlandse 'canoniek' heeft betrekking op katholiek kerkrecht, dus 'canonieke eenheid' lijkt als vertaling niet helemaal bruikbaar. Voor je het weet, ben je geëxcommuniceerd.)

Voor de diverse soorten eenheden wordt in de specificatie voor elk een standaardeenheid opgegeven, zodat er een soort gemeenschappelijke reken-eenheid voor de verschillende soorten eenheden bestaat. Voor lengte is de standaardeenheid de px, de pixel. Intern werkt een browser alleen met de eenheid px. Elke andere eenheid wordt door de browser, bij het opbouwen van de pagina, omgerekend naar px.

Als je 'n site maakt, weet je nooit op wat voor scherm die site wordt weergegeven. Je kunt bezwaarlijk bij elke bezoeker over de schouder meekijken. De browser weet dat echter wel, want die is wel bij elke bezoeker aanwezig. Voor de browser is het daarom een koud kunstje om alles bij het opbouwen van de pagina om te rekenen naar de px, de standaardeenheid voor lengte.

Als bijvoorbeeld als lettergrootte 1em is opgegeven, is dit voor de browser hetzelfde als 16 px (aangenomen dat de standaardlettergrootte van de browser niet is veranderd door de bezoeker). Als je als breedte 80% opgeeft, weet je bij het maken van de pagina niet hoeveel dat is. Maar bij het weergeven weet de browser dat wel, omdat dan de grootte van het browservenster bekend is. En als die grootte bekend is, kan die 80% simpel worden omgerekend naar het aantal px.

Hierdoor kunnen bij berekeningen met calc() verschillende lengte-eenheden worden gebruikt in één berekening. De browser rekent verschillende eenheden om naar de eenheid px, waarna ermee gerekend kan worden. De uitkomst van een berekening met calc() is dan ook altijd in px.

Omdat het de browser is die alles omrekent naar px, merk je daar zelf heel weinig van.

Erfelijkheid

In het Engels: 'inheritance'. Veel eigenschappen erven de eigenschappen van hun ouder, maar dat geldt lang niet voor alle eigenschappen. font-size bijvoorbeeld wordt geërfd door de nakomelingen van het element, maar border-width niet. Meestal zijn er goede redenen voor het wel of niet erfelijk zijn van een eigenschap. Zo zou het bijvoorbeeld niet handig zijn, als alle nakomelingen van een <article> de border van <article> zouden erven. Maar bij color is het bijvoorbeeld wel makkelijk, als dat wordt geërfd. Zodra in de css een waarde aan een eigenschap wordt gegeven, overrulet deze een eventueel geërfde waarde.

Bij veel eigenschappen hangt de erfelijkheid (mede) van de gebruikte eenheid af. Bij font-size bijvoorbeeld is een waarde van 16 px altijd 16 px. Maar een waarde in em is ten opzichte van de ouder van het element. Bij procenten kan het helemaal sterk verschillen. Meestal is een waarde in procenten ten opzichte van de ouder, maar bij bijvoorbeeld transform: translate() is het ten opzichte van het element zelf.

Voor zover dat van belang is, wordt bij de eenheden zelf hier nog op teruggekomen. Onderaan de pagina staan links naar sites, waarop bij de verschillende eigenschappen het al dan niet erfelijk zijn kan worden opgezocht, en hoe dat eventueel werkt bij procenten en dergelijke. Dit is maar heel beperkt in dit artikel opgenomen, omdat er zo veel eigenschappen zijn. Bovendien komen er voortdurend eigenschappen bij, dus een lijst hiervan zou ook heel snel verouderen.

Toegestane eenheden en waarden

Niet elke eenheid kan bij elke eigenschap worden gebruikt. Het heeft weinig zin om bij font-size seconden op te geven, om maar iets te noemen. Welke eenheden kunnen worden gebruikt bij welke eigenschap, verschilt per eigenschap.

Hetzelfde geldt voor waarden: niet elke waarde is bij elke eigenschap toegestaan. Een marge bijvoorbeeld mag een negatieve waarde hebben, want dat verplaatst gewoon het hele element. Maar een negatieve waarde bij de breedte van een border kan niet, want dat zou een border zijn die smaller is dan zichzelf. Of zoiets. Bij sommige eigenschappen is er een minimum- en/of maximumwaarde.

Als een eigenschap een bepaalde eenheid uit een groep accepteert, accepteert die eigenschap in principe álle eenheden uit die groep. Als een eigenschap de lengte-eenheid px accepteert, kunnen in principe álle lengte-eenheden bij die eigenschap worden gebruikt. Wel is het vaak zo dat de ene eenheid (veel) meer geschikt is dan de andere. Dat verschilt per eigenschap. Ook zijn sommige eenheden beter dan andere, als er bijvoorbeeld geprint gaat worden.

Bij lengte-eenheden mag de eenheid worden weggelaten, als de waarde 0 (nul) is. padding: 0; is volstrekt correcte css.

Onderaan de pagina staan links naar sites, waarop bij de verschillende eigenschappen de toegestane waarden kunnen worden opgezocht. Dit is maar heel beperkt in dit artikel opgenomen, omdat er zo veel eigenschappen zijn. Bovendien komen er voortdurend eigenschappen bij, dus een lijst hiervan zou ook heel snel verouderen.

JavaScript

Met JavaScript kan de waarde van een eigenschap worden opgevraagd. De waarde die je krijgt, is niet de in de css opgegeven waarde, maar de bij weergave daadwerkelijk door de eigenschap gebruikte waarde. Deze waarde wordt in het Engels de 'computed value', de 'berekende waarde' genoemd.

Bij lengte-eenheden is de standaardeenheid de px, de pixel. Intern werkt een browser altijd met pixels, ongeacht met welke eenheid een lengte is opgegeven. Als je met JavaScript een lengte opvraagt, krijg je het antwoord dan ook altijd in px. Dat geldt ook voor dingen als de lettergrootte. En zelfs als bij bijvoorbeeld een regelhoogte helemaal geen eenheid, maar alleen een getal is gebruikt. Je krijgt de daadwerkelijk gebruikte lengte, als alles is berekend, waarbij ook rekening wordt gehouden met waarden bij eventuele voorouders (als de eigenschap erfelijk is). Omdat de waarde altijd in px is, kun je met JavaScript makkelijk met de lengte werken.

Ook als in de css helemaal niets is opgegeven, kun je toch nog een waarde opvragen. Je krijgt immers niet de in de css opgegeven waarde, maar de daadwerkelijk gebruikte waarde. En die is er bij lengte ook zonder specifiek te zijn opgegeven. Als je geen lettergrootte opgeeft, is de lettergrootte standaard 1 em (wat de browser omrekent naar px). Een <div> zonder inhoud en zonder een met height opgegeven hoogte, blijkt bij opvragen een hoogte van 0 px te hebben.

(Als je in het ontwikkelgereedschap van de browser kijkt naar de 'Berekende waarde', 'Computed value', of hoe het ook heet in de betreffende browser, zie je ook daar alle lengte-eenheden, inclusief de lettergrootte, weergegeven in px. De in de css opgegeven eenheden en waarden zijn te vinden onder 'Regels', 'Styles', of hoe het in de betreffende browser ook heet.)

Het opvragen van de daadwerkelijk gebruikte waarden in px is relatief makkelijk in JavaScript. Het opvragen van de in de css opgegeven eigenschappen, eenheden en waarden is daarentegen extreem ingewikkeld. Je moet dan de hele stylesheet inlezen en echt op zoek gaan naar de selector die bij een bepaald element hoort. Wat zelfs niet eens altijd mogelijk is. Maar in de praktijk is dit eigenlijk geen enkel probleem, omdat het altijd om de daadwerkelijk gebruikte waarde gaat. En die is wel simpel op te vragen.

(Er is trouwens één uitzondering op dat moeilijke opvragen: een inline style (iets als <div style="width: 100em;"> is wel makkelijk op te vragen, inclusief de gebruikte eenheid. Maar inline styles zijn, behalve voor testen en dergelijke, een bijzonder slecht idee.)

Wat veel belangrijker is: je kunt met JavaScript nieuwe lengtes opgeven, en daarbij kun je gewoon alle eenheden gebruiken. Je hoeft dus niet zelf elke eenheid naar px om te rekenen, maar kunt bij lettergrootte gewoon een eenheid als em of rem gebruiken.

Het voert voor dit artikel te ver om hier alle verschillende manieren om eigenschappen op te vragen of op te geven te bespreken. Als je op internet zoekt naar dingen als 'getComputedStyle' en 'setProperty', kun je er meer over vinden.

Welke lengte-eenheid gebruiken?

Er zijn vijftien lengte-eenheden. Eigenlijk zestien, als je procenten ook meerekent. Gelukkig wordt het grootste deel nooit gebruikt, want vijftien is echt overdadig veel. Hier staat alleen een heel beknopt spieklijstje. Bij de afzonderlijke eenheden worden allerlei dingen veel uitgebreider beschreven.

em en rem

em en rem zijn het meest geschikt voor de lettergrootte. Bij gebruik van deze eenheden kan de gebruiker de lettergrootte aanpassen, wat belangrijk is voor slechter ziende mensen.

Als de em wordt gebruikt bij de lettergrootte, is de em gebaseerd op de ouder van het element. Bij alle andere eigenschappen van het element is de em gebaseerd op de lettergrootte van het element. Bij een grotere lettergrootte is de em ook groter. De em is daarom geschikt voor alle maten, die zijn gekoppeld aan de lettergrootte.

De rem is, anders dan de em, altijd even groot. Hij is gebaseerd op de lettergrootte van <html>. De rem is hierdoor uitstekend geschikt voor dingen die niet rechtstreeks met de lettergrootte van het element te maken hebben, bijvoorbeeld positioneren.

Op internet zijn fanatieke emmers en remmers, die soms de indruk wekken dat ze de tegenpartij het liefst in 'n pixel zouden veranderen ('n pixel is het meest gruwelijke dat 'n fanatieke emmer of remmer zich voor kan stellen). De Evangelische Omroep had vroeger ook van dat soort verhalen: Hoe Ik Van De Heroïne En De Em Afkwam Nadat Ik De Heere En De Rem Had Ontdekt.

Niets van aantrekken: gebruik gewoon wat je goed bevalt en vooral: wat het beste werkt in de gegeven situatie. Waarbij je toegankelijkheid niet moet vergeten. Zelf werk ik veel met em, omdat ik dat gewend ben. Maar soms is 'n rem beter, en dan gebruik ik die.

ex en ch

De ex is gebaseerd op de hoogte van de letter 'x', of op een andere letter, of op iets anders. Of zoiets. De specificatie levert hier echt een ongeëvenaarde prestatie in onduidelijkheid. De ch is gebaseerd op de breedte van het cijfer '0'. Maar mag ook 0,5 em zijn. Deze eenheden heb ik nog nooit ergens gebruikt zien worden.

vw en vh

vw en vh zijn gebaseerd op de breedte en de hoogte van het browservenster. De vw is 1% van de breedte van het venster, de vh 1% van de hoogte. Omdat deze eenheden rechtstreeks zijn gekoppeld aan de grootte van het venster, kun je de grootte van een element rechtstreeks aan de grootte van het venster koppelen. De grootte van voorouders van het element is onbelangrijk. Als de grootte van het venster verandert, bijvoorbeeld door draaien van tablet of smartphone, verandert de grootte van deze eenheden ook.

vmin en vmax

vmin en vmax werken ongeveer hetzelfde als vw en vh. 1 vmin is 1% van de kortste kant van het browservenster, 1 vmax is 1% van de langste kant het browservenster. Als het venster 1200 px breed is en 800 px hoog, dan is 1 vmin 1% van 800px, en 1 vmax is 1% van 1200px.

px

De pixel wordt niet geërfd, waardoor deze in principe overal even groot is. Een lettergrootte in px kan niet in alle browsers worden veranderd, wat een probleem kan zijn voor slechter ziende mensen. Daarom is de px meestal niet geschikt voor de lettergrootte. De px is geschikt voor zaken die dezelfde grootte moeten houden, zoals lijntjes, de grootte van een thumbnail en marges.

cm, mm, q, in en pc

De centimeter, millimeter, kwart millimeter, inch en pica. Omdat al deze maten zijn gebaseerd op de px, die in grootte varieert, variëren ook deze maten. Een centimeter in css kan meer of minder dan een centimeter op een liniaal zijn. Ook bij printen kunnen deze eenheden meer of minder afwijken van de 'echte' eenheden. Behalve de in en de cm heb ik geen van deze maten ooit gebruikt zien worden. De in wordt soms en de cm heel soms gebruikt om bij printen de marges aan de randen van het papier aan te geven.

pt

De pt, de 'point', is de meest geschikte eenheid voor printen. In stylesheets voor printen wordt de eenheid em vaak omgerekend naar de pt.

%

Percentages zijn een wat vreemde eend in de bijt, omdat een percentage eigenlijk geen zelfstandige eenheid is. Een percentage is altijd gebaseerd op een andere waarde. Meestal is dat een waarde bij een ouder, maar dat hoeft niet. Percentages kunnen bijvoorbeeld worden gebruikt om drie knoppen in een menu elk 33,3% breed te maken. Als een percentage bij de lettergrootte wordt gebruikt, werkt het hetzelfde als de eenheid em.

calc()

calc() is geen eenheid, maar staat hier voor het gemak ook. calc() wordt gebruikt om berekeningen met eenheden uit te voeren. Iets als font-size: 3calc(); bestaat niet, iets als font-size: calc(1em + 3px); kan wel.

Font-relatieve eenheden

In css bestaan de font-relatieve eenheden em,   rem,   ex en   ch. Deze eenheden zijn alle vier op een of andere manier op de lettergrootte gebaseerd.

em

Hieronder is te zien, of deze browser em ondersteunt.

em

Van oorsprong was een em in de typografie de breedte van de letter 'm', maar de eenheid em uit css heeft daar niets meer mee te maken. Helemaal zeker is die oorsprong trouwens niet, maar dat is voer voor typohistorigrafici.

Veel mensen hebben nogal wat moeite deze eenheid in de vingers te krijgen, omdat het radicaal anders is dan bij drukwerk. Weliswaar kan een ondernemende peuter een gedrukte krant nog van grootte doen veranderen, maar daar worden de ouders om een of andere reden vaak niet blij van. Diezelfde peuter kan wel probleemloos de grootte van de weergave op z'n tablet aanpassen. (Peuters zouden helemaal geen tablet moeten gebruiken, maar dat terzijde.)

De em werkt op twee verschillende manieren, afhankelijk van waar hij wordt gebruikt. Er is een bepaald effect als de em bij de eigenschap font-size wordt gebruikt, en een ander effect als de em bij alle andere eigenschappen wordt gebruikt.

De werking van em bij font-size

De lettergrootte van een element wordt geërfd van de ouder van het element. Omdat de browser intern alleen met de lengte-eenheid px werkt, wordt die geërfde lettergrootte ook in de eenheid px uitgedrukt. Dat is verder niet van echt praktisch belang, maar het maakt het iets makkelijker de werking van de em uit te leggen.

Standaard heeft de browser een lettergrootte van 16 px. Het buitenste element van een pagina is <html>, dus dit is de lettergrootte van <html>. Als de lettergrootte van de browser niet is aangepast, is de lettergrootte van <html> 16 px. Het enige (zichtbare) kind van <html> is <body>. Omdat de lettergrootte wordt geërfd, is de lettergrootte van <body> ook 16 px.

Als je nu met em de lettergrootte van <body> gaat aanpassen, wordt die nieuwe lettergrootte gebaseerd op de geërfde lettergrootte. Met font-size: 2em; wordt de lettergrootte twee keer zo groot als de geërfde lettergrootte van 16 px. 2 x 16 = 32 px, dus de nieuwe lettergrootte is 32 px. Bij font-size: 1.5em; wordt de lettergrootte 1,5 x 16 = 24 px. Bij font-size: 0.75em; is de nieuwe lettergrootte 0,75 x 16 = 12 px. Enzovoort.

Als <body> een kind <article> heeft, erft <article> weer de lettergrootte van <body>. En weer in de eenheid px, want dat is de eenheid waarmee de browser intern werkt. Ook als bij <body> een nieuwe lettergrootte in em (of welke eenheid dan ook) is opgegeven.

Als bij <body> de lettergrootte met font-size: 2em; is verdubbeld tot 32 px, erft <article> die lettergrootte van 32 px. Als je nu bij <article> font-size: 0.5em; opgeeft, wordt de lettergrootte bij <article> 0,5 x 32 px (de van <body> geërfde lettergrootte) = 16 px.

Dit blijft zo doorgaan: elk element erft de lettergrootte van zijn directe ouder. Met behulp van em (of een andere eenheid) kan in dat element vervolgens de geërfde lettergrootte weer worden veranderd. De eenheid, waarin de lettergrootte in de ouder is opgegeven, maakt hierbij helemaal niets uit, omdat de eenheid altijd in px aan de kinderen wordt doorgegeven.

Als de gebruiker de lettergrootte van de browser verandert, wordt feitelijk de lettergrootte van <html> veranderd. Omdat de lettergrootte wordt geërfd, wordt daarmee de lettergrootte van álle elementen op de pagina veranderd. (Het veranderen van de lettergrootte is iets heel anders dan zoomen, zoals beschreven bij Het verschil tussen zoomen en lettergrootte (font-size) veranderen.)

Aan de hierboven beschreven werking verandert helemaal niets, alleen wordt de uitgangswaarde anders. Stel dat de gebruiker de lettergrootte heeft verdubbeld. De standaardlettergrootte van <html> is dan 2 x 16 = 32 px. Nu erft <body> die lettergrootte van 32 px. Als de lettergrootte bij <body> met font-size: 2em; wordt veranderd, is de nieuwe lettergrootte van <body> nu 2 x 32 = 64 px.

Dit maakt ook duidelijk, waarom het normaal genomen belangrijk is em te gebruiken bij de lettergrootte. Als de lettergrootte in een absolute eenheid als px is opgegeven, kan de lettergrootte niet in alle browsers door de bezoeker worden veranderd. De eenheid em is, als je de hele keten terug volgt, uiteindelijk gebaseerd op de lettergrootte van <html>. Verander de lettergrootte van <html> en alle nakomelingen veranderen mee. Een absolute eenheid als px staat los van de lettergrootte van de ouder en kan daarom niet altijd door de gebruiker worden veranderd.

(De relatieve eenheden rem en percentage zijn ook geschikt voor lettergroottes, omdat ook deze door de gebruiker kunnen worden veranderd.)

De werking van em bij andere eigenschappen dan font-size

Elk element heeft een bepaalde lettergrootte. Zelfs als helemaal geen lettergrootte is opgegeven, want dan gebruikt het element gewoon de geërfde lettergrootte.

Stel dat een element een lettergrootte van 37 px heeft. In dat element heeft de em dan ook een lengte van 37 px. Als je een element met een lettergrootte van 37 px een border met een breedte van 1 em geeft, is die border 37 px breed. Als een element een lettergrootte van 20 px heeft, is een padding met een breedte van 1 em in dat element 20 px breed.

De em is dus, als deze niet bij font-size wordt gebruikt maar bij andere eigenschappen, gebaseerd op de lettergrootte van het element. Dit maakt de em heel geschikt voor eigenschappen die te maken hebben met de lettergrootte van het element, zoals bijvoorbeeld text-indent (inspringen van de eerste regel van een alinea).

Voorbeelden

In de voorbeelden hieronder wordt ervan uitgegaan dat de lettergrootte niet door de gebruiker is aangepast: <html> heeft een lettergrootte van 16 px. Als de lettergrootte wel is aangepast, moeten gewoon de waarden worden aangepast. Als de bezoeker de lettergrootte bijvoorbeeld heeft verdubbeld tot 32 px, worden alle geërfde lettergroottes ook twee keer zo groot.

Stel dat op de hele pagina slechts één <div> aanwezig is: div 1. In deze <div> zit 'n andere <div> met de creatieve naam div 2. In div 2 zitten weer twee <p>'s: p 1 en p 2. Makkelijker kunnen we het niet maken, wel moeilijker, en dat gaat later ook ruimschoots gebeuren, maar nu nog even niet.

Omdat de lettergrootte nog nergens is veranderd en de lettergrootte wordt geërfd, hebben beide <div>'s en beide <p>'s een lettergrootte van 16 px:

div 1 {font-size: 16px;})

div 2 {font-size: 16px;}

p 1 {font-size: 16px;}

p 2 {font-size: 16px;}

Hierboven zijn de <div>'s en <p>'s weergegeven. In alle vier de elementen staat een lettergrootte van 16 px. De lettergrootte is niet opgegeven in de css, maar geërfd. Als je in het ontwikkelgereedschap van de browser naar font-size kijkt onder 'Berekende waarde', 'Computed value', of hoe het ook heet in de betreffende browser, zie je daar een lettergrootte in px, ook als in de css geen lettergrootte is opgegeven.

(De waarde van die lettergrootte kan iets afwijken van '16', vanwege afrondingsverschillen. Soms zie je de wildste getallen, iets als 15.9926px. Dat zijn verschillen die zo klein zijn, dat ze meestal volledig onbelangrijk zijn.)

Omdat de eenheid em is gebaseerd op de lettergrootte van het element, is in alle vier de elementen 1 em hetzelfde als 16 px.

Als je de geërfde lettergrootte van 16 px vervangt door font-size: 1em;, verandert er niets. Als em bij een lettergrootte wordt gebruikt, wordt de lettergrootte aangepast. Maar een lettergrootte van 1 em wil zeggen: 1 x de geërfde lettergrootte, dus dat verandert niets:

div 1 {font-size: 1em;} (=16 px)

div 2 {font-size: 1em;} (=16 px)

p 1 {font-size: 1em;} (=16 px)

p 2 {font-size: 16 px;}

In de twee <div>'s en de eerste <p> die hier gelijk boven staan, is bij elk een lettergrootte van 1 em opgegeven. Zodra in de css iets wordt opgegeven, overrulet dit altijd een geërfde eigenschap. De lettergrootte is nu dus daadwerkelijk veranderd van 16 px naar 1 em. Dat maakt hier echter geen enkel verschil, omdat 1 em precies 1 x de geërfde lettergrootte is.

Bij de tweede <p> is een lettergrootte van 16 px opgegeven. Die lettergrootte wordt niet geërfd, omdat deze in px is opgegeven. Maar omdat deze 16 px even groot is als de geërfde lettergrootte, is ook daar is niets veranderd.

(Een lettergrootte opgeven in px is in het algemeen een bijzonder slecht idee, omdat die lettergrootte niet in alle browsers door de gebruiker kan worden aangepast. Dat kan grote problemen opleveren voor mensen die slechter zien.)

Nu wordt in de buitenste <div> de lettergrootte met font-size: 1.25em; veranderd:

div 1 {font-size: 1.25em;} (=20 px)

div 2 {font-size: 1em;} (=20 px)

p 1 {font-size: 1em;} (=20 px)

p 2 {font-size: 16px;}

Een lettergrootte in em is gebaseerd op de geërfde lettergrootte. De geërfde lettergrootte van div 1 is 16 px. De nieuwe lettergrootte van 1,25 em in div 1 wordt dus 1,25 x 16 = 20 px. Dat is ook te zien aan de grotere letter in de eerste <div>. De op de lettergrootte gebaseerde em is nu ook 20 px geworden. Een border van 2 em in de eerste <div> zou nu 2 x 20 = 40 px zijn. Door de lettergrootte te veranderen, is ook de daarop gebaseerde relatieve eenheid em veranderd.

Omdat de lettergrootte door de kinderen van de eerste <div> wordt geërfd, is de lettergrootte van de tweede <div> nu ook 20 px. En daarmee is ook de op de lettergrootte gebaseerde em in de tweede <div> nu 20 px breed. Een breedte van 10 em in de tweede <div> is nu 10 x 20 = 200 px.

De eerste <p> is een kind van de tweede <div> en erft dus weer de lettergrootte van 20 px van de tweede <div>. Waarmee ook de op de lettergrootte gebaseerde em in de eerste <p> nu 20 px breed is. Een breedte van 10 em in de eerste <p> is nu 10 x 20 = 200 px.

Bij de tweede <p> ligt het anders. Daar is bij de lettergrootte de absolute eenheid px gebruikt: font-size: 16px;. Ook de tweede <p> heeft weliswaar de lettergrootte van 20 px geërfd van de tweede <div>, z'n ouder. Maar zodra in de css iets wordt opgegeven, overrulet dit een geërfde waarde. Een px is een absolute waarde. Waar je een px ook gebruikt, hij is altijd even groot. Al zouden er 37 andere lettergroottes tussen <html> en deze <p> zijn opgegeven, dan nog zou de hier opgegeven 16 px die overrulen. En omdat de em op de lettergrootte is gebaseerd, is 1 em in deze tweede <p> 16 px. Een breedte van 10 em is in de tweede <p> slechts 10 x 16 = 160 px.

(Een lettergrootte opgeven in px is in het algemeen een bijzonder slecht idee, omdat die lettergrootte niet in alle browsers door de gebruiker kan worden aangepast. Dat kan grote problemen opleveren voor mensen die slechter zien.)

Hieronder blijft de lettergrootte in de eerste <div> 1,25 em, maar in de tweede <div> wordt de lettergrootte verhoogd naar 1,2 em. Dat levert het volgende resultaat op:

div 1 {font-size: 1.25em;} (=20 px)

div 2 {font-size: 1.2em;} (=24 px)

p 1 {width: 10em; font-size: 0.5em;} (=12 px)

p 2 {width: 10em; font-size: 16px;}

De tweede <div> erft nog steeds de lettergrootte van 20 px van de eerste <div> In de tweede <div> zou de op de lettergrootte gebaseerde em dus ook nog steeds 20 px zijn. Maar nu is bij de tweede <div> een lettergrootte van 1,2 em opgegeven. De geërfde lettergrootte van 20 px wordt daarom 1,2 keer zo groot. 1,2 x 20 = 24 px. 24 px is de nieuwe lettergrootte van de tweede <div>. En daarmee is de grootte van 1 em in de tweede <div> ook 24 px geworden. Een breedte van 10 em is in de tweede <div> nu 10 x 24 = 240 px.

De eerste <p> is een kind van de tweede <div> en erft dus de lettergrootte van 24 px van de tweede <div>. Waarmee ook de op de lettergrootte gebaseerde em in de eerste <p> 24 px breed zou worden. Maar in de eerste <p> is een lettergrootte van 0,5 em opgegeven. De lettergrootte wordt 0,5 x de geërfde 24 px = 12 px. Daarmee wordt ook de op de lettergrootte gebaseerde em 12 px lang. Een breedte van 10 em in de eerste <p> is nu 10 x 120 = 120 px. Om dat duidelijk te maken, heeft de eerste <p> ook echt een breedte van 10 em gekregen.

Bij de tweede <p> is de lettergrootte niet veranderd, die is nog steeds 16 px. Daarmee is ook de op de lettergrootte gebaseerde em hier nog steeds 16 px lang. Een breedte van 10 em is in de tweede <p> nog steeds 10 x 16 = 160 px. Om dat duidelijk te maken, heeft de tweede <p> ook echt een breedte van 10 em gekregen.

De eerste en de tweede <p> hebben beide een breedte van 10 em. Maar omdat de em is gebaseerd op de lettergrootte en die lettergrootte verschilt bij beide <p>'s, is de uiteindelijke breedte verschillend. Bij de eerste <p> is de breedte 10 x 12 = 120 px, bij de tweede <p> is de breedte 10 x 16 = 160 px.

(Een lettergrootte opgeven in px is in het algemeen een bijzonder slecht idee, omdat die lettergrootte niet in alle browsers door de gebruiker kan worden aangepast. Dat kan grote problemen opleveren voor mensen die slechter zien.)

Geschikt voor

De em is uitstekend geschikt om de lettergrootte mee aan te geven. Omdat het een relatieve eenheid is, kan de gebruiker probleemloos de letter vergroten. Dit is belangrijk voor mensen die slechter kunnen zien. De lettergrootte kan trouwens ook verkleind worden, maar dat zal niet vaak gebeuren.

Een em is uitermate goed te gebruiken, als iets aan de lettergrootte van het element gekoppeld moet worden. Omdat de em echt is afgestemd op de lettergrootte binnen het element, kun je allerlei dingen koppelen aan de lettergrootte van alleen dat ene specifieke element. (Omdat de lettergrootte wordt geërfd, geldt die koppeling ook voor de nakomelingen van het element, tenzij daar de lettergrootte wordt veranderd.)

Bij een grotere letter zou je bijvoorbeeld de padding ook groter kunnen laten worden, door de padding op te geven in em. Door bij text-indent de eenheid em te gebruiken, kun je de lengte van het inspringen van de eerste regel koppelen aan de lettergrootte. De lengte van de regel kan worden gekoppeld aan de lettergrootte, zodat te korte of te lange regels worden voorkomen. Kortom: alles wat op een of andere manier met de lettergrootte van het element te maken heeft.

Minder geschikt voor

Omdat bij em de lettergrootte is gebaseerd op de lettergrootte van de ouder, kan dat tot een opeenstapeling van veranderingen leiden. Als je in de css hebt staan div {font-size: 0.8em;}, en je hebt vijf geneste <div>'s, is de lettergrootte bij de vijfde <div> 0,8 x 0,8 x 0,8 x 0,8 x 0,8 = (afgerond) 0,17 em. Want bij elke <div;> wordt die lettergrootte weer 0,8 keer zo klein. Dat moet dan weer worden gecorrigeerd.

Veel sitebouwers worden horendol van dat voortdurende corrigeren. Veel ook niet, kennelijk is dat iets, wat je wel of niet ligt. Gelukkig is er een oplossing voor de horendollen onder ons: de eenheid rem, die als volgende wordt behandeld. Maar eerst een voorbeeld van een ander probleem met de eenheid em dat voor iedereen speelt, horendol of niet, en dat ook met de rem is op te lossen.

.boven {height: 2em; font-size: 0.8em;} (=13 px)

.links {width: 5em; font-size: 1.2em; top: 2em;} (=19 px)

.rechts {top: 2em; left: 5em;} (geen lettergrootte, dus 16 px)

Hierboven zijn in een met smaakvolle kleuren opgemaakte compositie drie verschillende elementen gebruikt: bovenaan een gele <div>, links een groene <div> en rechts een oranje <div>. De groene en oranje <div> zijn absoluut gepositioneerd. De drie <div>'s moeten netjes op elkaar aansluiten.

Dat aansluiten lukt niet helemaal probleemloos. Dit is trouwens hoe dan ook een heel slecht ontwerp, maar daar gaat het even niet om.

(Omdat sommige browsers een door de gebruiker ingestelde minimum lettergrootte kunnen hebben, kan het voorbeeld hierboven iets afwijken van de beschrijving hieronder.)

Er wordt van uitgegaan dat de lettergrootte van de ouders van deze constructie gewoon 16 px is. De gele <div> bovenaan heeft een lettergrootte van 0,8 em. In px is dat 0,8 x 16 px = (afgerond) 13 px. De op de lettergrootte gebaseerde em is dus 13 px lang. De hoogte van de gele <div> is 2 em, dat is dus 2 x 13 = 26 px.

De groene <div> heeft een lettergrootte van 1,2 em. In px is dat 1,2 x 16 = (afgerond) 19 px. De op de lettergrootte gebaseerde em is hier dus 19 px. De groene <div> is met top: 2em; gelijk onder de gele <div> gezet, die 2 em hoog is.

En daar gaat het dus mis: de gele <div> is 2em hoog, maar dat is daar maar 26 px. De groene <div> is 2 em vanaf de bovenkant neergezet, maar dat is hier 2 x 19 = 38 px. Oftewel: hoewel bij beide elementen 2 em is opgegeven, zit er een gat van 12 px tussen, omdat bij beide elementen de em een andere lengte heeft.

Voor de oranje <div> geldt ongeveer hetzelfde. Hier is geen lettergrootte opgegeven, dus die heeft nog gewoon de geërfde lettergrootte van 16 px. 1 em is daarom hier ook 16 px. De oranje <div> is met top: 2em; weer gelijk onder de gele <div> neergezet. Maar in de oranje <div> is 2 em 32 px, terwijl de gele <div> slecht 26 px hoog is. Tussen de gele en de oranje <div> zit dus een gat van 6 px.

De groene <div> is 5 em breed. 1 em is in de groene <div> 19 px, dus de breedte van de groene <div> is 5 x 19 = 95 px. De oranje <div> is met left: 5em; naast de groene <div> gezet. Alleen is in de oranje <div> een em slechts 16 px, dus 5 em is 80 px. De oranje <div> staat daarmee 15 px over de groene <div> heen.

Kortom: een puinhoop.

Je zou de verschillende maten kunnen aanpassen door het aantal em's te verlagen of te verhogen, zodat alles precies op elkaar aansluit. Dat is 'n mogelijkheid, maar het is dan wel verstandig vast 'n plaatsje te reserveren in Rusthuis Sitezicht, want dat werkt voor geen meter. Er zitten kleine verschillen tussen de verschillende browsers en tussen schermen met verschillende resoluties, dus je krijgt dat nooit helemaal goed. En als je over drie weken besluit de lettergrootte ergens te veranderen, kun je weer van voren af aan beginnen. Als de bezoeker de lettergrootte wijzigt, wijzigen ook breedte en hoogte en dergelijke, maar niet overal evenveel. En wat had u gedacht van mensen die, zonder enige rekening te houden met de arme websitebouwer, een minimum lettergrootte instellen in de browser?

Absolute maten? Hoogte en positie in px of zoiets? Dat werkt. Maar dan heb je weer kans dat de tekst er niet meer in past, of dat er juist te veel lege ruimte is. Om het nog maar niet te hebben over gebruikers die de lettergrootte aanpassen.

Wat je eigenlijk zou moeten hebben, is een relatieve maat die niet is gekoppeld aan de lettergrootte van 'n bepaald element. En die bestaat. Het is de rem die, uiteraard volledig toevallig, gelijk hieronder wordt besproken.

Browser support

em wordt door alle geteste browsers ondersteund.


rem

Hieronder is te zien, of deze browser rem ondersteunt.

rem

Algemeen

De rem lijkt heel erg op de hierboven beschreven em. De 'r' aan het begin staat voor 'root', 'wortel'. De root is het buitenste element. Bij een pagina van een site is dit altijd het <html>-element.

Werking

De em is gebaseerd op de lettergrootte van het element, waarin de em wordt gebruikt. Als de lettergrootte in bijvoorbeeld een <div> 20 px is, is 'n em in die <div> 20 px. Als de lettergrootte 10 px is, is de em in die <div> ook 10 px.

De rem is niet gebaseerd op de lettergrootte van het element, maar op de lettergrootte van <html>. Daarom is een rem overal op de pagina precies even groot, ongeacht de lettergrootte van het element waarin de rem wordt gebruikt. Als de lettergrootte van <html> 16 px (de standaardlettergrootte) is, is de rem overal op de pagina 16 px. Als de lettergrootte van <html> 10 px is, is de rem overal 10 px, ook in een <div> met een lettergrootte van 200 px. En ook in een <h1>, terwijl die standaard een grotere lettergrootte heeft.

Als de lettergrootte van <html> wordt veranderd, verandert ook de grootte van de rem. Dit gebeurt ook, als de gebruiker de lettergrootte van de browser aanpast. Omdat de rem overal evenveel verandert, blijven alle onderlinge verhoudingen gelijk.

Anders dan de em, is de rem overal hetzelfde. De lengte is op de lettergrootte van <html> gebaseerd, en niet op de lettergrootte van het element, waarbij de rem wordt gebruikt. Als de lettergrootte van <html> 16 px is, is een lettergrootte van 1,25 rem 1,25 x 16 = 20 px. Daarmee is 1 em in het element met die lettergrootte ook 20 px: width: 1em; levert een breedte van 20 px op. Maar een breedte van 1 rem is nog steeds 16 px, want die rem is gebaseerd op de lettergrootte van <html>.

div 1 {font-size: 1.25em;} (=20 px)

div 2 {font-size: 1rem;} (=16 px)

p 1 {font-size: 1.5rem;} (=24 px)

p 2 {font-size: 16px;}

In de constructie hierboven heeft <html> de standaardlettergrootte van 16 px. De eerste <div> heeft een lettergrootte van 1,25 em gekregen: 1,25 x 16 = 20 px.

De tweede <div> erft, zoals altijd, de lettergrootte van de eerste <div>: 20 px. Maar omdat de tweede <div> in de css een eigen lettergrootte van 1 rem heeft gekregen, overrulet die de geërfde lettergrootte. Omdat rem op de lettergrootte van <html> is gebaseerd, is de lettergrootte van de tweede <div> 16 px. Even groot als de lettergrootte van <html> Al hadden er 372 <div>'s met elke een andere lettergrootte tussen <html> en deze <div> gezeten, dan nog zou 1 rem zijn gebaseerd op de lettergrootte van <html>.

De eerste <p> is een kind van de tweede <div> en erft dus de lettergrootte van 16 px van de tweede <div>. Maar ook deze <p> heeft in de css een eigen lettergrootte gekregen, dus die overrulet de geërfde lettergrootte. De lettergrootte van 1,5 rem is gebaseerd op de lettergrootte van <html> en is dus 1,5 x 16 = 24 px.

Bij de tweede <p> ligt het anders. Daar is als lettergrootte de absolute eenheid px gebruikt: font-size: 16px;. Ook de tweede <p> heeft weliswaar de lettergrootte van 16 px geërfd van de tweede <div>, z'n ouder, maar ook die wordt overruled door de in de css opgegeven lettergrootte. Een px is een absolute waarde. Waar je een px ook gebruikt, hij is even groot. Al zouden er 37 andere lettergroottes tussen <html> en deze <p> zijn opgegeven, dan nog zou de hier opgegeven 16 px die overrulen.

Als hier overal em was gebruikt in plaats van rem, zou de lettergrootte lastiger zijn door de opeenstapeling van geërfde lettergroottes. De eerste <div> is dan met font-size: 1,25em; nog steeds 1,25 x 16 = 20 px. De tweede <div> heeft dan met font-size: 1em; dezelfde lettergrootte van 20 px. De eerste <p> heeft dan met font-size 1.5em; een lettergrootte van 1,5 x 20 = 30 px. Bij nog meer nakomelingen wordt dit een steeds langere reeks berekeningen. Persoonlijk heb ik hier geen moeite mee, maar heel veel mensen gebruiken om deze reden geen of nauwelijks em, maar geven de voorkeur aan rem.

Bij het laatste voorbeeld onder em is het geen kwestie van voorkeur, daar levert het gebruik van em echt problemen op in de vorm kieren tussen en overlappingen van elementen. Dit is heel goed met de rem op te lossen. Dat laatste voorbeeld nog eens, maar nu met op een aantal plaatsen rem in plaats van em:

.boven {height: 2rem; font-size: 0.8em;} (=13 px)

.links {width: 5rem; font-size: 1.2em; top: 2rem;} (=19 px)

.rechts {top: 2rem; left: 5rem;} (geen lettergrootte, dus 16 px)

De css is grotendeels hetzelfde als bij het eerdere voorbeeld. Alleen zijn de gele, groene en oranje <div> nu niet met em, maar met rem op hun plaats gezet. Bij gebruik van em is de grootte van de em bij breedte en dergelijke gebaseerd op de lettergrootte van het element. Een andere lettergrootte levert een andere lettergrootte van de em op. Waardoor 2 em bij bijvoorbeeld de gele <div> 26 px is, en bij de groene <div> 38 px.

Als voor breedte en dergelijke de eenheid rem wordt gebruikt, is die overal hetzelfde, omdat die is gebaseerd op de lettergrootte van <html>, en niet op de verschillende lettergroottes van de <div>'s.

Bij de gele <div> is height: 2em; vervangen door height: 2rem;.

Bij de groene <div> is width: 5em; vervangen door width: 5rem; en top: 2em; door top: 2rem;.

Bij de oranje <div> is top: 2em; vervangen door top: 2rem; en left: 5em; door left: 5rem;.

Omdat rem overal dezelfde lengte heeft, zijn de kieren en overlappingen nu verdwenen.

(Je kunt hetzelfde bereiken door absolute eenheden zoals px te gebruiken. Een px groeit echter niet mee met de lettergrootte. Een rem verandert mee, als de gebruiker de lettergrootte aanpast. Hierdoor wordt bijvoorbeeld de linkerkolom niet te smal voor de tekst erin.)

Geschikt voor

De rem is uitstekend geschikt om de lettergrootte mee aan te geven. Omdat het een relatieve eenheid is, kan de gebruiker probleemloos de letter vergroten. Dit is belangrijk voor mensen die slechter kunnen zien. De lettergrootte kan trouwens ook verkleind worden, maar dat zal niet vaak gebeuren.

Als elementen ten opzichte van elkaar gepositioneerd moeten worden, is een rem ook prima te gebruiken. Anders dan de op de lettergrootte van het element gebaseerde em is de rem altijd overal even groot, omdat de rem op de lettergrootte van <html> is gebaseerd.

Browser support

rem wordt door alle geteste browsers ondersteund.

Bugs

In Internet Explorer 11 op Windows 7 en Windows 8 wordt een regelhoogte in rem in met ::before en ::after aangemaakte pseudo-elementen genegeerd. In Internet Explorer 11 en Edge op Windows 10 is deze bug gerepareerd.

Deze bug is simpel te omzeilen. Als je de regelhoogte in em opgeeft, werkt het wel. Omdat de regelhoogte, net als de em, meestal op de lettergrootte zal zijn gebaseerd, zal dit vrijwel altijd op deze manier zijn op te lossen.


ex

Hieronder is te zien, of deze browser ex ondersteunt.

ex

Algemeen

De ex is gebaseerd op de hoogte van de 'x' van het gebruikte font. Maar de grootte van ex wordt ook berekend, als er geen 'x' aanwezig is. Of 'n willekeurige kleine letter mag worden gebruikt, waarbij de specificatie als voorbeeld de 'o' geeft. Mogelijk is er ook een waarde voor ex in het font opgegeven. Als de ex helemaal niet te berekenen valt, is de ex gelijk aan 0,5 em.

Kortom: een eenheid die tegelijkertijd zo supernauwkeurig en zo superflexibel is, dat ze hem in Californië schijnen te gebruiken om flexibele aardbevingsbestendige bruggen mee te ontwerpen.

Geschikt voor

Op internet kun je tal van poëtische verhalen lezen over de wonderschone mogelijkheden van deze eenheid. In de praktijk heb ik deze eenheid nog nooit gebruikt zien worden.

Deze eenheid is in ieder geval niet geschikt om de grootte van het font op te geven, want daar is de ex nou juist op gebaseerd.

Je zou de ex kunnen gebruiken om bij superschrift (<sup>) en subschrift (<sub>) de tekst hoger en lager te kunnen zetten. In theorie zou dat dan met precies de hoogte van de 'x' moeten gebeuren. In de praktijk werkt dat dus nauwelijks: het verschilt per browser en systeem. Hieronder staan twee regels. In de bovenste regel is de middelste 'x' 1 ex naar boven gezet, in de onderste 1 ex naar beneden.

xxxxxxxxxxx

xxxxxxxxxxx

Afhankelijk van de browser en het besturingssysteem kan de 'x' te hoog, te laag of - o wonder - precies goed staan. Ook een aardige variant: de ene x staat goed, en de andere staat te hoog of te laag. Je kunt trouwens precies hetzelfde effect met em bereiken, maar dan overal goed.

Gevoelige lezers hebben het mogelijk al begrepen: ik heb 't ding nooit gebruikt en zou werkelijk niet weten, waar het nuttig voor is.

Browser support

ex wordt door alle geteste browsers ondersteund.


ch

Hieronder is te zien, of deze browser ch ondersteunt.

ch

Algemeen

De ch is gebaseerd op de breedte van het cijfer '0' van het gebruikte font. Als dat niet lukt, heeft de ch een breedte van 0,5 em. Ook deze eenheid heb ik nog nooit ergens gebruikt zien worden. En ik zou ook niet weten, waar hij nuttig voor is. Wel zijn ook over deze eenheid verhalen over de veelbelovende typografische mogelijkheden te vinden, maar zonder ook maar één enkel praktisch voorbeeld. Althans: ík heb ze niet kunnen vinden.

Geschikt voor

Ook deze eenheid is in ieder geval niet geschikt om de grootte van het font op te geven, want daar is de ch nou juist op gebaseerd. Waar je 'm wel voor zou kunnen gebruiken, zou ik niet weten.

Hieronder staan vier <p>'s met in elk tien keer het cijfer '0'. De <p>'s hebben een breedte van 10 ch gekregen, dus in theorie zouden de <p>'s precies even breed moeten zijn als de nullen. De eerste <p> gebruikt dezelfde lettersoort, als waarin deze tekst is geschreven. De tweede <p> heeft font-family: monospace;, de derde <p> heeft font-family: serif; en de vierde <p> heeft font-family: sans-serif;. Bij de laatste drie <p>'s mag de browser dus zelf de lettersoort opzoeken, als deze maar monospace, serif of sans-serif is.

0000000000

0000000000

0000000000

0000000000

Tja, geschikt voor? Ik zou het niet weten. Mocht je deze eenheid willen gebruiken, kijk dan eerst nog even gelijk hieronder bij Browser support en Bugs.

Browser support

ch wordt door alle geteste browsers ondersteund, behalve onderstaande:

UC browser op Android (alle versies) kent ch niet. Dolphin herkent ch pas in Android 5.0.1. Oudere versies van Android kennen deze eenheid ook niet. Dit is trouwens redelijk makkelijk op te vangen:

width: 6em; width: 10ch;

Browsers die ch niet kennen, negeren de tweede width. Voor browsers die ch wel kennen, overrulet de tweede width de eerste width. In theorie zou 1 ch ongeveer even groot moeten zijn als 0,5 em, dus met 6 em zou je zeker goed moeten zitten. Maar dat is dus 'in theorie' ...

Bugs

Afbeelding 1: verkeerde grootte van ch

  • In alle versies van Internet Explorer 11 (ook mobiel) en in UC browser op Windows Phone 8.1 is de <p> slechts negen nullen breed. De tiende nul staat rechts buiten de <p>. Dit is te zien op de linkerafbeelding hierboven.
  • In Safari en UC browser op iOS zijn de eerste, tweede en vierde <p> te smal. De tiende nul wordt op de volgende regel gezet. Alleen de derde <p>, die met serif is correct. Dit is te zien op de rechterafbeelding hierboven.
  • Dit heeft weinig met ch te maken. Apple-bezitters zullen dit wel weer een feature noemen: op iOS behandelt Safari de tien nullen automatisch als een link naar een telefoonnummer. De nullen worden blauw en onderstreept. Bij aanraken biedt Safari aan het nummer te bellen, in je contacten te zetten, en dergelijke. Best handig, maar waarom gebeurt dit ongevraagd? Je kunt de blauwe kleur en de onderstreping weghalen met:

    a[href^="tel"] {color: black; text-decoration: none;}

    Deze selector geldt voor alle links, waarvan href begint met 'tel'. De tekst wordt zwart en de onderstreping verdwijnt. Dit past álle links naar telefoonnumers op de pagina aan. Maar als het nummer bijvoorbeeld in een <p> met class="a-ch-demo-smal' zit (zoals op deze pagina), zet je gewoon .a-ch-demo-smal ervoor, en dan schakelt het alleen de links uit binnen die <p>.

    Helaas blijft de telefoonlink wel gewoon werken: als je de cijfers iets langer aanraakt, wordt nog steeds aangeboden het nummer te bellen en dergelijke. Het automatisch herkennen van telefoonnummers in Safari op iOS kan alleen worden uitgeschakeld door in de <head> de volgende regel te zetten:

    <meta name="format-detection" content="telephone=no">

    Nu behandelt ook Safari op iOS een link alleen als een telefoonlink, als deze href="tel:" bevat.

    (Dit is trouwens ook een aardige illustratie van het verschil tussen de html die jij schrijft, en de html die de browser uiteindelijk gebruikt. Safari voegt gewoon eigenhandig een zogenaamde telefoonlink toe: een link die begint met href="tel:". Deze soort link staat nog in geen enkele specificatie, maar wordt wel door vrijwel alle mobiele browsers herkend. Alle browsers passen de html aan, maar gelukkig is Safari op iOS de enige die ongevraagd een telefoonlink toevoegt.)


Percentage bij font-size

De eenheid % wordt besproken bij Percentage. % wordt hier ook even vermeld, omdat de werking van % bij font-size precies hetzelfde is als De werking van em bij font-size. De lettergrootte wordt geërfd van de ouder, en vervolgens met het bij font-size opgegeven percentage vergroot of verkleind. font-size: 1.5em; is precies hetzelfde als font-size: 150%;.

Een lettergrootte in procenten kan door de bezoeker worden aangepast, wat voor slechtziende mensen belangrijk kan zijn.

Bij alle andere eigenschappen dan font-size kan % op de meest uiteenlopende wijze werken, dat verschilt per eigenschap. Meer daarover bij Percentage.

Viewport percentages

De viewport is hetzelfde als het venster van de browser. Op mobiele apparaten is de viewport, en daarmee het browservenster, normaal genomen even groot als het scherm van het apparaat. Op de desktop kan het venster kleiner zijn dan het scherm. Om schrijfkramp te voorkomen wordt op deze pagina 'browservenster' of 'venster van de browser' gebruikt. Wat betreft dit artikel is dat hetzelfde als de viewport en - op mobiele apparaten - het scherm.

Viewport percentages zijn op een of andere manier gekoppeld aan de grootte van het browservenster. Css kent de viewport percentages vw,   vh,   vmin en   vmax.

vw

Hieronder is te zien, of deze browser vw ondersteunt.

vw

Werking

1 vw is 1% van de breedte van het venster van de browser. (Op mobiele apparaten is dat venster normaal genomen even breed als het scherm.) Als het venster 1000 px breed is, is 1 vw 10 px. Als het venster 320 px breed is, is 1 vw 3,2 px.

Je zou kunnen denken dat deze eenheid hetzelfde is als 'n percentage, maar dat is niet zo. Een breedte in procenten bijvoorbeeld is gebaseerd op de breedte van de ouder van het element. Een lettergrootte in procenten is gebaseerd op de lettergrootte van de ouder. Een vw is altijd gebaseerd op de grootte van het browservenster, ongeacht hoeveel voorouders het element heeft.

Als je de breedte van het browservenster verandert (of je tablet draait), verandert de lengte van vw mee. Daardoor worden ook de met vw opgegeven maten aangepast aan de nieuwe breedte.

De specificatie besteedt maar heel weinig woorden aan viewport percentages. Als gevolg hiervan verschillen dingen als zoomen nogal in verschillende browsers. Bij de een verandert de lengte van de vw mee, bij de ander niet. Zolang je niet zoomt, is de werking van vw overal grotendeels hetzelfde.

Als er een scrollbalk aanwezig is, zoals op deze pagina in veel browsers het geval is, houdt vw geen rekening met die scrollbalk. Als het browservenster 1000 px breed is en de scrollbalk is 16 px breed, is 100 vw nog steeds 1000 px. Terwijl de bruikbare breedte van het venster slechts 1000 - 16 = 984 px is. Als je de breedte met width: 100%; zou opgeven, wordt daarbij wél rekening gehouden met een eventueel aanwezige scrollbalk.

Bij de hierboven genoemde breedtes is width: 100%; 984 px, maar width: 100vw; is 1000 px. Dit is van zo'n grote genialiteit, dat schrijver dezes helaas niet in staat is het waarom van dit verschil te begrijpen. Een uitgebreid verhaal hierover, inclusief hoe dit briljante verschil soms is te corrigeren, is te vinden bij Horizontaal centreren ten opzichte van venster.

Voorbeelden

<div> met een breedte ten opzichte van vensterbreedte
background: white; color: black; width: 50vw; min-width: 300px; max-width: 98%; margin: 0 auto; border: black solid 1px; padding: 0 5px 5px;

Hierboven staat een <div>. De voor deze <div> gebruikte css staat ook hierboven.

width: 50vw;: de <div> heeft een breedte van 50% van het browservenster, ongeacht de breedte van eventuele voorouders. In vensters breder dan ongeveer 800 px is ook duidelijk te zien dat de <div> breder is dan de helft van de erboven en eronder staande tekst. In browsers die vw niet kennen, wordt de <div> gewoon even breed als de tekst (Blok-elementen zoals een <div> worden normaal genomen, als geen breedte is opgegeven, automatisch even breed als hun ouder. De breedte wordt wel beperkt door de ook opgegeven maximumbreedte van 98%.)

min-width: 300px;: om te voorkomen dat de <div> te smal wordt in heel smalle browservensters, is een minimumbreedte opgegeven. In vensters smaller dan 312 px (breedte plus 2 x 5 px padding + 2 x 1 px border) moet horizontaal worden gescrold.)

max-width: 98%: voorkomt dat de <div> in brede browservensters breder wordt dan de kolom met tekst. Anders dan vw geldt een breedte in procenten ten opzichte van de ouder van het element. Dat is hier, via 'n paar tussenliggende <section>'s, uiteindelijk <main>, waarin alle tekst staat.

(Als de maximumbreedte van 98% minder blijkt te zijn dan de minimumbreedte van 300 px, dan 'wint' de minimumbreedte.)

De rest van de css is gewone opmaak, die niets met vw te maken heeft.

Hoogte koppelen aan breedte venster

Onderstaande <div> wordt lager en hoger, afhankelijk van de breedte van het browservenster.

height: 20vw;

In de <div> staat alleen de css die van belang is voor dit voorbeeld.

20 vw is 20% van de breedte van het browservenster. In een breder venster is 20 vw (veel) meer dan in een smaller venster. Omdat dit wordt gebruikt om de hoogte op te geven, is de <div> in een breder venster hoger dan in een smaller venster. Als je de breedte van het venster verandert, of de tablet of smartphone draait, zie je de hoogte van de <div> veranderen.

In browsers die vw niet ondersteunen, krijgt de <div> normaal genomen automatisch precies genoeg hoogte om de inhoud ervan weer te kunnen geven.

Lettergrootte afhankelijk van breedte venster
font-size: 2em; font-size: 3vw;

een twee drie vier vijf zes zeven acht negen tien elf twaalf dertien

De hierboven staande <div> heeft een eigen lettergrootte gekregen. De eerste lettergrootte heeft als eenheid em, die alle browsers kennen. Browsers die de eenheid vw kennen, zullen de tweede lettergrootte gebruiken, omdat deze later in de css staat en dus de eerste lettergrootte overrulet. Browsers die vw niet kennen, negeren de tweede lettergrootte.

Omdat de lettergrootte mee verandert met de breedte van het browservenster, kun je op deze manier ongeveer hetzelfde aantal letters naast elkaar zetten, ongeacht de breedte van het venster. Dit kan bijvoorbeeld bruikbaar zijn in een header, waarin je grote letters gebruikt. Je moet de lettergrootte niet te klein nemen, want dan heb je in smalle vensters een verrekijker nodig om de tekst nog te kunnen lezen.

Een andere mogelijkheid is om dit met behulp van media query's pas te laten werken als het browservenster een bepaalde minimumbreedte heeft:

@media screen and (min-width: 500px) {font-size: 3vw;}

Voor smallere browservensters kun je dan 'n kleinere lettergrootte opgeven. Op dezelfde manier kun je voorkomen dat de header in een heel breed venster op de voorpagina van De Telegraaf gaat lijken, door boven een bepaalde vensterbreedte de lettergrootte niet meer te laten groeien.

In bovenstaande <div> staan de getallen een tot en met dertien. In principe moeten deze altijd op één regel staan, ongeacht de breedte van het browservenster. Bij een breder venster worden de letters gewoon groter, en omgekeerd.

Als het browservenster breder is dan ongeveer 750 px, werkt dit voorbeeld echter niet meer goed: de getallen staan op meer regels. De <div> met de getallen staat namelijk in een <main> met een breedte van maximaal 750 px. Bovendien kan <body> niet breder dan 1500 px worden. De <div> kan daardoor niet breder dan 750 px worden, terwijl de lettergrootte gewoon blijft toenemen in vensters breder dan 750 px. Op een gegeven moment past de tekst dan niet meer op één regel.

Op de desktop werkt inzoomen (vergroten) meestal goed, als je 'n kleiner browservenster wilt simuleren. In sommige browsers kun je met het ontwikkelgereedschap kleinere vensters simuleren. Hier werken deze methodes echter niet goed, omdat browsers de vw op verschillende wijze aanpassen (of niet aanpassen). Wat wel overal werkt: het venster van de browser daadwerkelijk versmallen tot 750 px.

Als je aan de css voor deze pagina main {width: auto;} en body {max-width: none;} toevoegt, kan de <div> even breed als het browservenster worden en werkt het wel goed.

Breedte beperken tot breedte venster

In sommige pagina's op de site worden sommige elementen met margin-left naar rechts gezet. Dat heeft soms tot gevolg dat de pagina te breed wordt voor heel smalle browservensters. Met vw is dat heel simpel op te lossen:

margin-left: 40px; max-width: calc(100vw - 40px);

margin-left: 40px;: het hele element wordt 40 px naar rechts gezet.

max-width: calc(100vw - 40px);: het element (en daarmee alles erin) mag maximaal 100 vw breed zijn, even breed als het venster van de browser. Min de 40 px van de marge links. Nu blijft alles netjes binnen het venster en hoeft er in smalle vensters niet horizontaal gescrold te worden. (Over de werking van calc() is meer te vinden bij calc().)

max-width: 100%; zou hier niet werken, omdat er allerlei voorouders tussen browservenster en het element zitten. Met 100 vw wordt de breedte rechtstreeks aan het venster gekoppeld, zonder dat voorouders daar invloed op hebben.

In het voorbeeld hieronder wordt het probleem van een eventueel aanwezige scrollbalk besproken. vw negeert een scrollbalk. Hier speelt dat probleem echter niet, omdat dit alleen nodig was in browservensters smaller dan 540 px. Dat zijn touchscreens, en die hebben geen scrollbalk. Althans: geen scrollbalk die echt ruimte inneemt.

Dit werkt niet in browsers die vw en/of calc() niet kennen, maar dat zijn er niet zoveel meer. Bovendien is dat geen ramp, want in die browsers moet dan op sommige plaatsen even horizontaal worden gescrold. Het gaat niet om de hele pagina die te breed is, maar om slechts enkele alinea's.

Ook zonder iets als een marge is vw heel simpel te gebruiken om de breedte te beperken:

width: 400px; max-width: 96vw;

Dit element is 400 px breed. In browservensters smaller dan 400 px wordt het element echter versmald tot 96% van de breedte van het venster. Het wordt hierdoor nooit breder dan het venster, en er is ruimte voor een marge links en/of rechts van maximaal 4%. (1 vw = 1% van de breedte van het venster.) In browsers die vw niet kennen, kan het element in smalle vensters te breed worden. Lastig, maar niet het einde van de wereld.

Een voorbeeld hiervan kan niet worden gegeven, omdat een werkend voorbeeld niet in deze pagina is in te passen. In ieder geval niet zonder heel veel kunst- en vliegwerk, en vliegende computers overtreden de garantievoorwaarden.

Horizontaal centreren ten opzichte van venster

Je zou denken dat horizontaal centreren ten opzichte van het venster van de browser een fluitje van 'n cent is. Bij gebruik van vw is dat echter niet het geval. Soms kan het echter met wat extra moeite toch.

In veel browsers is, als de pagina lang genoeg is, een verticale scrollbalk aanwezig. In veel (mobiele) browsers ontbreekt die scrollbalk. De breedte van de eventuele scrollbalk is niet in alle browsers precies hetzelfde.

Stel dat het browservenster 1000 px breed is en de scrollbalk 16 px. De bruikbare ruimte van het venster is dan 1000 - 16 = 984 px. Als je met left: 50vw; iets halverwege het venster zou zetten, zou je dan ook verwachten dat dat op 492 px vanaf links komt te staan, op de helft van de bruikbare ruimte. Bij gebruik van vw wordt echter altijd uitgegaan van de breedte zónder scrollbalk. left: 50vw; is dus, als het venster 1000 px breed is, hetzelfde als left: 500px;.

Als je in dezelfde situatie left: 50%; (met als eenheid procent) gebruikt, wordt wél gecorrigeerd voor een eventueel aanwezige scrollbalk. left: 50%; is hetzelfde als left: 492px;.

Het is zelfs nog wat vreemder. vw houdt alleen geen rekening met een eventuele scrollbalk, als bij <html> overflow: auto; is aangegeven. Dit is de standaardwaarde, waarbij scrollbalken alleen verschijnen, als dat nodig is. Als bij <html> overflow: hidden; wordt opgegeven, houdt vw wél rekening met een eventuele scrollbalk. Maar dan ís er dus helemaal geen scrollbalk, want die verberg je met overflow: hidden;, en kun je ook niet scrollen.

Als bij <html> overflow: scroll; wordt opgegeven, houdt vw ook rekening met een eventuele scrollbalk. Maar dan heb je altijd twee scrollbalken, of dat nou nodig is of niet. Vooral de overbodige horizontale scrollbalk is foeilelijk. Dit is niet op te lossen met overflow-x: hidden; overflow-y: scroll (geen horizontale scrollbalk, altijd een verticale scrollbalk), want dat werkt in de meeste browsers niet.

Waarom dit bij het gebruik van procenten wel wordt gecorrigeerd en bij het gebruik van vw niet, behoort tot de Grote Raadselen Der Kosmos. Ik wil bij deze mijn hartelijke complimenten overbrengen aan de dames en heren van de specificatie, die iets zo simpels (kijk maar naar 50%) even ingewikkeld als de Nederlandse belastingwetgeving hebben weten te maken.

De iets hieronder staande verticale lijntjes zijn geen modern kunstwerk. Ze horen bij vier <div>'s die worden gebruikt om dit probleem te illustreren en een mogelijke oplossing te geven.

De vier <div>'s zijn absoluut gepositioneerd. Omdat er geen voorouders met een positie zijn, staan de <div>'s gepositioneerd ten opzichte van het venster van de browser. Precies de bedoeling. De <div>'s hebben elk een hoogte van 20 px en zijn helemaal leeg. Normaal genomen wordt een blok-element zoals een <div> even breed als z'n ouder, maar dat geldt niet voor absoluut gepositioneerde elementen. De <div>'s hebben hierdoor geen enkele breedte. Alle vier hebben rechts een border van 1 px breed. De totale breedte van elke <div> is hiermee niet meer dan 1 px.

Dit levert vier verticale lijntjes op. Deze lijntjes zijn simpel te positioneren: positioneer de <div> en de border gaat braaf mee. Dat je geen vier lijntjes ziet, maar slechts twee of drie (afhankelijk van de browser waarin je dit bekijkt), heeft met het positioneren van de <div>'s te maken. Waar het hier verder over lijntjes gaat, gaat het dus eigenlijk over de borders aan de rechterkant van de <div>'s.

Er zijn twee <div>'s naast elkaar gezet. Dat levert twee regels met <div>'s op: een <div> linksboven, eentje rechtsboven, eentje linksonder en eentje rechtsonder. De bedoeling is om de <div>'s horizontaal te centreren. Als dat zou lukken, staan de borders van de naast elkaar staande <div>'s over elkaar heen in het midden en zou je in totaal slechts twee van de vier verticale lijntjes zien.

De <div> linksboven is gecentreerd met left: 50%; left: 50vw;. De <div> rechtsboven is gecentreerd met right: 50%; right: 50vw;. In principe zouden de lijntjes nu over elkaar heen moeten komen te staan, omdat beide <div>'s op precies dezelfde plaats in het midden staan. Mogelijk komen de lijntjes door afrondingsverschillen - als de browser de precieze positie berekent - niet over elkaar, maar tegen elkaar aan te staan, maar dat zijn minieme afwijkingen.

Browsers die vw niet kennen, gebruiken 50%. De lijntjes worden op 50% vanaf de linker- of rechterkant van het browservenster neergezet. In veel browsers is een scrollbalk aanwezig, omdat de pagina vrij lang is. Als er een scrollbalk is, wordt daarvoor gecorrigeerd: de lijntjes staan in het midden van de ruimte links van de scrollbalk.

Browsers die vw wel kennen, gebruiken 50 vw, omdat deze later in de css staat. Je zou denken dat de lijntjes nu ook in het midden staan. 50 vw is halverwege het browservenster, zowel vanaf links als vanaf rechts. Dat is ook precies wat er formeel gebeurt. Stel voor het gemak even dat het venster 1000 px breed is. Het linkerstreepje staat op 500 px vanaf links, en het rechter streepje staat op 500 px vanaf rechts. Beide staan op dezelfde plaats. Althans: als de browser geen scrollbalk heeft.

Als er een scrollbalk aanwezig is, is het bruikbare deel van het browservenster smaller. Hoeveel het precies smaller is, ligt aan de browser, want niet alle scrollbalken zijn even breed. Als het venster 1000 px breed is en de scrollbalk is 16 px breed, ligt het midden van het bruikbare deel van het venster niet op 500 px, maar op 492 px. Maar bij gebruik van de eenheid vw wordt hier geen rekening mee gehouden. 50 vw is in een venster met een breedte van 1000 px altijd 500 px, scrollbalk of niet.

Als gevolg hiervan staat bij de bovenste lijntjes hieronder het linkerlijntje 8 px te veel naar rechts, en het linkerlijntje 8 px te veel naar rechts. Ze staan niet over elkaar heen, maar 16 px uit elkaar. Althans: als de browser een scrollbalk heeft. Als er geen scrollbalk aanwezig is, staan ze op de juiste plaats in het midden, want dan is de daadwerkelijk beschikbare ruimte in een 1000 px breed browservenster ook echt 1000 px. (Ook in browsers die vw niet kennen en daarom 50% gebruiken, staan ze op de juiste plaats.)

De onderste twee lijntjes staan in alle browsers in het midden, en daarmee over elkaar heen. De <div> linksonder is gepositioneerd met left: 50%; left: calc(50vw - ((100vw - 100%) / 2));, en de <div> rechtsonder met right: 50%; right: calc(50vw - ((100vw - 100%) / 2));. (De onderste twee lijntjes kunnen ook tegen elkaar aan staan door afrondingsverschillen in de berekening bij calc().)

Browsers die vw niet kennen, gebruiken weer de 50%. Die browsers zetten de lijntjes weer altijd op de juiste plaats, want procenten houden rekening met de scrollbalk.

In de tweede helft van left en right wordt calc() gebruikt, een relatief nieuwe functie. Ook de browsers die calc() niet kennen, gebruiken de 50%. (Over browser support en dergelijke voor calc() is meer te vinden bij calc().)

Browsers die vw kennen én met calc() uit de voeten kunnen, gebruiken de tweede positie. Daar staat in het begin nog steeds 50 vw, maar dat wordt nu met behulp van calc() gecorrigeerd voor de scrollbalk.

100vw: de breedte van het browservenster, inclusief dus die vermaledijde scrollbalk. Bij een vensterbreedte van 1000 px is dit 1000 px.

100%: omdat er geen voorouders zijn met een positie, zijn de <div>'s gepositioneerd ten opzichte van het venster van de browser. De breedte van 100% geldt daarom ten opzichte van het venster van de browser. Bij een vensterbreedte van 1000 px en een scrollbalk met een breedte van 16 px is dit 1000 - 16 = 984 px, omdat bij procenten wordt gecorrigeerd voor de scrollbalk. (Dit is ook gelijk het zwakke punt van deze methode: als één van de voorouders van de <div> is gepositioneerd, geldt de breedte van 100% ten opzichte van die voorouder.)

(100vw - 100%): de breedte van het browservenster inclusief scrollbalk (1000 px) min de breedte van het browservenster zonder scrollbalk (984 px). Dit verschil levert de breedte van de scrollbalk op. Daarmee is de breedte bekend, waarmee gecorrigeerd moet worden: 1000 - 984 = 16 px. De breedte van scrollbalken verschilt iets in verschillende browsers, maar dat is geen probleem, want de berekening blijft gewoon hetzelfde, alleen gebruikt de browser dan andere getallen. De browser weet, hoe breed de scrollbalk is.

/ 2: de hierboven gevonden breedte van de scrollbalk wordt door twee gedeeld: ((100vw - 100%) / 2). Bij een browservenster met een breedte van 1000 px en een scrolbalk met een breedte van 16 px is dat ((1000 px - 984 px) / 2) = 8 px. De bij left en right opgegeven positie van 50 vw moet hiermee worden verminderd, om precies het midden te vinden van het 'echte' browservenster: de ruimte zonder scrollbalk. Als je zou corrigeren met 16 px, de breedte van de scrollbalk, zou het midden 8 px te veel naar links komen te staan.

Dit is wat calc(50vw - ((100vw - 100%) / 2) doet. Bij een browservenster van 1000 px breed en een scrollbalk van 16 px breed wordt deze berekening calc(500 - ((1000 - 984) / 2) = calc(500 - (16 / 2)) = calc(500 - 8) = 492 px. Met left: 492px; en right: 492px; staan de lijntjes ook in browsers met een scrollbalk in het midden.

In alle geteste browsers staan de onderste twee lijntjes inderdaad netjes in het midden over elkaar heen (of tegen elkaar aan, door afrondingsverschillen.)

(Uiteraard is bovenstaande css voor deze pagina veel te ingewikkeld. In dit geval kun je gewoon volstaan met left: 50%; en right: 50%. Het ging er echter om te laten zien, hoe je met gebruik van vw toch soms het bruikbare midden kunt bepalen, en niet alleen het formeel juiste, maar onbruikbare midden.)

Browser support

vw wordt in alle geteste browsers ondersteund, behalve de onderstaande:

UC browser op Android herkent vw niet. Android browser werkt soms vanaf Android 4.4.2. Soms, want fabrikanten kunnen Android aanpassen. Opera Mini werkt op Android pas vanaf Android 4.4.2.

Dit is soms niet goed op te lossen, soms wel. Als het is op te lossen, is het principe ongeveer als volgt:

width: 30%; width: 10vw;

De hierboven genoemde browsers zullen de tweede width negeren, omdat ze vw niet kennen. Nieuwere browsers gebruiken de tweede width, omdat deze later in de css staat en dus de eerste width overrulet.

In principe hoeft dit dus geen onoverkomelijke problemen op te leveren in deze browsers, het ziet er alleen anders uit. Oudere versies van Android verdwijnen in rap tempo. UC browser wordt regelmatig bijgewerkt, en zal dus hopelijk deze eenheid ook snel gaan ondersteunen. Lang zal dit probleem dus niet meer bestaan.

Vaak is voor oudere browsers een zogenaamde 'polyfill' te vinden: een stukje JavaScript dat iets ook in oudere browsers laat werken. De kwaliteit daarvan is echter, vriendelijk gezegd, sterk wisselend. Modernizr heeft een uitgebreide sortering kwalitatief goede polyfills, waaronder enkele voor vm (en vh). Omdat het echter vrijwel nooit grote problemen oplevert, als deze eenheden niet worden ondersteund, wordt daar in de voorbeelden geen gebruik van gemaakt.

Bugs

  • In Google Chrome schijnt deze eenheid problemen te geven bij printen. Voor printen is het veel beter de eenheid pt te gebruiken, dus dit is makkelijk te omzeilen.
  • Geen bug, want volgens de specificatie hoort dit zo: bij het bepalen van de breedte van het browservenster wordt een eventuele scrollbalk niet meegerekend. Als het venster 1000 px breed is en de scrollbalk is 16 px breed, is 100 vw nog steeds 1000 px. Terwijl de bruikbare breedte van het venster slechts 1000 - 16 = 984 px is. Hoewel dit zo is bedoeld, blijkt dit in de praktijk vergelijkbaar met een bug. Een uitgebreid verhaal hierover, inclusief hoe dit soms is te corrigeren, is te vinden bij het voorbeeld Horizontaal centreren ten opzichte van venster.

vh

Hieronder is te zien, of deze browser vh ondersteunt.

vh

Werking

1 vh is 1% van de hoogte van het venster van de browser. (Op mobiele apparaten is dat venster normaal genomen even hoog als het scherm.) Als het venster 1000 px hoog is, is 1 vh 10 px. Als het venster 320 px hoog is, is 1 vh 3,2 px.

De vh is precies hetzelfde als de vw, alleen gaat het nu om de hoogte van het browservenster. Het hele verhaal over vw geldt ook voor vh, maar dan in de hoogte. En het gaat niet om een eventuele verticale scrollbalk die wordt genegeerd, maar om een eventuele horizontale scrollbalk.

Voorbeelden

Alle bij vw genoemde voorbeelden werken in principe ook met vh. (Alleen moet je uiteraard hoogte en breedte omwisselen.)

hoog in de bol

De hiernaast staande <div> is minimaal bijna even hoog als het venster van de browser. Bijna, op 50 px na. 30 px lager vanwege de menubalk bovenaan het venster, en 20 px omdat je anders niet goed kunt zien dat de volle hoogte van het hele venster wordt bestreken. Als je die 50 px er niet aftrekt, is de <div> precies even hoog als het venster.

In browsers die vh niet ondersteunen, wordt de <div> hoog genoeg om de inhoud ervan weer te kunnen geven.

Deze constructie kan ook worden gebruikt om een pagina minimaal even hoog als het browservenster te maken. Onderin zou je dan een footer kunnen zetten. Overigens zitten daar in de praktijk nog wat haken en ogen aan, maar het kan in principe wel. Je zou het bijvoorbeeld ook kunnen gebruiken bij onder elkaar zittende afbeeldingen. Elke afbeelding kan dan steeds de volle hoogte van het venster vullen.

De bijbehorende css is heel simpel: min-height: 100vh;. 100 vh is 100% van de hoogte van het browservensters, dus even hoog als het venster. (In werkelijkheid is hier min-height: calc(100vh - 50px); gebruikt, vanwege de menubalk bovenaan het venster en een kleine speling voor de zichtbaarheid.)

Dit is anders dan min-height: 100%;, want dat is een minimumhoogte ten opzichte van de ouder van het element. vh is altijd ten opzichte van het venster van de browser. Als je de pagina minstens 100% hoog wilt maken, moet je ook <html> en <body> minimaal 100% hoog maken. Bij gebruik van vh is dat niet nodig.

(De tekst 'hoog in de bol' staat alleen in de <div>, omdat browsers die vh niet kennen anders alleen de border laten zien. In dit voorbeeld geldt dat trouwens ook voor browsers die calc() niet kennen. In de 'echte' css voor dit voorbeeld is ook calc() gebruikt, omdat wat correcties nodig waren voor menu en dergelijke.

Browser support en bugs

Browser support en bugs zijn voor vh precies hetzelfde als browser support en bugs voor vw.


vmin en vmax

Hieronder is te zien, of deze browser vmin ondersteunt.

vmin

Hieronder is te zien, of deze browser vmax ondersteunt.

vmax

Werking

vmin en vmax werken vrijwel hetzelfde als vw en vh (op browser support en bugs na). Alles wat voor vw en vh geldt, geldt ook voor deze eenheden.

1 vmin is 1% van de breedte of de hoogte van het browservenster, afhankelijk van welk van de twee het kortst is. 1 vmax is ook 1% van de breedte of de hoogte van het venster, maar dan van de langste kant. Omdat je op 1% van de breedte of hoogte van het venster uitkomt, net als bij vw en vh, is de werking precies hetzelfde als bij vw en vh.

Stel dat je een tablet hebt van 1000 px breed en 500 px hoog (makkelijke maten, vanwege de luiheid en zo). Als je die tablet in landschapsstand houdt, is de breedte 1000 px en de hoogte 500 px. vmin is dan 1% van de kortste kant, de hoogte: 5 px. En vmax is 1% van de langste kant, de breedte: 10 px.

Als je de tablet in portretstand houdt, is de hoogte 1000 px en de breedte 500 px. vmin is nog steeds 1% van de kortste kant, maar dat is nu de breedte: 5px. En vmax is nog steeds 1% van de langste kant, nu de hoogte: 10 px.

Het enige verschil met vw en vh: vw en vh zijn de échte hoogte en breedte. Als je de tablet kantelt, veranderen ze van waarde. vmax en vmin zijn altijd de langste en kortste van de breedte en hoogte, hoe je de tablet ook vasthoudt.

Browser support

vmin en vmax worden door alle geteste browsers ondersteund, behalve de onderstaande:

vmin: werkt op Android niet in UC browser. Android browser werkt soms vanaf Android 4.4.2. Opera Mini op Android werkt pas vanaf Android 4.4.2. (Dit is precies hetzelfde als bij vw en vh.)

vmax: werkt niet in Internet Explorer en Edge. Werkt niet in UC browser op Android en Windows Phone 8.1. Android browser werkt soms vanaf Android 4.4.2. Opera Mini op Android werkt pas vanaf Android 4.4.2. Dolphin op Android werkt pas vanaf Android 6.

Vaak is voor oudere browsers een zogenaamde 'polyfill' te vinden: een stukje JavaScript dat iets ook in oudere browsers laat werken. De kwaliteit daarvan is echter, vriendelijk gezegd, sterk wisselend. Modernizr heeft een uitgebreide sortering kwalitatief goede polyfills, waaronder enkele voor vmin. Voor vmax lijkt geen polyfill te bestaan. Omdat het echter vrijwel nooit grote problemen oplevert, als deze eenheden niet worden ondersteund, wordt daar in de voorbeelden geen gebruik van gemaakt.

Bugs

In Edge werken de transform-functies translateX() en translateY() niet, als de eenheid vmin wordt gebruikt.

Absolute eenheden

In css bestaan de absolute eenheden px (pixel), cm (centimeter), mm (millimeter), q (kwart millimeter), in (inch), pt (point) en pc (pica).

Absolute eenheden zijn in zoverre verschillend van font-relatieve eenheden en viewport percentages, dat ze niet afhankelijk zijn van iets anders. Font-relatieve eenheden worden afgeleid van de lettergrootte en viewport percentages worden afgeleid van de grootte van het venster van de browser. Absolute eenheden staan op zichzelf.

In css zijn alle absolute lengte-eenheden uiteindelijk gebaseerd op de px, de pixel. En daar zit een probleem: die pixel heeft geen vaste grootte. Ooit was dat wel het geval, maar tegenwoordig niet meer. Omdat ook eenheden als centimeter, inch en dergelijke op de pixel zijn gebaseerd, hebben in css ook de centimeter, inch, en dergelijke geen vaste lengte. Meer hierover is te vinden bij css-pixels en schermpixels.

In principe kunnen absolute eenheden voor de weergave op het scherm worden gebruikt, maar heel vaak is dat een bijzonder slecht idee. Omdat absolute eenheden vaak niet door de gebruiker kunnen worden aangepast, kan een pagina die overal absolute eenheden gebruikt heel slecht toegankelijk worden. Bovendien kan zo'n pagina zich slecht of niet aanpassen aan grote en kleine schermen. Als een pagina altijd 780 px breed is, is die soms te smal in brede schermen, en veel te breed op smartphones. In sommige browsers kan de lettergrootte altijd worden aangepast, maar dan veranderen hoogte en dergelijke niet, als daarvoor absolute eenheden zijn gebruikt. En wordt de lay-out al snel een enorme puinhoop.

Zoomen kan wel altijd, maar daarbij wordt de hele pagina breder of smaller. Als de pagina breder wordt dan het scherm, zoals bij inzoomen (vergroten) op een tablet en in een smartphone gebeurt, moet de gebruiker horizontaal gaan scrollen om alles te zien.

Absolute eenheden kunnen wel prima worden gebruikt voor dingen als de dikte van een border, want die dikte hoeft meestal niet gewijzigd te kunnen te worden. Behalve de px heb ik nog nooit een van de andere absolute eenheden gebruikt zien worden voor weergave op een scherm. Dat is ook weinig zinvol. Als een centimeter toch waarschijnlijk geen 'echte' centimeter is, waarom zou je hem dan gebruiken? Als je als architect je ontwerp in centimeters op het scherm zet, en die centimeters kloppen niet, dan ben je waarschijnlijk je opdracht kwijt. Als de opdrachtgever toevallig eigenaar van een lachspiegelpaleis is, maak je nog 'n kans, maar daar zijn er niet zoveel van.

Voor printen zijn absolute eenheden beter geschikt. Eenheden als em en rem kunnen worden omgezet in pt, de eenheid die al eeuwenlang in de drukkerwereld wordt gebruikt. Voor kantlijnen en dergelijke kunnen eenheden als inch en centimeter worden gebruikt. Volgens de specificatie hoort bijvoorbeeld een centimeter echt een centimeter te zijn bij printen, maar in de praktijk kun je daar niet op vertrouwen. Meer over printen is te vinden bij Printen.

px

Hieronder is te zien, of deze browser px ondersteunt.

px

Algemeen

Pixel is een afkorting van 'picture element': een lichtgevend puntje op een beeldscherm. In css is de betekenis inmiddels veranderd, maar de naam pixel is blijven bestaan en wordt afgekort tot px. De oorspronkelijke pixel heet tegenwoordig schermpixel (in het Engels 'device pixel') of dot, maar wordt helaas meestal nog gewoon 'pixel' genoemd. De pixel, zoals die in css wordt gebruikt, wordt tegenwoordig ook wel 'css-pixel' genoemd. Over het verschil tussen schermpixels en css-pixels is meer te vinden bij css-pixels en schermpixels.

De grootte van een pixel

Elke andere lengte-eenheid in css is te herleiden tot de eenheid px. Intern werkt de browser altijd met de eenheid px, ongeacht de eenheid die in css wordt gebruikt. Bij opvragen van een lengte met JavaScript geeft de browser altijd de waarde in px. calc() kan berekeningen met verschillende lengte-eenheden maken, omdat de browsers alles omrekent naar dezelfde eenheid: px.

Bovenstaande opsomming maakt duidelijk, waarom de grootte van een pixel zo belangrijk is.

Tot enkele jaren geleden kon je er in de praktijk van uitgaan dat 1 inch even groot was als 96 pixel. De meeste beeldschermen hadden een resolutie van 96 pixels per inch: 96 beeldpuntjes in elke inch. Hoewel de pixel nooit als een precieze maat in de specificatie heeft gestaan, maakte dat in de praktijk weinig uit. Je had alleen maar desktopcomputers, en de schermen daarvan waren allemaal ongeveer hetzelfde opgebouwd. 1 px, zoals gebruikt in css, kwam ongeveer overeen met 1 pixel in een beeldscherm.

Inmiddels is dat niet meer zo. Op een smartphone is een pixel veel kleiner dan op een breed beeldscherm voor de desktop. Een smartphone wordt meestal veel dichter bij het oog gehouden dan een los beeldscherm. Daarom kan een pixel daar kleiner zijn. Zou de pixel even groot zijn op een smartphone als op een los beeldscherm, dan zouden er op de smartphone maar heel weinig letters naast elkaar passen.

Websites zijn gebouwd vanuit de veronderstelling dat er 96 pixels in 'n inch gaan. Dat stond weliswaar niet in de specificatie, maar in de praktijk was dat gewoon zo. Als die veronderstelling los wordt gelaten, zouden miljoenen sites niet meer goed worden weergegeven. Maar nu ontstaat er wel een probleem: als tegenwoordig de maat van de pixel varieert, varieert ook de maat van de inch, als er nog steeds 96 pixels in die inch gaan. En dat is ook precies wat er gebeurt: een inch is op een smartphone (veel) kleiner dan een inch op een los beeldscherm. Hetzelfde geldt voor millimeter, centimeter, en dergelijke.

In de specificatie level 3 staat expliciet, dat inches, centimeters, en dergelijke mogen afwijken van de 'echte' inch, centimeter, en dergelijke. Voor 'printers en soortgelijke hogeresolutie-apparaten' horen deze eenheden volgens de specificatie nog wel correct te worden weergegeven. Maar ook op hogeresolutieschermen kunnen deze eenheden (veel) groter of kleiner zijn dan hun 'echte' tegenhangers. En ook bij printen is een centimeter meestal (veel) meer of (veel) minder dan een 'echte' centimeter. (Over printen is meer te vinden bij Printen.)

Voor mensen die nieuw met css beginnen, lijken eenheden in css vooral te bestaan om de relativiteitstheorie te onderbouwen. Maar dat heeft dus een historische oorzaak. Niemand had twintig jaar geleden smartphones kunnen bedenken. Je zou eenheden als de inch en de centimeter alsnog helemaal los kunnen koppelen van de pixel, zodat ze 'n echte inch en centimeter kunnen blijven. Maar dat zou bestaande websites zwaar in de problemen hebben gebracht, dus dat was ook geen oplossing.

Tot nu toe gaat het steeds over de pixel, zoals die in css wordt gebruikt: de 'css-pixel'. Die pixel is op een smartphone kleiner dan op een los beeldscherm. Maar er speelt nog iets. Op hogeresolutieschermen zijn ook de fysieke pixels in het beeldscherm, de beeldpuntjes, niet íéts kleiner, maar veel en veel kleiner. Ze zitten veel dichter op elkaar, dan vroeger gebruikelijk was, zodat je een betere weergave krijgt. Deze pixels worden 'schermpixels' genoemd en zijn iets heel anders dan 'css-pixels'.

css-pixels en schermpixels

Met 'css-pixel' wordt de eenheid px bedoeld, zoals die in css wordt gebruikt: je geeft er allerlei lengtes mee aan. Tot 'n paar jaar geleden was 'n css-pixel even groot als 'n 'schermpixel': de fysieke pixel in het beeldscherm (in het Engels 'device pixel'). Die schermpixel was in kleinere schermen misschien iets kleiner dan op grotere schermen, maar dat maakte in de praktijk niets uit. 'n Border van 1 px breed was overal goed te zien. Het woord 'pixel' kon probleemloos voor zowel css-pixels als schermpixels worden gebruikt.

Stel dat je een tablet hebt met 1024 x 768 schermpixels. De schermpixels zijn iets kleiner dan op een los beeldscherm met 96 pixels per inch, maar veel maakt dat niet uit. Een pagina op een los beeldscherm is misschien iets groter, maar ook op de tablet is alles goed te zien.

Nu wordt die tablet veranderd in een hogeresolutiescherm: er worden vier keer zoveel schermpixels op dezelfde ruimte gezet 4096 x 3072 px. Als er niets wordt aangepast, is de pagina nu opeens vier keer zo klein. Het beeld is prachtig scherp, maar heel klein. Een border van 1 px breed wordt zo smal, dat deze niet meer is te zien. Tekst wordt zo klein, dat deze onleesbaar wordt. Foto's zijn ongelooflijk scherp, maar alleen met een vergrootglas te bekijken.

Om dit te voorkomen wordt een verschil gemaakt tussen css-pixels en schermpixels. De css-pixels zijn gebaseerd op de – tot voor enkele jaren – normale beeldschermen van de desktop (1 css-pixel is 1 schermpixel). De schermpixels zijn het werkelijk op het apparaat aanwezige aantal pixels (dat is het aantal pixels, waarvoor je hebt betaald). Met de naam 'pixel' worden inmiddels dus twee volstrekt verschillende dingen bedoeld: de rekenkundige eenheid px en het aantal fysieke, met de soldeerbout aangebrachte, schermpixels.

Om te voorkomen dat alles op die tablet met een vier keer zo hoge resolutiedichtheid vier keer zo klein wordt, doet de tablet net alsof er maar 1024 x 768 pixels zijn. Alsof het 'n scherm is, zoals dat tot voor enkele jaren gebruikelijk was. Hierdoor kunnen nog steeds gewoon css-pixels worden gebruikt om lengtes op te geven, net zoals dat altijd al gebeurde.

De tablet gebruikt echter voor de weergave voor elke css-pixel zestien schermpixels (vier schermpixels naast elkaar en vier boven elkaar in elke css-pixel). Hierdoor is de border van 1 px breed nog steeds te zien op zo'n hogeresolutiescherm. Maar wel veel duidelijker, want er zijn zestien keer zoveel beeldpunten ter beschikking. Dat zie je vooral bij ronde hoeken, tekst, foto's, en dergelijke: die zijn veel scherper (foto's en dergelijke moeten wel worden aangepast voor hogeresolutieschermen om de betere kwaliteit te kunnen zien).

Ook bij media query's en dergelijke geeft de tablet niet het aantal schermpixels, maar het aantal css-pixels.

De verhouding tussen het aantal css-pixels en het aantal schermpixels, de 'resolutie' of 'resolutiedichtheid' wordt opgegeven in een aantal eigen eenheden: dpi (dots per inch), dpcm (dots per centimeter) en dppx (dots per pixel). Daarnaast gebruikt Apple nog een eigen niet-standaard eenheid. De makkelijkste eenheid, die waarschijnlijk uiteindelijk het meest gebruikt gaat worden, is dppx. Die tablet van hierboven heeft een resolutiedichtheid van 4 dppx, wat 'n heel simpel getal is. Makkelijker dan iets als 4096 dpi.

Het verhaal hierboven gaat over een tablet met een vier keer zo hoge resolutiedichtheid, maar het geldt voor elke resolutiedichtheid: de browser rekent het aantal css-pixels om naar het aantal schermpixels, en bij media query's en dergelijke wordt het aantal css-pixels gebruikt.

Dezelfde naam voor twee verschillende dingen is uiteraard ongelooflijk verwarrend. Daarom wordt in toenemende mate niet over 'pixels' gesproken, als schermpixels worden bedoeld, maar over 'dots'. Dat is bij de eenheden voor resolutie hierboven al te zien. Maar het zal nog wel even duren, voor dit echt is ingeburgerd, als dat al ooit gebeurt. Fabrikanten geven de maat van het scherm op in pixels. Mensen zijn daaraan gewend, dus een tablet met als maat 4096 x 3072 pixel zal dat niet snel veranderen in iets als 1024 x 768 pixel en 4 dppx, omdat kopers dan eerder de 4096 x 3072 zullen kopen. Want dat lijkt meer.

Geschikt voor

Nog niet zo lang geleden werd zo'n beetje elke maat in px opgegeven. Dat is drastisch veranderd. Met de komst van hogeresolutieschermen en een enorme variëteit in schermgroottes, is de pixel een veel minder bruikbare eenheid geworden. Font-relatieve eenheden en viewport percentages zijn tegenwoordig vaak veel geschikter. Hetzelfde geldt voor percentages. Met al deze maten is het relatief makkelijk een pagina aan de grootte van het scherm, de resolutiedichtheid, en dergelijke aan te passen.

Tot niet zo lang geleden werd ook de lettergrootte in px opgegeven. Omdat dit een absolute eenheid is, wordt de lettergrootte niet geërfd, zoals bij bijvoorbeeld de relatieve eenheden em en rem wel het geval is. Bij absolute eenheden is de lettergrootte een op zichzelf staande grootte. 16 px is altijd 16 px, ongeacht de grootte van de ouder. Hierdoor is het lastig lettergroottes op elkaar af te stemmen. Bij gebruik van relatieve eenheden is het veel makkelijker lettergroottes onderling op elkaar af te stemmen.

Bij gebruik van een absolute eenheid zoals px kan de gebruiker de lettergrootte niet veranderen. Zoomen kan altijd, maar dat kan het weer nodig maken horizontaal te moeten scrollen, zoals beschreven bij Het verschil tussen zoomen en lettergrootte (font-size) veranderen.

Waar is deze arme, van z'n troon gevallen eenheid dan nog wel geschikt voor? Voor maten die niet hoeven te veranderen, zoals de breedte van een border. Ook voor minimumbreedte en -hoogte is de pixel nog steeds heel geschikt. Als je de hoogte van 'n menuknop afhankelijk maakt van de lettergrootte, kan die al snel te laag worden. Een minimumhoogte in pixels kan dat voorkomen.

Bij afbeeldingen kan in de html de breedte en hoogte worden opgegeven. Daar staat geen eenheid bij, maar het gaat om pixels. Als die maten al in de html worden opgegeven, kan de browser al ruimte vrij laten bij het opbouwen van de pagina. De afbeelding wordt waarschijnlijk iets later gedownload, en als er geen ruimte vrij is, 'verspringt' de pagina mogelijk als de afbeelding in de pagina wordt gezet. Maar ook dit is aan het veranderen. Bij kleine afbeeldingen kan dit nog steeds, maar bij grotere afbeeldingen wordt vaak een andere afbeelding voor een smartphone gebruikt, dan voor een breedbeeldscherm. En in dat geval moet je geen breedte en hoogte aan de afbeelding geven in de html.

Soms kan het handig zijn om de regelhoogte in pixels op te geven. Deze verandert dan niet bij een andere lettergrootte. Bij een grote letter, zoals in een logo in een header, kan een lettergrootte in px voorkomen dat deze letter té groot wordt, als de gebruiker de lettergrootte verandert. De letter moet dan wel al zo groot zijn dat deze ook door mensen die iets slechter zien goed is te lezen.

Browser support

px wordt door alle geteste browsers ondersteund.


pt

Hieronder is te zien, of deze browser pt ondersteunt.

pt

De pt ('point' of 'punt') is bedoeld voor printen. In principe kan een pagina van een website worden geprint. Maar je hebt (iets) meer controle over het resultaat, als relatieve eenheden als de em en de rem worden omgerekend naar absolute eenheden zoals de pt. Voor dat omrekenen kunnen de omrekentabellen worden gebruikt. Omdat deze eenheid vooral voor printen wordt gebruikt, wordt deze verder besproken in het hoofdstukje Printen.

De pt wordt door alle geteste browsers ondersteund.

in en cm

Hieronder is te zien, of deze browser in ondersteunt.

in

Hieronder is te zien, of deze browser cm ondersteunt.

cm

Ook de in (inch) en de cm (centimeter) zijn niet bedoeld voor weergave op het scherm, maar voor printen. Ook deze eenheden zijn op de pixel gebaseerd. Omdat de grootte van een pixel kan variëren, kunnen ook deze eenheden in grootte variëren. Deze eenheden zijn dus zeker niet geschikt om een exacte weergave met juiste maten te produceren.

Deze eenheden zijn bedoeld voor printen, maar ook daar kunnen ze afwijken van de 'echte' inch en centimeter. Ze worden wel gebruikt om bij printen de breedte van de kantlijn op te geven. Bij Printen wordt dit uitgebreider besproken.

De in en de cm worden door alle geteste browsers ondersteund.

mm, q en pc

Hieronder is te zien, of deze browser mm ondersteunt.

mm

Hieronder is te zien, of deze browser q ondersteunt.

q

Hieronder is te zien, of deze browser pc ondersteunt.

pc

De mm (millimeter), q (kwart millimeter) en pc (pica) heb ik nog nooit ergens gebruikt zien worden. Ik zou er het nut ook niet van weten, mede omdat ze alle drie zijn gebaseerd op de pixel. Omdat de grootte van een pixel kan variëren, kunnen ook deze eenheden in grootte variëren. Dat geldt ook voor de millimeter. Deze eenheden zijn dus zeker niet geschikt om een exacte weergave met juiste maten te produceren.

Net als alle absolute eenheden, zijn ook deze eenheden minder geschikt voor weergave op een scherm. Ze zijn bedoeld voor printen, maar ook daar kunnen ze afwijken van de 'echte' millimeter en pica.

mm en pc worden door alle geteste browsers ondersteund. De q wordt alleen door Firefox ondersteund. Maar dan wel in álle versies van Firefox, dat wel...

Percentage

Hieronder is te zien, of deze browser % ondersteunt.

%

Werking

De eenheid % is een apart geval, omdat procenten zelfstandig niet kunnen bestaan. Als ik op de markt ga staan en roep: "Alleen vandaag: twintig procent voor vijf euro", dan zullen de meeste mensen vragen, waarvan dat dan wel twintig procent mag zijn. Met css is dat net zo: procent waarvan?

Bij gebruik van procenten staat de soort eenheid niet op voorhand vast. Tien procent van 'n liter is een deciliter, maar tien procent van 'n kilo is 'n hectogram. Ook dat is net zo bij css. Als het om lengte gaat, worden procenten omgezet naar de standaardeenheid voor lengte: de pixel.

Meestal heeft een percentage betrekking op de ouder, maar dat is lang niet altijd zo. width: 80%, wil zeggen dat het element 80% van de breedte van de ouder van het element krijgt. font-size: 200%; wil zeggen dat de lettergrootte van het element twee keer zo groot wordt als de lettergrootte van de ouder van het element (waarmee ook de font-relatieve eenheden van grootte veranderen).

Soms kan het gebruik van percentages tot onverwachte resultaten leiden. Bij margin-left: 5%; is de linkermarge niet 5% van de breedte van de linkermarge van de ouder, maar 5% van de breedte (width) van de ouder. Dan zou je denken dat margin-top: 5%; is gebaseerd op de hoogte (height) van de ouder, maar ook dat is gebaseerd op de breedte van de ouder. Bij het opstellen van de specificatie is geprobeerd aan te sluiten bij wat meestal het gewenste resultaat is, vandaar deze ogenschijnlijk vreemde kronkel. (Hoewel dat gewenste resultaat soms wel voor enige discussie vatbaar is...)

transform: translateX(5%); verplaatst het element 5% naar rechts ten opzichte van de breedte van het element zelf, niet ten opzichte van de breedte van de ouder. Maar position: relative; left: 10%; verplaatst het element weer ten opzichte van de ouder.

Bij de links staan pagina's, waarop de werking van % bij de verschillende eigenschappen kan worden opgezocht.

Voorbeelden

Flexibele kolommen

Percentages zijn uitstekend geschikt voor een responsieve lay-out. Je kunt bijvoorbeeld de linkerkolom 30% van de breedte van de pagina maken, met een minimum- en maximumbreedte:

width: 30%; min-width: 20em; max-width: 150px;

width: 30%;: 30% van de breedte van de ouder.

min-width: 20em;: minimaal 20 em breed. Door het gebruik van de relatieve eenheid em verandert de minimumbreedte met de lettergrootte, zodat de kolom niet te smal wordt bij een grotere letter.

max-width: 150px;: maar er zijn wel grenzen aan die breedte. Bij 150 px is de kolom breed genoeg, ook bij een grotere letter. Zou de kolom breder worden, dan komt de rechterkolom in de verdrukking. (Als de maximumbreedte van 150 px minder blijkt te zijn dan de minimumbreedte van 20 em, dan 'wint' de minimumbreedte.)

De waarden die je moet gebruiken, hangen natuurlijk van de inhoud van de kolom en dergelijke af.

Geneste gecentreerde elementen

position: relative;
width: 80%; height: 50%; position: absolute; top: 50%; left: 50%; translate(-50%, -50%);

Bij de css in de hierboven staande <div>'s is alleen de relevante css weergegeven.

De buitenste <div> heeft een relative positie, zodat de binnenste <div> ten opzichte van deze <div> gepositioneerd kan worden.

De binnenste <div> heeft een breedte en hoogte in procenten. Omdat procenten zijn gebruikt, veranderen breedte en hoogte van de binnenste <div> automatisch mee, als breedte en/of hoogte van de buitenste <div> veranderen.

De binnenste <div> is absoluut gepositioneerd. Met behulp van top: 50%; en left: 50%; wordt de binnenste <div> in het midden van de buitenste <div> gezet. Omdat procenten zijn gebruikt, wordt dit midden automatisch aangepast als de buitenste <div> van grootte verandert.

De binnenste <div> is halverwege de buitenste <div> geplaatst. Dat wil zeggen dat de linkerbovenhoek van de binnenste <div> in het midden van de buitenste <div> staat. Als de binnenste <div> de helft van de breedte naar links wordt verplaatst, en de helft van de hoogte naar boven, staat de middelste <div> precies in het midden van de buitenste <div>. Dat is wat er met transform: translate(-50%, -50%;); gebeurt: de helft van de breedte naar links, en de helft van de hoogte naar boven verplaatsen.

translate() verplaatst ten opzichte van het element zelf, in dit geval de binnenste <div>. Omdat procenten zijn gebruikt, wordt altijd de helft van de breedte en hoogte verplaatst, ongeacht de grootte van de binnenste <div>.

Met deze constructie staat de binnenste <div> altijd horizontaal en verticaal gecentreerd in de buitenste <div>, waarbij de maten van beide <div>'s er niet toe doen. Je kunt op deze manier dus blok-elementen met onbekende grootte centreren.

Afbeelding met maximumbreedte

Afbeelding 2: afbeelding is te breed
Afbeelding 3: afbeelding past

Hierboven staan twee <div>'s die elk 100 px breed zijn. Ze hebben geen hoogte, waardoor ze precies hoog genoeg worden om de inhoud ervan weer te geven. (De <div>'s hebben een zwarte border van 3 px breed, zodat de grootte van de <div>'s is te zien.) In beide <div>'s staat dezelfde afbeelding met een breedte van 180 en een hoogte van 161 px. Deze hoogte is in de html opgegeven.

In de css voor de linker afbeelding staat verder niets voor breedte en hoogte, waardoor deze afbeelding gewoon op z'n ware grootte wordt weergegeven. Omdat overflow standaard op visible staat, wordt de afbeelding hierdoor breder dan de <div>, waar hij in staat, want deze is slechts 100 px breed. De <div> wordt automatisch hoog genoeg om de erin zittende afbeelding weer te geven.

Bij de rechter afbeelding is in de css toegevoegd: max-width: 100%; height: auto;. Omdat css altijd 'wint' van html, overrulen deze breedte en hoogte de in de html bij de afbeelding opgegeven breedte en hoogte. De maximumbreedte in procenten is ten opzichte van de ouder. Dat is hier de <div>, waar de afbeelding in staat. In de html is bij de afbeelding een hoogte van 161 px opgegeven. Als dat niet wordt aangepast, wordt de afbeelding 100 px breed, maar blijft 161 px hoog: totaal vervormd. Door de hoogte op auto te zetten, wordt de hoogte door de browser in dezelfde verhouding als de breedte aangepast, waardoor de afbeelding niet wordt vervormd.

Omdat de maximumbreedte in procenten is opgegeven, wordt deze automatisch aangepast aan de breedte van de ouder, hier de <div>, waar de afbeelding in staat.

(Het is geen goed idee op deze manier afbeeldingen sterk te verkleinen. Als de afbeelding sterk wordt verkleind, wordt nog steeds het grote bestand gedownload. Daar maak je mensen die betalen voor bandbreedte niet blij mee, en het downloaden kan ook (heel) lang duren op tragere verbindingen. Deze methode is wel uiterst geschikt voor kleinere correcties.)

Relatief positioneren

position: relative; top: calc(50% - 1.2em);

Omdat de browser bij gebruik van calc() alle lengte-eenheden omrekent naar de standaardeenheid voor lengte px, kan ook bij calc() gewoon % worden gebruikt.

In bovenstaande constructie is in een <div> een element <code> neergezet, dat als blok-element wordt weergegeven. In de <code> staat de voor dit voorbeeld relevante css. De <code> wordt relatief gepositioneerd met behulp van calc().

Als de <code> halverwege de buitenste <div> moet staan, kun je gewoon top: 50%; gebruiken. Dat is wat hier ook vooraan calc() staat. Maar vervolgens wordt de <code> 1,2 em naar boven gezet door met behulp van calc() 1,2 em van de 50% af te trekken. Omdat procenten zijn gebruikt staat de <code> altijd 1,2 em boven het midden van de buitenste <div>, ongeacht de hoogte van deze <div>.

Als de lettergrootte wordt veranderd, verandert de lengte van em ook, zoals beschreven bij De werking van em bij andere eigenschappen dan font-size. De uiteindelijke plaats van de <code> wordt dus bepaald door een combinatie van de hoogte van de buitenste <div> en de lettergrootte. Tijdens het maken van de pagina zijn beide onbekend, maar bij het weergeven kent de browser de maten wel en kan het op de juiste plaats neerzetten. Browser die calc() niet kennen, plaatsen de binnenste <div> gewoon aan de bovenkant van de buitenste <div>.

(In dit voorbeeld is de grootte van de buitenste <div> wel bekend, maar dat kon niet anders, omdat het op deze pagina moet passen en zo.)

Gradiënts

Hierboven staan twee <p>'s met in elk een gradiënt, een verlopende achtergrond. De css voor de bovenste achtergrond:

{background: linear-gradient(to right, white 0, black 25px, white 50px, black 75px, white 100px);}

De css voor de onderste achtergrond:

{background: linear-gradient(to right, white 0%, black 25%, white 50%, black 75%, white 100%);}

Bij de bovenste gradiënt zijn pixels gebruikt, bij de onderste gradiënt percentages. Bij de bovenste achtergrond is de gradiënt altijd precies hetzelfde, ongeacht de breedte van de <p>. Op 25 px vanaf de linkerkant is de gradiënt zwart, op 50 px vanaf links wit, op 75 px vanaf links weer zwart, en vanaf 100 px vanaf links is de gradiënt wit. Deze afstanden wijzigen niet, als de breedte van de <p> wijzigt.

Bij de onderste gradiënt zijn dezelfde waarden gebruikt, maar nu is de eenheid niet px, maar %. Op 25% vanaf de linkerkant is de gradiënt zwart, op 50% vanaf links wit, op 75% vanaf links weer zwart, en op 100% vanaf links (dat is helemaal rechts) weer wit. Omdat hier percentages zijn gebruikt, wordt de gradiënt aangepast aan de breedte van de <p>. Hoe breed de <p> ook is, op 50% (halverwege) is de kleur altijd wit. En helemaal rechts is de kleur ook altijd wit. Tussen deze twee punten in, op 75% vanaf links, is de kleur altijd zwart. Ongeacht hoe breed de <p> is, de verdeling van de kleuren in de gradiënt is altijd hetzelfde.

(Overigens is er niets op tegen om bijvoorbeeld pixels en percentages door elkaar heen te gebruiken.)

Bugs en problemen

Bij het gebruik van percentages kunnen er afrondingsproblemen ontstaan. Hierboven staat een <div> met een breedte van 187 px en een border van 1 px breed. Binnen deze <div> staan vier naar links gefloate <div>'s die elk 25% breed zijn. De <div>'s hebben ook een border, maar omdat box-sizing: border-box; is gebruikt, staat die border binnen de 25%. De vier binnenste <div>'s moeten dus precies de buitenste <div> vullen.

Elke binnenste <div> zou 46,75 pixel breed moeten zijn. In het verleden rondden sommige browsers dit altijd naar 46 af, sommige altijd naar 47 en sommige rondden 0,4 en lager naar beneden en 0,5 en hoger naar boven af. Op de huidige schermen kunnen ook nog subpixels worden gebruikt. Op de afbeelding hieronder is te zien, tot welke problemen dit afronden kan leiden. Op problemen met afronden en mogelijke oplossingen hiervoor wordt dieper ingegaan bij Onderverdelen in gelijke stukken bij calc(), waar soortgelijke afrondingsproblemen spelen.

Afbeelding 4: afrondingsfouten bij percentages
Afbeelding 4: links als te hoog wordt afgerond, rechts als te laag wordt afgerond. Links passen de <div>'s niet naast elkaar, rechts ontstaat een kier.

Browser support

% wordt door alle geteste browsers ondersteund.


calc()

Hieronder is te zien, of deze browser calc() ondersteunt.

calc()

Syntax en werking

Met behulp van calc() kunnen berekeningen worden uitgevoerd: vermenigvuldigen, delen, optrekken en aftellen. Hierbij is het niet nodig dat precieze waarden bekend zijn. 'n Berekening als 80px - 50px kun je ook zelf wel maken. Maar 'n berekening als 80% - 1em kun je nooit zelf maken, omdat onbekend is hoeveel pixels 80% en hoeveel pixels 1 em is. Dat ligt immers aan dingen als de grootte van het venster van de browser, en die ken je niet bij het maken van de pagina. De browser kent die maten echter bij weergave wel en kan 80% en 1 em vervangen door het aantal pixels op dat bepaalde scherm.

Het grootste nut van calc() is dat je allerlei verhoudingen en dergelijke kunt opgeven bij het maken van de pagina, waarbij dingen als de grootte van het browservenster onbekend zijn. De daadwerkelijke berekening wordt pas uitgevoerd door de browser op het moment van weergave, als dingen als de grootte van het venster bekend zijn.

Het teken voor optellen is +, voor aftrekken -, voor delen / en voor vermenigvuldigen *. Links en rechts van de + en de - moet een spatie staan, om verwarring met negatieve en positieve getallen te voorkomen. Rondom de / en de * hoeft geen spatie te staan. Maar om fouten te voorkomen, en voor een betere leesbaarheid, kun je het best overal spaties gebruiken.

Voor het uitvoeren van de berekeningen gelden de gewone rekenkundige voorrangsregels: eerst worden vermenigvuldigingen en delingen uitgevoerd, daarna optellingen en aftrekkingen. Als er meerdere gelijke bewerkingen zijn, worden die van links naar rechts uitgevoerd. Je kunt de volgorde van de berekeningen beïnvloeden met haakjes.

width: calc(100px - 2px * 10);

Omdat vermenigvuldigen voor aftrekken gaat, wordt hierboven eerst 2px * 10 uitgevoerd: 20 px. Vervolgens wordt die 20 px van de 100 px afgetrokken. De uiteindelijke uitkomst is dan 80 px.

width: calc((100px - 2px) * 10);

Nu veranderen de haakjes de volgorde van de bewerkingen. 100 px - 2 px gaat nu voor, omdat dat tussen haakjes staat. De uitkomst 98 px wordt vervolgens met 10 vermenigvuldigd. Het uiteindelijke resultaat is nu 980 px, dat is 900 px meer dan dezelfde berekening zonder haakjes opleverde, een verschil dat niet helemaal onbelangrijk is.

Het resultaat van een berekening met calc() heeft automatisch de juiste eenheid. calc() leidt die eenheid af uit de bij de berekening gebruikte eenheden. Bij een berekening als calc(10vw -2em); zet de browser vw en em om in pixel. Het resultaat zal dus in pixels zijn.

Dat de browser relatieve eenheden als vw (een viewport percentage) en em (een font-relatieve eenheid) omzet naar de absolute eenheid px, maakt niet uit. Bij het maken van de pagina zijn dit relatieve eenheden: 10 vw is 10% van de breedte van een browservenster met een onbekende breedte. Maar bij het weergeven kent de browser de breedte en kan de relatieve eenheid vw probleemloos worden omgezet naar de absolute eenheid px. Zodra de grootte van venster en dergelijke bekend zijn, is het relatieve niet meer van belang.

Als op de desktop de grootte van het browservenster wordt veranderd, of als tablet of smartphone wordt gekanteld, veranderen breedte en hoogte. Als bij de berekening gebruik wordt gemaakt van eenheden die iets met breedte en/of hoogte van het venster te maken hebben, wordt de berekening onmiddellijk opnieuw uitgevoerd met de nieuwe waarden.

Als een percentage wordt gebruikt, wordt de eenheid gebruikt die bij de eigenschap hoort. Bij bijvoorbeeld width zijn dat pixels, bij opacity is dat gewoon 'n getal. Als een percentage wordt gebruikt, waar dat normaal genomen niet kan (bijvoorbeeld opacity: 10%;), is calc() ongeldig en wordt de hele calc() genegeerd. Als er voor percentages speciale regels gelden (zoals bij een verticale marge in procenten, die wordt genomen ten opzichte van de breedte van de ouder), gelden die regels ook bij de percentages in calc().

Beperkingen

Voor berekeningen met calc() gelden bepaalde regels. Als niet aan die regels wordt voldaan, is de hele berekening ongeldig en wordt de hele calc() genegeerd.

Je kunt alleen berekeningen uitvoeren met gelijke soorten eenheden. Omdat Einstein al was overleden, toen css werd ontwikkeld, kun je helaas geen centimeters met seconden vermenigvuldigen. Je kunt echter wel alle lengte-eenheden door elkaar heen gebruiken. De browser zet al die eenheden om naar de pixel, de standaardeenheid voor lengte. Omdat alle waarden dan dezelfde eenheid hebben, kunnen de berekeningen worden uitgevoerd. Hetzelfde geldt voor bijvoorbeeld seconden en milliseconden (die eenheden worden overigens nog niet door alle browsers bij gebruik in calc() ondersteund).

Als je om een breedte te berekenen milliseconden en seconden gebruikt, is dat wat calc() betreft geen enkel probleem. Er wordt vrolijk gerekend met die tijdseenheden. Stel dat de uitkomst van de berekening 0,1 seconde is, dan wordt de css uiteindelijk width: 0.1s;. De berekening is volledig correct en dus geldig, maar het resultaat wordt alsnog genegeerd omdat een breedte in seconden ietwat onbruikbaar is.

Met behulp van de css-functie attrib() kunnen waarden worden opgevraagd en gebruikt:

abbr[title]:after {content: " (" attr(title) ") ";}

Bovenstaande css zet de inhoud van het title-attribuut van de <abbr> met behulp van content tussen aanhalingstekens achter de <abbr> (wat bij bijvoorbeeld printen handig kan zijn).

Op dit moment kan attrib() alleen bij content worden gebruikt en kan de inhoud van attrib() alleen tekst zijn, zoals de inhoud van het title-attribuut. In de specificatie voor waarden en eenheden staat echter al, dat attrib() ook gebruikt kan worden om waarden als breedte, doorzichtigheid, en dergelijke op te vragen en te gebruiken. attrib() kan dan ook getallen en dergelijke bevatten. In principe kan de inhoud van attrib() dan ook bij berekeningen met calc() worden gebruikt. Uiteraard alleen als die inhoud zinvol is voor de berekening, frequenties kunnen nog steeds niet door pixels worden gedeeld. Op dit moment ondersteunt nog geen enkele browser dit, en waarschijnlijk wordt dit uitgesteld tot de volgende versie van de specificatie over waarden en eenheden.

Browsers moeten minstens twintig 'terms' in calc() ondersteunen. Omdat 'term' niet verder wordt omschreven, is onduidelijk of dat 20 aparte waarden zijn, of dat iets als (20em - 4%) ook als één 'term' telt. Hoe dan ook: twintig is rijkelijk veel. Als je boven die twintig komt, kan het vermoedelijk simpeler op een andere manier. Als calc() meer 'terms' bevat, dan de browser ondersteunt, is de hele calc() ongeldig en wordt genegeerd.

In principe kunnen binnen calc() css-variabelen ('custom properties') worden gebruikt. Dit zijn vrij nieuwe attributen:

:root {--lekker-breed: 2000px;}

:root is het buitenste element. Bij html is dat altijd <html>. In plaats van width: 2000px; kan nu width: var(--lekker-breed); worden gebruikt. Voordeel: je hoeft de breedte nu maar op één plaats te wijzigen. Een css-variabele begint met twee afbreekstreepjes, daaraan herkent de browser dat het een css-variabele is. (--lekker-breed kan overal 2000 px vervangen. height: var(--lekker-breed); geeft 'n hoogte van 2000 px. Alleen is de naam van de variabele dan wat stom gekozen...)

Deze variabelen kunnen ook binnen calc() worden gebruikt. De ondersteuning voor css-variabelen is echter nog vrij slecht. Internet Explorer, UC Browser en Samsung Internet, om er maar 'n paar te noemen, ondersteunen het nog niet. Edge ondersteunt het pas vanaf versie 15. Daarnaast zitten er nogal wat bugs in. Zo wordt de hele calc() op iOS9 genegeerd, als er meer dan één css-variabele in zit. Daarom is het beter dit nog niet te gebruiken binnen calc(). Onderaan de pagina staan links naar pagina's, waar je de browser support kunt opzoeken.

Als bij het berekenen van breedte of hoogte van een tabel en alle bij een tabel horende elementen percentages worden gebruikt in calc(), mag de browser calc() negeren en als waarde voor breedte of hoogte 'auto' gebruiken. Dit vanwege de ingewikkeldheid van het berekenen van breedte en hoogte bij tabellen.

Voorbeelden

Op bepaalde afstand vanaf bovenkant plaatsen

margin: calc(30px + 2em - 3vh) auto 0;

Hierboven staan twee <div>'s. Alleen de relevante css is weergegeven: de marge rondom de binnenste <div>.

calc() zet alle eenheden om naar de standaardeenheid voor lengte: de pixel.

Als de lettergrootte niet is veranderd, is 2 em hetzelfde als 32 pixel. Als de gebruiker de lettergrootte wel heeft veranderd, weet de browser dat, en is 2 em een ander aantal pixels. Voor browsers die calc() niet kennen, staat voor deze css nog margin: 32px auto 0;. Deze browsers zullen deze regel zonder calc() gebruiken, en browsers die calc() wel kennen de hierboven staande css.

1 vh is 1% van de hoogte van het browservenster. Als het venster 1000 px hoog is, is 1 vh 10 px. En 3 vh is dan 30 px. Die hoogte is onbekend bij het maken van de pagina, maar bij het weergeven kent de browser de hoogte van het venster.

Als de lettergrootte niet is veranderd en het browservenster 1000 px hoog is, wordt de berekening: calc(30px + 32px - 30px) = 32 px. Als de lettergrootte wel is veranderd, of bij een andere vensterhoogte, zal de uitkomst anders zijn.

(In werkelijkheid is in dit voorbeeld de lettergrootte enigszins aangepast, omdat de pagina al van alles heeft veranderd.)

Ruimte aan onderkant vrijlaten voor bijvoorbeeld onderschrift

Afbeelding 5: afbeelding met onderschrift
Onderschrift

Ook dit voorbeeld is niet echt goed te zien binnen deze pagina. Het is bedoeld om kleine browservensters zo optimaal mogelijk te gebruiken bij weergave van een afbeelding met onderschrift, zonder dat scrollen nodig is.

Twee <div>'s staan in een gezamenlijke ouder-<div>. De ouder-<div> krijgt de hoogte van het browservenster. Dat kan met height: 100vh; bij de ouder-<div>, of door <html>, <body> en de ouder-<div> height: 100%; te geven. Als verder geen breedte aan <html>, <body> en de ouder-<div> wordt gegeven, worden deze - omdat het blok-elementen zijn - automatisch even breed als het venster. Het venster wordt dus optimaal benut.

(In dit voorbeeld heeft de buitenste <div> een hoogte van 200 px gekregen, omdat het anders niet binnen de pagina past. Ook enkele andere maten zijn om dezelfde reden aangepast.)

Binnen deze buitenste <div> staan twee andere <div>'s. In de bovenste van deze twee staat de afbeelding. De hoogte van deze <div> wordt opgegeven met height: 100%; height: calc(100% - 40px);. Onder deze bovenste <div> komt een tweede <div> met een hoogte van 40 px voor het onderschrift. Browsers die calc() niet kennen, gebruikten height: 100%;. In deze browsers komt het onderschrift onder het browservenster te staan en moet worden gescrold om dit te kunnen lezen.

Browsers die calc() wel kennen, gebruiken de hoogte met calc(). 100% hoog, even hoog als het browservenster. Daarvan wordt echter 40 px afgetrokken, zodat er aan de onderkant ruimte is voor een onderschrift. Omdat dit van de hoogte van het venster wordt afgetrokken, is scrollen niet nodig: ook het onderschrift staat binnen het venster.

De onderste <div> met het onderschrift heeft een hoogte van 40 px gekregen. Deze hoogte moet uiteraard worden aangepast aan de lengte van het onderschrift, en het onderschrift kan niet al te lang zijn, omdat het anders niet meer in de buitenste <div> past. (Wat trouwens ook niet het einde van de wereld is, alleen moet er dan worden gescrold om het hele onderschrift te kunnen zien.)

De afbeelding heeft in de html gewoon hoogte en breedte gekregen. Maar in de css staat img {max-width: 100%; max-height: 100%;}, zodat de afbeelding nooit breder of hoger dan de bovenste binnenste <div> kan worden.

Scrollbare <div> met header

height: 25%; height: calc(2.4em + 5px);

height: 75%; height: calc(100% - 2.4em - 5px); overflow: auto;

De onderste <div> is 50 px lager dan de buitenste <div>.

Omdat er meer tekst in staat, dan erin past, en overflow op auto staat, kan de <div> worden gescrold. De bovenste <div> scrolt niet mee.

Hèhè, met deze laatste zin is het kreng eindelijk hoog genoeg.

Dacht ik dus, maar op sommige smartphones moet er nog wat bij.

Ik ben zo blij, het is mei, ik leg een ei.

Als je dit wilt gebruiken, lees dan even de hele tekst bij dit voorbeeld, want op iOS kan dit nogal problematisch zijn.

Alleen de belangrijkste css is hierboven weergegeven. Het geheel staat in een <div> met een hoogte van 300 px.

In deze <div> staat bovenin een <p>. De hoogte hiervan is height: 25%; height: calc(2.4em + 5px);. De eerste height is weer voor browsers die geen calc() kennen. Browsers die wel calc() ondersteunen, gebruiken de tweede height. De standaard regelhoogte is ongeveer 1,2 em. Een hoogte van 2,4 em is dus ongeveer twee keer de regelhoogte. Daar wordt nog 5 px bij opgeteld voor wat speling.

De eerste hoogte in procenten is ten opzichte van de buitenste <div>. Deze hoogte verandert niet mee met een andere lettergrootte in de bovenste <p>.

De tweede hoogte met em is gebaseerd op de lettergrootte en verandert mee met een andere lettergrootte. Bij een grotere letter, wordt de bovenste <p> hoger. Bij een heel grote letter zou de tekst toch nog onder de <p> uit kunnen komen en over de tekst in de onderste <div> kunnen komen te staan. Om dat te voorkomen is aan de <p> ook overflow: auto; gegeven. Als je de letters vergroot tot meer dan 150%, wordt de <p> scrollbaar. De hoogte met calc() past zich dus (veel) beter aan de lettergrootte aan.

Onder de <p> staat een <div>. Hier is de hoogte height: 75%; height: calc(100% - 2.4em - 5px);. Browsers die calc() niet kennen, gebruiken weer de height: 75%;. Deze combineert goed met de height: 25%; van de erboven staande <p>. Samen vullen ze de hoogte van de buitenste <div> volledig. Maar bij een andere lettergrootte, verandert de hoogte van <p> en <div> niet.

Browsers die calc() wel kennen, gebruiken de tweede height: 100%. Aan de <p> boven deze <div> is een hoogte van 2,4 em + 5 px gegeven. Die wordt hier van de 100% afgetrokken. Ook hier vullen <p> en <div> samen de volle hoogte van de buitenste <div>, maar nu wordt de <p> hoger en de <div> evenveel lager bij een grotere letter. (En bij een kleinere letter omgekeerd, maar dat zal niet vaak voorkomen.)

In beide gevallen komt de onderste <div> tot de onderkant van de buitenste <div>. Door aan de onderste <div> overflow: auto; te geven, kan de <div> worden gescrold, als dat nodig is.

Lang niet alle browsers en alle systemen geven met een scrolbalk duidelijk aan dat er gescrold kan worden. Soms is het daarom aan te raden op een of andere manier aan te geven, dat er meer tekst (of wat dan ook) is, dan zichtbaar is.

Verder is iOS van Apple zoals bekend nogal prijzig. Het is dan ook logisch dat dit superieure systeem een aantal extra's heeft, die goedkopere systemen helaas niet hebben.

Scrollen van een <div> gaat ongelooflijk schokkerig op iOS. Feitelijk gaat het zo schokkerig, dat het nauwelijks bruikbaar is. Dit probleem is al jaren bekend, maar het is - door de geweldige openheid van Apple - onbekend, of dit een bug is, of dat dit zo hoort. En of het ooit opgelost gaat worden. In dit voorbeeld speelt dit niet, omdat er maar 'n heel klein stukje gescrold hoeft te worden. Maar zodra er langer gescrold moet worden, is het echt volslagen onbruikbaar op iOS. (Dit geldt alleen voor scrollen bínnen een <div> en dergelijke, de hele pagina kan wel altijd goed gescrold worden.)

Als je aan de onderste <div> -webkit-overflow-scrolling: touch; toevoegt, scrolt de <div> wel soepel. Alleen staat internet vol met verhalen over ernstige problemen bij gebruik van deze eigenschap. Het kan goed gaan, maar er kunnen ook de vreemdste dingen misgaan, en er kunnen zelfs delen van de pagina volledig verdwijnen. Kortom: goed testen.

Als op iOS wordt gezoomd, kan stomweg helemaal niet meer worden gescrold. Althans: wel de hele pagina, maar niet binnen een <div> of zo. Ook hiervan is niet bekend, of dit een bug is, of dit zo hoort, of het ooit gerepareerd gaat worden, wat dan ook. Nou ja, we zullen maar zeggen: het kost 'n paar centen, maar dan heb je ook wel de nodige extra's.

(Chagrijnig over Apple? Ja, zeg. Zo gesloten als een oester waardoor je regelmatig zeeën van tijd kwijt bent met het oplossen van al hun peperdure problemen. Is Microsoft eindelijk - wat dit betreft - fatsoenlijk bezig, begint Apple.)

Lettergrootte altijd 5 px groter dan ouder.

De code hierachter font-size: 1.2em; font-size: calc(1em + 5px); is 5 px groter dan de rest van de tekst.

Deze tekst staat in een gewone <p>. De code staat in een <span>, waarvan de lettergrootte is aangepast.

De eerste font-size is voor browsers die calc() niet kennen. 1,2 em maakt de lettergrootte 1,2 keer zo groot als de tekst van de ouder. Als de lettergrootte niet is aangepast, is 1 em 16 px. Een lettergrootte van 1,2 em is dan 1,2 x 16 = 19,2 px, dat is 3,2 px groter. Maar als de lettergrootte van de <p> 3 em zou zijn, zou het 1,2 x 3 x 16 = 57,6 px worden. De tekst in de <p> zou dan 3 x 16 = 48 px, die in de <span> 57,6 px zijn. Dat is 9,6 px groter.

Bij de tweede font-size blijft de tekst met 1 em in eerste instantie even groot. Vervolgens komt er 5 px bij. Ongeacht hoe groot de tekst in de <p> is, de tekst in de <span> is altijd precies 5 px groter.

In dit geval heeft dat niet zoveel nut, omdat het maar om één <span> gaat. Maar je zou op deze manier bepaalde classes altijd 'n bepaald aantal px groter of kleiner kunnen maken dan de ouder, ongeacht welke lettergrootte de ouder heeft. De vergroting of verkleining is altijd even groot bij px, terwijl die bij gebruik van 'n relatieve eenheid als em niet altijd even groot is.

Onderverdelen in gelijke stukken

12
12
12
12
12
12
12
12
12
12
12
12
11
11
11
11
11
11
11
11
11
11
11
10
10
10
10
10
10
10
10
10
10
9
9
9
9
9
9
9
9
9
8
8
8
8
8
8
8
8
7
7
7
7
7
7
7
6
6
6
6
6
6
5
5
5
5
5
4
4
4
4
3
3
3
2
2

Hierboven is elf keer een <div> met een breedte van 300 px in gelijke stukken verdeeld. De bovenste <div> is in twaalf stukken verdeeld, die daaronder in elf, en zo verder tot de onderste, die in tweeën is gedeeld. Binnen de buitenste <div> staan bovenaan twaalf <div>'s, daaronder elf <div>'s, en zo verder tot de onderste, waarin twee <div>'s staan. Deze binnenste <div>'s zijn naar links gefloat, zodat ze naast elkaar staan. Ze hebben een border, maar omdat box-sizing: border-box; is gebruikt, valt die binnen de breedte van de binnenste <div>'s.

De reden dat hier maar liefst elf keer ongeveer dezelfde constructie staat: afrondingsproblemen zichtbaar maken. In de meeste browsers wordt een aantal van bovenstaande voorbeelden niet helemaal correct weergegeven. Bij een correcte weergave zijn alle borders 2 px breed, passen alle binnenste <div>'s naast elkaar op dezelfde regel, en zijn er rechts geen kieren tussen de border van de laatste binnenste <div> en de buitenste <div>.

Een <div> van 300 px breed in vieren delen, dat lukt elke browser. 300 : 4 = 75, dat is een mooi rond getal. (Wat trouwens al een hele prestatie is, want computers rekenen niet met het tientallig stelsel.) Maar zo'n <div> in zevenen of elven delen, dat is een ander verhaal. Overigens is het hieronder staande verhaal ook geldig voor procenten, want ook daarbij moet vaak worden afgerond.

De relevante css voor de bovenste binnenste <div>, die in twaalven wordt gedeeld: width: 8.33333%; width: calc(100% / 12);. De eerste width is weer voor browsers die calc() niet kennen. 8,33333% is (ongeveer) een twaalfde. Dezelfde berekening met calc() is veel simpeler: een breedte in procenten is ten opzichte van de ouder, hier de buitenste <div>. calc(100% / 12); wordt hier dus: calc(300px / 12);. Dat het simpeler is met calc() dan met procenten, wordt vooral duidelijk als door zeven wordt gedeeld: width: 14.28571%; width: calc(100% / 7);. En dan is de waarde bij de procenten nog niet eens precies, maar slechts een benadering.

Een ander voordeel van calc(): als je zes kolommen naast elkaar hebt die even breed zijn, en je wilt één kolom twee keer zo breed maken, dan wordt dat met calc() gewoon: width: calc((100% / 6) * 2);. Of als je, bij zeven kolommen, twee kolommen anderhalf keer zo breed wilt maken: calc((100% / 7) * 1.5);. De browser doet het rekenwerk, terwijl jij lekker in de zon kunt gaan zitten. Of in de regen, als je dat lekkerder vindt.

Maar het blijft natuurlijk wel css, dus het is gelukkig niet allemaal paradijs en halleluja. Dat zou al snel te saai worden. Het probleem zit hem in het afronden van getallen. Als je iets in drieën deelt, is elk deel 33⅓%. Als de buitenste <div> 300 px breed is, is dat 100 px. Maar als de <div> in zevenen wordt gedeeld, is elk deel 42,8571428571 px breed. En eigenlijk staan er nog veel meer decimalen achter de komma. Dat kun je dus nooit precies weergeven, want er bestaan (nog) geen pixels die dat stukje van 0,8571428571 px kunnen weergeven. De browser moet dus afronden. En daarbij gaat het enigszins mis. Het maakt hierbij niet uit of je met procenten of een deling in calc() werkt, want in beide gevallen moet worden afgerond.

In de specificatie staat niet, hoe een browser moet afronden. En uiteraard hebben de dames en heren browsermakers dus allemaal een andere methode gekozen.

In het verleden rondden sommige browsers gewoon alles naar beneden af: 42,8571428571 px werd gewoon 42 px. Waardoor je rechts een kier overhield van 6 px, want 7 x 42 = 294 px, en de buitenste <div> is 300 px breed.

Andere browsers rondden altijd naar boven af: 42,8571428571 px werd altijd 43 px. Dat was veel erger, want daardoor past de laatste <div> niet meer op de regel. 7 x 43 = 301 px, en dat past gewoon niet in een <div> van 300 px breed.

Weer andere browsers rondden naar onder af als achter de komma 0,4 of lager stond, en naar boven als er 0,5 of hoger stond. Het voordeel daarvan was dat je met je vrienden of collega's kon gokken: werden de binnenste <div>'s te smal of te breed? Of kregen ze toevallig de juiste breedte?

Niet geheel onverwacht levert het opdelen in zeven en elf stukken in de meeste browsers problemen op, maar ook het in negen delen gaat niet echt lekker in veel browsers. Hier een lijstje neerzetten heeft weinig nut, want bijvoorbeeld Firefox heeft problemen met zeven en elf delen als de buitenste <div> 300 px breed is, maar als de buitenste <div> 302 px breed is, gaat het bij alle elf de hierboven staande verdelingen goed. Ditzelfde geldt voor andere browsers, maar dan met andere verdelingen of breedtes. Bovendien lijkt de resolutie van het scherm ook een rol te spelen.

De hieronder staande afbeelding is gejat van het hoofdstukje over procenten, maar het principe is bij calc() precies hetzelfde:

Afbeelding 6: afrondingsfouten bij percentages
Afbeelding 6: links als te hoog wordt afgerond, rechts als te laag wordt afgerond. Links passen de <div>'s niet naast elkaar, rechts ontstaat een kier.

Inmiddels is de situatie gelukkig sterk verbeterd. Van de geteste browsers ronden alleen UC browser op Android, Android browser en Opera Mini op oudere versies van Android nog hersenloos naar beneden af. In de meeste van de hierboven staande voorbeelden is dan ook rechts een grotere of kleinere kier aanwezig in deze browsers.

Op twee uitzonderingen na, worden de binnenste <div>'s in geen enkele browser nog te breed. De eerste uitzondering is de verdeling in zeven stukken in Internet Explorer en Edge, die op zo'n manier afronden dat de zevende <div> op de volgende regel komt te staan. De tweede uitzondering is Edge op Windows 10 Mobile die niet alleen bij opdelen in zeven, maar ook bij opdelen in zes de laatste <div> op de volgende regel zet. In alle andere browsers is alleen een kleine kier aanwezig bij de verdeling in zeven, negen of elf delen, en nog niet eens op elk systeem.

Deze verbetering heeft te maken met betere afrondingsmethoden bij browsers, vooral bij de iets gebruikelijker getallen. Iets wordt eerder in drie, vier, vijf of acht verdeeld, dan in zeven of elf. Voor de liefhebber staan onderaan de pagina links naar pagina's die dieper op dit afronden door browsers ingaan.

Verder spelen de sterk verbeterde schermen een rol. Op nieuwere schermen kunnen subpixels worden gebruikt: de rode, groene en blauwe delen waaruit pixels zijn opgebouwd. Door subpixels van naast elkaar liggende pixels op 'n slimme manier te combineren, kun je de pixels als het ware iets verschuiven. Als dit op een goede manier gebeurt, kan het menselijk oog niet zien dat er eigenlijk wordt gesjoemeld met de weergave.

Tenslotte is de weergave op hogeresolutieschermen beter. Als je 300 css-pixels door zeven moet delen, en je hebt 1200 schermpixels tot je beschikking, dan is de kans dat het goed gaat natuurlijk veel groter dan wanneer je maar 300 schermpixels hebt. (Het verschil tussen css-pixels en schermpixels is te vinden bij css-pixels en schermpixels.)

Hoewel dit probleem dus veel minder is geworden, blijft goed testen noodzakelijk. Zeker als je met behulp van percentages of calc() iets door 'n onmogelijk getal als zeven wilt delen, of als het te delen element een 'vreemde' breedte heeft van bijvoorbeeld 723 px. In ieder geval worden nog met enige regelmaat bugs op dit gebied gemeld. Het is echter niet altijd echt duidelijk, of het dan om 'n bug gaat of om 'n fout in de code. Bovendien zijn dit soort afrondingsproblemen gewoon onmogelijk altijd volledig op te lossen door de browser.

Ook het probleem met de te brede <div>'s bij delen in zevenen in Internet Explorer en Edge is simpel op te lossen: als je de breedte bij de laatste binnenste <div> iets smaller maakt met {width: calc(100% / 7 - 0.1px)} gaat het goed. Die 0,1 px correctie is al voldoende om het op te lossen.

Dringend aan vakantie toe

font-size: 1.2em; font-size: calc((((1vh + 1vw - 1rem + 3em - 10px) / 2) + 5%) * 0.9);

Maar het kán. Als je zeker wilt weten dat niemand ooit nog begrijpt, wat je hebt gedaan, is dit één van de mogelijkheden.

Vooraan staat weer de font-size voor browsers die calc() niet kennen. Daarachter staat het overspannen deel.

1vh + 1vw - 1rem + 3em - 10px: optellen en aftrekken worden gewoon van links naar rechts afgewerkt. De lettergrootte wordt 1% van de hoogte van het browservenster (1vh) plus 1% van de breedte van het browservenster (1vw) min de lettergrootte van de browser (1rem, als dat niet is aangepast is dat 16 px) plus drie keer de lettergrootte van de ouder van het element (3em) min 10 px.

(1vh + 1vw - 1rem + 3em - 10px) / 2: hierachter staat een deling. Die heeft voorrang boven optellen en aftrekken, daarom worden haakjes om de optellingen en aftrekkingen aan het begin gezet. Zonder haakjes zou 10 px door twee worden gedeeld, voordat er wordt opgeteld en afgetrokken. Nu worden eerste de optellingen en aftrekkingen tussen de haakjes uitgevoerd, en de uitkomst daarvan wordt door twee gedeeld.

((1vh + 1vw + 1rem + 3em - 10px) / 2) + 5%: de nieuwe haakjes zijn niet nodig, want de optellingen en de aftrekkingen tussen haakjes en de deling hebben voorrang boven de optelling met 5%. Maar voor de leesbaarheid is het beter ze hier wel te gebruiken (voor zover je van 'beter' kunt spreken in dit dolhuis). Bij de eerder gevonden uitkomst wordt 5% van de huidige lettergrootte opgeteld.

calc((((1vh + 1vw - 1rem + 3em - 10px) / 2) + 5%) * 0.9);: er zijn weer nieuwe haakjes verschenen, want vermenigvuldigen gaat voor optellen. Zonder de haakjes voor 1vh en achter 5% zou 5% eerst met 0,9 worden vermenigvuldigd, want vermenigvuldigen gaat voor optellen en aftrekken. Nu wordt de uitkomst van de regel hierboven met 0,9 vermenigvuldigd. Het geheel staat nog 'ns tussen haakjes, maar dat zijn de haakjes die bij calc() zelf horen.

Als het enigszins kan, moet je zulke absurd ingewikkelde berekeningen vermijden. Als dat niet kan, moet je in ieder geval goed becommentariëren, waarom je wat doet. Hoe ingewikkelder de berekening, hoe groter ook de kans dat één of meer browsers het laten afweten. Dolphin op Android 5 bijvoorbeeld ondersteunt calc(), maar bovenstaande formule is kennelijk te ingewikkeld en wordt genegeerd. (Op Android 6 werkt het wel in Dolphin.)

Browser support

calc() werkt in alle geteste browsers, behalve in onderstaande:

UC browser op Android. Opera Mini op Android ondersteunt het pas vanaf Android versie 5. Android browser ondersteunt alleen optellen en aftrekken, geen delen en vermenigvuldigen.

Een goed werkende polyfill (JavaScript om iets werkend te krijgen in oudere browsers) lijkt niet te bestaan. Je zult dit dus per gebruik moeten oplossen door eerst iets bij een eigenschap op te geven zonder calc(), voor de browsers die het niet ondersteunen, en daarna iets met calc() voor de browsers die het wel ondersteunen. Dit is ook de manier die in de voorbeelden is gebruikt.

Bugs en problemen

Onderstaand overzicht is niet volledig. Het is soms nogal lastig om zeker te weten dat een bug alleen met calc() te maken heeft, en niet met bijvoorbeeld transform(). Onderaan de pagina staan links naar pagina's, waar je kunt zoeken naar gemelde bugs. Verder zijn in het lijstje hieronder exotische bugs in weinig gebruikte eigenschappen weggelaten. En het kan natuurlijk prima dat een bug inmiddels is gerepareerd.

De browsers die hieronder worden genoemd zijn Internet Explorer, Edge, Firefox, Safari en Google Chrome. Omdat (vrijwel) alle browsers een van de weergavemachines van deze browsers gebruiken, komen deze bugs waarschijnlijk in meer van de geteste browsers voor.

Als hieronder wordt geschreven 'werkt niet', wil dat zeggen dat calc() wordt genegeerd. Onbekende of foutieve eigenschappen horen genegeerd te worden. Soms zal dat tot gevolg hebben dat een eigenschap helemaal niet meer werkt, soms zal alleen een deel niet werken.

In alle vijf hierboven genoemde browsers werkt calc() meestal niet, of niet goed, als het wordt gebruikt bij <table> of een van de onderdelen van een tabel. Het is onduidelijk, of dit wel zou moeten werken, omdat tabellen een heel apart geval zijn.

In media query's wordt calc() niet ondersteund door Safari, Internet Explorer, Google Chrome en Firefox. In de toekomst zou dit opgelost moeten worden.

In Firefox werkt calc() alleen met lengte-eenheden (en percentages). Tijdseenheden en dergelijke werken niet, en dat zal nog wel even zo blijven.

In Firefox werkt clip:rect() niet, als calc() wordt gebruikt in rect().

In Internet Explorer en Firefox werkt calc() niet in rgb(), rgba(), hsl() en hsla().

In Firefox werken transform: scale() en transform: rotate() niet, als daarbinnen calc() wordt gebruikt.

In Internet Explorer werkt transform niet bij gebruik van calc().

Bij gebruik van calc() werken in Internet Explorer en Edge animate, gradiënts, text-indent en flexbox niet.


Printen

Hoewel dit artikel voornamelijk op weergave op schermen is gericht, toch nog enige aandacht voor printen.

Als je 'n pagina van je eigen site wilt printen, is het veel simpeler dat eerst naar PDF om te zetten en vervolgens die pdf te printen. Je kunt dan voor het printen de pdf bekijken en eventueel nog aanpassen, voordat je bergen papier verspilt met het rechtstreeks printen vanuit de browser. Als je 'n pagina van 'n andere site wilt printen, kun je alleen maar hopen dat die pagina printvriendelijk is.

Voor het maken van dingen als folders of tijdschriften zijn html en css niet echt geschikt. Je kunt dat beter doen met daarvoor bedoelde programma's, zoals Scribus of Inkscape.

In principe kun je met alle soorten eenheden printen. Maar dat is alleen aan te raden, als je van een wild en avontuurlijk leven houdt, of als je 'n hekel aan jezelf (of je bezoekers) hebt.

De precieze grootte van font-relatieve eenheden varieert, omdat deze allemaal op een of andere manier op de pixel zijn gebaseerd. En de grootte van de pixel kan variëren, dus ook de grootte van daarop gebaseerde eenheden. Het is dus heel erg lastig om, bij gebruik van eenheden als em, een afdruk met de juiste maten en verhoudingen te krijgen.

Viewport percentages zijn een percentage van de breedte en hoogte van venster van de browser. Een printer is geen browser, en daarom zijn deze eenheden ongeschikt voor printen.

Voor maten bij printen kun je het best de pt, de point, gebruiken. Dit is een eenheid die al eeuwenlang in drukkerijen wordt gebruikt. Niet alleen vanwege de traditie, maar ook omdat deze eenheid voor afdrukken veel bruikbaarder is dan bijvoorbeeld de millimeter, zal deze eenheid ook niet snel worden vervangen.

Betekent dit dat je met pt een heel precieze afdruk kunt maken? Nee, helaas niet. Er gaan 72 pt's in 1 inch. In theorie staat de maat van een pt daarmee vast, want 'n inch heeft (op papier, niet op het scherm) een vaste, bekende lengte. In de praktijk blijken er echter bij printen (forse) afwijkingen van de juiste maat te bestaan. Dingen kunnen (iets) groter of (iets) kleiner zijn, dan ze zouden moeten zijn. De verhoudingen kloppen wel: 72 pt is altijd twee keer zo groot als 36 pt. Het schijnt dat (hele) dure printers wel de juiste maten kunnen printen, maar daar heb ik geen ervaring mee. Overigens maakt dat weinig uit, want je kunt er niet van uitgaan dat 'n bezoeker een (peper)dure printer heeft.

Als de lettergrootte van de browser niet is veranderd, is dat standaard 16 px. Dat is even groot als 1 em en vergelijkbaar met 12 pt bij printen. Lettergrootte, grootte van koppen, enzovoort kun je aan de hand hiervan omrekenen van px naar pt. Voor het omrekenen kun je gebruik maken van de omrekentabellen iets hieronder. De omgerekende waarden en eenheden worden in een stylesheet voor printen gezet met behulp van de onderstaande code:

<link rel="stylesheet" href="pad-naar-map/print.css" media="print">

Soms worden bij het printen aan de kanten marges opgegeven. Dat gebeurt soms in in (inch) of cm (centimeter). Ook hier geldt, dat dit vaak iets meer of minder is dan 'n 'echte' inch of centimeter. Als je geen papier wilt verspillen, kun je beter geen marges opgeven. Laat degene die afdrukt, of de printer, dat maar bepalen. Om dezelfde reden kun je beter geen breedte aan de pagina geven: dat leidt al snel tot overbodig papiergebruik.

(Als de pagina een breedte heeft, kun je die in de stylesheet voor het printen weghalen. Met behulp van die stylesheet kun je ook dingen als menu's verbergen. Achter links zou je de volledige link kunnen printen, enzovoort. Maar daar gaat dit artikel verder niet over. Als je daarin geïnteresseerd bent, kun je de stylesheet voor het printen van deze pagina bekijken op print.css.)

Echt heel precies printen is heel lastig. Op deze site zijn alleen de artikelen printbaar gemaakt, omdat dat redelijk simpele pagina's zijn. Maar zelfs dan is het niet perfect. Op deze pagina bijvoorbeeld staan voorbeelden met relatieve maten. Die zien er bij printen anders uit dan op het scherm. De uitleg bij de voorbeelden op deze site kan niet worden geprint (nou ja, alles kan, maar dat wordt 'n puinhoop).

In plaats daarvan kan een pdf met de uitleg worden gedownload. Die is wel nauwkeurig te maken met behulp van bijvoorbeeld LibreOffice. Bovendien kun je in zo'n pdf werkende links en dergelijke gebruiken. Hopelijk gaan mensen het dan niet printen, wat weer 'n boom uitspaart.

Los van alles wat al hierboven is genoemd, heb je hoe dan ook weinig controle over hoe 'n bezoeker print. Sommige mensen printen alleen in zwart-wit. De meeste mensen printen geen achtergronden. Bij het printen kan verkleind worden. Of er worden vier pagina's op één vel afgedrukt. Enzovoort, enzovoort. Kortom: als je 'n stylesheet voor printen gebruikt, is een pagina prima af te drukken, maar reken er niet op dat je zelfs maar in de buurt komt van een 'echte' brochure of tijdschrift. Alles zal erop staan, de verhoudingen zullen (vrijwel) goed zijn, maar verder kun je alleen maar duimen.

Omrekentabellen

Bij de hieronder staande omrekeningen is de em een relatieve eenheid. Dat wil zeggen dat 1 em alleen 16 px is, als de gebruiker de lettergrootte van de browser niet heeft veranderd. Als de lettergrootte is verdubbeld naar 32 px, is 1 em geen 16 px meer, maar 2 x 16 = 32 px. Als de lettergrootte is verkleind tot 12 px (driekwart), is 1 em geen 16 px meer, maar 0,75 x 16 = 12 px. Als de pagina goed in elkaar zit, zou die andere lettergrootte geen (al te grote) problemen moeten opleveren.

Om het enigszins leesbaar te houden, zijn de maten afgerond op honderdsten.

1 px = 1/16 em = 0,75 pt = 1/96 in = 0,03 cm

1 em = 16 px = 12 pt = 0,17 in = 0,42 cm

1 pt = 1,33 px = 0.08 em = 1/72 in = 0,04 cm

1 in = 96 px = 6,22 em = 72 pt = 2,54 cm

1 cm = 37,8 px = 2,37 em = 28,35 pt = 0,39 in

Uitgaande van em
empxptincm
0,5860,080,21
0,558,86,60,090,23
0,69,67,20,10,25
0,6510,47,80,110,27
0,711,28,40,120,3
0,751290,130,32
0,812,89,60,130,34
0,8513,610,20,140,36
0,914,410,80,150,38
0,9515,211,40,160,4
116120,170,42
1,0516,812,60,170,44
1,117,613,20,180,46
1,1518,413,80,190,49
1,219,214,40,20,51
1,2520150,210,53
1,320,815,60,220,55
1,3521,616,20,220,57
1,422,416,80,230,59
1,4523,217,40,240,61
1,524180,250,63
1,5524,818,60,260,65
1,625,619,20,270,68
1,6526,419,80,270,7
1,727,220,40,280,72
1,7528210,290,74
1,828,821,60,30,76
1,8529,622,20,310,78
1,930,422,80,320,8
1,9531,223,40,320,82
232240,330,84
2,133,625,20,350,89
2,235,226,40,370,93
2,336,827,60,380,97
2,438,428,80,41
2,540300,421,05
2,641,631,20,431,1
2,743,232,40,451,14
2,844,833,60,481,18
2,946,434,80,481,22
348360,51,27
3,149,637,20,521,31
3,251,238,40,531,35
3,352,839,60,551,39
3,454,440,80,571,43
3,556420,581,48
3,657,643,20,61,52
3,759,244,40,611,56
3,860,845,60,631,6
3,962,446,80,651,65
464480,661,69
empxptincm

Over deze pagina

Als je de css van deze pagina bekijkt, zie je dat bij sommige voorbeelden de 'echte' css afwijkt van de css in de tekst. Dat zijn correcties voor op de site opgegeven waarden voor lettergrootte en dergelijke. De tekst klopt gewoon, alleen zijn sommige waarden (iets) anders.

Op deze pagina wordt een beetje JavaScript gebruikt. Dat is zuiver ter ondersteuning, ook zonder JavaScript werkt alles gewoon nog. Verspreid over de pagina wordt aangegeven, of een bepaalde eenheid wel of niet werkt in de gebruikte browser. Dat gebeurt met de kleuren groen (ondersteund) en lichtrood (niet-ondersteund). Met behulp van JavaScript wordt hier nog de tekst 'ondersteund door deze browser' of 'NIET' ondersteund door deze browser' aan toegevoegd. Dit is bedoeld voor kleurenblinden, omdat sommige kleurenblinden deze kleuren niet (goed) kunnen onderscheiden. Ook schermlezers hebben niets aan alleen de kleuren, maar de tekst lezen ze gewoon voor.

Engelse vertaling van sleutelbegrippen

NederlandsEngels
beginwaardeinitial value
berekende waardecomputed value
eenheidunit
erfinherit
erfelijkheidinheritance
geërfdinherited
initiële waardeinitial value
lettergroottefont-size
schermpixeldevice pixel
standaarddefault
standaard eenheidcanonical unit
standaard stijlbestand browserdefault style, browser style
stijlbestandstylesheet
stijlbestand van gebruikeruser stylesheet
waardevalue

Wijzigingen

Alleen grotere wijzigingen worden hier vermeld, geen dingen als een link die is geüpdatet.

:

Eerste versie.

6 juli 2017:

Bij Getest in waren de resoluties en dergelijke van de schermen vergeten. Toegevoegd.

Iets gevonden waar je wat aan hebt? Mooi. Als je je waardering wilt uiten, maak dan een donatie over aan War Child Nederland, een organisatie die kinderen uit oorlogsgebieden helpt hun trauma's te verwerken. Of - nog beter - wordt donateur:
Naar site van War Child Nederland