1/** 2 * WIRE Miami: Accessibility Widget 3 * =================================== 4 * Requires: wire-config.js loaded BEFORE this file. 5 * Uses accent color from WIRE config. Falls back to gold. 6 * 7 * Provides: larger text, high contrast, grayscale, underline links, 8 * readable font, pause animations. Saves prefs to localStorage. 9 * Links to /accessibility.html for full statement. 10 */ 11 12(function() { 13 'use strict'; 14 15 var accent = (window.WIRE && window.WIRE.colors && window.WIRE.colors.accent) || '#C9A96E'; 16 17 // Create floating accessibility button 18 var btn = document.createElement('button'); 19 btn.id = 'a11y-toggle'; 20 btn.setAttribute('aria-label', 'Accessibility options'); 21 btn.setAttribute('title', 'Accessibility options'); 22 btn.innerHTML = '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">' + 23 '<circle cx="12" cy="4" r="2"/><path d="M12 6v6m0 0l-4 6m4-6l4 6"/><path d="M6 10h12"/></svg>'; 24 btn.style.cssText = 'position:fixed;bottom:80px;left:20px;width:48px;height:48px;border-radius:0;' + 25 'background:#1a1a1a;color:#fff;border:2px solid rgba(255,255,255,0.2);cursor:pointer;z-index:9998;' + 26 'display:flex;align-items:center;justify-content:center;box-shadow:0 4px 12px rgba(0,0,0,0.2);' + 27 'transition:all 0.3s;'; 28 29 // Create panel 30 var panel = document.createElement('div'); 31 panel.id = 'a11y-panel'; 32 panel.setAttribute('role', 'dialog'); 33 panel.setAttribute('aria-label', 'Accessibility settings'); 34 panel.style.cssText = 'position:fixed;bottom:140px;left:20px;width:280px;background:#fff;' + 35 'border-radius:0;box-shadow:0 8px 32px rgba(0,0,0,0.15);z-index:9997;display:none;' + 36 'font-family:system-ui,sans-serif;overflow:hidden;'; 37 38 var panelHeader = '<div style="background:#1a1a1a;color:#fff;padding:16px 20px;font-size:0.85rem;' + 39 'font-weight:700;letter-spacing:0.04em;">Accessibility Settings</div>'; 40 41 var options = [ 42 { id: 'a11y-font-size', label: 'Larger Text', css: 'body{font-size:120% !important}' }, 43 { id: 'a11y-high-contrast', label: 'High Contrast', css: 'body{filter:contrast(1.4) !important}' }, 44 { id: 'a11y-grayscale', label: 'Grayscale', css: 'body{filter:grayscale(1) !important}' }, 45 { id: 'a11y-underline-links', label: 'Underline Links', css: 'a{text-decoration:underline !important}' }, 46 { id: 'a11y-readable-font', label: 'Readable Font', css: '*{font-family:Arial,Helvetica,sans-serif !important}' }, 47 { id: 'a11y-pause-animations', label: 'Pause Animations', css: '*,*::before,*::after{animation-duration:0.01ms !important;transition-duration:0.01ms !important}' } 48 ]; 49 50 var panelBody = '<div style="padding:12px 20px;">'; 51 options.forEach(function(opt) { 52 panelBody += '<label style="display:flex;align-items:center;justify-content:space-between;' + 53 'padding:10px 0;border-bottom:1px solid #f0f0f0;cursor:pointer;font-size:0.85rem;">' + 54 opt.label + 55 '<input type="checkbox" id="' + opt.id + '" style="width:18px;height:18px;accent-color:' + 56 accent + ';cursor:pointer;">' + 57 '</label>'; 58 }); 59 panelBody += '<div style="padding:12px 0;text-align:center;">' + 60 '<a href="accessibility.html" style="color:' + accent + ';font-size:0.78rem;font-weight:600;' + 61 'text-transform:uppercase;letter-spacing:0.06em;">Accessibility Statement</a></div></div>'; 62 63 panel.innerHTML = panelHeader + panelBody; 64 65 // Toggle panel 66 var isOpen = false; 67 btn.addEventListener('click', function() { 68 isOpen = !isOpen; 69 panel.style.display = isOpen ? 'block' : 'none'; 70 btn.setAttribute('aria-expanded', isOpen); 71 }); 72 73 // Close panel when clicking outside 74 document.addEventListener('click', function(e) { 75 if (isOpen && !panel.contains(e.target) && e.target !== btn && !btn.contains(e.target)) { 76 isOpen = false; 77 panel.style.display = 'none'; 78 btn.setAttribute('aria-expanded', 'false'); 79 } 80 }); 81 82 // Handle option toggles 83 document.addEventListener('change', function(e) { 84 var opt = options.find(function(o) { return o.id === e.target.id; }); 85 if (!opt) return; 86 var styleId = 'a11y-style-' + opt.id; 87 var existing = document.getElementById(styleId); 88 if (e.target.checked) { 89 var style = document.createElement('style'); 90 style.id = styleId; 91 style.textContent = opt.css; 92 document.head.appendChild(style); 93 } else if (existing) { 94 existing.remove(); 95 } 96 // Save preferences 97 try { 98 var prefs = JSON.parse(localStorage.getItem('a11y_prefs') || '{}'); 99 prefs[opt.id] = e.target.checked; 100 localStorage.setItem('a11y_prefs', JSON.stringify(prefs)); 101 } catch(ex) {} 102 }); 103 104 // Restore saved preferences 105 try { 106 var saved = JSON.parse(localStorage.getItem('a11y_prefs') || '{}');
107 Object.keys(saved).forEach(function(key) { 108 if (saved[key]) { 109 var cb = document.getElementById(key); 110 if (cb) { cb.checked = true; cb.dispatchEvent(new Event('change', { bubbles: true })); } 111 } 112 }); 113 } catch(ex) {} 114 115 document.body.appendChild(btn); 116 document.body.appendChild(panel); 117})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.