CSS doesn't have to be difficult, so long as you take the time to really understand it's key, foundational concepts — concepts like the CSS box model, the different types of CSS rules you can use, how cascading works, and a few other components. With the basics under your belt, CSS becomes a lot easier to use. And it can even be a whole lot of fun, too!
— Geoff ‘Ten Ton’

CSS intro

Functie van CSS

CSS is een declaratieve programmeertaal die beschrijft hoe een HTML-document moet getoond worden in de browser. Waar komt een bepaald element op een pagina te staan? In welk lettertype? Hoe groot? Welke voorgrond- en achtergrondkleur? In een raster?

Veel studenten sukkelen met CSS omdat ze nooit de moeite nemen om de taal voldoende te snappen. Na veel prutsen met eigenschappen, waarden, gekopieerde code van alle mogelijke bronnen komt er dan een resultaat dat min of meer voldoet aan wat er gewenst was, maar dat bij de minste verandering als een kaartenhuisje in elkaar stuikt.

Dit stuk probeert enkele basisbegrippen duidelijk voor te stellen. Neem er je tijd voor, experimenteer, lees artikels en bekijk filmpjes (liefst de goede en niet verouderde artikels).

CSS is leuk!
CSS is leuk!

Why is CSS so weird?

Dit filmpje op Mozilla Developer (auteur: Miriam Suzanne) legt heel goed uit waarom CSS zo anders is dan andere programmeertalen. Wie weet komt dit filmpje iets te vroeg in deze cursustekst. Maar bekijk het nu al eens en kom er later (als je wat meer CSS toegepast hebt in je site) op terug.

CSS-regel

Een stijlblad bestaat uit één of meer stijlinstructies (‘CSS regels’) die beschrijven hoe een element moet getoond worden. Een CSS regel selecteert een element en declareert hoe het er moet uitzien. Een voorbeeld van een CSS regel:

body {
    font-family: georgia, serif;
    color: #000;
}

Spaties, nieuwe regels, inspringen zijn onbelangrijk voor de browser, maar zorgen wel dat je CSS bestand leesbaar is. De meeste developers schrijven hun CSS zoals hierboven getoond, met spaties, accolades en inspringen waar het in dit voorbeeld staat. Enkele begrippen:

Selector
Deze regel selecteert het body element;
Declaratieblok
alles tussen beide accolades { … };
Eigenschap
font-family en color zijn eigenschappen;
Waarde
georgia, serif en #000 zijn de waarden voor beide eigenschappen.

Gewoonlijk schrijft men één declaratie per regel. Tussen twee declaraties moet altijd een ‘;’ staan. Strikt genomen moet er dus na #000 geen ‘;’ geschreven worden, maar de meeste developers doen dat wel.

Er bestaat software (bvb als extensie in VS Code) om een CSS bestand zo klein mogelijk te maken, de zogenaamde ‘minifiers’. De geminifieerde versie van bovenstaande CSS zou er zo kunnen uitzien:

