Skip to main content

ADR-DS-004: Web Components

StatusBesluttet
Statuslogg
2026-07-29Besluttet
2026-07-24Besluttet

Beslutning​

Komponentlogikk implementeres som Web Components i indeks-web og eksponeres til React-konsumenter via indeks-react. Shadow DOM unngås som hovedregel.

Eksempel: ix-field er ikke et input-felt — det er et omgivende lag som knytter label, input og feilmelding sammen med korrekte ARIA-relasjoner. Alt dette kan også gjøres med ren HTML; Web Component-en gjør det bare enklere og feilsikkert.

Shadow DOM: Vi unngår Shadow DOM som hovedregel. Shadow DOM isolerer markup fra resten av dokumentet, noe som gjør tilgjengelighetsarbeid vanskeligere — skjermlesere og ARIA-relasjoner må fungere på tvers av shadow boundaries, og konsumenter mister muligheten til å style med CSS-variabler. Unntaket er komponenter som viser visuelt innhold basert på universelt utformet data i markup — for eksempel et chart-element som rendres visuelt i Shadow DOM mens tabelldata forblir tilgjengelig i vanlig DOM.

Arkitektur: indeks-web (Web Components) → indeks-react (wrapper). React-pakken eksponerer komponentene med idiomatisk React API. Det meste av funksjonaliteten ligger i Web Component-en — React-laget håndterer React-spesifikke ting som event-mapping og prop-konvertering, slik at en ren-HTML-konsument får nøyaktig samme fullverdige komponent. Hvordan indeks-react bygges og distribueres er beskrevet i ADR-DS-005.

Bevisste rammeverk-tilpasninger: React-laget kan ha målrettede tilpasninger for å fungere godt med utbredte React-verktøy — for eksempel props som gjør en komponent kompatibel med React Hook Form (register()), slik at skjemakobling blir idiomatisk. Slike tilpasninger handler utelukkende om integrasjon — å få React til å fungere — aldri om å gi React-konsumenter mer funksjonalitet eller en annen brukeropplevelse.

UI og UX skal være identisk uansett konsument. En bruker skal ikke kunne se eller kjenne — verken visuelt eller via tastatur og skjermleser — om et felt er bygget med ren HTML eller React. HTML-versjonen av hver komponent er fullverdig i seg selv; React-wrapperen legger ikke til noe ekstra, den kabler bare det som allerede finnes lenger ned i stacken. Det som skal skille seg, er API-flaten: React får idiomatiske props, typer og JSX, mens HTML får attributter og web component. Ulikt grensesnitt, lik komponent.

Tilpasninger er tillatt så lenge to ting holder: (1) Web Component-en forblir kilden til sannhet — logikken legges i WC-en når den også gagner ikke-React-konsumenter (f.eks. native change-events og fokushåndtering på riktig element), og React-laget begrenser seg til tynn kabling; og (2) HTML-pariteten bevares — en ren-HTML- eller Vue/Angular-konsument får samme fullverdige komponent, ikke en redusert variant. En React-only-egenskap som ikke lar seg uttrykke i WC-en, skal være liten, veldokumentert og ikke bryte de andre konsumentene.

Drivere for beslutningen​

  • Rammeverk-uavhengighet — webstandard fungerer i React, Vue, Angular, Astro og ren HTML
  • Én kilde til sannhet for komponentlogikk
  • Fremtidssikring uavhengig av rammeverk-trender
  • Nettleser-native uten ekstra runtime

Bakgrunn​

De fleste komponentene i Indeks er ren HTML-markup med CSS-styling — React wrapper bare dette, og man kan like gjerne bruke ren HTML. Men noen komponenter trenger mer: logikk, tilstandshåndtering eller sammensatt atferd som knytter flere elementer sammen.

SpareBank 1 har team som bruker ulike teknologier — ikke bare React. En løsning som låser kompleks komponentlogikk til React er ikke fremtidssikret.

Problemstilling​

Hvordan implementerer vi komponentlogikk slik at den er tilgjengelig for alle team, uavhengig av hvilke JavaScript-rammeverk de bruker?

Konsekvenser​

