Doe meer met CSS

Soms lijkt het alsof het coderen van een moderne frontend-applicatie zo’n beetje alleen uit werken met JavaScript dan wel TypeScript bestaat. Maar CSS is er ook nog.

Niet zelden wordt de styling gezien als vervelend en lastig sluitstuk. Vandaar dat frameworks als Bootstrap grote populariteit genieten. Een nadeel daarvan is dan wel weer dat web applicaties een beetje saai worden. Ze lijken allemaal op elkaar.

Een tip van developer en UI-goeroe Rob Sutcliffe is daarom om eens uit te breken. Uit de strakke, maar geestdodende Bootstrap-vormgeving. Geef je site een subtiele persoonlijke twist. Maakt niet uit hoe. Het kan een excentrieke visual zijn, een schrijfrichting of een panel dat afwijkt van de 12-koloms-grid.

Breaking the grid

Breaking the grid

Breaking the grid, zoals hierboven, betekent dat je er alleen voor komt te staan in de wereld van CSS. Maar geen reden voor paniek. Want, zoals een ontwikkelaar als Negar Jamalifard graag benadrukt, CSS is je vriend.

Neem haar voorbeeld van een home made checkbox ter illustratie. Als we die ontdoen van alle styles die er een coole checkbox van maken, dan blijft de volgende HTML en SCSS code over:

<div class="checkbox">
  <input type="checkbox" id="checkbox0">
  <label for="checkbox0"></label>
  <div class="title">Check</div>  
</div>

.checkbox {
  display: flex;
  align-items: center;
  width: 15px;
  height: 15px;
  border: solid 1px;
  
  label {
    &::after {
      content: "✓";
      opacity: 0;
    }
  }
  
  input {
    display: none;
    &:checked {
      & ~ label::after {
        opacity: 1;
      }
    }
  }
  

  .title {
    margin: 0 0 0 10px;
  }
}

Als je dit in JSFiddle draait, zie je deze (lelijke) checkbox verschijnen:

check box

Merk op dat de HTML <input type=“checkbox”>  niet wordt getoond (display none). Het tonen en verbergen (toggle) van een vinkje wordt gedaan door twee speciale CSS selectors: pseudo-element en pseudo-class.

Via het ::after pseudo-element wordt een vinkje toegevoegd aan het <label>  element met een opacity 0, zodat deze default niet zichtbaar is. Via de :checked pseudo-class van het <input>  element wordt het vinkje zichtbaar gemaakt van het sibling (~) <label>  element (opacity 1).

Het mooie van deze code is dat het laat zien dat er niet altijd JavaScript noodzakelijk is om logica te implementeren. CSS is een krachtige taal, waarmee soms veel eenvoudigere en cleanere code te schrijven is.

Easy?

CSS heeft iets weg van het schaakspel: de regels zijn simpel, maar het spel vergt de nodige ervaring. Aan de hand van voorbeeld? Stel je wilt een alert box met een message (1). Omdat het niet fraai is als de tekst tegen de rand van de alert box zit, voegen we margin toe:

<div class="alert">
  <p class="message">Lorem ipsum dolor sit amet</p>
</div>

.alert {
  border: 1px solid gray;
  border-radius: 5px;
  width: 50%;

  .message {
    margin: 15px;
  }   
}

Example

Enige tijd later ontvangen we van de PO de requirement om hier een action button aan toe te voegen:

<div class="alert">
  <p class="message">Lorem ipsum dolor sit amet</p>
  <button>Just do it</button>
</div>

Example 2

Helaas, de button zit tegen de rand van de alert box. Dat kunnen we verbeteren door ook margin toe te voegen aan de button. Echter, dat leidt tot minder goed te onderhouden code. De spacing zit in dit voorbeeld op het verkeerde niveau. Spacing behoort bij de container en niet bij content van de container.

.alert {
  border: 1px solid gray;
  border-radius: 5px;
  width: 50%;
  padding: 15px;

  .message {
  }   
}

Example 3

Merk op dat wat ervaring goed van pas komt. Te beginnen bij, volgens Rachel Andrew (CSS-werkgroeplid bij W3C), deze CSS-fundamentals (2):

  • Selectors
  • Inheritance and Cascade
  • Box Model
  • Normal Flow
  • Formatting Context
  • Layout

Selectors doen wat op de verpakking staat: ze selecteren delen van het document dat je wilt stylen. Naast de bekende selectors: tag name, id en class, zijn er nog vele andere selectors (3).

