# π Marketing Shader Implementation Guide
**Doel:** Praktische implementatie van dot-shader component voor marketing website hero section.
---
## π Design Specificaties
### Hero Section Layout
```
βββββββββββββββββββββββββββββββββββββββ
β [Shader Background - zeer subtiel] β
β β
β βββββββββββββββββββββββββββββββ β
β β Hero Content Overlay β β
β β - Headline (wit/donker) β β
β β - Subheadline β β
β β - CTA Buttons β β
β β - Live Metrics Counter β β
β βββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββ
```
### Visuele HiΓ«rarchie
1. **Shader:** Opacity 0.02-0.03 (bijna onzichtbaar, maar aanwezig)
2. **Content Overlay:** Semi-transparant of solid (afhankelijk van contrast)
3. **Tekst:** Hoog contrast (wit op donker, of donker op licht)
---
## π§ Technische Implementatie
### Stap 1: Aangepaste Marketing Variant
```typescript
// components/ui/marketing-shader-background.tsx
'use client'
import { DotScreenShader } from './dot-shader-background'
import { useTheme } from 'next-themes'
import { useEffect, useState } from 'react'
interface MarketingShaderProps {
variant?: 'hero' | 'section'
className?: string
}
export function MarketingShader({ variant = 'hero', className }: MarketingShaderProps) {
const { theme } = useTheme()
const [mounted, setMounted] = useState(false)
const [reducedMotion, setReducedMotion] = useState(false)
useEffect(() => {
setMounted(true)
// 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)
return () => mediaQuery.removeEventListener('change', handleChange)
}, [])
// Fallback voor mobile of reduced motion
if (!mounted || reducedMotion) {
return (
)
}
// Desktop: shader component
return (
{/* Subtle overlay voor betere tekst leesbaarheid */}
)
}
```
### Stap 2: Hero Section Component
```typescript
// app/(marketing)/components/hero-section.tsx
import { MarketingShader } from '@/components/ui/marketing-shader-background'
import { LiveMetricsCounter } from './live-metrics-counter'
export function HeroSection() {
return (
{/* Shader Background */}
{/* Content Overlay */}
{/* Headline */}
Software on Demand
{/* Subheadline */}
Van β¬100.000 en 12 maanden naar β¬200 en 4 weken
{/* Live Metrics */}
{/* CTAs */}
)
}
```
### Stap 3: Shader Component Aanpassingen
```typescript
// Aanpassingen in dot-shader-background.tsx voor marketing gebruik
// In Scene component, update getThemeColors:
const getThemeColors = () => {
switch (theme) {
case 'light':
return {
dotColor: '#E2E8F0', // Match UX stylesheet border color
bgColor: '#F8FAFC', // Match UX stylesheet app background
dotOpacity: 0.03 // Zeer subtiel voor marketing
}
case 'dark':
return {
dotColor: '#475569', // Match secondary text
bgColor: '#0F172A', // Match primary text (inverted)
dotOpacity: 0.02 // Nog subtieler
}
default:
return {
dotColor: '#E2E8F0',
bgColor: '#F8FAFC',
dotOpacity: 0.03
}
}
}
// Update gridSize voor eleganter effect
const gridSize = 80 // Fijner dan standaard 100
```
---
## π¨ Styling Integratie
### Tailwind Classes voor Content Overlay
```css
/* Zorg voor goede contrast over shader */
.hero-content {
/* Optioneel: semi-transparante achtergrond voor tekst */
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
/* Of: solid achtergrond met padding */
background: white;
padding: 2rem;
border-radius: 1rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
```
### Contrast Check
**Test Scenario's:**
1. Wit tekst op shader achtergrond β Minimaal 4.5:1 contrast
2. Donker tekst op shader achtergrond β Minimaal 4.5:1 contrast
3. Met overlay β Contrast moet verbeteren
**Tools:**
- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
- Browser DevTools β Accessibility panel
---
## π± Mobile Responsive
### Breakpoint Strategie
```typescript
// components/ui/marketing-shader-background.tsx
export function MarketingShader({ variant, className }: MarketingShaderProps) {
const [isMobile, setIsMobile] = useState(false)
useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth < 768) // Tailwind md breakpoint
}
checkMobile()
window.addEventListener('resize', checkMobile)
return () => window.removeEventListener('resize', checkMobile)
}, [])
// Mobile: gradient fallback (performance)
if (isMobile) {
return (
)
}
// Desktop: shader
return
}
```
**Reden:**
- Mobile GPU performance beperkt
- Batterij impact
- UX: Gebruikers verwachten snelle laadtijden op mobile
---
## β‘ Performance Optimalisatie
### Lazy Loading
```typescript
import dynamic from 'next/dynamic'
// Lazy load shader alleen wanneer hero in viewport
const MarketingShader = dynamic(
() => import('@/components/ui/marketing-shader-background').then(mod => mod.MarketingShader),
{
ssr: false, // Client-side only
loading: () =>
}
)
```
### Intersection Observer
```typescript
// components/ui/marketing-shader-background.tsx
export function MarketingShader({ variant, className }: MarketingShaderProps) {
const [shouldRender, setShouldRender] = useState(false)
const ref = useRef(null)
useEffect(() => {
if (!ref.current) return
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setShouldRender(true)
observer.disconnect()
}
},
{ threshold: 0.1 }
)
observer.observe(ref.current)
return () => observer.disconnect()
}, [])
if (!shouldRender) {
return
}
return
}
```
---
## βΏ Toegankelijkheid
### Reduced Motion Support
```typescript
// Automatisch detecteren en respecteren
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (prefersReducedMotion) {
// Geen animaties, statische gradient
return
}
```
### Skip Animation Knop
```typescript
// Optioneel: knop om animatie uit te zetten
```
### ARIA Labels
```typescript
```
---
## π Monitoring & Metrics
### Performance Tracking
```typescript
// Track shader performance
useEffect(() => {
const startTime = performance.now()
// Na render
requestAnimationFrame(() => {
const renderTime = performance.now() - startTime
if (renderTime > 100) {
console.warn('Shader render tijd hoog:', renderTime)
// Mogelijk fallback activeren
}
})
}, [])
```
### A/B Test Setup
```typescript
// Variant A: Met shader
// Variant B: Zonder shader (gradient)
const useShader = () => {
// Cookie/localStorage based variant assignment
const variant = localStorage.getItem('hero-variant') || 'A'
return variant === 'A'
}
```
**Metrics te meten:**
- Time to Interactive (TTI)
- First Contentful Paint (FCP)
- Bounce rate
- Engagement tijd
- Conversion rate
---
## β
Checklist
### Pre-Launch
- [ ] Shader opacity op 0.02-0.03 (zeer subtiel)
- [ ] Kleuren matchen exact UX stylesheet
- [ ] Mobile fallback geΓ―mplementeerd
- [ ] Reduced motion support
- [ ] Contrast check gedaan (WCAG AA)
- [ ] Performance test (< 100ms render tijd)
- [ ] Lazy loading geΓ―mplementeerd
### Post-Launch
- [ ] Performance monitoring actief
- [ ] A/B test resultaten analyseren
- [ ] Gebruikersfeedback verzamelen
- [ ] Accessibility audit uitgevoerd
---
## π― Conclusie
Het shader component kan **strategisch** gebruikt worden op de marketing website, mits:
1. Zeer lage opacity (0.02-0.03)
2. Alleen in hero section
3. Mobile fallback aanwezig
4. Toegankelijkheid gewaarborgd
5. Performance geoptimaliseerd
**Resultaat:** Innovatieve uitstraling zonder UX compromissen.