# Custom Fonts & Dyslexia Support - Implementation Plan ## 📋 Overview Implement comprehensive font customization and dyslexia-friendly features to improve readability for all users, with special accommodations for those with reading difficulties or visual processing challenges. **Status:** Planning Phase **Priority:** 🟡 Medium **Estimated Time:** 1 week (40 hours) **Target Completion:** TBD --- ## 🎯 Goals & Objectives ### Primary Goals 1. Provide extensive font customization options 2. Integrate dyslexia-friendly fonts and features 3. Enable color overlay filters for visual comfort 4. Support custom font uploads 5. Offer letter/word spacing adjustments ### User Value Proposition - **For dyslexic readers**: Specialized fonts and spacing - **For visually impaired**: High contrast and large text options - **For personal preference**: Complete customization - **For comfort**: Reduce eye strain - **For accessibility**: WCAG AAA compliance --- ## ✨ Feature Specifications ### 1. Font Configuration ```typescript interface FontConfig { // Font Selection fontFamily: string customFontUrl?: string // For uploaded fonts // Size fontSize: number // 12-32px fontSizePreset: 'small' | 'medium' | 'large' | 'extra-large' | 'custom' // Weight & Style fontWeight: number // 300-900 fontStyle: 'normal' | 'italic' // Spacing letterSpacing: number // -2 to 10px wordSpacing: number // -5 to 20px lineHeight: number // 1.0 - 3.0 paragraphSpacing: number // 0-40px // Dyslexia Features isDyslexiaMode: boolean dyslexiaFontSize: number // Usually 14-18pt for dyslexia dyslexiaSpacing: 'normal' | 'wide' | 'extra-wide' boldFirstLetters: boolean // Bionic reading style // Visual Aids colorOverlay: string | null // Tinted overlay overlayOpacity: number // 0-100% highContrast: boolean underlineLinks: boolean // Advanced textTransform: 'none' | 'uppercase' | 'lowercase' | 'capitalize' textDecoration: 'none' | 'underline' | 'overline' } // Available font families const FONT_FAMILIES = { standard: [ { name: 'System Default', value: 'system-ui, -apple-system' }, { name: 'Arial', value: 'Arial, sans-serif' }, { name: 'Georgia', value: 'Georgia, serif' }, { name: 'Times New Roman', value: '"Times New Roman", serif' }, { name: 'Verdana', value: 'Verdana, sans-serif' }, { name: 'Courier New', value: '"Courier New", monospace' } ], readable: [ { name: 'Open Sans', value: '"Open Sans", sans-serif' }, { name: 'Lora', value: 'Lora, serif' }, { name: 'Merriweather', value: 'Merriweather, serif' }, { name: 'Roboto', value: 'Roboto, sans-serif' }, { name: 'Source Sans Pro', value: '"Source Sans Pro", sans-serif' } ], dyslexiaFriendly: [ { name: 'OpenDyslexic', value: 'OpenDyslexic, sans-serif', url: '/fonts/OpenDyslexic-Regular.woff2', description: 'Specially designed with weighted bottoms to prevent letter rotation' }, { name: 'Lexend', value: 'Lexend, sans-serif', url: 'https://fonts.googleapis.com/css2?family=Lexend:wght@300;400;500;600;700', description: 'Variable font designed to reduce visual stress' }, { name: 'Comic Sans MS', value: '"Comic Sans MS", cursive', description: 'Often recommended for dyslexia due to unique letter shapes' }, { name: 'Dyslexie', value: 'Dyslexie, sans-serif', url: '/fonts/Dyslexie-Regular.woff2', description: 'Premium font designed by a dyslexic designer', isPremium: true } ] } ``` ### 2. Font Selector Component ```typescript const FontSelector: React.FC<{ config: FontConfig onChange: (config: Partial) => void }> = ({ config, onChange }) => { const [activeCategory, setActiveCategory] = useState<'standard' | 'readable' | 'dyslexiaFriendly'>('standard') const [previewText, setPreviewText] = useState('In the beginning God created the heaven and the earth.') return ( Font Selection {/* Category Tabs */} setActiveCategory(v)} sx={{ mb: 2 }}> {/* Font List */} {FONT_FAMILIES[activeCategory].map(font => ( onChange({ fontFamily: font.value })} > {font.name} {font.isPremium && ( )} } secondary={font.description} /> loadFontPreview(font)}> ))} {/* Upload Custom Font */} {/* Preview */} Preview {previewText} ) } ``` ### 3. Font Size & Spacing Controls ```typescript const FontSizeControls: React.FC<{ config: FontConfig onChange: (config: Partial) => void }> = ({ config, onChange }) => { return ( Size & Spacing {/* Font Size Presets */} Quick Presets {/* Custom Font Size */} Font Size: {config.fontSize}px onChange({ fontSize: value as number, fontSizePreset: 'custom' })} min={12} max={32} step={1} marks={[ { value: 12, label: '12' }, { value: 16, label: '16' }, { value: 20, label: '20' }, { value: 24, label: '24' }, { value: 32, label: '32' } ]} /> {/* Letter Spacing */} Letter Spacing: {config.letterSpacing}px onChange({ letterSpacing: value as number })} min={-2} max={10} step={0.5} marks /> {/* Word Spacing */} Word Spacing: {config.wordSpacing}px onChange({ wordSpacing: value as number })} min={-5} max={20} step={1} marks /> {/* Line Height */} Line Height: {config.lineHeight} onChange({ lineHeight: value as number })} min={1.0} max={3.0} step={0.1} marks={[ { value: 1.0, label: '1.0' }, { value: 1.5, label: '1.5' }, { value: 2.0, label: '2.0' }, { value: 2.5, label: '2.5' }, { value: 3.0, label: '3.0' } ]} /> {/* Font Weight */} Font Weight: {config.fontWeight} onChange({ fontWeight: value as number })} min={300} max={900} step={100} marks={[ { value: 300, label: 'Light' }, { value: 400, label: 'Normal' }, { value: 700, label: 'Bold' }, { value: 900, label: 'Black' } ]} /> ) } ``` ### 4. Dyslexia Mode Settings ```typescript const DyslexiaSettings: React.FC<{ config: FontConfig onChange: (config: Partial) => void }> = ({ config, onChange }) => { return ( Dyslexia Support These settings are optimized for readers with dyslexia and reading difficulties. {/* Enable Dyslexia Mode */} { const enabled = e.target.checked onChange({ isDyslexiaMode: enabled, ...(enabled && { fontFamily: 'OpenDyslexic, sans-serif', fontSize: 16, letterSpacing: 1, wordSpacing: 3, lineHeight: 1.8 }) }) }} /> } label="Enable Dyslexia Mode" sx={{ mb: 2 }} /> {config.isDyslexiaMode && ( <> {/* Spacing Presets */} Spacing {/* Bold First Letters */} onChange({ boldFirstLetters: e.target.checked })} /> } label={ Bold First Letters (Bionic Reading) Makes the first part of each word bold to guide eye movement } sx={{ mb: 2 }} /> {/* High Contrast */} onChange({ highContrast: e.target.checked })} /> } label="High Contrast Mode" sx={{ mb: 2 }} /> {/* Underline Links */} onChange({ underlineLinks: e.target.checked })} /> } label="Underline All Links" /> )} ) } ``` ### 5. Color Overlay Filters ```typescript const ColorOverlaySettings: React.FC<{ config: FontConfig onChange: (config: Partial) => void }> = ({ config, onChange }) => { const overlayColors = [ { name: 'None', color: null }, { name: 'Yellow', color: '#FFEB3B', description: 'Reduces glare' }, { name: 'Blue', color: '#2196F3', description: 'Calming effect' }, { name: 'Green', color: '#4CAF50', description: 'Eye comfort' }, { name: 'Pink', color: '#E91E63', description: 'Reduces contrast' }, { name: 'Orange', color: '#FF9800', description: 'Warm tint' }, { name: 'Purple', color: '#9C27B0', description: 'Reduces brightness' } ] return ( Color Overlay Color overlays can help reduce visual stress and improve reading comfort. {/* Overlay Color Selection */} {overlayColors.map(overlay => ( onChange({ colorOverlay: overlay.color })} > {overlay.name} {overlay.description && ( {overlay.description} )} ))} {/* Opacity Control */} {config.colorOverlay && ( Overlay Opacity: {config.overlayOpacity}% onChange({ overlayOpacity: value as number })} min={10} max={100} step={5} marks /> )} {/* Preview */} {config.colorOverlay && ( )} Preview with overlay The quick brown fox jumps over the lazy dog. In the beginning God created the heaven and the earth. ) } ``` ### 6. Custom Font Upload ```typescript const CustomFontUpload: React.FC<{ onUpload: (fontUrl: string, fontName: string) => void }> = ({ onUpload }) => { const [uploading, setUploading] = useState(false) const [fontName, setFontName] = useState('') const handleFileSelect = async (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (!file) return // Validate file type const validTypes = ['.woff', '.woff2', '.ttf', '.otf'] const fileExt = file.name.substring(file.name.lastIndexOf('.')).toLowerCase() if (!validTypes.includes(fileExt)) { alert('Please upload a valid font file (.woff, .woff2, .ttf, .otf)') return } setUploading(true) try { // Upload to server or cloud storage const formData = new FormData() formData.append('font', file) formData.append('name', fontName || file.name) const response = await fetch('/api/fonts/upload', { method: 'POST', body: formData }) const data = await response.json() if (data.success) { onUpload(data.fontUrl, data.fontName) } } catch (error) { console.error('Font upload failed:', error) } finally { setUploading(false) } } return ( {}}> Upload Custom Font setFontName(e.target.value)} fullWidth sx={{ mb: 3 }} /> Supported formats: WOFF, WOFF2, TTF, OTF ) } ``` ### 7. Apply Font Configuration ```typescript // Apply configuration to reader const applyFontConfig = (config: FontConfig) => { const readerElement = document.querySelector('.bible-reader-content') if (!readerElement) return const styles = { fontFamily: config.fontFamily, fontSize: `${config.fontSize}px`, fontWeight: config.fontWeight, fontStyle: config.fontStyle, letterSpacing: `${config.letterSpacing}px`, wordSpacing: `${config.wordSpacing}px`, lineHeight: config.lineHeight, textTransform: config.textTransform, textDecoration: config.textDecoration } Object.assign(readerElement.style, styles) // Apply high contrast if (config.highContrast) { readerElement.classList.add('high-contrast') } else { readerElement.classList.remove('high-contrast') } // Apply color overlay if (config.colorOverlay) { const overlay = document.createElement('div') overlay.className = 'color-overlay' overlay.style.backgroundColor = config.colorOverlay overlay.style.opacity = (config.overlayOpacity / 100).toString() readerElement.prepend(overlay) } } // CSS for high contrast mode const highContrastStyles = ` .high-contrast { background-color: #000 !important; color: #fff !important; } .high-contrast .verse-number { color: #ffeb3b !important; } .high-contrast a { color: #00bcd4 !important; text-decoration: underline !important; } ` ``` --- ## 🗄️ Database Schema ```prisma model FontPreference { id String @id @default(cuid()) userId String @unique user User @relation(fields: [userId], references: [id]) fontFamily String @default("system-ui") customFontUrl String? fontSize Int @default(16) fontWeight Int @default(400) letterSpacing Float @default(0) wordSpacing Float @default(0) lineHeight Float @default(1.6) isDyslexiaMode Boolean @default(false) dyslexiaSpacing String @default("normal") boldFirstLetters Boolean @default(false) colorOverlay String? overlayOpacity Int @default(30) highContrast Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model CustomFont { id String @id @default(cuid()) userId String user User @relation(fields: [userId], references: [id]) name String url String format String // woff, woff2, ttf, otf fileSize Int createdAt DateTime @default(now()) @@index([userId]) } ``` --- ## 📅 Implementation Timeline ### Week 1 **Day 1-2:** Foundation - [ ] Font selector component - [ ] Size/spacing controls - [ ] Preview functionality **Day 3:** Dyslexia Features - [ ] Dyslexia mode settings - [ ] OpenDyslexic/Lexend integration - [ ] Bionic reading formatter **Day 4:** Visual Aids - [ ] Color overlay system - [ ] High contrast mode - [ ] Accessibility testing **Day 5:** Polish & Testing - [ ] Custom font upload - [ ] Performance optimization - [ ] Cross-browser testing - [ ] Documentation --- **Document Version:** 1.0 **Last Updated:** 2025-10-13 **Status:** Ready for Implementation