# Stilark

## Hva det er

Et stilark, skrevet «style sheet» i spesifikasjonene og ofte bare kalt
CSS-filen, er én samling CSS-regler som en nettleser henter, parser og bruker på
et dokument som en samlet enhet.

En side kan få stilark på fire måter. Et `link`-element med `rel="stylesheet"`
peker på en egen fil. Et `style`-element bærer reglene inne i selve HTML-en. En
`@import`-regel øverst i ett stilark trekker inn et annet. Og et skript kan lage
et med `new CSSStyleSheet()` og feste det til et dokument eller en skyggerot
gjennom `adoptedStyleSheets`. De to første ser slik ut i hodet på en side:

```html
<link rel="stylesheet" href="/css/site.css" />
<link rel="stylesheet" href="/css/print.css" media="print" />
<style>
    .banner {
        border-block-end: 2px solid;
    }
</style>
```

Forfatterens egne stilark er langt fra de eneste som er i spill. Hver nettleser
kommer med et standardstilark, og det er grunnen til at en overskrift uten stil
likevel blir stor og fet. En leser kan i tillegg legge til et eget
brukerstilark, og kaskaden veier de tre opphavene mot hverandre. Attributtet
`style` på ett enkelt element er noe annet: deklarasjoner uten selektor, som
hører til det elementet og ikke til noe stilark.

Når det er lastet, blir et stilark et `CSSStyleSheet`-objekt i CSS Object Model,
spesifikasjonen definisjonen av dette ordet er hentet fra. Objektet holder rede
på elementet som eier stilarket, mediene det gjelder for, tittelen, om det er
slått av, og listen over regler, og `document.styleSheets` lister stilarkene på
en side i rekkefølge. Ett flagg finnes av hensyn til personvernet: et stilark
hentet fra et annet opphav uten CORS regnes ikke som «origin-clean», og et
skript som ber om reglene i det, får en sikkerhetsfeil i stedet.

Attributtet `media` begrenser et stilark til ett bestemt miljø. Et stilark
merket `media="print"` lastes fortsatt ned, men nettleseren venter bare på
stilark der mediet stemmer, så et utskriftsark holder aldri skjermen igjen.

## Hvorfor det er viktig

Et stilark avgjør hva en nettleser må hente før den viser noe som helst.
HTML-standarden regner en stilarklenke som parseren finner, som blokkerende for
gjengivelsen når mediet stemmer. Den første opptegningen venter derfor til filen
er kommet fram og er parset, i stedet for å tegne siden uten stil og så tegne
den på nytt foran leseren. Størrelsen på filen, verten den ligger på og
rundturene som trengs for å oppdage den, ligger alle mellom et klikk og det
første leseren ser.

Antall filer er den første beslutningen. Én pakke som hver side deler, hentes én
gang og leveres deretter fra cachen, mens et stilark per side sender mindre i
starten og mer etter hvert som leseren beveger seg rundt. Regler lagt rett inn i
et `style`-element koster ingen ekstra forespørsel, men de reiser på nytt med
hver side, fordi de ikke kan bufres for seg, atskilt fra dokumentet som bærer
dem. Hver `@import` legger til en hel rundtur, siden nettleseren først får vite
den importerte adressen når filen som importerer, er kommet fram.

Hurtigbufring er den andre. Et stilark som får ligge i bufferen i et år, gjør
hver senere side raskere, men en endring må da nå lesere som sitter med den
gamle kopien. Det vanlige svaret er et filnavn med fingeravtrykk, der en hash av
innholdet står inne i navnet. En endret fil får da en ny adresse, og en gammel
kan bli liggende i bufferen så lenge noen vil, mens HTML-en som peker på den,
holdes kortlevd.

Tillit er den tredje. Et stilark avgjør hva som er synlig, og hvor. Det kan
skjule en advarsel, gi en knapp et helt nytt utseende eller hente bilder fra en
hvilken som helst adresse reglene navngir. Å laste et stilark fra en tredjepart
gir den makten til tredjeparten, og gir siden samtidig en vert den ikke kan vises
uten. Subresource Integrity lar siden oppgi en kryptografisk hash for filen, og
nettleseren avviser hver fil der bytene avviker.

## Slik ser det ut i praksis

Nettstedets egen stilarkpakke viser hvor langt én innebygd ressurs kan vippe
regnestykket. Med sine 87 KB var den, for et dokumentnettsted uten rammeverk,
mesteparten av vekten på en side, brukt før noe innhold var kommet fram. To
tredjedeler av det største stilarket viste seg å være én font kodet i base64:
omtrent 56 KB tekst som kodet omtrent 42 KB font. Den var bygd inn for en
gevinst ved første opptegning som en forhåndslastet ekstern fil får uansett, og
hver side betalte for den, enten vekten trengtes eller ei. Å ta den ut fikk den
filen ned fra 84 KB til 28 og
[hele pakken ned fra 87 KB til 31 KB](/nb/portfolio/cut-the-stylesheet-bundle-from-87kb-to-31kb-139/),
med fontvektene forhåndslastet i stedet. Base64 skriver hver gruppe på tre byte
som fire tegn, så en binærfil bygd inn i et stilark veier alltid omtrent en
tredjedel mer enn den samme filen levert for seg selv.

