Waarom jouw CSS “ineens niet werkt” in een trainingsopdracht

Je maakt in een training een eenvoudige website voor een fictieve opdrachtgever: een pagina met een navigatiebalk, een “call-to-action” knop en een contentblok met kaartjes. Je schrijft netjes CSS, maar je ziet iets raars: de knop blijft blauw terwijl je hem groen hebt gemaakt, of de linkjes in de navigatie blijven onderstreept terwijl je dat juist uit hebt gezet. Je past je code aan, refresht, en toch lijkt een regel genegeerd te worden.

Dit moment is precies waar selectors en specificity (specificiteit) over gaan. CSS “kiest” niet willekeurig welke stijl wint; er zit een vaste logica achter: welke selector is van toepassing en welke selector is sterker. Als je dat begrijpt, ga je veel sneller debuggen en schrijf je CSS die ook later (bij uitbreidingen of samenwerking) voorspelbaar blijft.

In deze les leer je selectors bewust kiezen en je krijgt grip op specificity, zodat je meteen kunt verklaren waarom een bepaalde stijl wint.


De basis: selector, declaratie en specificity in mensentaal

Een CSS-regel bestaat grofweg uit een selector en één of meer declaraties. De selector bepaalt welke HTML-elementen geraakt worden; de declaraties bepalen hoe ze eruitzien. Bijvoorbeeld: a { color: red; } selecteert alle links en maakt hun tekst rood. Tot zover voelt het logisch—totdat er meerdere regels tegelijk op hetzelfde element passen.

Specificity is het “gewicht” van een selector. Als twee (of meer) CSS-regels dezelfde property op hetzelfde element proberen te zetten—bijvoorbeeld twee keer color—dan wint meestal de regel met de hoogste specificiteit. Daardoor kan iets “niet werken” terwijl het wél correct geschreven is: je regel verliest simpelweg van een sterkere selector elders (bijvoorbeeld in een component-stijl of een framework).

Er zijn drie belangrijke principes die samen bepalen wat je uiteindelijk op het scherm ziet (vereenvoudigd maar bruikbaar voor beginners):

  • Toepasbaarheid: matcht de selector dit element überhaupt?

  • Specificity: als meerdere regels matchen, welke is “sterker”?

  • Volgorde (source order): bij gelijke sterkte wint de regel die later in de CSS staat.

Denk aan een discussie met meerdere “stemmen”: een algemene regel (“alle knoppen zijn blauw”) weegt minder zwaar dan een gerichte regel (“de knop met class .cta is groen”). Als ze even zwaar zijn, “spreekt” degene die het laatst aan het woord is.


Selectors die je het meest gebruikt (en waarom ze soms bijten)

