CSS Architecture for Design Systems

vi har lige oprettet et designsystem til en enorm organisation og etableret en CSS-arkitektur, vi er meget tilfredse med. Det er en af de første gange, jeg nogensinde er kommet til et projekts målstregen uden at ønske, at jeg havde gjort mindst et par ting anderledes. Så jeg troede, det ville være dejligt at dele, hvordan vi gik om at skabe vores systems CSS arkitektur.

for at give lidt af en kontekst for projektet fik vi til opgave at skabe et designsystem og stilguide, der skulle tjene organisationens tusinder af udviklere, der anvender en lang række teknologier til at opbygge deres over 500 interne internetapplikationer.

det overvældende flertal af organisationens udviklere specialiserer sig ikke i frontend-udvikling, men fokuserer snarere på applikationsprogrammering, data og logik. Fordi disse tidsstrakte udviklere har brug for at få deres apps i gang hurtigt, ville de ofte blot kopiere og indsætte frontend-kode fra andre applikationer eller nå ud til rammer som Bootstrap for at få UI-jobbet gjort. Som du måske forventer, er det kumulative resultat af disse handlinger en hodgepodge af inkongruente internetoplevelser. Selvfølgelig er det, hvad vi havde til formål at afhjælpe ved at opbygge organisationen deres egen en tankevækkende, robust UI design system.

etablere CSS-principper

i begyndelsen af projektet talte vi med udviklere om deres proces og smertepunkter og spurgte, hvordan et interface-designsystem kunne gøre deres liv lettere.

vi gennemgik og udfyldte mit spørgeskema om frontend-retningslinjer, hvilket resulterede i et sæt frontend-principper, der skulle indkapsles i systemet. Her er de CSS-specifikke principper, vi etablerede:

  • gør det modulært. – Designsystemet er modulopbygget på alle måder, hvilket i høj grad gælder for den måde, CSS er skrevet på. Der skal være klar adskillelse mellem komponenter.
  • læsbarhed er nøglen. – Udviklere skal være i stand til at forstå CSS-kode på et øjeblik og forstå formålet med en given vælger.
  • klarhed trumfer kortfattethed – designsystemet kan undertiden virke verbose, men det leverer klarhed og reslience i bytte. At holde CSS læselig og skalerbar betyder at ofre en kortere syntaks.
  • hold tingene flade – lange vælgerstrenge bør undgås, hvor det er muligt, for at holde CSS så DOM-uafhængig og modulopbygget som muligt.
  • undgå konflikter – da komponenter vil blive implementeret til mange forskellige applikationer, er det afgørende at sikre, at designsystemets CSS ikke er i konflikt med andre biblioteker og systemer. Dette opnås ved systemets navnepacing af klassenavne, beskrevet mere detaljeret nedenfor.

derfra etablerede vi konventioner og en syntaks, der omfavnede disse principper for at imødekomme udviklernes behov. Her er et kig på klassen syntaks vi kom op med:

globalt navneområde

alle klasser tilknyttet designsystemet er forud for et globalt navneområde, som er firmanavnet efterfulgt af en bindestreg:

.cn-

hvis du arbejder på en CSS-arkitektur, der kun er beregnet til at blive serveret til et enkelt sted, eller hvis du har meget kontrol over dit miljø, er det sandsynligvis unødvendigt at inkludere et globalt navneområde. Men hvis dit designsystem blander sig med andre teknologier, kan det være fornuftigt at oprette en identifikator for systemspecifik kode. Lightning Design System anvender en lignende tilgang til deres system (med præfikset .slds-), da tredjepartsudviklere bruger deres system i miljøer, som Salesforce muligvis ikke kontrollerer. I vores tilfælde bruger mange af vores klients udviklere Angular, så de allerede er bekendt med begrebet et navneområde, da Angular bruger ng- som et navneområde for Vinkelspecifik kode.

klassepræfikser

