Møtte CSS-kaos hos kunde, men fant løsningen: Cascade Layers

– Kunden hadde en blanding av Bootstrap og custom CSS, og en salig miks av !important, skriver Eivind Egge Christensen, som måtte løse problemet i nytt designsystem.

– Som frontend-utvikler som har jobbet noen år, er det nesten uungåelig og ikke måtte krangle med spesifisitet i CSS, skriver Eivind Egge Christensen. 📸: Privat
– Som frontend-utvikler som har jobbet noen år, er det nesten uungåelig og ikke måtte krangle med spesifisitet i CSS, skriver Eivind Egge Christensen. 📸: Privat Vis mer

Som frontend-utvikler som har jobbet noen år, er det nesten uungåelig og ikke måtte krangle med spesifisitet i CSS:

  • Kort forklart er CSS-spesifisitet et sett med regler som avgjør hvilke stiler som brukes på et element når flere CSS-regler treffer samme element.
  • Dette er basert på typene selektorer som brukes, f.eks. ID (#myId), klassenavn (.myClass) og elementselektorer (<p>).
  • Inline-stiler har den høyeste spesifisiteten, etterfulgt av ID-selektorer, klasse-/attributt-/pseudo-klasse-selektorer og til slutt elementselektorer, der den mest spesifikke regelen er den som gjelder.

Dette er et tema som har vært relevant hele min karriere, og man har prøvd å finne ulike metodikker for å unngå dem.

BEM er noen kjent med, og stylelint er noe man kan bruke for å sørge for at reglene følges. Nyere teknologier som CSS-Modules og JS-rammeverk som Svelte og Vue, scoper CSSen for deg ved å lage en unik verdi der CSSen blir brukt. Tailwind er i vinden om dagen, og kan løse mange av disse problemene.

Men, det er ikke alltid slik at man, kanskje spesielt som konsulent, starte fra scratch og velge teknologi selv. Ofte går man inn i et prosjekt som har holdt på en stund, og ikke benytter de nyeste teknologiene (dessverre).

Da kan Cascade Layers være redningen.

Valgte Tailwind

Dette gjaldt oss da vi skulle utvikle og implementere et designsystem hos en kunde:

Denne kunden hadde en blanding av Bootstrap og custom CSS og en salig miks av !important. Her hadde det vært flere både konsulenter og inhouse-utviklere innom i løpet av prosjektet, og det hadde gjennom tiden blitt mange måter å skrive CSS på.

Noen ganger var det veldig spesifikke selectors, mens andre ganger var det generelle selectors som f.eks at alle SVGer skulle være 25px brede.

Vi valgte å utvikle designsystemet med Tailwind. Nettopp for å unngå mange forskjellige måter å skrive CSS på. Med et rammeverk som Tailwind, mener vi det er lettere for flere utviklere å samarbeide, uavhengig av hvor mye CSS man har skrevet gjennom karrieren og hva slags rolle man har. Vi kunne også forhåndsdefinere farger, spacing osv i configen, for å sørge for at kun disse ble brukt.

Et av målene for designsystemet, var å få så mange utviklere involvert og engasjert som mulig.

Tailwind vs. Bootstrap vs. CSS

Problemet vi støtte på da var krangling med både Bootstrap og med custom CSS:

  • Et eksempel var en Alert-komponent hvor vi har SVG for å fremheve hvilken type Alert det er.
  • SVG var stylet med bredde og høyde i både SVGen, men også med klassenavn fra Tailwind.
  • Problemet her var at en så generell selector som alle svg under klassenavnet “.main”, traff våre SVGer og overstyrte CSSen vi hadde skrevet.
  • Dette skjedde med flere av komponentene våre, samt at Bootstrap også overstyrte Tailwind-klasser. Et eksempel som ofte brukes er p-4 som i Bootstrap er 3rem, mens i Tailwind er 1rem. Ouf.

Etter litt leting på nettet, og samtaler med gode kollegaer, kom vi frem til at prefix på Tailwind-klassene ville hjelpe en del. Dette ville løse overskriving av Tailwind-klassene fra Bootstrap, men ikke nødvendigvis den custom CSSen. I eksempelet over med SVGen, ble den fortsatt overskrevet. Vi ville også unngå en !important-krig mellom all CSSen ved å sette alle Tailwind-klassene til å overskrive alt annet.

DX (developer experience) var heller ikke optimal, men med plugins ble alle Tailwind-klassene prefixet automatisk.

Cascade Layers ble redningen

Vi var ikke helt fornøyde med dette, men noen kameler må man svelge. Noen kameler er derimot litt for store, så vi stoppet ikke helt med å lete etter alternative løsninger.

Til slutt kom vi over Cascade Layers. Layers har vært baseline siden 2022 og er støttet av alle de store browserne.

Layers lar deg dele opp CSSen din i “lag” hvor du selv definerer rekkefølgen av spesifisitet på lagene, fra minst viktig til viktigst. Ofte ser man en rekkefølge som f.eks reset, base, components, og til slutt utilities.

Kodeeksempel her:

/* establish a layer order, from lowest to highest priority */
@layer reset, thirdparty, custom, components, designsystem;

/* import stylesheets into a layer */
@import url('bootstrap.css') layer(thirdparty);
@import url('designsystem.css') layer(designsystem);

@layer custom {
  .main {
	   svg {
	      width: 25px;
     }
   }
 }
 
 @layer thirdparty {
	 svg {
		 width: 1em;
	 }
 }

Sånn henger det sammen

Spesifisitet i CSS er nesten et eget fagfelt, men for å holde det kort, skal vi enkelt forklare hvordan det henger sammen:

  • Normal styles i layers vs. utenfor layers: Vanlige stilregler utenfor lag trumfer alltid vanlige stilregler definert innenfor lag.
  • !important styles i layers: Her er det motsatt. Tidligere opprettede lag med !important-styles vinner over senere lag.
  • Inline styles vs. andre styles: Vanlige inline-styles (definert direkte på elementet) trumfer alle andre vanlige stilregler, både i og utenfor lag.
  • Important inline styles: Disse har høyest prioritet av alle, bortsett fra animerte stiler. Det finnes ingen enkel måte å overstyre viktige inline-stiler på.

Hva hvis vi setter rekkefølgen til at designsystemet som viktigst, custom css som mindre viktig, og det vi kalte for thirdparty (tenk Bootstrap, andre tredjepartsbiblioteker) til minst viktig? Voila!

Cascade layers er fint illustrert i dev console, hvor man kan se hvilken stil som tilhører hvilket layer, og rekkefølgen man har satt.

Så, krangler du masse med spesifisitet, eller bare har lyst til å prøve på noe nytt, så prøv Cascade Layers i ditt neste eller nåværende prosjekt!