Elementselectors (zoals p, button, h1) zijn breed: ze pakken alles van dat type. Dat is handig voor basis-typografie, maar kan later onbedoeld veel raken. Class selectors (.card, .btn) zijn de werkpaarden voor webpagina’s: herbruikbaar, duidelijk en goed te combineren. ID selectors (#header) zijn uniek bedoeld, maar hebben een hoge specificiteit—waardoor je later snel “tegen jezelf” moet opboksen als je iets wilt overschrijven.

Daarnaast heb je combinators en combinaties: selectors die relaties in de HTML volgen. Een selector als .nav a betekent: “alle a-elementen binnen een element met class nav.” Dat is krachtig omdat je stijlen kunt “scopen” naar een onderdeel (bijv. navigatie vs. content). Maar het kan ook te specifiek worden als je diepte blijft stapelen (.page .header .nav ul li a)—dan wordt overschrijven lastig en voelt CSS stroperig.

Tot slot zijn er pseudo-classes zoals :hover en :focus. Die selecteren geen andere elementen, maar een state. Bijvoorbeeld: .btn:hover is dezelfde knop, maar dan tijdens hover. Dit kan onverwacht lijken (“waarom verandert de kleur wel op hover maar niet normaal?”) als je vergeet dat je eigenlijk twee regels hebt: één voor de normale staat en één voor de hoverstaat.

Hieronder zie je de meest voorkomende selector-typen naast elkaar, inclusief wat ze “kosten” in specificity en wanneer je ze inzet.

Categorie Voorbeeld Wanneer handig Valkuil
Element selector button { ... } Basisstijl voor alle elementen van één type; snel voor typografie en defaults. Te breed: raakt ook knoppen waar je het niet bedoelt.
Class selector .btn { ... } Componenten/utility’s; herbruikbaar en goed te combineren. Naamgeving kan rommelig worden als je geen consistent patroon gebruikt.
ID selector #cta { ... } Zelden nodig; soms voor unieke ankers of specifieke pagina-elementen. Heel hoge specificity; je “vergrendelt” styling en maakt overrides lastiger.
Descendant combinator .nav a { ... } Styling scoped binnen een blok (navigatie, footer, etc.). Kan onbedoeld te veel links raken binnen dat blok.
Pseudo-class .btn:hover { ... } Interactie-states zoals hover/focus/active; essentieel voor UX. Je vergeet de basisregel, waardoor states inconsistent worden.

Specificity echt begrijpen: hoe CSS beslist wie wint

Specificity kun je zien als een score die CSS bijhoudt per selector. In de praktijk werkt het als een rangorde: inline styles zijn extreem sterk, daarna komen selectors met ID’s, daarna classes/attributes/pseudo-classes, en het lichtst zijn elementen/pseudo-elements. Voor beginners is het meestal genoeg om te onthouden dat ID’s zwaarder zijn dan classes, en classes zwaarder dan elementselectors—maar het helpt enorm om te snappen waarom je soms “meer” moet doen dan alleen een extra regel schrijven.

Een veelvoorkomende misvatting is: “Als ik mijn regel onderaan zet, wint hij altijd.” Dat klopt alleen als de specificity gelijk is. Een #cta selector blijft meestal winnen van .btn, ook als .btn later staat. Daardoor gaan beginners vaak onnodig “escaleren”: extra selectors toevoegen, of zelfs !important gebruiken. Dat werkt soms, maar maakt je CSS moeilijk te onderhouden omdat je steeds sterkere regels moet schrijven om eerdere sterke regels te verslaan.

Je kunt specificity praktisch benaderen met een simpele telling (zonder alles wiskundig te maken):

  • Elke ID in de selector maakt hem veel sterker.

  • Elke class, attribute selector ([type="text"]) of pseudo-class (:hover) maakt hem sterker, maar minder dan een ID.

  • Elk element (button, a) telt het minst.

Belangrijk: meer woorden betekent niet automatisch “sterker” op een zinvolle manier. .nav a is niet “beter” dan .link—het is alleen anders: de eerste is afhankelijk van HTML-structuur, de tweede is een bewuste class. In teams en trainingsopdrachten is het vaak beter om component-classes te gebruiken dan diep geneste selectors, omdat je dan minder breekbaar bent als de HTML later verandert.

Een tweede grote bron van verwarring is de rol van inheritance (overerving). Sommige properties (zoals color en font-family) erven standaard van het parent-element. Andere (zoals padding, border) niet. Daardoor kan het lijken alsof een selector “iets doet” zonder dat je het direct op dat element zet. In debugging: als je color op .card zet en de tekst in een p verandert mee, is dat overerving—geen mysterieuze selector die ineens p selecteert.

[[flowchart-placeholder]]


Slimme keuzes: best practices, valkuilen en misverstanden

Een duurzame aanpak in beginnersprojecten is: gebruik classes als primaire styling-haak en houd selectors vlak (niet te diep genest). Classes zijn expliciet: je “labelt” het element met intentie (.btn, .card, .nav-link). Daardoor begrijp je later sneller waarom een regel bestaat en kun je componenten verplaatsen zonder dat styling breekt. Dit sluit goed aan bij hoe veel teams component-gericht werken: styling hoort bij een blokje (component) en is niet te afhankelijk van de precieze plek in de DOM.

Een typische valkuil is te veel vertrouwen op ID’s of op lange ketens van selectors om “precies dat ene element” te pakken. Dat voelt snel als controle, maar het maakt je CSS rigide. Als je later een extra wrapper toevoegt of een element verplaatst, matcht je selector ineens niet meer. Of je krijgt het tegenovergestelde: de selector is zó specifiek dat je hem nergens meer netjes kunt overschrijven zonder nóg specifieker te worden.

!important is een andere bekende valkuil. Het kan handig zijn in uitzonderingssituaties (bijv. een tijdelijke hotfix of heel specifieke utility-regels), maar in leerprojecten is het meestal een teken dat je specificity-probleem niet is opgelost maar overschreeuwd. Als je !important eenmaal inzet, creëer je vaak een kettingreactie: je moet het later weer “overrulen” met nóg een !important, en je stylesheet wordt een gevecht in plaats van een ontwerp.

Misverstanden die vaak terugkomen:

  • “Descendant selectors zijn altijd slecht.” Niet waar: .nav a is prima voor scope, zolang je het niet tot een diep doolhof maakt.

  • “Een class is zwakker dan een elementselector als hij later staat.” Onjuist: een class is doorgaans sterker; volgorde is pas relevant bij gelijke specificity.

  • “Als ik een property op een parent zet, geldt het altijd voor children.” Alleen voor inheritable properties; spacing en borders erven meestal niet.

Het doel is niet om “de sterkste selector” te schrijven, maar de meest passende: zo sterk als nodig, zo eenvoudig mogelijk.


Voorbeeld 1: navigatie-links in een trainingswebsite (waarom onderstreping terugkomt)

Stel: je maakt een trainingsopdracht met een header en navigatie. Je schrijft:css a { text-decoration: none; color: #333; }

Je verwacht dus: geen onderstreping, donkergrijze links. Maar in de navigatie blijven sommige links toch blauw en onderstreept, of krijgen ze een andere kleur wanneer je erover hovert. Dat gebeurt vaak wanneer er óók een meer specifieke regel bestaat, zoals .nav a { ... } of .nav a:hover { ... }, of wanneer een standaard stylesheet van een template/framework specifiekere selectors gebruikt.

Stap voor stap kijk je dan naar de “wedstrijd” op één property, bijvoorbeeld color:

  1. Matchen er meerdere regels op dezelfde a? (bijv. a { ... } én .nav a { ... })
  2. Welke selector is specifieker? Een class + element (.nav a) wint meestal van alleen element (a).
  3. Als ze even specifiek zijn, welke staat later in de CSS?

Wat is in dit voorbeeld de praktische impact? Je algemene a { ... } is een prima basisregel, maar navigatie heeft vaak eigen wensen (hover, active state, contrast). Dan is het beter om bewust te scopen:

  • Basis: a { ... } voor algemene linkstijl.

  • Navigatie: .nav-link { ... } of .nav a { ... } voor het menu.

  • States: .nav-link:hover en .nav-link:focus zodat interactie consistent is.

De beperking van deze aanpak: als je puur .nav a gebruikt en je zet later in de navigatie ook een knop-link (bijv. “Aanmelden”), dan krijgt die mogelijk dezelfde styling als gewone links. Met een aparte class (.nav-link vs .nav-cta) hou je intenties zuiver en voorkom je dat scope “te breed” wordt.


Voorbeeld 2: een CTA-knop die niet van kleur verandert (ID vs class in de praktijk)

Je bouwt een “call-to-action” knop in een trainingspagina. In HTML staat:html <button id="cta" class="btn">Start nu</button>

Je schrijft eerst een herbruikbare stijl:css .btn { background: green; color: white; }

Maar de knop blijft rood, omdat er ergens anders staat:```css

cta {

background: red; } ```

Dit is specificity in actie. Zelfs als .btn { background: green; } onderaan staat, wint #cta { ... } meestal omdat een ID-selector veel zwaarder weegt dan een class-selector. Als je dit niet doorhebt, ga je vaak “duwen”: .container .btn of button.btn toevoegen of !important gebruiken. Dat kan werken, maar het is symptoombestrijding.

De nette oplossing is meestal ontwerpen met consistente hooks:

  1. Gebruik classes voor varianten: .btn, .btn--primary, .btn--danger.
  2. Reserveer ID’s voor unieke technische doelen (bijv. anker-links, JavaScript hooks als het echt nodig is).
  3. Houd varianten op hetzelfde niveau van specificity, zodat overrides voorspelbaar blijven.

De impact in een workflow (zoals opdrachten in training) is groot: beoordelaars en teamgenoten kunnen sneller zien wat basis is en wat een variant is. Een .btn--primary vertelt intentie (“primaire knop”), terwijl #cta vooral vertelt dat het element uniek is, niet hoe het zich hoort te gedragen. De beperking: varianten vragen discipline in naamgeving, anders eindig je met losse classes zonder systeem. Maar zelfs een simpel patroon (basis + modifier) is al veel onderhoudsvriendelijker dan ID-gestuurde styling.


Wat je vandaag moet onthouden (en hoe dit je sneller maakt)

Selectors bepalen welke elementen je raakt; specificity bepaalt welke regel wint als meerdere regels hetzelfde willen. Als je dit eenmaal doorziet, wordt CSS veel minder gokken en veel meer verklaren: je kijkt naar de selector, de sterkte en de volgorde, en je weet waarom iets gebeurt.

Belangrijkste takeaways:

  • Classes zijn je standaard gereedschap voor herbruikbare styling; ID’s maken overrides snel lastig.

  • Specificity wint van volgorde: “later in het bestand” helpt alleen bij gelijke specificity.

  • Scope bewust: .nav a is prima, maar vermijd diepe, fragiele selector-ketens.

  • Wees terughoudend met !important: het lost vaak niet op, het verplaatst het probleem.

Dit zet je up perfect voor Box model: spacing en sizing [30 minutes].

Last modified: Tuesday, 10 March 2026, 3:33 PM