434 lines
13 KiB
Markdown
434 lines
13 KiB
Markdown
# Mini-EPD Style Guide & UX
|
|
|
|
> Geëxtraheerd uit de mini-epd-prototype codebase. Gebruik dit document om dezelfde look & feel in een ander project toe te passen.
|
|
|
|
---
|
|
|
|
## 1. Tech Stack & Tooling
|
|
|
|
| Categorie | Keuze | Versie |
|
|
|---|---|---|
|
|
| Framework | Next.js (App Router) | 14.2 |
|
|
| Taal | TypeScript | 5.x |
|
|
| Styling | Tailwind CSS | 3.4 |
|
|
| Component variants | `class-variance-authority` (CVA) | 0.7 |
|
|
| Class merging | `tailwind-merge` | 3.4 |
|
|
| Animaties | `tailwindcss-animate` + Framer Motion | 1.0 / 12.x |
|
|
| UI primitives | Radix UI | diverse |
|
|
| Iconen | Lucide React | 0.553 |
|
|
| State management | Zustand | 5.x |
|
|
| Formulieren | React Hook Form + Zod | 7.x / 4.x |
|
|
| Rich text | TipTap | 3.x |
|
|
|
|
### Utility helper
|
|
|
|
```ts
|
|
// lib/utils.ts
|
|
import { clsx, type ClassValue } from 'clsx'
|
|
import { twMerge } from 'tailwind-merge'
|
|
|
|
export function cn(...inputs: ClassValue[]) {
|
|
return twMerge(clsx(inputs))
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Kleurensysteem
|
|
|
|
### 2.1 Basiskleuren
|
|
|
|
| Token | Hex | Gebruik |
|
|
|---|---|---|
|
|
| `app-background` | `#F8FAFC` | Pagina-achtergrond (slate-50) |
|
|
| `surface` | `#FFFFFF` | Cards, panels |
|
|
| `surface-sub` | `#F1F5F9` | Secundaire achtergrond (slate-100) |
|
|
| `text-primary` | `#0F172A` | Primaire tekst (slate-900) |
|
|
| `text-secondary` | `#475569` | Secundaire/meta-tekst (slate-600) |
|
|
| `border` | `#E2E8F0` | Standaard borders (slate-200) |
|
|
|
|
### 2.2 Brand (Teal-first)
|
|
|
|
De primaire brandkleur is **teal**. Alle interactieve elementen, focus-states en links gebruiken deze kleur.
|
|
|
|
| Token | Hex | Ratio | Gebruik |
|
|
|---|---|---|---|
|
|
| `brand-50` | `#F0FDFA` | — | Subtiele achtergrond |
|
|
| `brand-100` | `#CCFBF1` | — | Info-subtiel |
|
|
| `brand-200` | `#99F6E4` | — | Lichte tint |
|
|
| `brand-400` | `#2DD4BF` | — | Decoratief |
|
|
| `brand-500` | `#14B8A6` | — | Accent |
|
|
| `brand-600` | `#0D9488` | 3:1 | UI-componenten (non-text) |
|
|
| **`brand-700`** | **`#0F766E`** | **5.47:1** | **Primaire tekst & CTA (WCAG AA) ✓** |
|
|
| `brand-800` | `#115E59` | — | Hover state |
|
|
| `brand-900` | `#134E4A` | — | Donkerste tint |
|
|
|
|
```css
|
|
/* CSS custom properties */
|
|
--color-brand: #0F766E;
|
|
--color-brand-hover: #115E59;
|
|
--color-brand-active: #0D9488;
|
|
--color-brand-subtle: #F0FDFA;
|
|
```
|
|
|
|
### 2.3 AI Features (Amber)
|
|
|
|
AI-gerelateerde acties worden visueel onderscheiden door een **amber** kleurenpalet.
|
|
|
|
| Token | Hex | Gebruik |
|
|
|---|---|---|
|
|
| `ai-50` | `#FFFBEB` | Subtiele AI-achtergrond |
|
|
| `ai-500` | `#F59E0B` | Primaire AI-kleur |
|
|
| `ai-600` | `#D97706` | AI-tekst (WCAG AA large) |
|
|
| `ai-700` | `#B45309` | AI hover |
|
|
|
|
### 2.4 Module-accenten
|
|
|
|
Verschillende modules hebben een eigen kleurig accent:
|
|
|
|
| Module | Achtergrond | Accent | Border |
|
|
|---|---|---|---|
|
|
| Afspraken | `#E8F8EF` | `#16A34A` (groen) | `#CDECDC` |
|
|
| Medicatie | `#FEF6DC` | `#F59E0B` (amber) | `#F6E7B6` |
|
|
| Lab | `#FFEBDC` | `#F97316` (oranje) | `#FFD2B8` |
|
|
|
|
### 2.5 Status & Feedback
|
|
|
|
| Status | Kleur | Subtiel |
|
|
|---|---|---|
|
|
| Success | `#16A34A` | `#ECFDF5` |
|
|
| Warning | `#EAB308` | `#FEFCE8` |
|
|
| Error | `#DC2626` | `#FEF2F2` |
|
|
| Info | `#0F766E` (teal) | `#CCFBF1` |
|
|
|
|
### 2.6 Severity Badges (DSM-light)
|
|
|
|
| Ernst | Achtergrond | Tekst |
|
|
|---|---|---|
|
|
| Low | `#E5E7EB` | `#374151` |
|
|
| Medium | `#FEF3C7` | `#92400E` |
|
|
| High | `#FEE2E2` | `#991B1B` |
|
|
|
|
### 2.7 Dark Mode
|
|
|
|
Dark mode is optioneel geïmplementeerd (MVP = licht thema):
|
|
|
|
```css
|
|
.dark {
|
|
--color-bg: #0a0a0a;
|
|
--color-surface: #1a1a1a;
|
|
--color-text: #ededed;
|
|
--color-text-secondary: #a1a1a1;
|
|
--color-border: #333333;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Typografie
|
|
|
|
### 3.1 Font Families
|
|
|
|
Drie fonts worden lokaal geladen via `next/font/local`:
|
|
|
|
| Variabele | Font | Gewichten | Gebruik |
|
|
|---|---|---|---|
|
|
| `--font-serif` | **Lora** | 400, 400i, 600 | Long-form content, manifesto teksten |
|
|
| `--font-sans` | **Roboto** | 400, 500, 600 (+italic) | UI, navigatie, metadata, body |
|
|
| `--font-mono` | **Source Code Pro** | 400, 600 | Code, technische details, nummers |
|
|
|
|
**Body default:** `var(--font-sans), system-ui, -apple-system, sans-serif`
|
|
|
|
### 3.2 Typografische Schaal
|
|
|
|
| Token | Waarde | Toepassing |
|
|
|---|---|---|
|
|
| `--text-hero` | `clamp(2.5rem, 6vw, 4rem)` | Hero quotes |
|
|
| `--text-h1` | `clamp(2rem, 5vw, 3rem)` | Sectie-koppen |
|
|
| `--text-h2` | `clamp(1.5rem, 4vw, 2.5rem)` | Subsecties |
|
|
| `--text-body` | `1.25rem` | Content, lopende tekst |
|
|
| `--text-small` | `0.875rem` | Metadata, labels |
|
|
|
|
### 3.3 Line Heights
|
|
|
|
| Token | Waarde | Gebruik |
|
|
|---|---|---|
|
|
| `--line-height-tight` | `1.2` | Headings |
|
|
| `--line-height-relaxed` | `1.8` | Body/content |
|
|
|
|
---
|
|
|
|
## 4. Shadows & Border Radius
|
|
|
|
### Shadows
|
|
|
|
| Token | Waarde |
|
|
|---|---|
|
|
| `shadow-sm` | `0 1px 2px rgba(15,23,42,0.06)` |
|
|
| `shadow-md` | `0 2px 6px rgba(15,23,42,0.08)` |
|
|
| `shadow-lg` | `0 8px 20px rgba(15,23,42,0.10)` |
|
|
|
|
> Alle shadows zijn gebaseerd op **slate-900** alpha — geen hard zwart.
|
|
|
|
### Border Radius
|
|
|
|
| Token | Waarde |
|
|
|---|---|
|
|
| `--radius` | `0.625rem` (10px) |
|
|
| `rounded-lg` | `var(--radius)` |
|
|
| `rounded-md` | `calc(var(--radius) - 2px)` ≈ 8px |
|
|
| `rounded-sm` | `calc(var(--radius) - 4px)` ≈ 6px |
|
|
|
|
---
|
|
|
|
## 5. Formulieren
|
|
|
|
| State | Property | Waarde |
|
|
|---|---|---|
|
|
| Default | bg | `#FFFFFF` |
|
|
| Default | border | `#CBD5E1` |
|
|
| Default | tekst | `#0F172A` |
|
|
| Placeholder | kleur | `#94A3B8` |
|
|
| Hover | border | `#94A3B8` |
|
|
| Focus | ring/border | `#0F766E` (teal-700) |
|
|
| Disabled | bg | `#F1F5F9` |
|
|
| Disabled | tekst | `#94A3B8` |
|
|
| Invalid | border | `#DC2626` |
|
|
| Invalid | tekst | `#B91C1C` |
|
|
|
|
**Input component patroon:**
|
|
```tsx
|
|
<input className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1
|
|
text-base shadow-sm transition-colors placeholder:text-muted-foreground
|
|
focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring
|
|
disabled:cursor-not-allowed disabled:opacity-50 md:text-sm" />
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Component Patronen
|
|
|
|
### 6.1 Component Architectuur
|
|
|
|
- Alle componenten gebruiken **`React.forwardRef`** voor ref-forwarding
|
|
- Varianten via **`class-variance-authority`** (CVA)
|
|
- Styling via Tailwind classes + `cn()` utility
|
|
- Radix UI als headless primitives (Dialog, Tabs, Select, Popover, etc.)
|
|
- **Default = server component**; `"use client"` alleen wanneer interactiviteit vereist is
|
|
|
|
### 6.2 Button
|
|
|
|
| Variant | Stijl |
|
|
|---|---|
|
|
| `default` | `bg-primary text-primary-foreground hover:bg-primary/90` |
|
|
| `destructive` | `bg-destructive text-destructive-foreground hover:bg-destructive/90` |
|
|
| `outline` | `border border-input bg-background hover:bg-accent` |
|
|
| `secondary` | `bg-secondary text-secondary-foreground hover:bg-secondary/80` |
|
|
| `ghost` | `hover:bg-accent hover:text-accent-foreground` |
|
|
| `link` | `text-primary underline-offset-4 hover:underline` |
|
|
|
|
| Size | Afmetingen |
|
|
|---|---|
|
|
| `default` | `h-10 px-4 py-2` |
|
|
| `sm` | `h-9 px-3` |
|
|
| `lg` | `h-11 px-8` |
|
|
| `icon` | `h-10 w-10` |
|
|
|
|
### 6.3 AI Button
|
|
|
|
Speciaal component voor AI-acties. Visueel onderscheiden door **amber gradient**.
|
|
|
|
| Variant | Stijl |
|
|
|---|---|
|
|
| `default` | Amber gradient (`from-amber-600 to-amber-700`) + witte tekst |
|
|
| `outline` | `border-2 border-amber-500`, tekst `amber-700` |
|
|
| `ghost` | Transparant, tekst `amber-700`, hover `amber-50` |
|
|
|
|
Features:
|
|
- Sparkle-icoon (✨) als visuele AI-indicator
|
|
- Loading state met `Loader2` spinner
|
|
- Drie maten: `sm`, `md`, `lg`
|
|
|
|
### 6.4 Card
|
|
|
|
```
|
|
┌─────────────────────────────┐
|
|
│ CardHeader (p-6) │
|
|
│ CardTitle (semibold) │
|
|
│ CardDescription (muted) │
|
|
│ CardContent (p-6 pt-0) │
|
|
│ CardFooter (p-6 pt-0) │
|
|
└─────────────────────────────┘
|
|
```
|
|
- `rounded-xl`, `border`, `bg-card`, `shadow`
|
|
|
|
### 6.5 Badge
|
|
|
|
Varianten: `default`, `secondary`, `destructive`, `outline`
|
|
Base: `rounded-md`, `px-2.5 py-0.5`, `text-xs font-semibold`
|
|
|
|
### 6.6 Tabs
|
|
|
|
- `TabsList`: `rounded-lg bg-muted p-1`
|
|
- `TabsTrigger`: `rounded-md px-3 py-1 text-sm font-medium`
|
|
- Active state: `bg-background text-foreground shadow`
|
|
|
|
### 6.7 Toast
|
|
|
|
- Positie: top op mobile, bottom-right op desktop
|
|
- Swipe-to-dismiss ondersteuning
|
|
- Slide-in animatie (top op mobile, bottom op desktop)
|
|
- Varianten: `default` en `destructive`
|
|
|
|
---
|
|
|
|
## 7. Layout & Navigatie
|
|
|
|
### 7.1 App Shell
|
|
|
|
```
|
|
┌──────────────────────────────────────┐
|
|
│ ┌──────────┐ ┌─────────────────────┐ │
|
|
│ │ │ │ │ │
|
|
│ │ Sidebar │ │ Main Content │ │
|
|
│ │ (fixed) │ │ (scrollable) │ │
|
|
│ │ │ │ │ │
|
|
│ │ │ │ │ │
|
|
│ └──────────┘ └─────────────────────┘ │
|
|
└──────────────────────────────────────┘
|
|
```
|
|
|
|
- **Container**: `min-h-screen bg-slate-50 flex`
|
|
- **Sidebar**: fixed, `w-78` open / `w-28` collapsed
|
|
- **Main content**: flex-grow, scrollable
|
|
|
|
### 7.2 Sidebar UX
|
|
|
|
| Eigenschap | Waarde |
|
|
|---|---|
|
|
| Desktop breedte (open) | `w-78` (312px) |
|
|
| Desktop breedte (collapsed) | `w-28` (112px) |
|
|
| Achtergrond | `bg-white` |
|
|
| Border | `border-r border-slate-200` |
|
|
| Transition | `duration-300 ease-in-out` |
|
|
| Mobile | Full overlay met `bg-black/40 backdrop-blur-sm` |
|
|
|
|
**Active nav item**: `bg-teal-50 text-teal-700` + icoon `text-teal-600`
|
|
**Inactive nav item**: `text-slate-600 hover:bg-slate-50 hover:text-slate-900`
|
|
|
|
**Collapsed state**: tooltips verschijnen links met een pijl-indicator (`bg-slate-800 text-white`)
|
|
|
|
**Profiel sectie**: avatar cirkel, naam, rol, online-indicator (`bg-green-500` dot)
|
|
**Logout**: `text-red-600 hover:bg-red-50`
|
|
|
|
---
|
|
|
|
## 8. Animaties
|
|
|
|
### 8.1 Float Animatie
|
|
|
|
Subtiele zweef-animatie voor badges of decoratieve elementen:
|
|
|
|
```css
|
|
@keyframes float {
|
|
0%, 100% { transform: translateY(0px) rotate(0deg); }
|
|
25% { transform: translateY(-5px) rotate(0.5deg); }
|
|
50% { transform: translateY(-3px) rotate(-0.5deg); }
|
|
75% { transform: translateY(-7px) rotate(0.3deg); }
|
|
}
|
|
.animate-float { animation: float 5s ease-in-out infinite; }
|
|
```
|
|
|
|
### 8.2 Artifact Slide-in
|
|
|
|
Panel slide-in van rechts:
|
|
|
|
```css
|
|
@keyframes artifact-enter {
|
|
from { transform: translateX(100%); opacity: 0; }
|
|
to { transform: translateX(0); opacity: 1; }
|
|
}
|
|
.artifact-enter { animation: artifact-enter 200ms ease-out; }
|
|
```
|
|
|
|
### 8.3 Transition Defaults
|
|
|
|
- Navigatie / sidebar: `transition-all duration-300 ease-in-out`
|
|
- Buttons / interactie: `transition-colors` of `transition-all duration-200`
|
|
- Toasts: slide-in + fade via `tailwindcss-animate`
|
|
|
|
### 8.4 Reduced Motion
|
|
|
|
Volledige `prefers-reduced-motion` ondersteuning:
|
|
|
|
```css
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*, *::before, *::after {
|
|
animation-duration: 0.02ms !important;
|
|
transition-duration: 0.03ms !important;
|
|
scroll-behavior: auto !important;
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Toegankelijkheid (A11y)
|
|
|
|
| Aspect | Implementatie |
|
|
|---|---|
|
|
| Focus indicator | `outline: 2px solid var(--color-brand)`, `outline-offset: 2px` |
|
|
| Focus ring (components) | `ring-2 ring-ring ring-offset-2` |
|
|
| Skip to main | Verborgen link, zichtbaar bij focus |
|
|
| Kleurcontrast | Brand teal-700 = 5.47:1 op wit (WCAG AA ✓) |
|
|
| ARIA labels | Alle interactieve elementen (toggle buttons, sidebar) |
|
|
| Reduced motion | Alle animaties gerespecteerd |
|
|
| Iconen | `aria-hidden="true"` op decoratieve iconen |
|
|
| Taal | `<html lang="nl">` |
|
|
|
|
---
|
|
|
|
## 10. AI UX Conventies
|
|
|
|
AI-functies worden visueel onderscheiden van standaard UI:
|
|
|
|
| Element | Conventie |
|
|
|---|---|
|
|
| Kleur | Amber-palet (geel/goud), nooit teal |
|
|
| Button | `AIButton` component met Sparkle-icoon (✨) |
|
|
| Highlight | `.ai-source-highlight` — lichtgeel (`#fef08a`) met `border-radius: 2px` |
|
|
| Loading | `Loader2` spinner (animatie: `animate-spin`) |
|
|
| Feedback | Confidence indicators bij AI-gegenereerde content |
|
|
|
|
---
|
|
|
|
## 11. Quick-Start Checklist
|
|
|
|
Om deze stijl in een nieuw project over te nemen:
|
|
|
|
1. **Installeer dependencies:**
|
|
```bash
|
|
pnpm add class-variance-authority clsx tailwind-merge tailwindcss-animate
|
|
pnpm add lucide-react framer-motion
|
|
pnpm add @radix-ui/react-dialog @radix-ui/react-tabs @radix-ui/react-select
|
|
# + overige Radix primitives naar behoefte
|
|
```
|
|
|
|
2. **Kopieer fonts** (Lora, Roboto, Source Code Pro) naar `public/fonts/` en configureer in `layout.tsx`
|
|
|
|
3. **Kopieer CSS custom properties** uit `globals.css` (sectie `:root`)
|
|
|
|
4. **Kopieer Tailwind config** — met name `colors`, `boxShadow`, `borderRadius` extensies
|
|
|
|
5. **Kopieer `cn()` utility** naar `lib/utils.ts`
|
|
|
|
6. **Kopieer UI components** uit `components/ui/` (shadcn-patroon met CVA)
|
|
|
|
7. **Zet `antialiased`** op de `<body>` tag
|
|
|
|
8. **Zet `lang="nl"`** op de `<html>` tag
|
|
|
|
---
|
|
|
|
*Gegenereerd op basis van de mini-epd-prototype codebase (maart 2026).*
|