Hvem påvirkes?​

  • Team Designsystem — implementerer Web Components
  • React-konsumenter — bruker React-wrapperen og slipper å forholde seg til maskineriet under; for team som vil bygge frontend uten å tenke på hvordan det fungerer, er dette et godt valg
  • Andre team — får tilgang til komponentlogikk uten React-avhengighet; team som bruker andre rammeverk eller vil tilpasse mer, ser naturlig mer av kompleksiteten bak — men får samme komponent
  • Designere — ingen endring

Abstraksjonsnivået skalerer altså med hvor mye konsumenten vil involvere seg: React skjuler mest, ren HTML og andre rammeverk eksponerer mer av maskineriet. Uansett nivå er komponenten — og brukeropplevelsen — den samme.

Ulemper​

  • SSR: customElements.define() er en nettleser-API som ikke finnes i Node.js. Vi har testet SSR i de tilfellene vi har prøvd, og det har fungert godt — men vi har ikke verifisert alle rammeverk (Next.js o.l.) uttømmende
  • Team Designsystem må beherske Web Components-standarden (Custom Elements API) — i praksis en liten kostnad, siden komponentene vi skriver er forholdsvis enkle og konsumenter merker lite (de bruker React-wrapperen eller ren HTML)
  • Konsumenter er selv ansvarlige for å laste indeks-web én gang — dette forhindrer kollisjoner ved registrering av custom elements

Tiltak mot ulemper​

  • Etablere klare mønstre for Web Components-utvikling tidlig
  • SSR-støtte må kartlegges per komponent — dette er et åpent spørsmål som vil bli adressert når aktuelle komponenter nærmer seg produksjon

Forkastede alternativer​

Kun React-komponenter​

Fortsette med React-only komponenter for all logikk.

Forkastet fordi: Begrenser gjenbruk til React-prosjekter. Vi skal støtte team som bruker andre teknologier enn React, og disse teamene ville vært utestengt fra de mest komplekse komponentene.

Lit​

Et lett bibliotek fra Google for å skrive Web Components med deklarativ template-syntaks og reaktivitetssystem. Brukes av blant andre Adobe (Spectrum), SAP og ING.

Forkastet fordi: Vi ønsker å holde indeks-web fri for eksterne bibliotek- og rammeverk-avhengigheter. Lits verdi ligger i deklarativ templating og reaktiv rendering inne i komponenten — noe vi ikke trenger, fordi vi ikke bruker Shadow DOM. Våre Web Components er tynne wrappere som manipulerer lys-DOM direkte, så native Custom Elements er håndterbart for komponentene vi faktisk skriver. Vi ser også til designsystemet.no som gjør det samme med native Web Components — det gir oss mulighet til å lære av og bygge videre på deres erfaringer.

Stencil.js (Ionic)​

Et kompilatorbasert rammeverk for Web Components med TypeScript-first API, JSX-syntaks og innebygd støtte for å generere React- og Vue-wrappers automatisk.

Forkastet fordi: Introduserer et kompilatorlag og en egen komponentmodell som gjør koden avhengig av Stencil — native Custom Elements er håndterbart uten dette. Automatisk wrapper-generering er en fordel vi ikke trenger siden vi uansett skriver React-wrapperen manuelt.

FAST (Microsoft)​

Et bibliotek fra Microsoft for å bygge Web Components, brukt som fundament for Fluent UI Web Components.

Forkastet fordi: Samme begrunnelse som Lit — vi ønsker å holde indeks-web fri for eksterne avhengigheter. FAST introduserer i tillegg en egen komponentmodell og attributt-system som binder komponentene tettere til biblioteket enn native Custom Elements.

Multi-framework med delt TypeScript-kjerne​

Dele forretningslogikk i et rammeverk-agnostisk TypeScript-bibliotek, og skrive rammeverk-spesifikke wrappers for React, Vue og Angular.

Forkastet fordi: Kompleks build-pipeline med mange wrappers å vedlikeholde. Fortsatt rammeverk-spesifikk kode i alle wrappers. Web Components løser det samme med én implementasjon.

Mitosis (Builder.io)​

Et verktøy som kompilerer én komponentdefinisjon til React, Vue, Angular og Web Components.

Forkastet fordi: Umodent verktøy med begrenset adopsjon. Abstraksjonslaget over komponentdefinisjonene gjør feilsøking vanskeligere, og output-koden er ikke idiomatisk i noe av målrammeverket.

Deltakere

Utarbeidet avTeam Designsystem