Een interessante selector is de pseudo-class. Deze selecteert een element indien deze in een bepaalde state verkeert. Deze selector heet zo omdat het lijkt alsof je een class op het element hebt toegepast. Syntax:

selector:pseudo-class {
  property: value;
}

De pseudo-element selector wordt zo genoemd omdat het lijkt alsof er dynamisch een element is toegevoegd. Syntax:
selector::pseudo-element {
  property: value;
}

In het volgende voorbeeld van Rachel Andrew gebruikt ze zowel de pseudo-class :first-child als het pseudo-element ::first-line. Ik vind dit prachtige code, omdat je met eenvoudige CSS en HTML een mooi effect bereikt.
body {
  padding: 20px;
  font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}

* {box-sizing: border-box;}

p {
  margin: 0 0 1em 0;
}

.container {  
  border: 5px solid rgb(111,41,97);
  border-radius: .5em;
  padding: 10px;
  column-count: 2;
}

.container p:first-child::first-line {
  font-weight: bold;
  color: rgb(111,41,97);
}

prachtig

Het lijkt alsof er een <span>  element om de eerste regel tekst is gezet. Als de container verkleint, gaat de tekst wrappen en wordt de lengte van de eerste regel korter. Merk dan op dat de ::first-line selector opnieuw wordt toegepast. Indien dit pseudo-element niet zou zijn gebruikt, maar in plaats daarvan een echt <span>  element, zou dit niet het geval zijn.

Dit complexe gedrag is met CSS op eenvoudige wijze te implementeren.

Met een selector een element selecteren, en daar een style op toepassen, leidt niet altijd tot het verwachte resultaat. Er blijkt dan in werkelijkheid een andere style toegepast te zijn. Frustratie alom. Je bent, zo ontdek je snel genoeg, schaakmat gezet door cascade.

Cascade bepaalt welke CSS-regel wint indien meerdere regels van toepassing zijn op een element. De cascade is gekoppeld aan inheritance en specificity. Inheritance bepaalt welke properties worden geërfd door de child elementen van het element waar die properties op worden toegepast (en welke niet). Specificity bepaalt welke selector wint indien meerdere selectors op hetzelfde element van toepassing zijn.

De spelregels van dit CSS-spel zijn:

1. Indien twee CSS-regels gelijke specificity hebben, wint de laatste in de source order. In dit voorbeeld wordt de header blauw:

<h1>This is my heading.</h1>

h1 { 
    color: red; 
}

h1 { 
    color: blue; 
}

2. Een regel met een selector met een hogere specificity wint van een regel met een selector met een lagere specificity. Een element selector is van toepassing op alle elementen van dat type in de page. Het is daarom minder specifiek dan een class selector die alleen van toepassing is op de elementen met die class attribuut-waarde. Daarom wordt de header in onderstaand voorbeeld rood ondanks dat <h1>  later is in de source order:
<h1 class="main-heading">This is my heading.</h1>

.main-heading { 
    color: red; 
}
        
h1 { 
    color: blue; 
}

3. Sommige CSS property-waarden die op een parent element worden toegepast, worden geërfd door hun child elementen, en sommige niet. In dit voorbeeld worden alle <p>  elementen blauw en de <span>  elementen zwart:
<p>As the body has been set to have a color of blue this is inherited through the descendants.</p>
<p>We can change the color by targetting the element with a selector, such as this <span>span</span>.</p>

body {
    color: blue;
}

span {
    color: black;
}

Inheritance kun je sturen met drie universele property-waarden, namelijk:

inherit: maakt de property-waarde gelijk aan dat van het parent element
initial: maakt de property-waarde gelijk aan de default-waarde van die property
unset: maakt de property-waarde gelijk aan de natuurlijke waarde. Als het een overerfbare property is wordt het gelijk aan inherit; anders wordt het gelijk aan initial.

In onderstaand voorbeeld wordt de tekst ‘bar’ groen, maar krijgt de tekst ‘foo’ de initiële zwarte kleur van de color property:

<div class="container">
  <p>bar</p>
  <p class="hup">foo</p>
</div>

.container {
  color: green;
}

.hup {
  color: initial;
}

De cascade helpt in het voorkomen van herhaling in de CSS code. Een best practice is om generieke styles te definiëren op basis van elementen en vervolgens een aantal classes te maken die sommige properties wijzigen. In dit voorbeeld is elke <h2>  op een bepaalde manier gestyled. En via de class selectors ‘small’ en ‘bright’ zijn individuele <h2>  elementen alsnog anders vorm te geven:
<h2>Heading with no class</h2>
<h2 class="small">Heading with class of small</h2>
<h2 class="bright">Heading with class of bright</h2>