body{font-family:georgia,serif;color:#000}

CSS koppelen aan HTML

Er zijn drie manieren (vier als je injectie via JS meerekent) om CSS te koppelen aan HTML. Voor je project moet je de eerste manier gebruiken, dus via één extern stijlbestand.

Extern stijlbestand

De voorkeursmethode is om alle stijlinformatie in één extern bestand te steken. Dit bestand wordt gedeeld door alle HTML-pagina's. Eenmaal het geladen is (bij het laden van de eerste HTML-pagina) staat het op de harde schijf van je bezoeker en hoeft het dus voor de volgende HTML-pagina's niet meer gedownload te worden. Dit belangrijk mechanisme heet ‘caching’. Het versnelt de browse-ervaring aanzienlijk.

Je linkt een CSS-bestand aan een HTML-pagina d.m.v. het link element, dat in de head van het document moet staan. De volgende code linkt het bestand ‘stijlblad.css’ (dat in dezelfde map staat als het huidig document) aan het huidige HTML-document.

<html>
<head>
  …
  <link rel="stylesheet" href="stijlblad.css">
  …
</head>
<body>
  …

Deze manier van werken heeft het groot voordeel dat de HTML en de CSS gescheiden worden. Als je hele site gebruik maakt van één CSS-bestand, dan zal ze automatisch meer consistent zijn: alle titels zullen er bvb op alle pagina's hetzelfde uitzien. En als je toch een lay-outaanpassing wilt doen aan die titels, dan hoef je maar op één plaats te kijken en iets te wijzigen.

Embedded stijlblad

Soms kan het nuttig zijn om alle stijlinformatie in het HTML-document zelf te zetten, bvb. als je maar één bestand wilt doorsturen naar iemand met een beperkte opmaak. Je kan deze CSS dan in het style element in de head zetten:

<html>
<head>
  …
  <style>
    body {
        font-family: georgia, serif;
        color: #000;
    }
  </style>
  …
</head>
<body>
  …

Inline stijlinformatie

Heel uitzonderlijk kan je bij één heel specifiek element stijlinformatie toevoegen via het style attribuut van dat element zelf:

<h1 style="color: red">Mijn titel</h1>

Dit is zelden een goed idee, omdat je op deze manier de inhoud en structuur (HTML) mengt met de vormgeving (CSS). We houden beide liefst zo gescheiden mogelijk. Gebruik deze inline CSS dus in heel specifieke gevallen.

Over ouders en kinderen

Bij HTML spreken we over ouders en kinderen, over (stam)bomen, familierelaties … Veel (niet alle!) eigenschappen worden overgeërfd van ouder op kind enz. Bekijken we eens de documentatie op Mozilla Developer Network i.v.m. de eigenschap font-family. Als je op deze pagina een beetje naar beneden scrollt, zie je:

font-family wordt overgeërfd van ouder op kind

De eigenschap font-family wordt dus wel degelijk overgeërfd van ouder op kind, kleinkind enz. Dat is een goede reden om je meest gebruikte lettertype in body te declareren, vermits de volledige inhoud van je pagina in body zit.

Onderzoek (bvb via de MDN developer pagina's waar we hierboven al aan refereerden) of de eigenschappen background-color en color wordt overgeërfd.

De eigenschap color wordt overgeërfd van ouder op kind, background-color echter wordt niet overgeërfd. De illusie dat kinderen toch dezelfde achtergrondkleur hebben als hun ouders is eenvoudig te verklaren doordat de standaardwaarde (‘default’) voor de achtergrondkleur de waarde transparent (doorschijnend) is.

Enkele soorten selectoren

We komen verder in een apart stuk nog terug op selectoren. Hieronder krijg je een drietal types van selectoren omdat we die nodig hebben om het begrip cascade en specificiteit uit te leggen.

Nakomelingselector (spatie)

De nakomelingselector (‘descendant selector’) gebruik je door twee of meer HTML elementen te combineren met een spatie ertussen. Een voorbeeld: volgende CSS-regel zegt dat alle p elementen die in main zitten links moeten uitgelijnd worden. Paragrafen die bvb. enkel in een footer te vinden zijn, gehoorzamen niet aan deze regel.

main p {
    text-align: left;
}

Class-selector (.)

In een HTML document kan je het attribuut class gebruiken. Je mag dezelfde klassenaam meerdere keren in hetzelfde document gebruiken. Stel dat je in je pagina een alinea hebt die als betekenis heeft dat het over een belangrijke update gaat, dan zou je in het HTML-bestand die p als attribuut class="update" kunnen geven. Zo zou ik de aandacht kunnen trekken op deze nieuwe alinea (update: door Corona is het restaurant gesloten) door er een rood randje rond te trekken.

p.update {
    border: 1px solid red;
}

Belangrijke opmerking: geef klassenamen op basis van betekenis en niet op basis van vormgeving. Doe dus dit niet:

<p class="rode-rand">Ons restaurant is gesloten omwille van Corona.</p>

Dit is semantisch (qua betekenis) veel beter:

<p class="update">Ons restaurant is gesloten omwille van Corona.</p>

I.p.v. p.update had je ook als selector .update kunnen gebruiken. Het verschil is dat deze laatste versie alle HTML elementen selecteert met class = "update". De eerste versie selecteert alleen alinea's met die klasse.

ID-selector (#)

De ID-selector doet hetzelfde als de klasseselector, met dit verschil: je mag een bepaald ID maar één keer gebruiken in hetzelfde HTML-document. We kunnen het voorbeeld van hierboven herhalen. Op voorwaarde dat er maar één HTML-element is met dit ID, zouden we in HTML kunnen schrijven:

<p id="update">Ons restaurant is gesloten omwille van Corona.</p>

De CSS om rond deze unieke alinea een rood randje te zetten wordt dan:

p#update {
    border: 1px solid red;
}

Zelfde opmerking als bij de klasseselector: we hadden in CSS ook #update kunnen schrijven i.p.v. p#update. Aangezien dit ID uniek moet zijn, maakt dit hier geen enkel verschil, behalve op het gebied van specificiteit, maar dat is voor volgend stukje.

De cascade en specificiteit

De ‘C’ van CSS staat voor ‘cascade’ (waterval). Het is een niet zo eenvoudig systeem dat regelt wat er moet gebeuren als er tegenstrijdige regels zijn. Die tegenstrijdigheid kan komen doordat CSS kan komen van de browser, de gebruiker zelf of van de developer of designer die de pagina maakte. En zelfs binnen de CSS die de developer schreef, kunnen er verschillende CSS-regels van toepassing zijn op een HTML-element. Welke regel haalt het dan? Daarover gaat de cascade en specificiteit.

Onderstaand filmpje geeft een goed overzicht, zonder te fel in detail te gaan. Het is belangrijk dat je dit principe goed probeert te snappen. Je kan uit de cascade veel voordeel halen (het is een essentieel onderdeel van de taal!) en je zal sneller fouten of onverwachte resultaten kunnen opspeuren in je eigen code.

Om specificiteit uit te leggen gebruikte Welsh designer Andy Clarke een Star Wars metafoor. HTML elementen in de selector zijn stormtroopers. Twee stormtroopers winnen van één. Een klasse is een Darth Maul. Darth Maul wint van gelijk hoeveel stormtroopers. Een ID is Darth Vader. Die is natuurlijk nog veel sterker. Een style attribuut (‘The Emperor’) wint van al het vorige. Een Death Star (!important achter een CSS eigenschap met waarde zetten) is het allersterkste. Deze !important toevoeging heb je zelden nodig.

Specificiteit met Star Wars figuurtjes

Gegeven onderstaande code. Voor de eenvoud staat de CSS in de head van het document. Welke kleur krijgen beide paragrafen?

<!DOCTYPE html>
<html lang="nl">
<head>
  <title>specificiteit</title>
  <style>
    body {
      color: black;
    }

    #container {
      color: green;
    }

    main p {
      color: red;
    }

    p.update {
      color: rebeccapurple;
    }

    p {
      color: blue;
    }
  </style>
</head>
<body>
    <div id="container">
      <main>
        <p>Dit is de eerste alinea.</p>
        <p class="update">Dit is de tweede alinea.</p>
      </main>
    </div>
</body>
</html>

De eerste p wordt rood, de tweede krijgt de kleur ‘rebeccapurple’. De meest gemaakte fout is dat mensen redeneren dat #container toch veel specifieker is dan al de rest en dat alles dus groen zal worden.

Dat klopt echter niet. Overerving wordt pas bekeken als er geen enkele regel van toepassing is op het element in kwestie. Concreet: als er geen enkele regel van toepassing zou zijn op de p elementen, dan zouden we overerving bekijken omdat de color eigenschap overgeërfd wordt. Dat hoeft echter niet in dit voorbeeld omdat er drie CSS-regels i.v.m. kleur van toepassing zijn op de alinea's:

  • Voor de eerste alinea heeft de selector main p een grotere specificiteit dan de selector p (ook al staat die laatste lager in de CSS), dus de kleur rood wordt toegepast.
  • De tweede alinea heeft een klasseselector die van toepassing is, nl. p.update, dus die is specifieker dan de andere twee selectoren die op deze alinea van toepassing zijn. De kleur wordt dus ‘rebeccapurple’.

Oefening op specificiteit

Specificiteit van selectoren is zo'n topic waar veel developers mee worstelen. Eén mogelijke oplossing is het probleem helemaal negeren door elke component in je pagina een unieke span mee te geven of door alles met inline styles te doen. Maar dan schakel je dit stukje van de cascade uit dat net ook in je voordeel kan werken.

Daarom deze oefening: CSS specificity, gemaakt door Simon Arnold. Het kost niet meer dan tien minuten om de twintig puzzeltjes op te lossen. Je zal gegarandeerd iets bijleren als je ze maakt.

Developer tools

Elke browser heeft ‘developer tools’ die je kan activeren door op een pagina rechts te klikken en ‘inspecteren’ te kiezen. Het maakt dus niet uit in welke browser je dat doen. We legden al uit in een vorig stuk dat we voor dit OPO altijd de developer tools van Firefox developer edition zullen gebruiken.

Een pagina bekijken in de developer tools kan je veel inzicht verschaffen over hoe de CSS in combinatie met de HTML (en de JS) werkt. Waarom geeft een CSS-regel die je schreef niet het gewenste resultaat? Bekijk het in de developer tools. Waarom heeft een bepaald element niet de gewenste afmetingen of staat het op een onverwachte plaats? De developer tools zijn je allerbeste vriend!

Onderstaande youtubefilm toont de developer tools van Firefox in actie om te bekijken hoe de cascade werkt.

Bovenaan dit document staat een CSS-animatie ‘CSS is leuk!’. Gebruik de developer tools om de animatieduur af te lezen.

Klik rechts op de animatie. In de HTML-code links merk je dat dit een div class="content" is. Deze div bevat twee h5 elementen. Klik op het tweede h5 element. In de CSS-kolom in de dev.tools lees je nu af dat de animatie oneindig doorloopt met een duurtijd van 3s.