Phase 2 — Normalize file naming (#59)
* 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.
This commit is contained in:
parent
dc3832ad54
commit
e10f8f81bc
27 changed files with 101 additions and 101 deletions
670
docs/styling.md
Normal file
670
docs/styling.md
Normal file
|
|
@ -0,0 +1,670 @@
|
|||
# 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.*
|
||||
Loading…
Add table
Add a link
Reference in a new issue