CSS Architecture for Design Systems

we hebben zojuist een ontwerpsysteem voor een grote organisatie gemaakt en een CSS-architectuur opgezet waar we erg blij mee zijn. Het is een van de eerste keren dat ik ooit heb gekregen aan de finish van een project zonder te wensen dat ik had gedaan op zijn minst een paar dingen anders. Dus ik dacht dat het geweldig zou zijn om te delen hoe we gingen over het creëren van ons systeem CSS architectuur.

om een beetje context voor het project te geven, kregen we de opdracht om een ontwerp-systeem en stijlgids te maken die bedoeld waren om de duizenden ontwikkelaars van de organisatie te dienen, die een breed scala aan technologieën gebruiken om hun meer dan 500 Interne webapplicaties te bouwen.

de overgrote meerderheid van de ontwikkelaars van de organisatie is niet gespecialiseerd in frontend-ontwikkeling, maar richt zich eerder op applicatieprogrammering, data en logica. Omdat deze tijd-gestrekte ontwikkelaars nodig hebben om hun apps up and running snel, ze zouden vaak gewoon kopiëren en plakken frontend code van andere toepassingen of bereiken voor frameworks zoals Bootstrap om de UI klus te klaren. Zoals je zou verwachten, het cumulatieve resultaat van deze acties is een mengelmoes van incongruente web ervaringen. Natuurlijk is dit wat we probeerden te verhelpen door het bouwen van de organisatie hun eigen een doordachte, robuuste UI design systeem.

vaststellen van CSS-principes

aan het begin van het project spraken we met ontwikkelaars over hun proces en pijnpunten, en vroegen we hoe een interface-ontwerpsysteem hun leven gemakkelijker zou kunnen maken.

we gingen door en vulden mijn frontend guidelines questionnaire in, wat resulteerde in een reeks frontend principes die binnen het systeem moesten worden ingekapseld. Hier zijn de CSS-specifieke principes die we hebben vastgesteld:

  • maak het modulair. – Het ontwerpsysteem is in alle opzichten modulair, wat zeer van toepassing is op de manier waarop CSS wordt geschreven. Er moet een duidelijke scheiding tussen de componenten zijn.
  • leesbaarheid is de sleutel. – Ontwikkelaars moeten in staat zijn om CSS code te begrijpen in een oogopslag en begrijpen het doel van een bepaalde selector.
  • helderheid is beter dan beknoptheid-het ontwerpsysteem kan soms uitgebreid lijken, maar het levert in ruil duidelijkheid en betrouwbaarheid. CSS leesbaar en schaalbaar houden betekent een kortere syntaxis opofferen.
  • Houd dingen plat – lange selector strings moeten waar mogelijk worden vermeden om CSS zo DOM-onafhankelijk en modulair mogelijk te houden.
  • vermijd conflicten – omdat componenten in veel verschillende toepassingen zullen worden geïmplementeerd, is het van cruciaal belang om ervoor te zorgen dat de CSS van het ontwerpsysteem niet in conflict komt met andere bibliotheken en systemen. Dit wordt bereikt door het systeem namespacing van klassenamen, hieronder meer in detail beschreven.

van daaruit hebben we conventies en een syntaxis opgesteld die deze principes omarmden om aan de behoeften van ontwikkelaars te voldoen. Hier is een blik op de class syntaxis we bedachten met:

globale naamruimte

alle klassen geassocieerd met het ontwerpsysteem worden voorafgegaan door een globale naamruimte, de bedrijfsnaam gevolgd door een koppelteken:

.cn-

