* docs(naming): drop scrum4me- prefix from doc filenames Rename 10 docs/scrum4me-*.md files to unprefixed kebab-case names. Update every internal link in docs/, CLAUDE.md, AGENTS.md, README.md. * docs(naming): lowercase API.md and MD3 filenames Rename docs/API.md → docs/api.md and docs/MD3_Color_Scheme_Documentation.md → docs/md3-color-scheme.md. Update all internal links across 7 files. * docs(naming): rename plan file to kebab-case ASCII Rename "docs/plans/Tweede Claude Agent — Planning Agent.md" → docs/plans/tweede-claude-agent-planning.md. No external links needed updating. * docs(naming): rename middleware.md to proxy.md (next 16) docs/patterns/middleware.md → docs/patterns/proxy.md following the Next.js 16 proxy.ts rename. Update link in CLAUDE.md. * docs(naming): polish CLAUDE.md doc-index after renames Fix doubled scrum4me-scrum4me-mcp repo references (cascade from prior sed) in CLAUDE.md, docs/architecture.md, backlog.md, agent-instruction-audit.md, and plans/ST-1109. Update 'Middleware' label to 'Proxy middleware' in patterns table.
670 lines
18 KiB
Markdown
670 lines
18 KiB
Markdown
# Scrum4Me — Styling & Design System
|
|
|
|
**Versie:** 0.1 — april 2026
|
|
**Onderdeel van:** CLAUDE.md context-set
|
|
|
|
---
|
|
|
|
## Overzicht
|
|
|
|
Scrum4Me gebruikt **Material Design 3 (MD3)** als kleurfilosofie, geïmplementeerd via CSS custom properties in `theme.css` en direct bruikbaar als Tailwind utility classes. **shadcn/ui** levert alle UI-primitieven (Button, Dialog, Sheet, Badge, etc.) en is volledig compatibel met het MD3-kleurensysteem via de legacy-token-mapping.
|
|
|
|
Lees dit document voordat je een component schrijft. Gebruik **nooit** willekeurige Tailwind-kleuren zoals `bg-blue-500` of `bg-green-600` — gebruik altijd de semantische tokens uit dit systeem.
|
|
|
|
---
|
|
|
|
## Setup
|
|
|
|
### 1. theme.css plaatsen
|
|
|
|
Kopieer het meegeleverde `theme.css` bestand naar:
|
|
|
|
```
|
|
app/globals.css ← importeer theme.css hier, of plak de inhoud direct
|
|
```
|
|
|
|
Of als apart bestand:
|
|
```
|
|
styles/theme.css
|
|
```
|
|
|
|
Importeer in `app/globals.css`:
|
|
```css
|
|
@import './styles/theme.css';
|
|
```
|
|
|
|
### 2. shadcn/ui initialiseren
|
|
|
|
```bash
|
|
npx shadcn@latest init
|
|
```
|
|
|
|
Kies bij de setup:
|
|
- Style: **Default**
|
|
- Base color: **Slate** (wordt overschreven door theme.css)
|
|
- CSS variables: **Yes**
|
|
|
|
De `theme.css` overschrijft alle shadcn default-kleuren via CSS custom properties. Geen extra configuratie nodig.
|
|
|
|
### 3. Tailwind configuratie
|
|
|
|
`theme.css` registreert alle tokens via `@theme inline` — ze zijn direct beschikbaar als Tailwind utility classes:
|
|
|
|
```tsx
|
|
// Werkt direct:
|
|
className="bg-primary text-primary-foreground"
|
|
className="bg-surface-container-low"
|
|
className="bg-status-done"
|
|
className="bg-priority-critical"
|
|
```
|
|
|
|
### 4. Dark mode
|
|
|
|
Dark mode werkt via de `.dark` class op `<html>`:
|
|
|
|
```tsx
|
|
// components/theme-toggle.tsx
|
|
'use client'
|
|
import { useState, useEffect } from 'react'
|
|
|
|
export function ThemeToggle() {
|
|
const [isDark, setIsDark] = useState(false)
|
|
|
|
useEffect(() => {
|
|
const stored = localStorage.getItem('theme')
|
|
if (stored === 'dark') {
|
|
document.documentElement.classList.add('dark')
|
|
setIsDark(true)
|
|
}
|
|
}, [])
|
|
|
|
const toggle = () => {
|
|
document.documentElement.classList.toggle('dark')
|
|
const next = !isDark
|
|
setIsDark(next)
|
|
localStorage.setItem('theme', next ? 'dark' : 'light')
|
|
}
|
|
|
|
return (
|
|
<button onClick={toggle} className="text-muted-foreground hover:text-foreground">
|
|
{isDark ? '☀️' : '🌙'}
|
|
</button>
|
|
)
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Kleurfilosofie
|
|
|
|
Drie hoofdrollen, elk met een semantische betekenis voor een Scrum-planner:
|
|
|
|
| Rol | Kleur | Betekenis | Gebruik in Scrum4Me |
|
|
|---|---|---|---|
|
|
| **Primary** | Blauw `#0061a4` | Productiviteit, vertrouwen | Primaire knoppen, actieve navigatie, Sprint Goal |
|
|
| **Secondary** | Paars `#5b5e71` | Planning, organisatie | Secundaire acties, filters, toolbar-items |
|
|
| **Tertiary** | Teal `#006874` | Voortgang, data | Voortgangsindicatoren, story-tellers, metrics |
|
|
|
|
Diepte wordt gecreëerd via **tonal elevation** (lichtere/donkerdere oppervlakken), niet via schaduwen.
|
|
|
|
---
|
|
|
|
## Surface Elevation System
|
|
|
|
Gebruik deze hiërarchie consequent — nooit `shadow-lg` voor diepte:
|
|
|
|
```
|
|
HOOGSTE ELEVATIE (voorgrond)
|
|
surface-container-lowest → dialogs, modals, popovers
|
|
surface-container-low → kaarten, panelen
|
|
surface-container → standaard container
|
|
surface-container-high → geneste containers
|
|
surface-container-highest → achtergrondcontainers
|
|
LAAGSTE ELEVATIE (achtergrond)
|
|
background → app-achtergrond
|
|
```
|
|
|
|
### In Scrum4Me specifiek
|
|
|
|
| Element | Surface token |
|
|
|---|---|
|
|
| App achtergrond | `bg-background` |
|
|
| Navigatiebalk | `bg-surface-container-low` |
|
|
| Gesplitst scherm (elk paneel) | `bg-surface-container-low` |
|
|
| PBI-rij | `bg-surface-container` |
|
|
| Geselecteerde PBI-rij | `bg-primary-container` |
|
|
| Story-blok | `bg-surface-container-low border border-border` |
|
|
| Story-blok (geselecteerd) | `bg-primary-container border border-primary` |
|
|
| Taakregel | `bg-surface-container` |
|
|
| Dialogs / modals | `bg-surface-container-lowest` |
|
|
| Slide-over (story detail) | `bg-surface-container-lowest` |
|
|
| Todo-item | `bg-surface-container` |
|
|
| Navigatiebar per paneel | `bg-surface-container-highest` |
|
|
|
|
---
|
|
|
|
## Statuskleur mapping
|
|
|
|
### Story- en taakstatus
|
|
|
|
Gebruik **altijd** icoon + tekst naast kleur (toegankelijkheid):
|
|
|
|
```tsx
|
|
// Status badge component
|
|
const statusConfig = {
|
|
OPEN: {
|
|
label: 'Open',
|
|
className: 'bg-status-todo text-white',
|
|
},
|
|
IN_SPRINT: {
|
|
label: 'In Sprint',
|
|
className: 'bg-status-in-progress text-white',
|
|
},
|
|
DONE: {
|
|
label: 'Done',
|
|
className: 'bg-status-done text-white',
|
|
},
|
|
}
|
|
|
|
// Taakstatus
|
|
const taskStatusConfig = {
|
|
TO_DO: {
|
|
label: 'To Do',
|
|
className: 'bg-status-todo text-white',
|
|
},
|
|
IN_PROGRESS: {
|
|
label: 'In Progress',
|
|
className: 'bg-status-in-progress text-white',
|
|
},
|
|
DONE: {
|
|
label: 'Done',
|
|
className: 'bg-status-done text-white',
|
|
},
|
|
}
|
|
```
|
|
|
|
### Prioriteitskleur mapping
|
|
|
|
```tsx
|
|
const priorityConfig = {
|
|
1: {
|
|
label: 'Kritiek',
|
|
className: 'bg-priority-critical text-white',
|
|
borderClassName: 'border-l-4 border-priority-critical',
|
|
},
|
|
2: {
|
|
label: 'Hoog',
|
|
className: 'bg-priority-high text-white',
|
|
borderClassName: 'border-l-4 border-priority-high',
|
|
},
|
|
3: {
|
|
label: 'Middel',
|
|
className: 'bg-priority-medium text-white',
|
|
borderClassName: 'border-l-4 border-priority-medium',
|
|
},
|
|
4: {
|
|
label: 'Laag',
|
|
className: 'bg-priority-low text-white',
|
|
borderClassName: 'border-l-4 border-priority-low',
|
|
},
|
|
}
|
|
```
|
|
|
|
### Story-activiteitenlog
|
|
|
|
```tsx
|
|
const logTypeConfig = {
|
|
IMPLEMENTATION_PLAN: {
|
|
label: 'Implementatieplan',
|
|
className: 'bg-info-container text-info-container-foreground border-l-4 border-info',
|
|
},
|
|
TEST_RESULT: {
|
|
PASSED: {
|
|
label: 'Tests geslaagd',
|
|
className: 'bg-success-container text-success-container-foreground border-l-4 border-success',
|
|
},
|
|
FAILED: {
|
|
label: 'Tests mislukt',
|
|
className: 'bg-error-container text-error-container-foreground border-l-4 border-error',
|
|
},
|
|
},
|
|
COMMIT: {
|
|
label: 'Commit',
|
|
className: 'bg-secondary-container text-secondary-container-foreground border-l-4 border-secondary',
|
|
},
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## shadcn/ui componenten — gebruik in Scrum4Me
|
|
|
|
Alle shadcn-componenten gebruiken automatisch het MD3-kleurensysteem. Hieronder de aanbevolen varianten per context.
|
|
|
|
### Button
|
|
|
|
```tsx
|
|
import { Button } from '@/components/ui/button'
|
|
|
|
// Primaire actie (Sprint starten, PBI aanmaken, Opslaan)
|
|
<Button>Sprint starten</Button>
|
|
|
|
// Secundaire actie (Annuleren, Filters, Exporteren)
|
|
<Button variant="secondary">Annuleren</Button>
|
|
|
|
// Destructieve actie (Verwijderen, Archiveren)
|
|
<Button variant="destructive">Verwijderen</Button>
|
|
|
|
// Ghost (icon-knoppen in navigatiebar)
|
|
<Button variant="ghost" size="icon">
|
|
<PlusIcon className="h-4 w-4" />
|
|
</Button>
|
|
|
|
// Outline (minder urgente acties)
|
|
<Button variant="outline">Details bekijken</Button>
|
|
```
|
|
|
|
### Badge (status en prioriteit)
|
|
|
|
```tsx
|
|
import { Badge } from '@/components/ui/badge'
|
|
|
|
// Gebruik custom className voor MD3-kleuren
|
|
// shadcn Badge variant="secondary" is ook bruikbaar voor neutrale badges
|
|
|
|
<Badge className="bg-status-done text-white">Done</Badge>
|
|
<Badge className="bg-priority-critical text-white">Kritiek</Badge>
|
|
<Badge className="bg-status-in-progress text-white">In Sprint</Badge>
|
|
|
|
// Neutrale info badge (bijv. "3 taken")
|
|
<Badge variant="secondary">3 taken</Badge>
|
|
```
|
|
|
|
**PBI-status (READY / BLOCKED / DONE):** hergebruikt bestaande tokens —
|
|
`status-todo` voor READY, `status-blocked` voor BLOCKED, `status-done` voor
|
|
DONE. Centraal gedefinieerd in `components/shared/pbi-status-select.tsx`
|
|
(`PBI_STATUS_LABELS`, `PBI_STATUS_COLORS`); importeer die in plaats van
|
|
kleuren ad-hoc te kopiëren.
|
|
|
|
### Dialog (bevestigingsdialogen)
|
|
|
|
```tsx
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
} from '@/components/ui/alert-dialog'
|
|
|
|
// Standaard bevestigingsdialoog voor verwijderacties
|
|
<AlertDialogContent className="bg-surface-container-lowest">
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>PBI verwijderen?</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
Dit verwijdert ook alle gekoppelde stories en taken. Deze actie is niet ongedaan te maken.
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>Annuleren</AlertDialogCancel>
|
|
<AlertDialogAction className="bg-destructive text-destructive-foreground">
|
|
Verwijderen
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
```
|
|
|
|
### Sheet (story detail slide-over)
|
|
|
|
```tsx
|
|
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet'
|
|
|
|
<Sheet>
|
|
<SheetContent
|
|
side="right"
|
|
className="w-[480px] bg-surface-container-lowest border-l border-border"
|
|
>
|
|
<SheetHeader>
|
|
<SheetTitle className="text-foreground">{story.title}</SheetTitle>
|
|
</SheetHeader>
|
|
{/* story detail inhoud */}
|
|
</SheetContent>
|
|
</Sheet>
|
|
```
|
|
|
|
### Input en Textarea
|
|
|
|
```tsx
|
|
import { Input } from '@/components/ui/input'
|
|
import { Textarea } from '@/components/ui/textarea'
|
|
|
|
// shadcn Input gebruikt --input-background automatisch uit theme.css
|
|
<Input
|
|
placeholder="PBI titel"
|
|
className="bg-input-background border-border focus:ring-primary"
|
|
/>
|
|
|
|
<Textarea
|
|
placeholder="Omschrijving (optioneel)"
|
|
className="bg-input-background border-border focus:ring-primary resize-none"
|
|
/>
|
|
```
|
|
|
|
### Select (prioriteit dropdown)
|
|
|
|
```tsx
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from '@/components/ui/select'
|
|
|
|
<Select>
|
|
<SelectTrigger className="bg-input-background border-border">
|
|
<SelectValue placeholder="Prioriteit" />
|
|
</SelectTrigger>
|
|
<SelectContent className="bg-surface-container-lowest border-border">
|
|
<SelectItem value="1">
|
|
<span className="flex items-center gap-2">
|
|
<span className="w-2 h-2 rounded-full bg-priority-critical" />
|
|
Kritiek
|
|
</span>
|
|
</SelectItem>
|
|
<SelectItem value="2">
|
|
<span className="flex items-center gap-2">
|
|
<span className="w-2 h-2 rounded-full bg-priority-high" />
|
|
Hoog
|
|
</span>
|
|
</SelectItem>
|
|
<SelectItem value="3">
|
|
<span className="flex items-center gap-2">
|
|
<span className="w-2 h-2 rounded-full bg-priority-medium" />
|
|
Middel
|
|
</span>
|
|
</SelectItem>
|
|
<SelectItem value="4">
|
|
<span className="flex items-center gap-2">
|
|
<span className="w-2 h-2 rounded-full bg-priority-low" />
|
|
Laag
|
|
</span>
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
```
|
|
|
|
### Skeleton (loading states)
|
|
|
|
```tsx
|
|
import { Skeleton } from '@/components/ui/skeleton'
|
|
|
|
// PBI lijst skeleton
|
|
function PbiListSkeleton() {
|
|
return (
|
|
<div className="space-y-2 p-4">
|
|
{Array.from({ length: 5 }).map((_, i) => (
|
|
<Skeleton key={i} className="h-12 w-full bg-surface-container-high" />
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// Story blokken skeleton
|
|
function StoryGridSkeleton() {
|
|
return (
|
|
<div className="flex flex-wrap gap-3 p-4">
|
|
{Array.from({ length: 6 }).map((_, i) => (
|
|
<Skeleton key={i} className="h-24 w-[10%] min-w-[100px] bg-surface-container-high" />
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
### Tooltip (demo-gebruiker write-protection)
|
|
|
|
```tsx
|
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
|
|
|
// Gebruik voor alle uitgeschakelde knoppen bij demo-gebruiker
|
|
function DemoProtectedButton({ children, isDemo, onClick, ...props }) {
|
|
if (!isDemo) {
|
|
return <Button onClick={onClick} {...props}>{children}</Button>
|
|
}
|
|
|
|
return (
|
|
<TooltipProvider>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<span>
|
|
<Button disabled {...props}>{children}</Button>
|
|
</span>
|
|
</TooltipTrigger>
|
|
<TooltipContent className="bg-surface-container-lowest border-border">
|
|
<p className="text-sm text-muted-foreground">Niet beschikbaar in demo-modus</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
)
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Scrum4Me component patronen
|
|
|
|
### PBI-rij
|
|
|
|
```tsx
|
|
// Geselecteerd PBI heeft primary-container achtergrond
|
|
<div
|
|
className={cn(
|
|
"flex items-center gap-3 px-4 py-3 rounded-lg cursor-pointer transition-colors border-l-4",
|
|
priorityConfig[pbi.priority].borderClassName,
|
|
isSelected
|
|
? "bg-primary-container text-primary-container-foreground"
|
|
: "bg-surface-container hover:bg-surface-container-high"
|
|
)}
|
|
onClick={() => setSelectedPbi(pbi.id)}
|
|
>
|
|
<span className="flex-1 text-sm font-medium truncate">{pbi.title}</span>
|
|
<Badge className={priorityConfig[pbi.priority].className}>
|
|
{priorityConfig[pbi.priority].label}
|
|
</Badge>
|
|
</div>
|
|
```
|
|
|
|
### Story-blok
|
|
|
|
```tsx
|
|
// ~10% schermbreedte, compacte weergave
|
|
<div
|
|
className={cn(
|
|
"relative flex flex-col gap-1 p-3 rounded-lg border cursor-pointer",
|
|
"min-w-[100px] w-[10%] h-24 text-xs",
|
|
"transition-colors hover:border-primary",
|
|
"bg-surface-container-low border-border"
|
|
)}
|
|
>
|
|
<span className="font-medium leading-tight line-clamp-2">{story.title}</span>
|
|
<div className="mt-auto flex items-center justify-between">
|
|
<span className={cn("px-1.5 py-0.5 rounded text-[10px] font-medium", statusConfig[story.status].className)}>
|
|
{statusConfig[story.status].label}
|
|
</span>
|
|
<span className={cn("w-2 h-2 rounded-full", `bg-priority-${priorityLabel}`)}>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
### Prioriteitsgroep scheidingslijn
|
|
|
|
```tsx
|
|
// Visuele scheiding per prioriteitsgroep in PBI-lijst en story-grid
|
|
<div className="mt-4 mb-2">
|
|
<div className="flex items-center gap-2">
|
|
<span className={cn(
|
|
"text-xs font-semibold uppercase tracking-wider",
|
|
priority === 1 && "text-priority-critical",
|
|
priority === 2 && "text-priority-high",
|
|
priority === 3 && "text-priority-medium",
|
|
priority === 4 && "text-priority-low",
|
|
)}>
|
|
{priorityConfig[priority].label}
|
|
</span>
|
|
<div className={cn(
|
|
"flex-1 h-px",
|
|
priority === 1 && "bg-priority-critical/30",
|
|
priority === 2 && "bg-priority-high/30",
|
|
priority === 3 && "bg-priority-medium/30",
|
|
priority === 4 && "bg-priority-low/30",
|
|
)} />
|
|
<span className="text-xs text-muted-foreground">{count}</span>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
### Voortgangsindicator (story → taken)
|
|
|
|
```tsx
|
|
// Gebruikt tertiary kleur voor voortgang
|
|
function StoryProgress({ done, total }: { done: number; total: number }) {
|
|
const pct = total === 0 ? 0 : Math.round((done / total) * 100)
|
|
|
|
return (
|
|
<div className="flex items-center gap-2 text-xs">
|
|
<div className="flex-1 h-1.5 bg-surface-container-highest rounded-full overflow-hidden">
|
|
<div
|
|
className="h-full bg-tertiary rounded-full transition-all"
|
|
style={{ width: `${pct}%` }}
|
|
/>
|
|
</div>
|
|
<span className="text-muted-foreground tabular-nums">
|
|
{done}/{total}
|
|
</span>
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
### Activiteitenlog entry
|
|
|
|
```tsx
|
|
function LogEntry({ entry }: { entry: StoryLog }) {
|
|
const config = entry.type === 'TEST_RESULT'
|
|
? logTypeConfig.TEST_RESULT[entry.status ?? 'PASSED']
|
|
: logTypeConfig[entry.type]
|
|
|
|
return (
|
|
<div className={cn("rounded-lg p-3 text-sm", config.className)}>
|
|
<div className="flex items-center justify-between mb-1">
|
|
<span className="font-medium text-xs uppercase tracking-wide">
|
|
{config.label}
|
|
</span>
|
|
<span className="text-xs opacity-70">
|
|
{formatDate(entry.created_at)}
|
|
</span>
|
|
</div>
|
|
<p className="text-sm leading-relaxed whitespace-pre-wrap">
|
|
{entry.content}
|
|
</p>
|
|
{entry.commit_hash && (
|
|
<a
|
|
href={`${repoUrl}/commit/${entry.commit_hash}`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="mt-1 inline-flex items-center gap-1 text-xs font-mono opacity-80 hover:opacity-100 underline"
|
|
>
|
|
{entry.commit_hash.slice(0, 7)} — {entry.commit_message}
|
|
</a>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
### Sprint Goal banner
|
|
|
|
```tsx
|
|
// Prominent bovenaan Sprint-schermen
|
|
<div className="bg-primary-container text-primary-container-foreground rounded-lg px-4 py-3 mb-4">
|
|
<span className="text-xs font-semibold uppercase tracking-wider opacity-70">
|
|
Sprint Goal
|
|
</span>
|
|
<p className="mt-0.5 font-medium">{sprint.sprint_goal}</p>
|
|
</div>
|
|
```
|
|
|
|
### Toast notificaties (Sonner)
|
|
|
|
```tsx
|
|
import { toast } from 'sonner'
|
|
|
|
// Success (aanmaken, opslaan)
|
|
toast.success('PBI aangemaakt')
|
|
toast.success('Story toegevoegd aan Sprint')
|
|
|
|
// Error (mislukte Server Action)
|
|
toast.error('Opslaan mislukt. Probeer opnieuw.')
|
|
|
|
// Info (neutrale melding)
|
|
toast.info('Sprint afgerond')
|
|
|
|
// Geen toast bij drag-and-drop (te frequent)
|
|
```
|
|
|
|
---
|
|
|
|
## Regels (nooit overtreden)
|
|
|
|
```
|
|
❌ bg-blue-500, bg-green-600, bg-red-400 → gebruik semantische tokens
|
|
❌ shadow-lg, shadow-md → gebruik surface elevation
|
|
❌ opacity-50 op een primary button → gebruik -container variant
|
|
❌ Kleur alleen voor status (geen tekst) → altijd tekst + kleur
|
|
❌ Hardcoded hex-waarden in className → altijd via CSS token
|
|
❌ bg-white of bg-black → bg-background of bg-foreground
|
|
|
|
✅ bg-primary text-primary-foreground
|
|
✅ bg-surface-container-low
|
|
✅ bg-status-done + tekst "Done"
|
|
✅ bg-error-container text-error-container-foreground
|
|
✅ border-l-4 border-priority-critical
|
|
```
|
|
|
|
---
|
|
|
|
## Bestandslocaties
|
|
|
|
```
|
|
styles/
|
|
theme.css ← bronbestand, niet aanpassen
|
|
app/
|
|
globals.css ← importeert theme.css
|
|
components/
|
|
ui/ ← shadcn/ui (auto-gegenereerd, niet aanpassen)
|
|
shared/
|
|
status-badge.tsx ← herbruikbare status badge
|
|
priority-badge.tsx ← herbruikbare prioriteit badge
|
|
demo-button.tsx ← Button met demo-protection tooltip
|
|
story-log.tsx ← activiteitenlog entries
|
|
story-progress.tsx ← voortgangsindicator
|
|
priority-group.tsx ← prioriteitsgroep scheidingslijn
|
|
```
|
|
|
|
---
|
|
|
|
## Toegankelijkheid
|
|
|
|
- Alle kleurcombinaties voldoen aan **WCAG AA** (contrast ratio ≥ 4.5:1 voor normale tekst)
|
|
- Gebruik **altijd** tekst + kleur voor statusindicatoren, nooit kleur alleen
|
|
- Alle interactieve elementen hebben een zichtbare `focus:ring-primary`
|
|
- Dark mode is volledig ondersteund via de `.dark` class
|
|
|
|
---
|
|
|
|
*Bijlage bij CLAUDE.md — lees beide voor je begint met bouwen.*
|