Gå til indholdet

ADR-002 — Design-system: UI/UX-refresh gennem MUI 6-themet

Status: GODKENDT 2026-06-11 — med tillæg (se nederst): ambitionsniveauet er hævet fra token-konvertering til egentlig modernisering pr. view. Dato: 2026-06-11 Forudsætning: MUI 6 er etableret; refreshet sker GENNEM theme-systemet (palette, typography, shape, komponent-overrides i ét centralt theme) — IKKE et nyt UI-bibliotek.


Baggrund og problem

Der findes allerede et centralt theme (frontend/src/utils/theme.ts: mint-grøn primærfarve #7be6c9, Inter, pill-knapper) — men det bliver undermineret i praksis:

Måling (main, 2026-06-11) Antal
Filer med hardcodede hex-farver 26
Inline linear-gradient(...)-forekomster 76
Lokale farve-arrays (AVATAR_COLORS, CLASS_COLORS, SUBJECT_COLORS) 4+ kopier
Ekstra ad-hoc-theme (maintenanceTheme i App.tsx) 1 dublet

Konsekvenser: dark mode er generisk (#121212) selvom kortene har deres egen mørkegrønne identitet (#0d1f1a-familien); samme CTA-gradient er copy-pastet 30+ steder med småvariationer; workroom'en (fuldskærm uden for MainLayout) hardcoder sin baggrund (#f5f5f5) og styles uden tokens; og flere views (login, admin) ligner standard-MUI-skabeloner.

Beslutning 1 — Tokens: én sandhed i utils/theme.ts

Den eksisterende visuelle identitet kodificeres (vi opfinder ikke et nyt udtryk — vi gør det konsistent og tilgængeligt via theme).

Palette

// Brand (uændret identitet, defineret ét sted)
mint:        #7be6c9   // primary.main  (CTA, aktive tilstande)
mintDark:    #5ac3a7   // primary.dark
teal:        #0d9488   // secondary.main (avatars, accenter — findes allerede)
inkLight:    #2d3748 / #718096   // text primary/secondary (lys)
// Dark mode: den mørkegrønne identitet fra kortene gøres global
darkBg:      #0b1512   // background.default (i dag generisk #121212)
darkPaper:   #11201b   // background.paper   (afledt af kortenes #0d1f1a)
mintOnDark:  #7be6c9   // primær på mørk — kontrast AA mod darkPaper

Semantiske farver fastlåses (success/warning/error/info = MUI-baseline, men altid via theme.palette.* — aldrig hex).

Custom theme-felter (module augmentation)

Alt det, der i dag tvinger folk til hex, bliver theme-tokens:

declare module '@mui/material/styles' {
  interface Palette {
    gradients: { brand: string; brandHover: string; accent: string; accentHover: string };
    subjects: string[];   // 10-farvers kategorisk skala (én kilde — i dag 4 kopier)
    surface: { sunken: string; raised: string; overlay: string };
  }
}
  • gradients.brand = mint-CTA'en (#7be6c9 → #6bd4b8), accent = chat-lilla (#667eea → #764ba2). De 76 inline-gradients erstattes af disse to + hover-varianter.
  • subjects = den kategoriske 10-farveskala fra StudentAssignment — bruges af fag-chips, mapper, avatars og grafer.

Typography, shape og spacing

  • Inter (allerede valgt). Skala strammes: h1 2.25rem/800 → caption 0.75rem; button uden uppercase (som i dag); tal i tabeller/statistik får fontVariantNumeric: tabular-nums.
  • Radius-tokens: 12 px (inputs/knapper/små chips), 16 px (kort/paper), 24 px (pill-CTA). shape.borderRadius = 12 som base.
  • Spacing: 8 px-grid (MUI-default) håndhæves — ingen px-magi i sx.

Beslutning 2 — Komponent-overrides (det anti-skabelon-agtige lag)

Ét sted i getThemeOptions, så hver view slipper for sx-rod:

Komponent Override
MuiButton Pill-radius; ny variant gradient (brand-CTA — erstatter alle inline-gradient-knapper); size="small" strammere
MuiCard/MuiPaper 16 px radius; lys: hårfin kant + ingen skygge; mørk: darkPaper + subtil mint-kant ved hover; venstre accent-kant via ny prop-variant accent (gruppe-/dokumentkort)
MuiChip Status-varianter som theme-variants: status-draft, status-submitted, status-locked, status-planned, status-help — i dag håndbygges de i 6+ filer
MuiDialog 16 px radius, ensartet padding, titel-typografi
MuiTextField/MuiSelect 12 px radius, ens label/helper-stil — fjerner inputBgSx-hacket i wizard'en
MuiAlert Radius + ikon-farver via semantik (help-flag/finish-bannere)
MuiSkeleton, MuiTooltip, MuiTableCell, MuiTab(s), MuiStepper Ensretning (mint-indikator, head-celler, wave-skeletons)
MuiCssBaseline Global scrollbar-styling + selection-farve (små detaljer, stor "ikke-skabelon"-effekt)

Regel (håndhæves i review + CI-grep): komponenter må ikke indeholde hex-farver eller linear-gradient — kun theme.palette.*-referencer. sx er til layout (flex/grid/spacing), ikke til farver/typografi. CI får et grep-step der fejler på nye #[0-9a-f]{6}/linear-gradient i frontend/src (med en kort allowlist for SVG-assets).

Beslutning 3 — Fælles state-komponenter

Nyt frontend/src/components/feedback/:

  • EmptyState (ikon + titel + undertekst + valgfri CTA) — bruges i alle lister/mapper/tabeller (i dag: blandede ad-hoc-Papers og rå tekst)
  • ErrorState (besked + "Prøv igen"-retry-callback)
  • LoadingState (eksisterende skeletons genbruges; ny generisk side-skeleton til admin-siderne der mangler)

Standard: hvert view har eksplicit loading- (skeleton), empty- og error-state. Ingen view må rendere "tomt hvidt" eller en rå fejlstreng.

Beslutning 4 — Responsivitet (tablet + desktop)

Mål: iPad portræt (768) og landskab (1024) + desktop. Mobil holdes fungerende (eksisterende isMobile-greb beholdes), men optimeres ikke.

  • Breakpoint-konvention: md = tablet, lg+ = desktop.
  • Dashboard-/oversigtsgrids: 1 kolonne < md, 2 ved md, 3 ved lg.
  • Wizard: Stepper vandret ≥ md, kompakt (kun aktiv label) < md; fordelings-boardet wrapper kolonner ved md.
  • Workroom: dokumentgrid 2 kolonner på md (i dag spring fra 1 til 4); topbar-knapper kollapser til ikon-knapper < lg.
  • Tabeller (admin/klassepaneler): vandret scroll-container ved < lg i stedet for klemte kolonner.

Beslutning 5 — Udrulning view-for-view (én MR pr. view-gruppe)

MR Omfang Nøglepunkter
A1 — Fundament + login + skal utils/theme.ts (tokens/overrides/augmentation), fjern maintenanceTheme-dubletten, components/feedback/*, login-siden, MainLayout/TopBar/SideNav Login er førstehåndsindtrykket — væk fra skabelon-look: brand-panel + formular, gradient-CTA, fejl-states
A2 — Dashboards Lærer-dashboard (overblik, help-flags, kommende frister), elev-forside Help-flag-sektionen som tydelig prioritet; "kommende frister"-liste (data findes via assignments); stat-kort på tokens
A3 — Grupper & forløb TeacherGroups (lukkede mapper), StudentGroups, GroupCard, GroupDetails, ClassDetails-paneler Mappe-hierarkiet (hold→fag→forløb) får ens ikonografi/indrykning; status-chips via theme-varianter
A4 — Oprettelses-wizard views/CreateGroup + creation/* Fjern inputBgSx-hacket; trin-layout på grid; GroupBuilder-boardet på tokens; sekventiel picker poleres
A5 — Workroom & dokumenter WorkRoomFolderView, OnlyOfficeWorkspace, WorkRoomChat Kører fuldskærm UDEN FOR MainLayout — al styling skal via theme (i dag hardcodet #f5f5f5-baggrund, gradient-knapper, chat-lilla); editor-topbar og chat-overlay på tokens
A6 — Admin + aktivitetslog Reports, UserManagement, Tenant*, SubjectManagement, ActivityTimeline/Feed, UserProfileView Mest "rå MUI" i dag — tabeller, tomme states og skeletons mangler flere steder

Hver MR: (1) erstat hardcodede farver/gradients med tokens i de berørte filer, (2) tilføj manglende loading/empty/error-states, (3) verificér md/lg-breakpoints, (4) visuel røgtest i begge roller + begge themes på review-appen. CI-grep-checket (Beslutning 2) aktiveres i sidste MR (A6), når alle filer er konverteret.

Forkastede alternativer

  • Nyt UI-bibliotek / Tailwind: eksplicit udelukket af opgaven, og unødvendigt — identiteten findes, den skal bare samles.
  • CSS-variabler ved siden af MUI: to kilder til sandhed; MUI 6's theme + module augmentation dækker behovet typed.
  • Big-bang-omskrivning i én MR: urevidérbart; view-for-view giver testbare, små diffs og en review-app pr. trin.

Konsekvenser

  • ✅ Én kilde til farver/typografi/radius; dark mode bliver en ægte identitet i stedet for gråt standard-MUI.
  • ✅ Status-chips, gradient-CTA'er og fag-farver bliver konsistente og genbrugelige via theme — nye views kan ikke "komme til" at afvige.
  • ⚠️ variant="gradient" på Button og chip-varianterne kræver TS-module- augmentation — defineres i A1 og er derefter typed.
  • ⚠️ Visuelle regressioner er muligt — afbødes af view-for-view-MR'er med røgtest pr. rolle/theme på review-apps, og af at INGEN funktionel kode ændres (kun styling/states).

Dokumentation

docs/design-system.md oprettes i A1 og vedligeholdes pr. MR: tokens, komponent-varianter, state-standarder, responsive konventioner og "sådan style du et nyt view"-guide.


Tillæg (godkendt 2026-06-11): Modernisering, ikke kun konvertering

Token-disciplinen, MR-opdelingen (A1–A6) og "ingen funktionel kode ændres" fastholdes — men hvert view skal MODERNISERES. Designretning for alle MR'er:

  1. Login (A1) redesignes fra bunden: split-layout med brand-panel (mørkegrøn flade med subtilt mønster/gradient i mint, produktnavn, én sætning om produktet) + ren formular-side. Store inputs, gradient-CTA, inline-validering med tydelige fejl-states, loading-state på knappen. Benchmark: moderne SaaS-logins (Linear/Notion-klassen) — ikke MUI-demo.
  2. Rum og hierarki: mere whitespace, større sektionsoverskrifter, færre rammer — adskil med spacing og baggrundsnuancer (surface.sunken/raised) frem for borders overalt. Maks. én primær CTA pr. view.
  3. Dybde og bevægelse: subtile hover-løft på kort (transform + skygge via theme), bløde transitions (150–200 ms) på interaktive elementer, Fade/Grow på dialoger og lister. Ingen tunge animationsbiblioteker — MUI's transitions rækker. Respektér prefers-reduced-motion.
  4. Dashboards (A2): moderne app-forside — hilsen + dato, stat-kort med ikon og trend, help-flags som prioriteret sektion øverst, "kommende frister" som tidslinje frem for tabel. Tomme dashboards skal se designede ud (EmptyState med illustration/ikon, ikke bare tekst).
  5. Lister og kort (A3): tydeligt visuelt hierarki: fag-farve-accent, status-chip, deadline med relativ tid ("om 3 dage", rød < 24 t), medlems-avatars som stack. Mapper (lukkede som default) med smooth expand/collapse.
  6. Wizard (A4): trin-fremdrift tydelig, ét spørgsmål i fokus ad gangen, opsummerings-trin til sidst før oprettelse.
  7. Workroom (A5): editor-topbar slankes (ikon-knapper med tooltips), chat-overlay moderniseres (bobler, avatars, timestamps grupperet), tydelig LOCKED/SUBMITTED-banner-tilstand.
  8. Admin (A6): tabeller med sticky header, zebra via surface-nuance, row-hover, og filtre samlet i én toolbar.

Anti-mål: ingen glassmorphism/neon/dribbble-effekter, ingen nye farver uden for paletten, ingen dekorative animationer der ikke kommunikerer tilstand. Moderne = roligt, rummeligt, hurtigt — ikke pyntet. Alt skal bestå CI-grep'et og AA-kontrast i begge themes.

Proces: pr. MR vises screenshot (review-app, lys + mørk) af det vigtigste view FØR merge.