als je werkt aan een CSS architectuur die alleen bedoeld is om te worden geserveerd aan een enkele site of als je veel controle over uw omgeving, met inbegrip van een globale naamruimte is waarschijnlijk onnodig. Maar als je ontwerpsysteem zich vermengt met andere technologieën, is het misschien zinvol om een identifier te maken voor systeemspecifieke code. Lightning Design System maakt gebruik van een soortgelijke aanpak voor hun systeem (met het voorvoegsel .slds-) als derden ontwikkelaars gebruik maken van hun systeem in omgevingen waar Salesforce mogelijk geen controle over heeft. In ons geval gebruiken veel ontwikkelaars van onze client Angular, zodat ze al bekend zijn met de notie van een naamruimte, omdat Angular ng- gebruikt als een naamruimte voor Hoekspecifieke code.

Klasse-voorvoegsels

naast een globale naamruimte hebben we voorvoegsels toegevoegd aan elke klasse om duidelijker te maken welke taak die klasse uitvoert. Hier is op welke klassenvoorvoegsels we geland zijn:

ik werd in kennis gesteld van dit concept door Harry Roberts, en hoewel ik dacht dat ze zinvol waren, was ik in het begin een beetje sceptisch, gewoon omdat het extra karakters waren en ik dacht dat de voorvoegsels de leesbaarheid van de code zouden kunnen verminderen. Dat was helemaal niet het geval. Na het implementeren van de class voorvoegsels, vonden we ze zeer nuttig voor het verduidelijken van de rol van elke klasse en maakte het gemakkelijk om een toepassing codebase ontcijferen in een oogopslag. Dit soort duidelijkheid is vooral nuttig voor het ontwerp systeem gebruikers in staat zijn om gemakkelijk kop of staart van dingen te maken.

bem syntaxis

BEM staat voor” Block Element Modifier”, wat betekent:

deze methodologie is steeds populairder geworden, en door deze concepten te combineren met de Globale naamruimte en class-voorvoegsels konden we nog explicietere, ingekapselde klassenamen maken.

alles bij elkaar: anatomy of a class

de combinatie van een globale naamruimte, categorievoorvoegsels en bem-syntaxis resulteert in een expliciete (en ja, uitgebreide) class string die ontwikkelaars in staat stelt om te bepalen welke taak het speelt bij het construeren van de gebruikersinterface.

laten we eens kijken naar het volgende voorbeeld:

.cn-c-btn--secondary
  • cn- is de Globale naamruimte voor alle stijlen afkomstig van het ontwerpsysteem.
  • c- is de categorie van klasse, die in dit geval c- betekent “component””
  • btn is de bloknaam (“blok “is de” B ” in BEM)
  • --secondary is een modifier, die een stilistische variatie van het blok aangeeft (“Modifier” is de “M” in BEM)

hier is een ander voorbeeld:

.cn-l-grid__item
  • cn- opnieuw is de Globale naamruimte van het systeem.
  • l- is de categorie van klasse, wat in dit geval l- betekent “lay-out””
  • grid is de bloknaam
  • __item is een element, dat aangeeft dat dit een kind van het blok is (“Element” is de “E” in BEM)

en nog een:

.cn-c-primary-nav__submenu
  • cn- is de Globale naamruimte van het systeem.
  • c- is de categorie van klasse, die in dit geval C- betekent “component”
  • primaire-nav is de bloknaam
  • __submenu is een element, wat aangeeft dat dit een kind van het blok is (“Element” is de “E” in BEM)

nogmaals, er is geen twijfel dat deze klassen uitgebreider zijn dan de meeste andere benaderingen die er zijn, maar voor dit specifieke systeem waren deze conventies erg zinvol.

andere trucs

expliciet zijn met minutia

om te voorkomen dat dingen uit elkaar vallen, hebben we gedetailleerd beschreven hoe veel van de kleine details te behandelen, zoals commentaren, spatiëring rond codeblokken, tabs vs spaties, enz. Gelukkig, Harry Roberts heeft samen een uitstekende en uitgebreide bron genaamd CSS richtlijnen, die we gebruikten als onze basislijn voor dit soort conventies. We hebben alles uitgekamd en gebieden gemarkeerd waar we van plan waren af te wijken van wat Harry zei.

