Skip to content

Architecture

Luc edited this page Nov 20, 2025 · 2 revisions

Understanding the codebase structure and how the Accessibility Widget works.

πŸ—οΈ Overview

The Accessibility Widget is a self-contained, client-side plugin built with vanilla JavaScript and CSS. It has no dependencies and works on any modern web browser.

πŸ“ Architecture Principles

  • Self-Contained: No external dependencies (except Boxicons for icons)
  • Isolated: Uses namespacing to avoid conflicts
  • Modular: Features are organized into logical functions
  • Performant: Optimized for speed and efficiency
  • Accessible: Follows WCAG guidelines

πŸ“ File Structure

accessibility-plugin/
β”œβ”€β”€ accessibility-plugin.js      # Main plugin (1618 lines)
β”œβ”€β”€ accessibility-plugin.css      # Styles (885 lines)
└── accessibility-config.js       # Configuration (67 lines)

πŸ”§ Core Components

1. Initialization

(function() {
    'use strict';
    
    // Namespace isolation
    const ACCESSIBILITY_NAMESPACE = 'accessibilityPlugin';
    
    // Default settings
    const defaultSettings = { ... };
    
    // State management
    let currentState = { ... };
    
    // Initialize on DOM ready
    function init() { ... }
    init();
    
    // Expose API
    window.AccessibilityPlugin = { ... };
})();

2. State Management

The plugin uses a centralized state object:

let currentState = {
    fontSize: 100,
    contrast: 'normal',
    colorBlindness: 'none',
    // ... more settings
};

State Flow:

  1. User changes setting β†’ Update currentState
  2. Call applySettings() β†’ Apply to DOM
  3. Call savePreferences() β†’ Save to localStorage

3. Widget Creation

The widget is created dynamically:

function createWidget() {
    // Create container
    const widget = document.createElement('div');
    widget.className = 'accessibility-widget';
    
    // Create toggle button
    const button = createToggleButton();
    
    // Create panel
    const panel = createPanel();
    
    // Append to body
    document.body.appendChild(widget);
}

4. Settings Application

Settings are applied to the DOM:

function applySettings() {
    // Apply font size
    document.body.style.fontSize = currentState.fontSize + '%';
    
    // Apply contrast
    if (currentState.contrast === 'high') {
        document.body.classList.add('accessibility-contrast-high');
    }
    
    // Apply color blindness filter
    applyColorBlindnessFilter();
    
    // ... more settings
}

🎨 CSS Architecture

Scoping

All CSS is scoped to avoid conflicts:

/* Widget-specific classes */
.accessibility-widget { ... }
.accessibility-panel { ... }
.accessibility-toggle-btn { ... }

Isolation

Widget is isolated from page styles:

.accessibility-widget {
    isolation: isolate !important;
    contain: none !important;
    z-index: 10000 !important;
}

Responsive Design

Mobile-first approach:

/* Base styles */
.accessibility-panel {
    width: 260px;
}

/* Mobile adjustments */
@media (max-width: 768px) {
    .accessibility-panel {
        width: calc(100vw - 20px);
    }
}

πŸ”„ Feature Implementation

Text-to-Speech

function speakText(text) {
    if (!speechSynthesis) return;
    
    const utterance = new SpeechSynthesisUtterance(text);
    utterance.rate = currentState.ttsRate;
    utterance.pitch = currentState.ttsPitch;
    utterance.volume = currentState.ttsVolume;
    
    speechSynthesis.speak(utterance);
}

Color Blindness Filters

function applyColorBlindnessFilter() {
    if (currentState.colorBlindness === 'none') {
        document.body.style.filter = '';
        return;
    }
    
    // Create SVG filter
    const svg = createSVGFilter();
    document.body.appendChild(svg);
    
    // Apply filter
    document.body.style.filter = 'url(#colorblind-filter)';
}

Reading Guide

function enableReadingGuide() {
    const guide = document.createElement('div');
    guide.className = 'accessibility-reading-guide';
    
    document.addEventListener('mousemove', function(e) {
        guide.style.top = e.clientY + 'px';
    });
    
    document.body.appendChild(guide);
}

πŸ“¦ Module Organization

Settings Module