h2 {
    font-size: 2em;
    color: #000;
    font-family: Georgia, 'Times New Roman', Times, serif;
}
        
.small {
    font-size: 1em;
}
        
.bright {
    color: rebeccapurple;
}

Zaak om goede HTML te hebben ook; daarover later meer.

The Box Model. In CSS draait alles om boxes. Alles wat je op het scherm ziet, heeft een box. De Box Model bepaalt de maten van die box, rekeninghoudend met margins, padding en borders.

Het standaard Box Model telt de padding en border op bij de width. Hierdoor wordt de ruimte die het element inneemt groter dan de width die je eraan gegeven hebt. Er is tegenwoordig een alternatief box model die dit niet doet.

Onderstaand voorbeeld gebruikt de standaard content-box.

In de Firefox DevTools is te zien dat de padding en border aan de width worden toegevoegd. Hierdoor zou je de vergissing kunnen maken dat de <div>  300 pixels breed is, terwijl deze in werkelijkheid rendert tot 380 pixels breed.

<div class="container">
  hello box
</div>

.container {
  width: 300px;
  padding: 35px;
  margin:  10px;
  border: 5px solid gray;
} 

Padding spacing margins borders

Indien we de box model wijzigen in border-box, dan zien we dat de width wel 300px blijft:

.container {
  width: 300px;
  padding: 35px;
  margin:  10px;
  border: 5px solid gray;
  box-sizing: border-box;
}

border-box

Normal Flow (of Flow Layout) is de wijze waarop je site block en inline elementen (boxes!) toont op een pagina, voordat er wijzigingen zijn aangebracht aan hun layout. Waarbij inline elementen worden getoond in de richting waarop woorden in een zin worden getoond volgens de Writing Mode van het document. Block elementen worden na elkaar getoond zoals paragrafen in de Writing Mode van het document.

De Writing Mode is een CSS-module die internationale schrijfrichtingen bevat: van links naar rechts, van rechts naar links, gemixt of verticaal.

Met de writing-mode property kunnen we de alignment van de tekst wijzigen. In onderstaand voorbeeld worden de <p>  elementen naast elkaar getoond zoals in Chinees schrift. In het eerste <p>  element wordt de tekst van rechts naar links getoond zoals in het Arabisch:

<p class="vertical-rl">The quick brown fox</p>
<p class="vertical-lr">jumps over the lazy dog.</p>

.vertical-rl {
  writing-mode: vertical-rl;
  display: inline-block;
  max-height: 100px;
  color: blue;
}

.vertical-lr {
  writing-mode: vertical-lr;
  display: inline-block;
  max-height: 100px;
  color: red;
}

 
Chinees

Gegeven een bepaalde Writing Mode is elk onderdeel van de content van de pagina ‘in flow’: het weet van de andere onderdelen van de content en zal daar niet mee overlappen. Als een onderdeel ‘out of flow’ is, werkt het niet meer samen met de rest.

Elementen ‘in flow’ respecteren elkaars ruimte. Voor een element ‘out of flow’ is dat niet het geval. Als een element de CSS property position met een absolute waarde heeft, is deze ‘out of flow’. Zie het volgende voorbeeld:

<div class="container">
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
  <div class="item">bla bla bla</div>
</div>

.container {
  max-width: 300px;
}

.item {
  position: absolute;
  top: 30px;
  left: 40px;
  background-color: yellow;
  padding: 5px;
}

Out of flow

Een andere manier om ‘out of flow’ te raken is met de property float. Weliswaar wrapt de content van de andere elementen om het floating element heen, maar eigenlijk wordt de ruimte van het floating element niet echt gerespecteerd door de andere elementen. Dit is te zien als we de andere elementen een achtergrondkleur geven:

<div class="container">
  <div class="item">bla bla bla</div>
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>

.container {
  max-width: 300px;
    background-color: orange;
}

.item {
  float: left;
  padding: 5px;
}

float

Aangezien je als developer de spacing en overlap zelf moet regelen bij absoluut gepositioneerde en floating elementen, is het raadzaam om zoveel mogelijk met de Normal Flow mee te gaan (‘go with the flow’). In dit opzicht is een goed opgemaakt HTML-document erg belangrijk als startpunt voordat je met CSS aan de slag gaat.

Helaas hebben veel Full Stack Developers weinig kaas gegeten van HTML (4), stelt Bruce Lawson. En hij heeft enig recht van spreken, als W3C Mobile Web Applications Best Practices vertegenwoordiger van de Opera browser.