Papir er også et medium et stilark må betjene. Casestudiene på nettstedet er det
noen skriver ut og tar med inn i et møte, så
[ett stilark på 637 linjer](/nb/portfolio/built-a-637-line-print-stylesheet-136/)
med én sideregel og én utskriftsblokk sørger for at de skrives ut riktig på
ukjent papir i begge fargetemaene. Sideregelen setter en marg og bevisst ingen
papirstørrelse, og en linjelengde på rundt syttito tegn hindrer at et ark i
liggende format gir linjer på rundt hundre og ti. Fire oppførsler i
nettlesermotorene er skrevet ned som feller, og hver av dem har ført til en
reell feil: visningsområdeenheter, som én motor regner ut fra sideboksen og en
annen fra vinduet, noe som kuttet bort en tredjedel av hver linje; elementer med
fast posisjon som males på hvert eneste ark; en palett med hvit tekst som
standard for det mørke temaet, som skrev ut fire seksjonsetiketter hvitt på
hvitt; og en flex- eller grid-boks som sidedelte medier ikke kan dele, som ga et
tomt første ark. En sjekk i seks deler dekker seks kombinasjoner av papir og
retning, begge innstillingene for fargevalg og det faktiske sidetallet. Det var
den sjekken som merket det da en forvillet kommentaravslutning fikk CSS-linteren
til å svelge en hel regel og passere feilfritt to ganger.

[Slik arbeider vi sikkert](/nb/security/), nettstedets side om sikkerhet, lister
Subresource Integrity på stilarket: pakken får fingeravtrykk og hash når
nettstedet bygges, så nettleseren til leseren kan bekrefte at den kom fram
uendret. Den samme siden ber leseren telle vertene i nettverkspanelet i
nettleseren og gir svaret selv: én, nettstedets egen.

## Hvor det går galt

Et stilark nettleseren får vite om sent, kommer sent fram. En lenke nederst i
`body`, et stilark som et skript setter inn, eller en kjede av `@import`-regler
lar hver for seg siden bli tegnet før stilene er på plass, så rå tekst hopper inn
i et oppsett, eller et ferdig oppsett forskyver seg. En importkjede ser harmløs
ut i kilden:

```css
/* site.css: the browser finds base.css only after this file arrives */
@import url("base.css");
@import url("theme.css");
```

Legg stilarklenkene i hodet på siden, og flat ut importene i pakken ved bygging.

En produksjonssetting kan koble ny markering med gamle regler. Lesere som har
det forrige stilarket i cachen, får HTML som venter seg klasser den gamle filen
aldri har hørt om, og oppsettet deres er ødelagt til bufferen går ut, mens alle
som tester med en tom buffer, ser et nettsted som virker. Årsaken er et
stilarknavn som aldri endres, kombinert med lang levetid i bufferen, og et navn
med fingeravtrykk fjerner den.

Et stilark som leveres med feil innholdstype, blir oversett. HTML-standarden tar
et lenket stilark i bruk bare når det kommer som `text/css`, med ett unntak for
en fil fra samme opphav på en side i quirks-modus. En server som merker
`.css`-filer som `text/plain`, eller som svarer på en fil som mangler, med en
HTML-side og en suksesstatus, etterlater siden uten stil, mens nettverkspanelet
viser en forespørsel som tilsynelatende gikk bra. Sjekk headeren på filen slik
den ligger i produksjon.

En integritetshash blir utdatert når filen endres etter at den ble hashet: en
håndredigering på serveren, et CDN som minifiserer underveis, eller et bygg som
regner ut hashen før den siste omskrivingen. Nettleseren behandler avviket som
en nettverksfeil og viser siden uten stilarket. Fra et annet opphav krever
integritet også CORS: et `crossorigin`-attributt på lenken og en tilsvarende
header fra serveren. Regn ut hashen i det trinnet som skriver den endelige
filen.

Et stilark for et rammeverk på et offentlig CDN ble en gang forsvart med at
leserne allerede hadde det i cachen fra andre nettsteder. Fetch-standarden deler
HTTP-cachen opp etter toppnivånettsted, så hvert nettsted henter og lagrer sin
egen kopi, og det som er igjen, er en ekstra vert på den kritiske veien, styrt av
noen andre. Å levere filen fra nettstedets egen vert koster de samme bytene, men
sparer den ekstra tilkoblingen.

## Les mer

- [CSS Object Model (CSSOM)](https://www.w3.org/TR/cssom-1/#css-style-sheet)
  — hva et stilark er inne i nettleseren, og grensesnittet skript bruker til å
  lese og endre det.
- [HTML Standard: Links](https://html.spec.whatwg.org/multipage/links.html)
  — lenketypen `stylesheet`, regelen om innholdstype og når et stilark blokkerer
  gjengivelsen.
- [Subresource Integrity](https://www.w3.org/TR/SRI/)
  — attributtet `integrity` og hva nettleseren gjør med en fil som feiler
  sjekken.
- [Fetch Standard: HTTP cache partitions](https://fetch.spec.whatwg.org/#http-cache-partitions)
  — hvordan nøkkelen til cachen bygges på toppnivånettstedet.
- [CSS Paged Media Module Level 3](https://www.w3.org/TR/css-page-3/)
  — `@page`-regelen og sidemargene et utskriftsstilark arbeider med.
- [Media Queries Level 5](https://www.w3.org/TR/mediaqueries-5/)
  — print, screen, innstillingene for fargevalg og de andre betingelsene et
  `media`-attributt kan teste.

<https://engineer.company/nb/glossary/stylesheet/>
