21-Dec

CSS

Tre Grids for generasjon Flex

CSS Grids er nå godt støttet av alle moderne nettlesere. Hensikten med Grids er å designe layouts over to dimensjoner. Men uansett hvor mange dimensjoner, ender jeg nesten alltid opp med å ty til en Flexbox.

5 min read

·

By Kjetil Svalestuen

·

December 21, 2023

Faktisk lærte jeg Flexbox før jeg lærte meg CSS, som en del av pakken til React Native. Og når jeg begynte å jobbe med webutvikling, tok det tok ikke lang tid før du så meg Flexboxe alt.

Innføringen av Grids har endret spillereglene noe, i hvert fall på større skjermer. I følge lærebøker og bloggposter skal Grids skal løse mye av det Flexbox ikke fungerer så godt til – layouts over to dimensjoner. I praksis er det verre, og jeg opplever å fungere som en forutsigbar språkmodell. Putt meg foran et display:, og du kan vedde på at setningen sluttes med en flex.

Dette sitter dypt. Så i det siste har jeg prøvd å notere meg tilfeller der jeg først prøvde meg på en Flexbox, angret, refaktorerte koden og endte opp med en skikkelig solid Grid.

Her er tre Grids for generasjon Flexbox.

1) Kortkomponent

Her er en typisk komponent; du har et ikon, en tittel og en beskrivelse. Kall det gjerne et «kort»:

Eksempel på en kortkomponent

Jeg implementerte nylig et slikt kort, og valgte først å bruke Flexbox. Koden har du her:

Dette fungerte fint. Tilsynelatende! Kodeeksempelet over er redigerbart. Prøv å skrive en lengre tittel, og du kan se at ikonet blir bitte, bitte lite. Det viser seg nemlig at flexboxen flexer for mye! Heldigvis hadde jeg et triks på lager for å unngå ikonkrymping:

.ikon {
	flex-shrink: 0;
}

Voilà, ikonet krymper ikke lenger! Allikevel sitter jeg igjen med en bismak. For hvorfor i all verden bruker jeg Flexbox hvis jeg ender opp med å skru av selve fleksingen?

Refaktorert med Grids

Her er det samme kortet, bare skrevet med en Grid i stedet for Flexbox:

For det første, legg merke til at vi har kvittet oss med div-en som omringet tekstelementene. Færre HTML-elementer er alltid fint for lesbarheten. For det andre er ikke Griden fleksibel, så ikonet får alltid sin tildelte 1rem med bredde, uansett hvor mye tekst vi skriver inn i kortet.

Som en bonus har jeg også lagt til en gammel kjenner fra Flexbox, align-items: center, som sørger for at ikonet alltid er på linje med tittelen. Nydelig.

2) Søk og filter

Du har kanskje laget dette layoutet før. Du har en liste med ting, og trenger et søk og filtreringsmuligheter for å snevre inn treffene. Over listen ønsker du å vise antall treff.

På mobil er dette enkelt, for alt går i én kolonne. Men på større skjermer ønsker du filteret i en kolonne til venstre, og resultatlisten i midten:

Layout for søk og filter

Her er et eksempel på hvordan man kan løse dette med Flexbox. Vi legger kolonnene i hver sin div. På store skjermer plasserer vi dem ved siden av hverandre, og lar hver kolonne også være sin egen vertikale Flexbox. Endre gjerne på vindusstørrelsen for å se selv.

Legg merke til at de to kolonnene fungerer uavhengig av hverandre. Hvis vi ønsker å sørge for at filteret og listen starter på samme linje, måtte vi ha sørget for at søkefeltet og antall treff har akkurat samme høyde. Det blir fort vrient å vedlikeholde.

Refaktorert med Grids

Her er et alternativ til koden over, bare løst med Grids.

Med Grids slipper vi å opprette egne divs for hver kolonne – de er heller bygget inn i rutenettet som vi definerer med grid-template-columns.

Én utfordring med dette er at rekkefølgen på HTML-elementene blir feil, siden Grid-algoritmen fyller ut hver rad etter tur, fra venstre til høyre. For å fikse rekkefølgen bestemmer vi derfor noen grid-areas, og sier at filteret skal ligge under søkefeltet. Resten av elementene følger gridens naturlige algoritme og havner der vi forventer dem.

Som en bonus setter vi align-items: center slik at søkefeltet og «antall treff» alltid havner på samme linje. Da må vi også definere et unntak for filteret og listen, slik at de justeres til starten av raden sin.

I sum får vi omtrent like mye CSS som Flexbox-alternativet, men mindre og ryddigere HTML. Jeg begynner å like Grids!

3) Poengtavle

Idéen med denne komponenten er ganske enkel; du har en kolonne med navn til venstre, og en kolonne med poengsummer til høyre:

En poengtavle-komponent

Listen bør være kodet med semantiske listeelementer (ol og li) for å bevare tilgjengeligheten for skjermlesere. Navnet og poengsummen på hver rad bør også stå ved siden av hverandre, slik at rekkefølgen gir mening for skjermlesere.

Dette er vanskelig å løse med Flexbox. Hvis vi legger listene i én kolonne og poengsummene i en annen, bryter vi opp HTML-strukturen. Hvis vi lager én Flexbox per rad, vil de være uavhengig av hverandre, og det blir vanskelig å justere poengsummene vertikalt. Vi kan hardkode lengden på navnene, men da blir løsningen lite dynamisk for navn med ulik lengde.

Grids, derimot, løser dette kjempefint:

Her definerer vi opp et rutenett med to kolonner, og fyller hver rad med navn og poengsum. I HTML-en er disse elementene omringet av et list item-element. For å la Grid-algoritmen hoppe over denne bruker vi display: contents.

Vi kunne satt en statisk bredde på navnekolonnen. Da ville løsningen blitt omtrent som Flexbox-alternativet vi diskuterte. Men vi kan gjøre det enda bedre. Med fit-content(25%) sørger vi for at kolonnen bare tar så mye plass som det lengste navnet, men aldri mer enn en fjerdedel av komponentens bredde.

Oppsummering

Alle disse eksemplene er faktiske komponenter jeg har laget den siste måneden. Først med Flexbox, deretter refaktorert med Grids.

Men alle eksemplene går også over to dimensjoner – og det er akkurat det Grids er laget for. Sånn sett er det kanskje ikke noen overraskelse at Grids produserer ryddigere HTML, enklere CSS og færre bugs enn tilsvarende løsninger med Flexbox.

Samtidig er det innmari vrient å endre vaner som har satt seg i muskelminne!

Kanskje du er som meg, som har brukt Flexbox til alt. I så fall håper jeg disse eksemplene har inspirert deg til å prøve en Grid neste gang du skal jobbe i to dimensjoner. Lykke til!

Obs! Det er foreløbig ikke anbefalt å ha en knapp inni et element med display: contents. Se CanIuse for mer informasjon om denne feilen.