ud over et globalt navneområde tilføjede vi præfikser til hver klasse for at gøre det mere tydeligt, hvilket job klassen udfører. Her er hvilke klassepræfikser vi landede på:

jeg blev introduceret til dette koncept af Harry Roberts, og mens jeg troede, de gav mening, var jeg først lidt skeptisk, simpelthen fordi det var ekstra tegn, og jeg troede, at præfikserne faktisk kunne mindske kodelæsbarheden. Det var slet ikke tilfældet. Efter implementering af klassepræfikserne fandt vi dem yderst nyttige til at afklare hver klasses rolle og gjorde det nemt at dechiffrere en applikations kodebase med et øjeblik. Denne form for klarhed er især nyttigt for design system brugere at være i stand til nemt at gøre hoveder eller haler af ting.

bem syntaks

BEM står for “Block Element Modifier”, hvilket betyder:

denne metode har fået en masse popularitet, og at kombinere disse begreber med det globale navneområde og klassepræfikser tillod os at skabe endnu mere eksplicitte, indkapslede klassenavne.

at sætte det hele sammen: anatomi af en klasse

kombinationen af et globalt navneområde, kategoripræfikser og bem-syntaks resulterer i en eksplicit (og ja, verbose) klassestreng, der giver udviklere mulighed for at udlede, hvilket job det spiller i konstruktionen af brugergrænsefladen.

lad os se på følgende eksempel:

.cn-c-btn--secondary
  • cn- er det globale navneområde for alle stilarter, der kommer fra designsystemet.
  • c- er kategorien af klasse, som i dette tilfælde c- betyder “komponent”
  • btn er bloknavnet (“blok” er “B” i BEM)
  • --secondary er en modifikator, der angiver en stilistisk variation af blokken (“modifikator” er “M” I BEM)

her er et andet eksempel:

.cn-l-grid__item
  • cn- endnu en gang er systemets globale navneområde.
  • l- er kategorien af klasse, som i dette tilfælde l- betyder “layout”
  • grid er bloknavnet
  • __item er et element, der angiver, at dette er et barn af blokken (“Element” er “E” i BEM)

og en mere:

.cn-c-primary-nav__submenu
  • cn- er systemets globale navneområde.
  • c- er kategorien af klasse, som i dette tilfælde C- betyder “komponent”
  • primær-nav er bloknavnet
  • __submenu er et element, der indikerer, at dette er et barn af blokken (“Element” er “E” i BEM)

igen er der ingen tvivl om, at disse klasser er mere verbose end de fleste andre tilgange derude, men for dette specifikke system gav disse konventioner meget mening.

andre tricks

at være eksplicit med minutia

for at forhindre, at ting falder fra hinanden, detaljerede vi, hvordan vi håndterer mange af de mindre detaljer som Kommentarer, afstand omkring kodeblokke, faner vs mellemrum osv. Heldigvis, Harry Roberts har sammensat en fremragende og omfattende ressource kaldet CSS retningslinjer, som vi brugte som vores basislinje for denne slags konventioner. Vi kæmpede gennem alt og markerede områder, hvor vi planlagde at afvige fra Det, Harry stavede ud.

Sass parent selectors

et problem, jeg altid har haft med CSS, er at finde ud af, hvor i helvede at sætte en given regel. Hvis jeg har en primær navigationskomponent, men ønsker at justere dens justering, når den vises i en overskriftskomponent, sætter jeg disse stilarter i min overskrift eller primær navigationssass delvis? Heldigvis findes Sass parent selectors, som giver os mulighed for at holde alle komponentspecifikke stilarter under et tag:

dette betyder, at alle mine primære navigationsstile kan findes i den primære navigations Sass delvis, snarere end at opdele dem mellem flere filer.

eksplicitte regler omkring Sass nesting

Nesting i Sass kan være meget praktisk, men løber risikoen for dårlig output med alt for lange vælgerstrenge. Vi fulgte Startreglen og indlejrede aldrig Sass mere end tre lag dybt.