Veel developers vinden HTML een bijzaak. Zo stelt het artikel “10 things to learn for becoming a full-stack JavaScript developer“ doodleuk (5):

As for HTML, there’s not much to learn right away and you can kind of learn as you go, (…)

Het is raadzaam om HTML te valideren ter verhoging van de codekwaliteit. Analoog aan het linten van JavaScript. Echter, het belang hiervan is verminderd sinds het Parsing Algorithm van HTML5.

Dit algoritme is een stukje technisch vernuft waar weinigen bij stilstaan, inclusief ondergetekende. Alle moderne browsers construeren er dezelfde DOM mee, onafhankelijk of de HTML well-formed is of niet (6).

Goede HTML is semantische HTML

Er zijn specifieke HTML-elementen voor specifieke soorten content (‘right element to do the job’). Dus in plaats van overal <div>  elementen te gebruiken (ook wel ‘divitus’ genoemd), is het beter om een HTML-element te kiezen dat een betekenis in het document heeft.

Het document wordt hierdoor niet alleen beter leesbaar, het profiteert van standaard browsergedrag. Dit gedrag is ‘gratis’ en hoef je niet alsnog via JavaScript te coden (7).

Denk aan keyboardondersteuning van elementen, of de automatische focus op het <input>  element bij een click op het bijbehorende <label> , enz. Een interessant voorbeeld van Bruce Lawson zijn de elementen die regio’s op een pagina aanduiden: <nav> , <main> , <header>  en <footer> .

Screenreaders voor braillelezers kunnen direct naar het <main>  element springen. Dit maakt de webpagina veel sneller bruikbaar voor mensen met een visuele uitdaging. Het voorkomt immers herhaling van de informatie in de header-, footer- en menuregio’s. Een ander voorbeeld: het <nav>  element zorgt ervoor dat screenreaders direct naar het menu kunnen springen. Dus in plaats van

<div class=”nav”></div>

kunnen we ook
<nav></nav>

gebruiken.

Naast de leesbaarheid, gratis browsergedrag, usability-verbetering voor gebruikers met fysieke uitdaging en mooi meegenomen SEO-bonuspunten is semantische HTML bovenal een goed startpunt voor de styling. In bovenstaand voorbeeld kan de styling direct op het <nav> element worden toegepast. Dit maakt de CSS code minder complex, cleaner en beter onderhoudbaar.

Gegeven semantische HTML in Normal Flow, kun je vervolgens de Formatting Context van elementen aanpassen. Formatting context definieert het outer en inner display type. Dit doe je met CSS property display (8). Het outer type bepaalt het gedrag van het element in relatie tot andere elementen op de pagina (block of inline). Het inner display type bepaalt de layout van de children van het element (flow, grid of flex).

CSS Layout, tot besluit, gaat over hoe elementen / boxes gepositioneerd worden ten opzichte van de viewport en tot elkaar. Moderne layout-tools zijn Flexbox (9), Grid (10) en Multiple-column layout (11).

Conclusie

Bootstrap is een handige library, maar levert saaie web applicaties op. CCS is een krachtige taal, waardoor JavaScript soms onnodig is en je cleanere code bereikt. Om chaos en frustratie te voorkomen is kennis van de CSS-fundamentals noodzakelijk.

Clean CSS-code vereist semantische HTML. Semantische HTML is beter leesbaar en heeft tal van andere voordelen. En, als kers op de taart, leidt aandacht voor een interessantere look & feel, via CSS en HTML, ook nog eens tot een beter onderhoudbare applicatie!

 

Referenties
1) Voorbeeld van Negar Jamalifard, Third Piece of the Frontend Pie: CSS – JSworld 2021
2) smashingmagazine.com/2019/01/how-to-learn-css/
3) Zie voor een overzicht: w3schools.com/css/css_selectors.asp
4) brucelawson.co.uk/2018/the-practical-value-of-semantic-html
5) levelup.gitconnected.com/10-things-to-learn-for-becoming-a-solid-full-stack-javascript-developer-8b76467711ac
6) Zie html.spec.whatwg.org/multipage/parsing.html
7) Vaak is er geen JavaScript: kryogenix.org/code/browser/everyonehasjs.html
8) Zie voor een overzicht: developer.mozilla.org/en-US/docs/Web/CSS/display
9) developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Flexbox
10) developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Grids
11) developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Multiple-column_Layout

Leave a Reply

Your email address will not be published. Required fields are marked *