Sass parent selectors

een probleem dat ik altijd heb gehad met CSS is uitzoeken waar de hel om een bepaalde regel te zetten. Als ik een primaire navigatiecomponent heb, maar de uitlijning ervan wil aanpassen wanneer deze binnen een koptekstcomponent verschijnt, zet ik die stijlen dan in mijn koptekst of primaire navigatie Sass partial? Gelukkig bestaan er Sass parent selectors, wat ons in staat stelt om alle componentspecifieke stijlen onder één dak te houden:

dit betekent dat al mijn primaire navigatiestijlen gevonden kunnen worden in de primaire navigatie Sass partial, in plaats van ze te splitsen tussen meerdere bestanden.

expliciete regels rond Sass nesten

nesten in Sass kan erg handig zijn, maar loopt het risico van slechte uitvoer met te lange selector strings. We volgden de Inception regel en nooit genest Sass meer dan drie lagen diep.

rekening houdend met het CSS-vlakheidsprincipe van het ontwerpsysteem, wilden we de nesten beperken tot de volgende use cases:

  1. Modifiers van een stijlblok
  2. mediaqueries
  3. Ouderkiezers
  4. Staten

1. Style block modifiers

voor modifiers, als de regel slechts een paar regels lang is, kan de modifier worden genest in de ouder zoals dit:

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

dankzij het & symbool, zou dit compileren naar:

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

voor langere stijl blokken hebben we niet nest de modifier code als het verminderde de leesbaarheid van de code.

2. Mediavragen

Componentspecifieke mediavragen MOETEN in het componentblok worden genest.

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

dit compileert naar:

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

3. Parent selector

het ontwerpsysteem zal gebruik maken van het parent selector mechanisme van Sass. Hierdoor kunnen alle regels voor een bepaald onderdeel op één locatie worden gehandhaafd.

dit compileert naar:

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

alle stijlen voor cn-c-primary-nav moeten op één plaats gevonden worden, in plaats van verspreid over meerdere gedeeltelijke bestanden.

4. Staten

Staten van een component moeten worden opgenomen als een genest element. Dit omvat de staten hover, focus en active :

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

dit zal compileren naar:

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

Staten kunnen ook de vorm aannemen van nutsklassen, zoals is- en has-:

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

dit zal compileren naar:

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

de invoering van deze regels gaf ons een aantal beperkingen en conventies die we moesten naleven om een solide systeem te creëren. Wanneer we in gevallen waarin een conventie was niet voor de hand liggend of een oplossing kan worden behandeld op een paar verschillende manieren, zouden we een gesprek over hoe om te gaan met het en update de richtlijnen indien nodig.

werkt dit voor iedereen?

voordat u helemaal opgewonden raakt en het oneens bent met de specifieke beslissingen die we hebben genomen bij het maken van ons systeem, moet u erkennen dat deze architectuur zinvol was voor het systeem waaraan we werkten. Betekent dit dat het een kogelvrije oplossing is voor elk project? Nee, en dat stel ik niet voor. De specifieke behoeften en opzet van de organisatie zouden de CSS-architectuur van uw ontwerpsysteem sterk moeten beïnvloeden.

ik werk aan tal van projecten waar ik het met strings als .table-of-contents li a kan redden, maar die projecten worden meestal door mij beheerd. Voor clientprojecten die werken in een teamomgeving, ik ben zeer sterk aangetrokken tot meer uitgebreide, expliciete syntaxen zoals ik hierboven beschreven, omdat ze minder ruimte voor mensen om dingen te verpesten. Het is geweldig om te zien dat andere teams zoals Sparkbox tot soortgelijke conclusies komen.

na een paar weken van het project, keren we terug om verder te werken aan Versie 1.1 van het ontwerpsysteem. Ik kijk ernaar uit om terug te komen naar deze code base en te zien hoe snel ik kan opnieuw wennen met het!

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.