Deze stijlgids is bedoeld voor iedereen die in aanraking komt met het schrijven van CSS. De richtlijnen die hier genoemd staan zijn een sterke aanmoediging voor het gebruik van bestaande, algemene en verstandige patronen.
Dit is een document in ontwikkeling en nieuwe ideeën zijn altijd welkom.
Introductie
CSS is geen mooie taal. Hoewel het eenvoudig is om te leren en mee aan de slag te gaan, kan het al snel een probleem worden op grotere schaal. We kunnen niet veranderen hoe CSS werkt, maar we kunnen wel de manier waarop we CSS schrijven en structuren aanpassen.
Als we werken aan grote, langlopende projecten, met meerdere ontwikkelaars van verschillende specialiteiten en capaciteiten, is het belangrijk dat we allemaal op dezelfde manier werken zodat o.a.:
- de stylesheets onderhoudbaar blijven;
- de code transparant, logisch en leesbaar blijft
- de stylesheets schaalbaar blijven
In dit document staan verschillende aanbevelingen, regels en technieken die we kunnen gebruiken om ons te helpen aan al deze doelen te voldoen.
Syntax & opmaak
Het is prettig om met code te werken die er netjes uit ziet. Het is voor andere teamleden ook uitnodigend om het mooi te houden. Lelijke code geeft een verkeerd voorbeeld.
Een standaard manier van CSS schrijven (letterlijk schrijven) helpt mee om alles netjes te houden. Het betekent ook dat code er altijd hetzelfde uit ziet en vertrouwd aanvoelt bij alle leden van het team.
80 karakters breed
Waar mogelijk, limiteer je CSS tot maximaal 80 karakters breed.
- het geeft de mogelijkheid om meerdere bestanden naast elkaar open te hebben;
- zorgt voor een comfortabele lijn lengte voor commentaar
/**
* Dit is een lang commentaar-blok. Hier beschrijf ik in detail de CSS die hierna
* volgt. Dit is zo'n lang commentaar-blok dat deze makkelijk over de 80 karakter
* grens gaat, en dus opgedeeld wordt over meerdere regels.
*/
Anatomie van een stijlregel
Voordat we het hebben over de daadwerkelijke stijlregels, laten we eerst de relevante terminologie benoemen:
selector {
eigenschap: waarde;
/* <-- declaratie ---> */
}
Uit het onderstaande voorbeeld,
.box, .box--promo,
.widget {
display: block;
background-color: #c0ffee;
color: #decaff;
}
kun je het volgende afleiden:
- gerelateerde selectors op dezelfde regel,
niet gerelateerde selectors op een nieuwe regel; - een spatie voor de open accolade (
{
); - de open accolade (
{
) op dezelfde regel als de laatste selector; - de eerste declaratie op een nieuwe regel na de open accolade (
{
); - eigenschappen en waardes op dezelfde regel;
- een spatie achter de dubbele punt tussen een eigenschap en een waarde (
:
); - elke declaratie op een nieuwe regel;
- elke declaratie één tab ingesprongen;
- elke regel eindigen met een puntkomma.
- de sluit accolade (
}
) op een nieuwe regel;
Multi-line CSS
Zoals je ook in het vorige voorbeeld ziet, schrijven we CSS altijd over meerdere regels, behalve in hele specifieke gevallen. Hier zijn een aantal voordelen aan:
- Je kan sneller door je styles scannen om te zien welke eigenschappen zijn gebruikt.
- Een kleinere kans op merge conflicten, omdat elke stukje functionaliteit op z'n eigen regel staat.
- Je kan beter 'comparen' met oude check-ins, omdat elke regel maar 1 verandering omvat.
Uitzonderingen en kleine afwijkingen
Uitzonderingen zijn stijlregels die maar één declaratie bevatten. Dit soort stijlregels kunnen beter op één regel, omdat je dan de selectors snel wilt kunnen scannen. Deze zijn in dit geval belangrijker dan de daadwerkelijke eigenschappen en waardes.
.icon {
display: inline-block;
width: 16px;
height: 16px;
background: url(img/icon-sprite.png);
}
.icon--1 { background-position: 0 0; }
.icon--2 { background-position: 0 -16px; }
.icon--3 { background-position: 0 -32px; }
.icon--4 { background-position: 0 -48px; }
Volgorde van declaraties
Als je er op kan rekenen dat bepaalde properties altijd op dezelfde plaats staan, begrijp je de CSS een stuk sneller (minder zoeken). Dit is belangrijk bij het werken in een team en helpt bij het lezen van code waar je minder bekend mee bent, omdat je het niet zelf geschreven hebt.
De voorkeur gaat uit naar het groeperen van gerelateerde eigenschappen. Structureel belangrijke eigenschappen (b.v. positionering en box-model) worden vóór typografische-, achtergronds- en kleureigenschappen gedeclareerd.
In onderstaand stuk code zie je een voorbeeld van juiste groepering van eigenschappen in een selector met meerdere declaraties. (Het commentaar en de witregel na elk blokje kun je uiteraard weglaten).
selector {
/* Positionering */
position: absolute;
z-index: 10;
top: 0;
right: 0;
/* Display & Box Model */
display: inline-block;
overflow: hidden;
box-sizing: border-box;
width: 200px;
height: 100px;
margin: 10px;
padding: 10px;
border: 10px solid #333;
border-radius: 4px;
box-shadow: 3px 0 10px 0 rgba(50, 50, 50, 0.5);
/* Kleuren */
background: #000;
color: #fff;
/* Text */
font-family: sans-serif;
font-size: 16px;
line-height: 1.4em;
text-align: right;
text-decoration: none;
/* Overig */
cursor: pointer;
white-space: pre;
transition: all 0.3s ease;
}
Kleuren
Bij het typen van een kleur in hexadecimale notering, gebruik je kleine letters. Dit omdat we vaak kleurcodes kopiëren uit Photoshop, en we dan één formatering aanhouden. Je mag 3-cijferparen of 6-cijferparen gebruiken, maar waar mogelijk de 3-cijfer versie. Dit bespaard een paar bytes.
color: #ff4e50; /* goed */
color: #FF4E50; /* fout */
color: #aaa; /* goed */
color: #AAA; /* fout */
Afmetingen
Bij het noteren van afmetingen, width
, height
, margin
, padding
,
border
gebruik je altijd een eenheid (b.v. em
, px
of %
).
Als de waarde 0 is, laat je de eenheid weg.
width: 12px; /* goed */
width: 12%; /* goed */
width: 12em; /* goed */
width: 0; /* goed */
width: 12; /* fout */
width: 0px; /* fout */
Notatie van aanhalingstekens
Gebruik bij voorkeur enkele (' ') i.p.v. dubbele (" ") aanhalingstekens.
De URL-waarde, van b.v. de background
-property
behoeft geen aanhalingstekens.
font-family: "open sans", arial, sans-serif; /* fout */
font-family: 'open sans', arial, sans-serif; /* goed */
background: url('/Img/sprite.png'); /* fout */
background: url(/Img/sprite.png); /* goed */
Shorthand notaties
Wanneer mogelijk is het gebruik van shorthand
notaties gewenst. Deze kunnen worden gebruikt bij o.a. background
, border
,
margin
, padding
, list-style
en font
declaraties.
/* goed */
background: #e2fda1 url(/img/icon.png) no-repeat left top;
/* fout */
background-color: #e2fda1;
background-image: url(/img/icon.png);
background-repeat: no-repeat;
background-position: left top;
/* goed */
border: 2px dashed #f90;
/* fout */
border-width: 2px;
border-style: dashed;
border-color: #f90;
/* goed */
padding: 5px 10px 20px;
/* fout */
padding-top: 5px;
padding-right: 10px;
padding-bottom: 20px;
padding-left: 10px;
Vermijd onnodige shorthand declaraties
Wel moet je uitkijken dat je hiermee geen eerder gezette waardes overschrijft. Als je b.v. alleen maar de margin-bottom
wilt wijzigen, doe je dat het best zonder de shorthand notatie.
/* gevaarlijk */
margin: 0 0 20px;
/* in dit geval beter */
margin-bottom: 20px;
Pas op voor onopzettelijke CSS resets
Elke keer dat je een shorthand oplossing gebruikt in CSS, zet je alle waarden voor alle eigenschappen van het element waar het om gaat. Als je dus een waarde bepaald kun je deze in een regel later per ongeluk weer resetten.
.module {
background-repeat: no-repeat;
background: url(test.jpg);
/* Oeps! Hier wordt de repeat overschreven. */
}
Zinvolle witruimte
Door het verstandig gebruik van witruimte tussen de stijlregels kunnen we een heleboel informatie verstrekken.
- Gebruik 1 witregel tussen nauw verwante stijlregels
- Gebruik 2 witregels tussen minder verwante stijlregels
- Gebruik 5 witregels om een nieuw blok aan te geven
.box { eigenschap: waarde; }
.box__hdr { eigenschap: waarde; }
.box--special { eigenschap: waarde; }
/* Widgets
------------------------------------- */
.widget { eigenschap: waarde; }
Inspringen
Voor het inspringen gebruiken we tabs, een tab is gelijk aan 4 spaties en is binnen Microsoft visual studio vast in te stellen. Dit kun je doen onder het menu 'Tools' > 'Options', in het scherm zoals hieronder in het voorbeeld weergegeven kun je de waardes instellen. Deze stellen we standaard in op 4.
HTML
Aangezien HTML en CSS onafscheidelijk aan elkaar verbonden zijn, moeten we ook hierover wat richtlijnen benoemen.
Gebruik altijd aanhalingstekens bij attributen, al werken ze ook zonder. Dit verkleint de kans op ongelukken, en is ook het meest vertrouwd bij ontwikkelaars.
<div class=box> <!-- dit zal gewoon werken -->
<div class="box"> <!-- maar dit geniet de voorkeur -->
Bij meerdere waardes in een class attribuut, scheid je deze met 2 spaties. Dit doen we om de leesbaarheid te vergroten. Zeker bij meer dan 2 classes is dit erg prettig.
<li class="widget__item box box--large push-left"> <!-- slecht leesbaar -->
<li class="widget__item box box--large push-left"> <!-- Dit is beter -->
Ook in de HTML is zinvolle witruimte tussen bepaalde blokken aan te raden om ze visueel van elkaar te scheiden. Dit zorgt dat ontwikkelaars snel losse delen van de DOM kunnen scannen.
Commentaar
Goed becommentarieerde code is extreem belangrijk. Bij een groot project is het soms al lastig om de stukken CSS die je zelf schrijft te onthouden en begrijpen, laat staan die van een collega. Daarom wordt commentaar sterk aangemoedigd.
CSS heeft meer comments nodig
Door alleen naar een stuk CSS te kijken is het is vaak moeilijk om te onderscheiden
- of een stuk CSS afhankelijk is van een ander stuk code;
- wat het aanpassen van een stuk code voor gevolgen heeft op een andere plek;
- waar de CSS nog meer wordt gebruikt;
- welke styles iets overerven (bewust of niet);
- hoe en waar een stuk CSS bedoeld is te gebruiken;
Omdat met alleen de CSS vaak niet het hele plaatje duidelijk is, is het een taal die echt baat heeft bij veel commentaar. Alles dat niet vanzelf spreekt vanuit de CSS-code zou je kunnen voorzien van commentaar. Ga dus niet alles becommentariëren, alleen als het verduidelijking vereist.
Het commentaar mag dan meteen achter de declaratie staan.
Dit commentaarblok komt dan voorafgaand aan de selector. Hou bij deze blokken ook rekening met de 80 karakters regel.
Secties en modules
Begin ieder nieuwe sectie of module met een titel commentaarblok.
/* Sectie of module titel commentaarblok
-------------------------------------------- */
Specifieke declaratie
Soms is het nodig om bij een specifieke declaratie commentaar toe te voegen om bijvoorbeeld een "hack" toe te lichten.
.google-marker {
color: #222 !important; /* Nodig omdat op het element een inline style staat. */
}
Meerdere regels
Als je een langere uitleg nodig hebt, die over meerdere regels gaat, gebruik je het best een groep commentaarblok:
/*
* Groep commentaarblok.
* Ideaal voor uitleg over meerdere regels en documentatie die uitleg vereist
* over een bepaalde module
*/
selector {
eigenschap: waarde;
}
Comment Summary
Om een toelichting te geven aan bepaalde CSS blokken is het mogelijk en overzichtelijk om een comment summary toe te passen. Hiermee kun je op 1 plek in de CSS puntsgewijs een toelichting geven op de CSS die daarop volgt doormiddel van een comment.
/*
* [1] Background-color for some element
* [2] Font-size for some text
* [3] Height & Width for a content area
*/
selector {
background-color: #000; /*[1]*/
font-size: 14px; /*[2]*/
height: 50%; /*[3]*/
width: 50%; /*[3]*/
}
Naamgevingsconventies
Naamgevingsconventies zijn erg nuttig om de CSS heel strikt, transparant en informatief te maken. Het maakt duidelijk
- wat een class doet;
- waar een class gebruikt kan worden;
- waar een class betrekking op heeft.
Gescheiden met koppelteken
Selector benamingen altijd in lowercase, indien nodig gescheiden met een koppelteken (-
).
.widget {}
.main-menu {}
.portaal-user-login {}
Camel case en een liggend streepje (_
) worden niet gebruikt voor normale classes. Onderstaande
voorbeelden zijn dus niet juist:
.headerLogo {} /* Camelcasing is niet gewenst in CSS*/
.main_menu {} /* Liggend streepje niet gebruiken als koppelteken */
BEM
Voor objecten in CSS gebruiken we de naamgevings methode van 'BEM'.
BEM splits de classes van bepaalde onderdelen in 3 groepen:
- Block: Het hoofdobject
- Element: Een onderdeel van het Block
- Modifier: Een variant of uitbreiding van het Block
.widget {} /* Block */
.widget__header {} /* Element */
.widget--special {} /* Modifier */
Elementen worden gescheiden met twee (2) liggende streepjes (__
), en Modifiers worden gescheiden
door twee (2) koppeltekens (--
).
<div class="widget">
<div class="widget__header">
<h1 class="widget__title">Titel</h1>
<!-- ... -->
</div>
<div class="widget__body">
<!-- ... -->
</div>
</div>
<div class="widget widget--special">
<!-- ... -->
</div>
Meer lagen
Het is het niet nodig om elke laag van de DOM te doorlopen. Zoals je in het vorige voorbeeld kan zien zit in de
.widget__header
nog een titel element. De classes volgen de DOM structuur niet 1 op 1, dus wordt
dit niet .widget__header__title
maar .widget__title
.
Modifiers
Je kan een Modifier op een Block element gebruiken (zoals in het vorige voorbeeld met .widget--special
),
maar ook direct op een Element:
<div class="widget"> <!-- Block -->
<div class="widget__intro widget__intro--small"> <!-- Elem + Modifier -->
<!-- ... -->
</div>
</div>
Naamgevingsconventies in HTML
Deze vorm van naamgeving is niet meteen al nuttig in je CSS. Waar de kracht van deze naamgeving ligt is in je markup. In het voorbeeld hieronder een stukje HTML, zonder naamgevingsconventie:
<div class="box reactie definitief">
<img class="icon image"/>
<p class="datum">...</p>
</div>
Hoe zijn de classes reactie
en icon
aan elkaar zijn gerelateerd? Of zijn ze dat
helemaal niet? Kan je de class definitief
naast datum
gebruiken? Staan de classes
image
en reactie
bij elkaar in de CSS? Kun je icon
ook nog ergens anders
gebruiken?
Met de voorgaande opmaak, is het moeilijk om op al deze vragen een antwoord te geven. Het gebruik van een naamgevingsconventie verandert de zaak:
<div class="box reactie reactie--definitief">
<img class="icon reactie__image"/>
<p class="reactie__datum">...</p>
</div>
Nu kunnen we duidelijk zien welke classes wel, en welke niet aan elkaar gerelateerd zijn, en hoe. We weten welke classes we niet buiten het component kunnen gebruiken, en welke classes we overal kunnen hergebruiken.
Javascript hooks
Het is niet verstandig om CSS styling en javascript functionaliteit aan dezelfde class te verbinden. Deze zouden
anders niet los van elkaar te gebruiken zijn. We gebruiken daar dus een aparte class voor, en deze worden
altijd voorafgegaan door js-
.
<a class="btn js-reageer-btn">Ik wil reageren</a>
Dit betekent dat we door de btn
class toe te voegen, een ander element dezelfde styling kunnen
geven, maar zonder de functionaliteit die aan js-reageer-btn
gekoppeld zit.
CSS Selectors
Misschien enigszins verrassend, maar een van de meest belangrijke aspecten bij het schrijven van onderhoudbare en schaalbare CSS zijn de selectors.
Selector Intentie
Bij het schrijven van CSS is het belangrijk dat we goed opletten wat het bereik is van een selector, en dat we
de juiste dingen selecteren voor de juiste redenen.
Selector Intentie is het proces van het
beslissen en definiëren wat we willen stylen en hoe we het gaan selecteren. Als je bijvoorbeeld het hoofdmenu
van een website wilt stylen, is het onverstandig om een selector als deze te gebruiken:
.menu ul {}
De Selector's Intentie is om elke ul
in de class .menu
te stylen, waar het
onze bedoeling was om het hoofdmenu te stylen. Dit is slechte Selector Intentie; je kunt
namelijk meer dan één .menu
element op een pagina hebben, en deze kunnen ook meerdere ul
's
bevatten, dus een selector als deze loopt het risico dat die zeer specifieke styling toepast op een groot
aantal elementen.
Het is dan beter om een selector als deze te gebruiken:
.main-nav {}
Dit is een eenduidige, expliciete selector. We selecteren nadrukkelijk het juiste ding voor precies de juiste reden.
Slechte Selector Intentie is een van de grootste redenen voor problemen bij een project met veel CSS. Als je CSS niet expliciet genoeg is, loop je vaak tegen onverwachte bijwerkingen aan, zoals selectors die ongerelateerde stijlregels beïnvloeden en in de weg zitten.
Herbruikbaarheid
Omdat we steeds meer UI's willen maken op basis van componenten, is herbruikbaarheid erg belangrijk. We willen de optie hebben om componenten te verplaatsten, kopieëren en te hergebruiken in onze projecten.
Daarom maken we veel gebruik van classes. ID's zijn naast het feit dat ze over-specifiek zijn, ook niet meer dan één keer te gebruiken op een pagina. Classes daarentegen kunnen oneindig keer worden hergebruikt.
Locatie afhankelijkheid
Aangezien projecten aan veranderingen onderhevig zijn, is het beter om dingen niet te stylen op basis van waar ze staan in het design. Componenten moeten helemaal onafhankelijk zijn van de locatie waar ze zich bevinden.
Stel je hebt een knop in een inschrijfformulier die je styled met de volgende selector:
.inschrijven a {}
Dit is niet alleen slechte Selector Intentie, omdat het elke link in het inschrijfformulier zal stylen, maar ook erg locatie afhankelijk. We kunnen deze styling niet hergebruiken voor een andere knop buiten het inschrijfformulier. Een veel betere selector zou zijn:
.btn {}
Deze class kan overal worden hergebruikt en zal altijd de juiste styling hebben. Door een betere selector, is dit component verplaatsbaar, herbruikbaar, en heeft het geen afhankelijkheden. Een component zou niet op een specifieke plaats moeten staan om er op een bepaalde manier uit te zien.
Verplaatsbaarheid
Locatie afhankelijkheid willen we zoveel mogelijk terugdringen, het liefst zelfs helemaal voorkomen. Ook zet je beter geen element-naam voor een selector.
input.btn {} /* deze class is niet inzetbaar voor andere elementen */
Door de input
voor je selector te zetten gaat de herbruikbaarheid van je component verloren. Als je
de input
hier weg laat kan de styling ook worden hergebruikt voor een <a>
of
een <button>
.
Naamgeving
Naamgeving binnen CSS kan erg lastig zijn. Het advies is om namen te gebruiken die verstandig zijn, maar ook
niet té specifiek. Let hierbij vooral op grote herbruikbaarheid. Bijvoorbeeld, een class als .side-nav
kun je beter vervangen door .sub-nav
. Of i.p.v. .footer-links
te gebruiken, kies je
beter voor .sub-links
.
Het verschil in naamgeving zit in dat de eerste van elk van de 2 voorbeelden is gebonden aan een specifiek geval. Ze kunnen alleen worden gebruikt als navigatie in de sidebar, of als links in de footer. Door een wat minder specifieke naam te kiezen vergroten we de mogelijkheid om deze componenten ook in andere omstandigheden in te zetten.
Specificiteit
Als je ooit problemen hebt gehad met specificiteit in CSS weet je hoe vervelend dit kan zijn. Het beste is om de specificiteit zo laag mogelijk te houden, en het zo veel mogelijk proberen te vermijden. De volgende punten helpen je hierbij:
- Gebruik géén IDs in je CSS
#footer {}
- Geen geneste selectors
.content .widget p {}
- Zet het element-type niet voor je selector
input.btn-primary {}
- Selectors niet koppelen
.widget.box {}
IDs in CSS
Om de specificiteit zo laag mogelijk te houden is er een simpele regel: gebruik géén IDs in je CSS. Ze zijn niet herbruikbaar, en ze geven een element een enorm hoge specificiteit (100). Door deze regel besparen we onszelf een hele hoop problemen. Mocht er toch sprake zijn van een uitzondering, bijvoorbeeld in geval van script toepassingen op specifieke IDs, dan heeft de volgende notatie de voorkeur:
div[id="wrapper"] { ... }
Nesting
Bij het declareren van je style gebruik je het minimum aantal selectors dat nodig is om een element te stylen. Bekijk het volgende voorbeeld:
.mod {
padding: 10px;
border: 1px solid #bada55;
}
.mod > .mod__title {
color: blue;
}
Om het element .mod__title
te stylen hebben we een selector die 2 keer zo specifiek is als nodig.
Dat betekent dat als we de titel willen aanpassen, we een minstens gelijkwaardige selector nodig hebben:
.mod { ... }
.mod > .mod__title { ... }
.mod > .mod__title--sub {
color: orange;
}
Dit is iets wat je wilt vermijden. Als regel kun je stellen, als een selector werkt zonder genest te zijn, gebruik dan geen nesting.
!important
Het gebruik van !important
is meestal een direct gevolg van specificiteits problemen. Het word vaak
gebruikt als laatste uitweg om een element toch maar goed te kunnen stylen, omdat je eerder in de CSS een
selector te specifiek hebt gemaakt. Je kunt het vergelijken met 'cheaten'.
Gebruik geen !important
in je CSS om een specificiteits probleem op te lossen. Het mag alléén
gebruikt worden wanneer het gebruikt word als een garantie i.p.v. een fix. Bij voorbeeld:
.is-hidden {
display: none !important;
}
Deze helper-class heeft een erg specifiek doel (content verbergen). Je gebruikt deze class alleen als je die
echt wilt. Daarom is in dit geval een !important
wel toegelaten. Bedenk dus voor je !important
wilt gebruiken goed na of het niet een 'fix' is voor een eerder veroorzaakt probleem.
Gebruik de !important
alleen in combinatie met een utility class of zoals hierboven in het
voorbeeld in een bepaalde state.
SASS
Sinds 2016 passen we SASS (Syntactically Awesome Style Sheets) toe op nieuwe projecten. SASS is een uitbreiding van CSS die kracht en elegantie toevoegt aan de basistaal. Hiermee kun je variabelen, extends, mixins, imports, en meer gebruiken met een volledig CSS-compatible syntax. SASS zorgt ervoor dat grote stylesheets goed georganiseerd blijven, en krijgt kleine stylesheets super snel up and running.
SCSS Syntax
Maar waarom gebruiken wij SCSS en niet SASS? Dit komt eigenlijk gewoon neer op een kwestie van persoonlijke smaak en keuze. De voornamelijke reden waarom wij SCSS toepassen en niet SASS is dat de syntax van SCSS nagenoeg hetzelfde is aan die van CSS. Een standaard CSS bestand is met de syntax ook gewoon een legitiem SCSS bestand, alleen worden hierin dan niet de handigheden zoals variabelen, nesting en mixins gebruikt. En omdat SCSS veel meer weg heeft van de CSS syntax, is het overstappen en het toepassen ervan natuurlijk veel makkelijker gebleken.
Geen SMACSS maar ITCSS
Bij het toepassen van SCSS is de SMACSS architectuur niet meer relevant. In ons geval maken we gebruik van de ITCSS (Inverted Triangle CSS) architectuur. Waarom deze architectuur? Deze vorm vertegenwoordigt een model wat zorgt voor een geordende CSS structuur, en dat in de meest effectieve, minst verspillende manier. Binnen deze ITCSS architectuur maken we gebruik van namespacing.
Een van de belangrijkste principes van ITCSS is dat het je CSS onderbrengt in lagen, en dat in de vorm van een omgekeerde driehoek (Inverted triangle). Dit betekend dat je CSS is onderverdeeld van bovenin de algemene zaken, zoals bijvoorbeeld het kleurgebruik tot aan onderin de hele specifieke zaken, zoals een bepaalde button.
De lagen zijn dan alsvolgt ingedeeld:
- Settings - variabelen die worden gebruikt voor de preprocessor zoals lettertypes, kleuren, enz.
- Tools - algemeen gebruik van mixins en functies. Het is de belangrijkste, niet uit te voeren CSS, in de eerste 2 lagen.
- Generic - reset en/of normalize stijlen, box-sizing definities, enz. Dit is de eerste laag waarin de clean CSS wordt gegenereert.
- Elements - styling voor kale HTML-elementen (zoals H1, A, enz.). Deze komen met standaard styling van de browser, zodat we ze hierin opnieuw definiëren.
- Objects - objecten die vooral weinig design stijlen bevatten, neem hierin het media object als voorbeeld.
- Components - specifieke UI-componenten, Dit is waar de meerderheid van ons werk plaatsvindt.
- Utility - betreft helper classes die de mogelijkheid hebben om alle voorafgaande stijlen in de driehoek te overschrijven met een specifieke stijl. Zoals bijvoorbeeld een hide class.
De driehoek laat ook zien hoe de stijlen van de selectors worden gerangschikt in de resulterende CSS: van generieke stijlen tot zeer expliciete, van laag-specifieke selectors tot zeer-specifieke selectors en van ver reikend tot gelokaliseerde selectors.
Namespacing
Het gebruik van een namespacing is niet alleen handig om een specifiek element te voorzien van opmaak, ook door de namespacing is een element makkelijk terug te vinden in een project, waar het element allemaal terug komt.
o-
: Dit zijn objecten, deze objecten kunnen op verschillende manieren worden toegepast. Met de objecten moet je oppassen want bij het maken van aanpassingen zou je een domino-effect kunnen veroorzaken op verschillende plaatsen.c-
: Componenten zijn implementatie-specifieke stukjes van UI. Ze zijn vrij veilig te wijzigen zonder dat er elders wat omvalt. Alles met een c- is een specifiek element.u-
: Utilities zijn componenten die een specifieke en gerichte taak hebben. Het is ook gebruikelijk dat deze regels voorzien zijn van een!important
regel. Ze doen maar één ding in een zeer hardhandige en onelegante manier. Ze worden gebruikt als een laatste redmiddel wanneer geen andere CSS oplossingen beschikbaar is. Ze zijn slechts één stap verwijderd van het inline stijlen, dus deze pas gebruiken wanneer het niet anders kan.is-, has-
: Betekent dat het element in kwestie momenteel is vormgegeven als gevolg van een staat of toestand.js-
: Javascript bindt op deze class. Deze specifieke class mag nooit terugkomen in de styling omdat deze class op geen enkele voorwaarde ooit voorzien van opmaak.
Door alleen dit korte lijstje al kun je zien hoe veel meer informatie we ontwikkelaars kunnen geven door simpelweg één of twee tekens toe te voegen aan onze classes.
Imports
CSS heeft een import-optie waarmee je CSS kunt splitsen in kleinere, meer onderhoudbare porties. Het enige nadeel bij het gebruik van de @import mogelijkheid binnen CSS is dat je bij elke import dan ook elke keer een HTTP request uitvoert. SCSS bouwt verder op de huidige CSS @import functie, maar in plaats van een HTTP-verzoek, zal SCSS het bestand dat je wil importeren, combineren met het bestand dat je importeert. Hiermee stuur je uiteindelijk maar één CSS bestand richting de browser. In onze projecten wordt het meest gebruik gemaakt van de @import in onze main SCSS, dit ziet er alsvolgt uit:
>@charset "utf-8";
/* 1 SETTINGS - Global variables, site-wide settings, config switches etc.
*
* The order in which these Sass files are loaded is critical!
* So please be careful when editing
=========================================================== */
@import "1-Settings/settings.colors";
@import "1-Settings/settings.config";
@import "1-Settings/settings.typography";
@import "1-Settings/settings.spacing";
@import "1-Settings/settings.responsive";
@import "1-Settings/settings.grid";
@import "1-Settings/settings.site";
@import "1-Settings/settings.zindex";
/* 2 Tools - Site wide mixins and functions
=========================================================== */
@import "2-Tools/tools.debug";
@import "2-Tools/tools.givebreakpoint";
@import '2-Tools/tools.icons-sprite-main';
@import "2-Tools/tools.toolbox";
/* 3 Generic - Ground-zero styles / Low-specificity, far-reaching rulesets. (resets, box-sizing etc.)
=========================================================== */
@import "3-Generic/generic.normalize";
@import "3-Generic/generic.reset";
/* 4 Elements - Unclassed HTML elements (type selectors)
=========================================================== */
@import "4-Elements/elements.animations";
@import "4-Elements/elements.images";
@import "4-Elements/elements.lists";
@import "4-Elements/elements.page";
@import "4-Elements/elements.typography";
/* 5 Objects - Design patterns (e.g. .media)
=========================================================== */
@import "5-Objects/objects.arrange";
@import "5-Objects/objects.datalist";
@import "5-Objects/objects.grid";
@import "5-Objects/objects.lists";
@import "5-Objects/objects.media";
@import "5-Objects/objects.wrappers";
/* 6 Components - Discrete complete chunks of UI. Designed components
=========================================================== */
@import "6-Components/components.headings";
@import "6-Components/components.icons";
@import "6-Components/components.labels";
@import "6-Components/components.links";
@import "6-Components/components.loader";
@import "6-Components/components.messages";
@import "6-Components/components.nav";
@import "6-Components/components.notice";
@import "6-Components/components.popup";
@import "6-Components/components.rte";
@import "6-Components/components.tabs";
@import "6-Components/components.toggle";
/* 7 Utility - High-specificity, very explicit selectors. Overrides and helper classes.
=========================================================== */
@import "7-Utility/utility.helpers";
@import "7-Utility/utility.spacing";
/* 8 Libs
=========================================================== */
@import "8-Libs/libs.libstyles";
Variabelen
Het gebruik van variabelen is een manier om de stijlen die je wilt hergebruiken in de stylesheet op te slaan en te gebruiken. Je stelt deze stijl in de variabelen 1 keer in en kunt deze vervolgens overal gaan gebruiken. Het grote voordeel hiervan is dat je de variabelen 1 keer hoeft te wijzigen, alle stijlen die voorzien zijn van deze variabele veranderd in deze automatisch mee.
Om een variabele te defineren maken we gebruik van het $
symbool. Een voorbeeld:
$font-stack: Helvetica, sans-serif;
$primary-color: #333;
body {
font: $font-stack;
color: $primary-color;
}
Wanneer de SCSS wordt verwerkt worden de variabelen die zijn gedefineerd voor de $font-stack en $primary-color omgezet naar normaal CSS. Dit is ideaal wanneer er wordt gewerkt met huisstijl kleuren die consequent door de hele site moeten worden toegepast. Het resultaat van bovenstaande voorbeeld is dan alsvolgt:
body {
font: Helvetica, sans-serif;
color: #333;
}
Functions
Met SCSS maken we gebruik van Functions en Mixins. Deze onderscheiden we in 2 apparte bestanden binnen de SCSS, een bestand voor Functions en een bestand voor Mixins. Voor website specifieke functions en mixins hebben we een gecombineerd toolbox bestand. Bij mixins krijg je CSS terug, bij functions is dit niet het geval, functions retourneren een waarde. Zie hieronder hoe je een function kunt toepassen:
@txtFunction calc-function($val1, $val2) {
@txtReturn $val1 + $val2
}
Na het maken van de function kun je deze gebruiken in je SCSS waaronder ook in je Mixins. Een simpel voorbeeld van het toevoegen van een function in de SCSS:
.my-module {
padding: calc-function(10px, 5px);
}
Mixins
Met een mixin is het mogelijk om een groep CSS stijlen te maken en deze vervolgens overal te kunnen hergebruiken op de website. Ook is het mogelijk om een variabele mee te geven om het nog flexibeler te maken. Hieronder een mixin voorbeeld voor het afbreken van een titel wanneer deze de maximale breedte overschrijd:
@mixin truncate($truncation-boundary) {
max-width: $truncation-boundary;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.titel { @include truncate(500px); }
Om gebruik te maken van een mixin maak je gebruik van de @mixin richtlijnen en geef je deze een duidelijke en toepasselijke naam. In ons voorbeeld hebben we onze mixin truncate genoemd. Ook maken we in het voorbeeld gebruik van de variabele $truncation-boundary tussen de haakjes, hiermee kunnen we een waarde meegeven die wordt toegepast binnen de mixin. Nadat de mixin is gemaakt, is het vervolgens mogelijk om deze te gebruiken als een CSS stijl te beginnen met een @include gevolgd door de naam van de mixin. Wanneer de CSS wordt gegenereerd zal het er als volgt uitzien:
.titel {
max-width: 500px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
Selector Nesting
Selector nesting is een kenmerk van SCSS, hiermee kun je selectors binnen selectors plaatsen, het is hierbij niet verplicht om de hele classnaam te noteren, echter is dan het terugvinden van een class bij bijvoorbeeld een modifier (BEM) niet echt handig. Daarom hieronder een voorbeeld van hoe het niet en wel moet.
/* fout */
.c-button {
display: inline-block;
text-decoration: none;
color: #000;
&--light {
color: #FFF;
}
}
/* goed */
.c-button {
display: inline-block;
text-decoration: none;
color: #000;
}
.c-button--light {
color: #FFF;
}
In dit voorbeeld is .c-button--dark
de modifier die bij .c-button
geplaatst kan
worden. Bij het incorrecte voorbeeld is het door het hele project heen de class .c-button--dark
moeilijk vindbaar, mits je zoekt op --dark
, in dit geval is het wel sneller mogelijk dat de
classnaam vaker in de zoekresultaten terugkomen omdat het niet om een bijzonder specifieke classnaam gaat.
In het geval van een bijvoorbeeld een :hover
of een ::before
en ::after
passen we wel gemakkelijk de nesting toe. Zie hieronder een voorbeeld van de eerder genoemde situatie maar nu
inclusief een hover
:
.c-button {
display: inline-block;
text-decoration: none;
color: #000;
&:hover {
color: #FFF;
}
}
Gulp Autoprefixer
Werken met de Gulp autoprefixer is eenvoudig, je schrijft gewoon SCSS en de vendor prefixes kun je hierbij gewoon vergeten, deze worden door de autoprefixer op basis van de laatste W3C specs toegevoegd. In je Gulp file hoef je alleen nog maar aan te geven welke browsers je wil ondersteunen met 'Can I Use' statistieken. In de Gulpfile komt deze configuratie alsvolgt terug:
-
Eerst wordt er een variabele gezet waarin je aangeeft naar welke browsers gekeken moet worden waarvoor vendor prefixes moeten worden toegepast. Dit werkt via de
Browserlist key
in de gulp file, het specificeren van de browsers gaat dan via verschillende standaard queries -
Vervolgens wordt deze variabele geplaatst binnen de gulp build, waarbij de autoprefixer wordt geactiveerd en zorgt voor de vendor prefixes binnen de gegenereerde CSS daar waar dat nodig is.
Kortom, in de SCSS schrijf je de normale CSS en de autoprefixer zorgt voor de rest.
SVG Sprite
Binnen je project vind je de map svg-sprites
, in deze map vind je een verzameling van je svg
bestanden. Via een Gulp
script wordt hiervan een sprite ontwikkeld en kun je deze via een makkelijke mixin gebruiken binnen het
project. Geef je svg
bestand binnen het project een unieke naam, deze naam kun je vervolgens in de
SCSS gebruiken om de Mixin toe te passen, de mixin zorgt er vervolgens voor dat er een template in elkaar wordt
gezet welke vervolgens uitgelezen kan worden en een CSS gebouwd kan worden. Een voorbeeld van een svg
toepassing vind je hieronder.
/* SCSS */
.c-icon {
@include sprite(message-notification);
}
.c-icon[data-icon='alert'] {
@include sprite(message-alert, elem);
}
.c-icon[data-icon='error'] {
@include sprite(message-error, elem);
}
/* Output */
.c-icon {
display: inline-block;
background-image: url("../img/Icons-sprite.svg");
background-size: 538px 532px;
font-size: 1rem;
width: 32px;
height: 32px;
background-position: -384px -384px;
}
.c-icon--alert {
width: 32px;
height: 32px;
background-position: -320px -320px;
}
.c-icon--error {
width: 32px;
height: 32px;
background-position: -352px -352px;
}