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.
Bruker bare én enhet i CSS: «Du trenger ikke andre!»
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.
Savna Y2K-design. Nå gir han ut løsningen
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!