// Default settings
const defaultSettings = { ... };

// Current state
let currentState = { ... };

// Load preferences
function loadPreferences() { ... }

// Save preferences
function savePreferences() { ... }

UI Module

// Create widget
function createWidget() { ... }

// Create controls
function createControl() { ... }

// Toggle panel
function togglePanel() { ... }

Features Module

// Apply all settings
function applySettings() { ... }

// Individual feature functions
function applyFontSize() { ... }
function applyContrast() { ... }
function applyColorBlindnessFilter() { ... }

πŸ”Œ API Layer

The plugin exposes a simple API:

window.AccessibilityPlugin = {
    toggle: togglePanel,
    reset: resetSettings,
    getSettings: function() { return { ...currentState }; },
    setFontSize: function(size) { ... },
    setContrast: function(mode) { ... },
    // ... more methods
};

πŸ’Ύ Data Persistence

localStorage

Settings are saved to localStorage:

function savePreferences() {
    try {
        localStorage.setItem(
            'accessibilitySettings',
            JSON.stringify(currentState)
        );
    } catch (e) {
        console.error('Failed to save preferences:', e);
    }
}

Loading

function loadPreferences() {
    try {
        const saved = localStorage.getItem('accessibilitySettings');
        if (saved) {
            currentState = { ...currentState, ...JSON.parse(saved) };
        }
    } catch (e) {
        console.error('Failed to load preferences:', e);
    }
}

🎯 Design Patterns

IIFE Pattern

(function() {
    // Isolated scope
    // No global pollution
})();

Observer Pattern

// Watch for settings changes
function watchSettings() {
    let lastState = { ...currentState };
    setInterval(function() {
        if (stateChanged(lastState, currentState)) {
            onSettingsChange();
            lastState = { ...currentState };
        }
    }, 100);
}

Factory Pattern

// Create controls dynamically
function createControl(label, key, options, value) {
    const control = document.createElement('div');
    // ... build control
    return control;
}

πŸ”’ Security

XSS Prevention

  • No user input is directly inserted into DOM
  • All text is properly escaped
  • No eval() or similar dangerous functions

CSP Compatibility

Works with Content Security Policy:

  • No inline scripts required
  • All code in external files
  • No unsafe-eval

⚑ Performance

Optimization Techniques

  1. Lazy Initialization: Widget created on DOM ready
  2. Event Delegation: Efficient event handling
  3. Debouncing: For frequent events (mousemove)
  4. CSS Isolation: Prevents style recalculation
  5. Minimal DOM Manipulation: Batch updates

Performance Considerations

  • Color filters may impact large pages
  • TTS uses browser APIs (efficient)
  • localStorage is fast (synchronous)
  • No heavy computations

🧩 Extensibility

Adding New Features

  1. Add to defaultSettings

    const defaultSettings = {
        // ... existing
        myNewFeature: false
    };
  2. Add to currentState

    let currentState = {
        // ... existing
        myNewFeature: settings.myNewFeature
    };
  3. Create UI Control

    const control = createControl('My Feature', 'myNewFeature', [...], value);
  4. Implement Logic

    function applySettings() {
        // ... existing
        if (currentState.myNewFeature) {
            // Your logic
        }
    }
  5. Add to API (optional)

    window.AccessibilityPlugin = {
        // ... existing
        setMyNewFeature: function(value) { ... }
    };

πŸ“Š Data Flow

User Action
    ↓
Update currentState
    ↓
Call applySettings()
    ↓
Modify DOM
    ↓
Call savePreferences()
    ↓
Save to localStorage

πŸ” Code Organization

Function Order

  1. Constants and defaults
  2. State management
  3. Utility functions
  4. Feature implementations
  5. UI creation
  6. Event handlers
  7. Initialization
  8. API exports

Naming Conventions

  • Functions: camelCase (applySettings)
  • Variables: camelCase (currentState)
  • CSS Classes: kebab-case (.accessibility-widget)
  • Constants: UPPER_SNAKE_CASE (ACCESSIBILITY_NAMESPACE)

πŸ“š Related Pages

πŸ†˜ Questions?


Last Updated: November 2025

Getting Started

Documentation

Guides

Development

Support

Clone this wiki locally