når vi holdt designsystemets CSS-fladhedsprincip i tankerne, ønskede vi at begrænse indlejring til følgende brugssager:

  1. modifikatorer af en stilblok
  2. medieforespørgsler
  3. Forældrevælgere
  4. Stater

1. Stilblokmodifikatorer

for modifikatorer, hvis reglen kun er et par linjer lang, kan modifikatoren indlejres inde i forælderen som sådan:

.cn-c-alert { border: 1px solid gray; color: gray; /** * Error Alert */ &--error { border-color: red; color: red; }}

takket være & symbolet vil dette kompilere til:

.cn-c-alert { border: 1px solid gray; color: gray;}.cn-c-alert--error { border-color: red; color: red;}

for længere stilblokke nestede vi ikke modifikationskoden, da den reducerede kodens læsbarhed.

2. Medieforespørgsler

Komponentspecifikke medieforespørgsler skal indlejres inde i komponentblokken.

.cn-c-primary-nav { /* Base styles */ /** * 1) On larger displays, convert to a horizontal list */ @media all and (min-width: 40em) { display: flex; }}

dette kompilerer til:

.cn-c-primary-nav { /* Base styles */}@media all and (min-width: 40em) { .cn-c-primary-nav { display: flex; }}

3. Forældrevælgere

designsystemet vil gøre brug af Sass ‘ s forældrevælgermekanisme. Dette gør det muligt at opretholde alle regler for en given komponent på et sted.

dette vil kompilere til:

.cn-c-header .cn-c-primary-nav { display: flex;}

alle stilarter for cn-c-primary-nav skal findes et sted i stedet for spredt gennem flere delvise filer.

4. Stater

stater for en komponent bør medtages som et indlejret element. Dette omfatter hover, focus og active stater:

.cn-c-btn { background: blue; &:hover, &:focus { background: red; }}

dette vil kompilere til:

.cn-c-btn { background: blue;}.cn-c-btn:hover, .cn-c-btn:focus { background: red;}

stater kan også tage form af utility klasser, såsom is- og has-:

.cn-c-accordion__panel { overflow: hidden; max-height: 0; &.cn-is-active { max-height: 40em; }}

dette vil kompilere til:

.cn-c-accordion__panel { overflow: hidden; max-height: 0;}.cn-c-accordion__panel.cn-is-active { max-height: 40em;}

at sætte disse regler på plads gav os nogle begrænsninger og konventioner, som vi var nødt til at overholde for at skabe et solidt system. Når vi løb ind i tilfælde, hvor en konvention ikke var indlysende, eller en løsning kunne håndteres på et par forskellige måder, ville vi have en samtale om, hvordan vi håndterer det og opdatere retningslinjerne, hvis det var nødvendigt.

virker dette for alle?

før du bliver helt varm og generet og begynder at være uenig i de specifikke beslutninger, vi tog for at skabe vores system, skal du erkende, at denne arkitektur gav mening for det system, vi arbejdede på. Betyder det, at det er en skudsikker løsning til hvert projekt? Nej, og det foreslår jeg ikke. De specifikke behov og opsætning af organisationen skal i høj grad påvirke dit designsystems CSS-arkitektur.

jeg arbejder på masser af projekter, hvor jeg kan klare mig med strenge som .table-of-contents li a, men disse projekter styres for det meste af mig. For klientprojekter, der involverer at arbejde i et teammiljø, er jeg meget graviterende mod mere verbose, eksplicitte syntakser som jeg beskrev ovenfor, fordi de giver mindre plads til folk til at skrue op. Det er dejligt at se andre hold som Sparkboks komme til lignende konklusioner.

efter et par uger væk fra projektet vender vi tilbage for at fortsætte arbejdet med version 1.1 af designsystemet. Jeg ser frem til at vende tilbage til denne kodebase og se, hvor hurtigt jeg kan blive akklimatiseret med det!

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.