feat(shared): add Playground design system v0.1 with Tier 1+2 components

Aksel/Digdir-aligned design system for plugin Playgrounds — visual self-service
UIs that complement terminal slash-commands. Targets ms-ai-architect, okr,
llm-security, ultraplan-local, config-audit. Built for Norwegian public sector
decision-makers plus developer power-users — one visual family, two info
densities.

Generated by claude.ai/design (Anthropic) in a dialog-based design session
driven by a comprehensive brief covering all five target plugins, Aksel/Digdir
conventions, and domain-specific visual standards (NS 5814 ROS matrices, EU AI
Act 4-tier pyramide, Doerr OKR scoring, NIST CSF, OWASP threat modeling).
Per Anthropic Consumer Terms §4, ownership of outputs is assigned to the user;
licensed MIT.

shared/playground-design-system/ (5874 lines CSS + JSON):
- tokens.css: Inter font, Digdir blue #0062BA, deuteranopia-safe severity ramp,
  distinct severity-red (#A40E26) vs failure-red (#7D1A1A), plugin scope colors,
  light + dark themes
- base.css: reset, typography (17px body, 65ch measure), focus rings, buttons,
  badges, forms, Aksel 3-tier inline messages, prefers-reduced-motion support
- components.css: Tier 1 — radar/spider, 5x5 matrix-heatmap (bottom-left
  origin, ROS/DPIA), findings-browser, critique-card, wizard/stepper,
  live-meter with antipattern lints
- components-tier2.css: Tier 2 — decision-tree, traffic-lights with rationale,
  diff-review, treemap, distribution P10/P50/P90, command-pipeline output, AI
  Act 4-color pyramide, pipeline-cockpit, verdict-pill + 5-band risk-meter,
  codepoint-reveal (Unicode steg), small-multiples grid (16-cat posture),
  OWASP badges (LLM/ASI/AST/MCP)
- print.css: A4 stylesheet with BW severity hatching, kommune-logo slot,
  signature lines for offentlige dokumenter
- schemas/: finding.schema.json, okr-set.schema.json, ros-threat.schema.json
- README.md: usage guide, design principles, component reference, provenance

shared/playground-examples/:
- index.html: system showcase with all components live
- ros-lier-kommune.html: Lier kommune Copilot ROS-rapport (Scenario A)
- okr-baerum.html: Baerum kommune T2-2026 OKR live writer (Scenario B)
- security-vegvesen.html: SVV ToxicSkills findings review, 85 funn BLOCK
  (Scenario C)
- templates.html: A4 print template demos
- ros-app.js + ros-data.js: Scenario A interactivity

WCAG 2.1 AA throughout (UU-loven krav for offentlig sektor): focus rings, ARIA
attributes, keyboard navigation, severity numerical redundancy for deuteranopia
and BW print, semantic HTML.

Known limitation: Inter loaded via Google Fonts CDN violates self-contained
no-CDN constraint. System-stack fallback works offline. Self-host woff2 files
in Phase 2.
This commit is contained in:
Kjell Tore Guttormsen 2026-05-02 06:59:19 +02:00
commit 4a2bf3567a
16 changed files with 6065 additions and 0 deletions

View file

@ -0,0 +1,126 @@
/* ros-data.js — Mock data for Lier kommune ROS, M365 Copilot Enterprise */
window.ROS_DATA = {
meta: {
id: 'ROS-2026-LIER-COPILOT-01',
system: 'M365 Copilot Enterprise (E5)',
sektor: 'kommune',
organisasjon: 'Lier kommune',
brukerantall: 1850,
dataresidens: 'EU (vurderer Sovereignty)',
oppdatert: '2026-05-01'
},
// 7-axis NS 5814 radar
radarAxes: [
{ key: 'personvern', label: 'Personvern', current: 4.2, target: 2.6 },
{ key: 'informasjonssikkerhet', label: 'Info.sikkerhet', current: 3.8, target: 2.4 },
{ key: 'dataintegritet', label: 'Dataintegritet', current: 2.9, target: 2.1 },
{ key: 'tilgjengelighet', label: 'Tilgjengelighet', current: 2.4, target: 2.0 },
{ key: 'leverandør', label: 'Leverandør', current: 3.6, target: 2.8 },
{ key: 'compliance', label: 'Compliance', current: 4.0, target: 2.2 },
{ key: 'omdomme', label: 'Omdømme', current: 3.2, target: 2.0 }
],
// 12 representative threats (rest aggregated as counts in cells)
threats: [
{ id: 'T-001', tittel: 'Eksponering av personopplysninger via Copilot Chat', sannsynlighet: 4, konsekvens: 5,
kategori: 'Personvern', kilde: 'Brukere kan ved feil dele klientdata fra arkiv inn i prompts.',
konsekvensBegrunnelse: 'Sensitive klientdata kan bli kontekst i utgående svar; brudd på taushetsplikt og GDPR Art. 5.',
sannsynlighetBegrunnelse: 'Copilot indekserer alle SharePoint-områder ansatt har tilgang til. 1 850 brukere uten Sensitivity Labels = høy treffsannsynlighet.',
mitigeringer: [
{ id: 'M-001', tittel: 'Sensitivity Labels på alle saksarkiv', status: 'planned' },
{ id: 'M-002', tittel: 'Endpoint DLP-policy for clipboard og prompt', status: 'planned' }
],
restrisiko: { sannsynlighet: 2, konsekvens: 4 }
},
{ id: 'T-002', tittel: 'Schrems II-eksponering ved cross-tenant-spørringer', sannsynlighet: 3, konsekvens: 4,
kategori: 'Compliance',
kilde: 'Web-grounded svar kan rute via amerikanske endepunkter.',
konsekvensBegrunnelse: 'Brudd på Schrems II ved overføring av personopplysninger til USA uten TIA.',
sannsynlighetBegrunnelse: 'EU Data Boundary er ikke aktivert per i dag.',
mitigeringer: [{ id: 'M-003', tittel: 'EU Data Boundary aktivert tenant-bredt', status: 'planned' }],
restrisiko: { sannsynlighet: 1, konsekvens: 4 }
},
{ id: 'T-003', tittel: 'Hallusinering i saksbehandlingsutkast', sannsynlighet: 4, konsekvens: 4,
kategori: 'Dataintegritet',
kilde: 'Copilot-genererte utkast kan inneholde påstander uten kildedekning.',
konsekvensBegrunnelse: 'Borgere får feilaktig vedtak; klagebehandling og omdømmetap.',
sannsynlighetBegrunnelse: 'Modell uten retrieval-tvang vil generere flytende, men ikke alltid faktariktige tekster.',
mitigeringer: [{ id: 'M-004', tittel: 'Obligatorisk Saksbehandler-review før utsendelse', status: 'implemented' }],
restrisiko: { sannsynlighet: 2, konsekvens: 3 }
},
{ id: 'T-007', tittel: 'Promptinjeksjon via mottatt e-post', sannsynlighet: 3, konsekvens: 5, kategori: 'Info.sikkerhet',
kilde: 'Skjult instruks i innkommende dokument kan kapre Copilot-kontekst.',
konsekvensBegrunnelse: 'Eksfiltrering eller manipulasjon av interne data.',
sannsynlighetBegrunnelse: 'Vektor er kjent (LLM01:2025). Lavt målrettet trusselbilde, men teknisk gjennomførbart.',
mitigeringer: [{ id: 'M-005', tittel: 'Defender for Cloud Apps prompt-shield', status: 'planned' }],
restrisiko: { sannsynlighet: 2, konsekvens: 4 }
},
{ id: 'T-012', tittel: 'Manglende sletting ved tjenesteslutt', sannsynlighet: 2, konsekvens: 4, kategori: 'Personvern',
kilde: 'Copilot-historikk og embeddings beholdes utover lovlig periode.',
konsekvensBegrunnelse: 'Brudd på lagringsbegrensning (GDPR Art. 5(1)(e)).',
sannsynlighetBegrunnelse: 'Default-policy er 90 dager; krav er 30.',
mitigeringer: [{ id: 'M-006', tittel: 'Purview retention policy 30 dager', status: 'proposed' }],
restrisiko: { sannsynlighet: 1, konsekvens: 3 }
},
{ id: 'T-019', tittel: 'Diskrimineringsbias i innbygger-svar', sannsynlighet: 3, konsekvens: 5, kategori: 'Compliance',
kilde: 'Ukvalifisert bruk av Copilot mot innbygger-portal.',
konsekvensBegrunnelse: 'EU AI Act Art. 5 forbud kan utløses; tilsynssak.',
sannsynlighetBegrunnelse: 'Krever direkte deployering mot publikum — i dag intern bruk, men ambisjon finnes.',
mitigeringer: [{ id: 'M-007', tittel: 'AI Act Art. 50 transparens-merking', status: 'proposed' }],
restrisiko: { sannsynlighet: 2, konsekvens: 3 }
},
{ id: 'T-022', tittel: 'Skygge-IT: alternative AI-verktøy', sannsynlighet: 4, konsekvens: 3, kategori: 'Info.sikkerhet',
kilde: 'Ansatte bruker ChatGPT/Claude for sensitive data parallelt.',
konsekvensBegrunnelse: 'Datalekkasje uten styringskontroll.',
sannsynlighetBegrunnelse: 'Allerede observert i 2 av 4 seksjoner.',
mitigeringer: [{ id: 'M-008', tittel: 'Defender web-policy + brukeropplæring', status: 'implemented' }],
restrisiko: { sannsynlighet: 2, konsekvens: 2 }
},
{ id: 'T-028', tittel: 'Avhengighet av leverandør-prising', sannsynlighet: 3, konsekvens: 3, kategori: 'Leverandør',
kilde: 'Microsoft har historisk hevet Copilot-prising på kort varsel.',
konsekvensBegrunnelse: 'Budsjettoverskridelse på 2026/2027-rammer.',
sannsynlighetBegrunnelse: 'Sannsynlig basert på 20242025 pristrend.',
mitigeringer: [{ id: 'M-009', tittel: 'Eksitstrategi vurdert i ADR', status: 'proposed' }],
restrisiko: { sannsynlighet: 2, konsekvens: 3 }
},
{ id: 'T-031', tittel: 'Audit-loggene ufullstendige', sannsynlighet: 2, konsekvens: 3, kategori: 'Info.sikkerhet',
kilde: 'Copilot-audit krever E5 Compliance-tier.',
konsekvensBegrunnelse: 'Ikke tilfredsstiller Riksrevisjonens dokumentasjonskrav.',
sannsynlighetBegrunnelse: 'E5 er på plass, men retention må konfigureres eksplisitt.',
mitigeringer: [{ id: 'M-010', tittel: 'Purview audit log 1 år', status: 'planned' }],
restrisiko: { sannsynlighet: 1, konsekvens: 2 }
},
{ id: 'T-035', tittel: 'Manglende klageadgang for AI-beslutning', sannsynlighet: 2, konsekvens: 4, kategori: 'Personvern',
kilde: 'Borgere får ikke vite at vedtak er AI-assistert.',
konsekvensBegrunnelse: 'GDPR Art. 22 / forvaltningsloven kan brytes.',
sannsynlighetBegrunnelse: 'Krever bevisst transparens-tiltak.',
mitigeringer: [{ id: 'M-011', tittel: 'Saksbehandlings-sjekkliste oppdatert', status: 'proposed' }],
restrisiko: { sannsynlighet: 1, konsekvens: 3 }
},
{ id: 'T-041', tittel: 'Tilgjengelighetsbrudd i Copilot-grensesnitt', sannsynlighet: 2, konsekvens: 2, kategori: 'Tilgjengelighet',
kilde: 'WCAG-konformitet ikke verifisert for nye Copilot-flater.',
konsekvensBegrunnelse: 'UU-tilsynet kan pålegge retting; omdømmesak.',
sannsynlighetBegrunnelse: 'Microsoft rapporterer AA-konformitet, men ikke testet i norsk språkdrakt.',
mitigeringer: [{ id: 'M-012', tittel: 'NVDA + VoiceOver pilot-test', status: 'proposed' }],
restrisiko: { sannsynlighet: 1, konsekvens: 2 }
},
{ id: 'T-047', tittel: 'Konfigurasjonsdrift mellom tenant og policy', sannsynlighet: 3, konsekvens: 3, kategori: 'Info.sikkerhet',
kilde: 'Ulike admin-er gjør usignerte endringer over tid.',
konsekvensBegrunnelse: 'Sikkerhetspolicyer eroderer; revisjonshendelser overses.',
sannsynlighetBegrunnelse: 'Standard mønster i Microsoft-tenanter med 5+ admins.',
mitigeringer: [{ id: 'M-013', tittel: 'config-audit-plugin kjørt månedlig', status: 'planned' }],
restrisiko: { sannsynlighet: 2, konsekvens: 2 }
}
],
// Distribution of all 49 threats by cell (for the matrix bubbles)
cellCounts: {
// key = "sann,kons", value = number of threats in that cell beyond the named ones
'1,1': 2, '1,2': 1, '2,1': 1, '2,2': 3, '3,1': 1, '1,3': 1,
'3,2': 2, '2,3': 4, '3,3': 3, '4,2': 1,
'2,4': 1, '4,3': 2, '3,4': 1, '4,4': 1,
'5,3': 0, '5,4': 1
}
};