Files
triqura-ecd/app/(marketing)/components/reading-progress.tsx
colinislit 273c00f9e8 Epic 2: Design System Migration - Teal-first implementatie
 E2.S1: Tailwind config update
- Teal-700 (#0F766E) als PRIMARY brand color (5.47:1 contrast)
- Amber-600→700 gradient voor AI features
- Updated ring color naar teal-700 voor WCAG AA

 E2.S2: Global CSS variables
- --color-brand → teal-700 (was blue-600)
- --color-info → teal-700 (was blue-500)
- --color-input-focus → teal-700 (was blue-500)
- --color-ai toegevoegd → amber-600

 E2.S3: Component color updates
- components/ui/sign-in.tsx: Alle blue → teal
- components/ui/timeline.tsx: Gradient blue → teal
- components/ui/reading-progress.tsx: Progress bar blue → teal
- components/ui/modern-side-bar.tsx: Logo, active states blue → teal

 E2.S4: AIButton component
- Nieuw component: components/ui/ai-button.tsx
- Amber-600→700 gradient voor WCAG compliance
- 3 variants: default, outline, ghost
- Loading state + Sparkles icon
- Fully accessible (WCAG AA focus states)

 E2.S5: Contrast testing
- scripts/test-contrast.ts: Automated WCAG testing
- docs/design/wcag-compliance.md: Compliance documentatie
- Resultaten: 7/11 AA Normal (4.5:1), 11/11 AA Large (3:1) 

WCAG AA Compliance:  PASS
- Teal-700 op wit: 5.47:1 (AA Normal)
- White op teal-700: 5.47:1 (AA Normal)
- White op amber-600: 3.19:1 (AA Large - OK voor buttons)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 17:38:22 +01:00

70 lines
2.1 KiB
TypeScript

'use client'
/**
* Reading Progress Bar Component
*
* Fixed top progress indicator that shows scroll progress through the page.
* Smooth animation, 2px height, accent blue color.
* Respects prefers-reduced-motion for accessibility.
*/
import { useEffect, useState } from 'react'
export function ReadingProgress() {
const [progress, setProgress] = useState(0)
const [reducedMotion, setReducedMotion] = useState(false)
useEffect(() => {
// Check for reduced motion preference
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)')
setReducedMotion(mediaQuery.matches)
const handleChange = (e: MediaQueryListEvent) => setReducedMotion(e.matches)
mediaQuery.addEventListener('change', handleChange)
const updateProgress = () => {
const windowHeight = window.innerHeight
const documentHeight = document.documentElement.scrollHeight
const scrollTop = window.scrollY || document.documentElement.scrollTop
// Calculate scroll progress percentage
const scrollableHeight = documentHeight - windowHeight
const currentProgress = scrollableHeight > 0
? (scrollTop / scrollableHeight) * 100
: 0
setProgress(Math.min(100, Math.max(0, currentProgress)))
}
// Initial calculation
updateProgress()
// Update on scroll
window.addEventListener('scroll', updateProgress, { passive: true })
window.addEventListener('resize', updateProgress, { passive: true })
return () => {
mediaQuery.removeEventListener('change', handleChange)
window.removeEventListener('scroll', updateProgress)
window.removeEventListener('resize', updateProgress)
}
}, [])
return (
<div
className="fixed top-[64px] left-0 right-0 z-50 h-0.5 bg-slate-200"
role="progressbar"
aria-valuenow={Math.round(progress)}
aria-valuemin={0}
aria-valuemax={100}
aria-label="Leesvoortgang"
>
<div
className={`h-full bg-teal-600 ${reducedMotion ? '' : 'transition-all duration-150 ease-out'}`}
style={{ width: `${progress}%` }}
/>
</div>
)
}