accessibility-widgets 2.0.4 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +7 -1
  2. package/package.json +1 -1
  3. package/widget.js +441 -111
package/README.md CHANGED
@@ -4,9 +4,15 @@
4
4
  [![License: GPL](https://img.shields.io/badge/License-GPL-blue.svg)](https://opensource.org/licenses/GPL-3.0)
5
5
  [![CDN](https://img.shields.io/badge/CDN-unpkg-orange.svg)](https://unpkg.com/accessibility-widgets)
6
6
 
7
+ <a href="https://github.com/sponsors/sinanisler">
8
+ <img src="https://img.shields.io/badge/Consider_Supporting_My_Projects_❤-GitHub-d46" width="300" height="auto" />
9
+ </a>
10
+ <br><br>
11
+
7
12
  A comprehensive, zero-dependency accessibility widget that enhances web accessibility for all users. This lightweight, single-file JavaScript solution provides 14+ accessibility features to make your website instantly more inclusive and compliant with WCAG 2.1 AA, Section 508, and EN 301 549 standards.
13
+ <br><br>
8
14
 
9
- <img width="1277" height="989" alt="image" src="https://github.com/user-attachments/assets/f6abe245-9d92-4f53-941c-06b77bde1cac" />
15
+ <img width="1793" height="1028" alt="image" src="https://github.com/user-attachments/assets/610138f5-e60d-4297-b5e3-ea040f361209" />
10
16
 
11
17
 
12
18
  ## ⚡ Quick Start
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "accessibility-widgets",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "description": "A comprehensive, lightweight accessibility widget that enhances web accessibility for all users. Provides multiple accessibility features including screen reader support, voice control, high contrast mode, and more.",
5
5
  "main": "widget.js",
6
6
  "scripts": {
package/widget.js CHANGED
@@ -1,8 +1,12 @@
1
- /*
2
- ===========================================
1
+ /* ===========================================
2
+
3
3
  ACCESSIBILITY WIDGET
4
- ===========================================
5
- */
4
+
5
+ github.com/sinanisler/accessibility-widgets
6
+
7
+ github.com/sponsors/sinanisler
8
+
9
+ =========================================== */
6
10
 
7
11
  // ===========================================
8
12
  // TRANSLATIONS
@@ -499,19 +503,171 @@ const DEFAULT_WIDGET_CONFIG = {
499
503
 
500
504
  // Voice Command Configuration - Developers can customize commands for different languages
501
505
  voiceCommands: {
502
- showMenu: ['show menu', 'open menu', 'accessibility menu'],
503
- highContrast: ['high contrast'],
504
- biggerText: ['bigger text', 'large text'],
505
- textSpacing: ['text spacing'],
506
- pauseAnimations: ['pause animations', 'stop animations'],
507
- hideImages: ['hide images'],
508
- dyslexiaFont: ['dyslexia friendly', 'dyslexia font'],
509
- biggerCursor: ['bigger cursor', 'large cursor'],
510
- lineHeight: ['line height'],
511
- textAlign: ['align text', 'text align'],
512
- screenReader: ['screen reader'],
513
- voiceControl: ['voice command', 'voice control'],
514
- resetAll: ['reset all', 'reset everything']
506
+ en: {
507
+ showMenu: ['show menu', 'open menu', 'accessibility menu', 'access menu'],
508
+ highContrast: ['high contrast', 'contrast', 'dark mode', 'increase contrast'],
509
+ biggerText: ['bigger text', 'large text', 'text size', 'increase text', 'bigger', 'larger text', 'text bigger', 'make text bigger', 'enlarge text'],
510
+ textSpacing: ['text spacing', 'spacing', 'letter spacing', 'text space'],
511
+ pauseAnimations: ['pause animations', 'stop animations', 'disable animations', 'no animations'],
512
+ hideImages: ['hide images', 'remove images', 'no images'],
513
+ dyslexiaFont: ['dyslexia friendly', 'dyslexia font', 'readable font', 'easy font'],
514
+ biggerCursor: ['bigger cursor', 'large cursor', 'cursor size', 'big cursor'],
515
+ lineHeight: ['line height', 'line spacing', 'space between lines', 'line space'],
516
+ textAlign: ['align text', 'text align', 'center text', 'alignment'],
517
+ screenReader: ['screen reader', 'read aloud', 'voice reader'],
518
+ voiceControl: ['voice command', 'voice control', 'voice commands'],
519
+ resetAll: ['reset all', 'reset everything', 'clear all', 'reset settings', 'reset']
520
+ },
521
+ de: {
522
+ showMenu: ['menü anzeigen', 'menü öffnen', 'barrierefreiheitsmenü', 'zugangsmenü'],
523
+ highContrast: ['hoher kontrast', 'kontrast', 'dunkler modus', 'kontrast erhöhen'],
524
+ biggerText: ['größerer text', 'großer text', 'textgröße', 'text vergrößern', 'größer', 'text größer'],
525
+ textSpacing: ['textabstand', 'abstand', 'buchstabenabstand', 'text abstand'],
526
+ pauseAnimations: ['animationen pausieren', 'animationen stoppen', 'animationen deaktivieren'],
527
+ hideImages: ['bilder ausblenden', 'bilder entfernen', 'keine bilder'],
528
+ dyslexiaFont: ['legasthenie freundlich', 'legasthenie schrift', 'lesbare schrift'],
529
+ biggerCursor: ['größerer cursor', 'großer cursor', 'cursor größe'],
530
+ lineHeight: ['zeilenhöhe', 'zeilenabstand', 'abstand zwischen zeilen'],
531
+ textAlign: ['text ausrichten', 'textausrichtung', 'text zentrieren'],
532
+ screenReader: ['screenreader', 'vorlesen', 'sprach reader'],
533
+ voiceControl: ['sprachbefehl', 'sprachsteuerung', 'sprachbefehle'],
534
+ resetAll: ['alles zurücksetzen', 'alle zurücksetzen', 'alle löschen', 'einstellungen zurücksetzen']
535
+ },
536
+ es: {
537
+ showMenu: ['mostrar menú', 'abrir menú', 'menú de accesibilidad', 'menú de acceso'],
538
+ highContrast: ['alto contraste', 'contraste', 'modo oscuro', 'aumentar contraste'],
539
+ biggerText: ['texto más grande', 'texto grande', 'tamaño de texto', 'aumentar texto', 'más grande'],
540
+ textSpacing: ['espaciado de texto', 'espaciado', 'espaciado de letras', 'espacio de texto'],
541
+ pauseAnimations: ['pausar animaciones', 'detener animaciones', 'desactivar animaciones'],
542
+ hideImages: ['ocultar imágenes', 'quitar imágenes', 'sin imágenes'],
543
+ dyslexiaFont: ['amigable para dislexia', 'fuente de dislexia', 'fuente legible'],
544
+ biggerCursor: ['cursor más grande', 'cursor grande', 'tamaño de cursor'],
545
+ lineHeight: ['altura de línea', 'espaciado de líneas', 'espacio entre líneas'],
546
+ textAlign: ['alinear texto', 'alineación de texto', 'centrar texto'],
547
+ screenReader: ['lector de pantalla', 'leer en voz alta', 'lector de voz'],
548
+ voiceControl: ['comando de voz', 'control de voz', 'comandos de voz'],
549
+ resetAll: ['restablecer todo', 'restablecer todo', 'borrar todo', 'restablecer configuración']
550
+ },
551
+ it: {
552
+ showMenu: ['mostra menu', 'apri menu', 'menu accessibilità', 'menu accesso'],
553
+ highContrast: ['alto contrasto', 'contrasto', 'modalità scura', 'aumenta contrasto'],
554
+ biggerText: ['testo più grande', 'testo grande', 'dimensione testo', 'aumenta testo', 'più grande'],
555
+ textSpacing: ['spaziatura testo', 'spaziatura', 'spaziatura lettere', 'spazio testo'],
556
+ pauseAnimations: ['pausa animazioni', 'ferma animazioni', 'disabilita animazioni'],
557
+ hideImages: ['nascondi immagini', 'rimuovi immagini', 'nessuna immagine'],
558
+ dyslexiaFont: ['adatto alla dislessia', 'font dislessia', 'font leggibile'],
559
+ biggerCursor: ['cursore più grande', 'cursore grande', 'dimensione cursore'],
560
+ lineHeight: ['altezza linea', 'spaziatura linee', 'spazio tra linee'],
561
+ textAlign: ['allinea testo', 'allineamento testo', 'centra testo'],
562
+ screenReader: ['lettore schermo', 'leggi ad alta voce', 'lettore vocale'],
563
+ voiceControl: ['comando vocale', 'controllo vocale', 'comandi vocali'],
564
+ resetAll: ['ripristina tutto', 'ripristina tutto', 'cancella tutto', 'ripristina impostazioni']
565
+ },
566
+ fr: {
567
+ showMenu: ['afficher menu', 'ouvrir menu', 'menu accessibilité', 'menu accès'],
568
+ highContrast: ['contraste élevé', 'contraste', 'mode sombre', 'augmenter contraste'],
569
+ biggerText: ['texte plus grand', 'grand texte', 'taille texte', 'augmenter texte', 'plus grand'],
570
+ textSpacing: ['espacement texte', 'espacement', 'espacement lettres', 'espace texte'],
571
+ pauseAnimations: ['mettre en pause animations', 'arrêter animations', 'désactiver animations'],
572
+ hideImages: ['masquer images', 'supprimer images', 'aucune image'],
573
+ dyslexiaFont: ['convivial dyslexie', 'police dyslexie', 'police lisible'],
574
+ biggerCursor: ['curseur plus grand', 'grand curseur', 'taille curseur'],
575
+ lineHeight: ['hauteur ligne', 'espacement lignes', 'espace entre lignes'],
576
+ textAlign: ['aligner texte', 'alignement texte', 'centrer texte'],
577
+ screenReader: ['lecteur écran', 'lire à haute voix', 'lecteur vocal'],
578
+ voiceControl: ['commande vocale', 'contrôle vocal', 'commandes vocales'],
579
+ resetAll: ['réinitialiser tout', 'réinitialiser tout', 'effacer tout', 'réinitialiser paramètres']
580
+ },
581
+ ru: {
582
+ showMenu: ['показать меню', 'открыть меню', 'меню доступности', 'меню доступа'],
583
+ highContrast: ['высокая контрастность', 'контрастность', 'темный режим', 'увеличить контрастность'],
584
+ biggerText: ['больший текст', 'большой текст', 'размер текста', 'увеличить текст', 'больше'],
585
+ textSpacing: ['межбуквенный интервал', 'интервал', 'интервал букв', 'пространство текста'],
586
+ pauseAnimations: ['приостановить анимацию', 'остановить анимацию', 'отключить анимацию'],
587
+ hideImages: ['скрыть изображения', 'убрать изображения', 'без изображений'],
588
+ dyslexiaFont: ['для дислексии', 'шрифт дислексии', 'читаемый шрифт'],
589
+ biggerCursor: ['увеличенный курсор', 'большой курсор', 'размер курсора'],
590
+ lineHeight: ['высота строки', 'интервал строк', 'пространство между строками'],
591
+ textAlign: ['выровнять текст', 'выравнивание текста', 'центрировать текст'],
592
+ screenReader: ['программа чтения', 'читать вслух', 'голосовой ридер'],
593
+ voiceControl: ['голосовая команда', 'голосовое управление', 'голосовые команды'],
594
+ resetAll: ['сбросить все', 'сбросить всё', 'очистить все', 'сбросить настройки']
595
+ },
596
+ tr: {
597
+ showMenu: ['menüyü göster', 'menü aç', 'erişilebilirlik menüsü', 'erişim menüsü'],
598
+ highContrast: ['yüksek kontrast', 'kontrast', 'karanlık mod', 'kontrastı artır'],
599
+ biggerText: ['daha büyük metin', 'büyük metin', 'metin boyutu', 'metni büyüt', 'daha büyük'],
600
+ textSpacing: ['metin aralığı', 'aralık', 'harf aralığı', 'metin boşluğu'],
601
+ pauseAnimations: ['animasyonları duraklat', 'animasyonları durdur', 'animasyonları kapat'],
602
+ hideImages: ['resimleri gizle', 'resimleri kaldır', 'resim yok'],
603
+ dyslexiaFont: ['disleksi dostu', 'disleksi yazı tipi', 'okunabilir yazı tipi'],
604
+ biggerCursor: ['daha büyük imleç', 'büyük imleç', 'imleç boyutu'],
605
+ lineHeight: ['satır yüksekliği', 'satır aralığı', 'satırlar arası boşluk'],
606
+ textAlign: ['metni hizala', 'metin hizalama', 'metni ortala'],
607
+ screenReader: ['ekran okuyucu', 'sesli oku', 'ses okuyucu'],
608
+ voiceControl: ['sesli komut', 'sesli kontrol', 'sesli komutlar'],
609
+ resetAll: ['hepsini sıfırla', 'tümünü sıfırla', 'hepsini temizle', 'ayarları sıfırla']
610
+ },
611
+ ar: {
612
+ showMenu: ['إظهار القائمة', 'فتح القائمة', 'قائمة إمكانية الوصول', 'قائمة الوصول'],
613
+ highContrast: ['تباين عالي', 'تباين', 'الوضع المظلم', 'زيادة التباين'],
614
+ biggerText: ['نص أكبر', 'نص كبير', 'حجم النص', 'تكبير النص', 'أكبر'],
615
+ textSpacing: ['تباعد النص', 'تباعد', 'تباعد الحروف', 'مساحة النص'],
616
+ pauseAnimations: ['إيقاف الرسوم المتحركة مؤقتا', 'إيقاف الرسوم المتحركة', 'تعطيل الرسوم المتحركة'],
617
+ hideImages: ['إخفاء الصور', 'إزالة الصور', 'بدون صور'],
618
+ dyslexiaFont: ['صديق لعسر القراءة', 'خط عسر القراءة', 'خط قابل للقراءة'],
619
+ biggerCursor: ['مؤشر أكبر', 'مؤشر كبير', 'حجم المؤشر'],
620
+ lineHeight: ['ارتفاع الخط', 'تباعد الأسطر', 'مساحة بين الأسطر'],
621
+ textAlign: ['محاذاة النص', 'محاذاة النص', 'توسيط النص'],
622
+ screenReader: ['قارئ الشاشة', 'اقرأ بصوت عالٍ', 'قارئ صوتي'],
623
+ voiceControl: ['الأمر الصوتي', 'التحكم الصوتي', 'الأوامر الصوتية'],
624
+ resetAll: ['إعادة تعيين الكل', 'إعادة تعيين جميع', 'مسح الكل', 'إعادة تعيين الإعدادات']
625
+ },
626
+ hi: {
627
+ showMenu: ['मेनू दिखाएं', 'मेनू खोलें', 'पहुंच मेनू', 'एक्सेस मेनू'],
628
+ highContrast: ['उच्च कंट्रास्ट', 'कंट्रास्ट', 'डार्क मोड', 'कंट्रास्ट बढ़ाएं'],
629
+ biggerText: ['बड़ा टेक्स्ट', 'बड़ा टेक्स्ट', 'टेक्स्ट का आकार', 'टेक्स्ट बढ़ाएं', 'बड़ा'],
630
+ textSpacing: ['टेक्स्ट स्पेसिंग', 'स्पेसिंग', 'अक्षर स्पेसिंग', 'टेक्स्ट स्पेस'],
631
+ pauseAnimations: ['एनिमेशन रोकें', 'एनिमेशन बंद करें', 'एनिमेशन अक्षम करें'],
632
+ hideImages: ['चित्र छिपाएं', 'चित्र हटाएं', 'कोई चित्र नहीं'],
633
+ dyslexiaFont: ['डिस्लेक्सिया के अनुकूल', 'डिस्लेक्सिया फ़ॉन्ट', 'पढ़ने योग्य फ़ॉन्ट'],
634
+ biggerCursor: ['बड़ा कर्सर', 'बड़ा कर्सर', 'कर्सर का आकार'],
635
+ lineHeight: ['लाइन की ऊंचाई', 'लाइन स्पेसिंग', 'लाइनों के बीच स्पेस'],
636
+ textAlign: ['टेक्स्ट अलाइन करें', 'टेक्स्ट संरेखण', 'टेक्स्ट केंद्र में करें'],
637
+ screenReader: ['स्क्रीन रीडर', 'जोर से पढ़ें', 'वॉयस रीडर'],
638
+ voiceControl: ['वॉयस कमांड', 'वॉयस नियंत्रण', 'वॉयस कमांड्स'],
639
+ resetAll: ['सभी रीसेट करें', 'सब कुछ रीसेट करें', 'सब साफ़ करें', 'सेटिंग्स रीसेट करें']
640
+ },
641
+ 'zh-cn': {
642
+ showMenu: ['显示菜单', '打开菜单', '辅助功能菜单', '访问菜单'],
643
+ highContrast: ['高对比度', '对比度', '暗模式', '增加对比度'],
644
+ biggerText: ['更大的文本', '大文本', '文本大小', '增大文本', '更大'],
645
+ textSpacing: ['文本间距', '间距', '字母间距', '文本空间'],
646
+ pauseAnimations: ['暂停动画', '停止动画', '禁用动画'],
647
+ hideImages: ['隐藏图片', '删除图片', '无图片'],
648
+ dyslexiaFont: ['阅读障碍友好', '阅读障碍字体', '可读字体'],
649
+ biggerCursor: ['更大的光标', '大光标', '光标大小'],
650
+ lineHeight: ['行高', '行间距', '行之间的空间'],
651
+ textAlign: ['对齐文本', '文本对齐', '居中文本'],
652
+ screenReader: ['屏幕阅读器', '大声朗读', '语音阅读器'],
653
+ voiceControl: ['语音命令', '语音控制', '语音命令'],
654
+ resetAll: ['重置全部', '重置所有', '清除全部', '重置设置']
655
+ },
656
+ jp: {
657
+ showMenu: ['メニューを表示', 'メニューを開く', 'アクセシビリティメニュー', 'アクセスメニュー'],
658
+ highContrast: ['ハイコントラスト', 'コントラスト', 'ダークモード', 'コントラストを上げる'],
659
+ biggerText: ['大きいテキスト', '大きなテキスト', 'テキストサイズ', 'テキストを大きく', 'より大きい'],
660
+ textSpacing: ['テキスト間隔', '間隔', '文字間隔', 'テキストスペース'],
661
+ pauseAnimations: ['アニメーション一時停止', 'アニメーション停止', 'アニメーション無効'],
662
+ hideImages: ['画像を非表示', '画像を削除', '画像なし'],
663
+ dyslexiaFont: ['ディスレクシア対応', 'ディスレクシアフォント', '読みやすいフォント'],
664
+ biggerCursor: ['大きいカーソル', '大きなカーソル', 'カーソルサイズ'],
665
+ lineHeight: ['行の高さ', '行間隔', '行間のスペース'],
666
+ textAlign: ['テキスト配置', 'テキスト配置', 'テキストを中央'],
667
+ screenReader: ['スクリーンリーダー', '音声で読む', '音声リーダー'],
668
+ voiceControl: ['音声コマンド', '音声制御', '音声コマンド'],
669
+ resetAll: ['すべてリセット', 'すべてリセット', 'すべてクリア', '設定をリセット']
670
+ }
515
671
  },
516
672
 
517
673
  // Grid Layout Configuration
@@ -1013,8 +1169,8 @@ const pageStyles = `
1013
1169
  // ===========================================
1014
1170
 
1015
1171
  const icons = {
1016
- buttonsvg: `<svg xmlns="http://www.w3.org/2000/svg" style="fill:white;" viewBox="0 0 24 24" width="30px" height="30px"><path d="M0 0h24v24H0V0z" fill="none"></path><path d="M20.5 6c-2.61.7-5.67 1-8.5 1s-5.89-.3-8.5-1L3 8c1.86.5 4 .83 6 1v13h2v-6h2v6h2V9c2-.17 4.14-.5 6-1l-.5-2zM12 6c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z"></path></svg>`,
1017
- highContrast: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" version="1.2" viewBox="0 0 35 35"><path fill="currentColor" fill-rule="evenodd" d="M1.89998 15.6285c0-7.58203 6.14649-13.72852 13.72852-13.72852 7.5821 0 13.7286 6.14649 13.7286 13.72852 0 .6081-.0395 1.2069-.1161 1.794.5933.2913 1.1478.6497 1.6534 1.0654.1725-.9268.2627-1.8825.2627-2.8594 0-8.57615-6.9524-15.5285244-15.5286-15.5285244C7.05235.0999756.0999756 7.05235.0999756 15.6285c0 8.5762 6.9523744 15.5286 15.5285244 15.5286 1.2241 0 2.415-.1416 3.5574-.4093-.4388-.4866-.8222-1.0242-1.1402-1.6028-.7847.1394-1.5924.2121-2.4172.2121-7.58203 0-13.72852-6.1465-13.72852-13.7286Z" clip-rule="evenodd"/><path fill="currentColor" fill-rule="evenodd" d="M2.35 15.6286C2.35 8.29502 8.29502 2.35 15.6286 2.35c7.3335 0 13.2785 5.94502 13.2785 13.2786 0 .5408-.0323 1.0741-.0951 1.5979.444.1881.8687.4128 1.2703.6703.1151-.7392.1748-1.4967.1748-2.2682C30.2571 7.54943 23.7077 1 15.6286 1 7.54943 1 15.6286 1 7.54943 1 15.6286c0 8.0791 6.54943 14.6285 14.6286 14.6285 1.0033 0 1.9831-.101 2.9297-.2934-.276-.3898-.52-.8038-.7282-1.2382-.716.1195-1.4515.1816-2.2015.1816-7.33358 0-13.2786-5.945-13.2786-13.2785Z" clip-rule="evenodd"/><path fill="currentColor" fill-rule="evenodd" d="M15.6286 1C7.54943 1 1 7.54943 1 15.6286c0 8.0791 6.54943 14.6285 14.6286 14.6285" clip-rule="evenodd"/><path stroke="currentColor" stroke-width="1.8" d="M15.6286 1C7.54943 1 1 7.54943 1 15.6286c0 8.0791 6.54943 14.6285 14.6286 14.6285"/><path fill="currentColor" fill-rule="evenodd" d="M22.8729 25.114c0-1.3811 1.0901-2.5007 2.4359-2.5007 1.3459 0 2.436 1.1196 2.436 2.5007 0 1.38-1.0901 2.4997-2.436 2.4997-1.3458 0-2.4359-1.1197-2.4359-2.4997Zm7.2258-2.0373c-.0899-.2248-.071-.4785.0512-.6875l.912-1.5598c.0898-.1532.0668-.3504-.0574-.4779l-1.0556-1.0832c-.1232-.1264-.3153-.1511-.4657-.0589l-1.5225.9374c-.201.1237-.4495.1427-.667.051-.2181-.092-.3797-.2819-.4358-.5118l-.4329-1.7763c-.0428-.1735-.1953-.2957-.3696-.2957h-1.4931c-.1744 0-.3268.1222-.3696.2957l-.433 1.7763c-.056.2299-.2177.4198-.4357.5118-.2176.0917-.466.0727-.6671-.051l-1.5225-.9374c-.1503-.0922-.3424-.0675-.4656.0589l-1.0556 1.0832c-.1243.1275-.1473.3247-.0575.4779l.9121 1.5598c.1222.209.1411.4627.0511.6875-.0895.2239-.2806.3916-.5142.4514l-1.7165.4395c-.1692.0439-.2882.2003-.2882.3803v1.5311c0 .18.119.3364.2882.3804l1.7165.4394c.2336.0599.4247.2276.5142.4515.09.2247.0711.4785-.0511.6874l-.9121 1.5599c-.0898.1532-.0668.3503.0575.4778l1.0556 1.0833c.1232.1264.3153.151.4656.0589l1.5225-.9374c.2011-.1238.4495-.1428.6671-.051.218.092.3797.2818.4357.5118l.433 1.7762c.0428.1736.1952.2968.3696.2968h1.4931c.1743 0 .3268-.1232.3696-.2968l.4329-1.7762c.0561-.23.2177-.4198.4358-.5118.2175-.0918.466-.0728.667.051l1.5225.9374c.1504.0921.3425.0675.4657-.0589l1.0556-1.0833c.1242-.1275.1472-.3246.0574-.4778l-.912-1.5599c-.1222-.2089-.1411-.4627-.0512-.6874.0896-.2239.2806-.3916.5142-.4515l1.7166-.4394c.1691-.044.2881-.2004.2881-.3804v-1.5311c0-.18-.119-.3364-.2881-.3803l-1.7166-.4395c-.2336-.0598-.4246-.2275-.5142-.4514Z" clip-rule="evenodd"/></svg>`,
1172
+ buttonsvg: `<svg xmlns="http://www.w3.org/2000/svg" style="fill:white;" viewBox="0 0 24 24" width="30px" height="30px"><path d="M0 0h24v24H0V0z" fill="none"></path><path d="M20.5 6c-2.61 0.7-5.67 1-8.5 1s-5.89-0.3-8.5-1L3 8c1.86 0.5 4 0.83 6 1v13h2v-6h2v6h2V9c2-0.17 4.14-0.5 6-1l-0.5-2zM12 6c1.1 0 2-0.9 2-2s-0.9-2-2-2-2 0.9-2 2 0.9 2 2 2z"></path></svg>`,
1173
+ highContrast: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" version="1.2" viewBox="0 0 35 35"><path fill="currentColor" fill-rule="evenodd" d="M1.89998 15.6285c0-7.58203 6.14649-13.72852 13.72852-13.72852 7.5821 0 13.7286 6.14649 13.7286 13.72852 0 0.6081-0.0395 1.2069-0.1161 1.794 0.5933 0.2913 1.1478 0.6497 1.6534 1.0654 0.1725-0.9268 0.2627-1.8825 0.2627-2.8594 0-8.57615-6.9524-15.5285244-15.5286-15.5285244C7.05235 0.0999756 0.0999756 7.05235 0.0999756 15.6285c0 8.5762 6.9523744 15.5286 15.5285244 15.5286 1.2241 0 2.415-0.1416 3.5574-0.4093-0.4388-0.4866-0.8222-1.0242-1.1402-1.6028-0.7847 0.1394-1.5924 0.2121-2.4172 0.2121-7.58203 0-13.72852-6.1465-13.72852-13.7286Z" clip-rule="evenodd"/><path fill="currentColor" fill-rule="evenodd" d="M2.35 15.6286C2.35 8.29502 8.29502 2.35 15.6286 2.35c7.3335 0 13.2785 5.94502 13.2785 13.2786 0 0.5408-0.0323 1.0741-0.0951 1.5979 0.444 0.1881 0.8687 0.4128 1.2703 0.6703 0.1151-0.7392 0.1748-1.4967 0.1748-2.2682C30.2571 7.54943 23.7077 1 15.6286 1 7.54943 1 1 7.54943 1 15.6286c0 8.0791 6.54943 14.6285 14.6286 14.6285 1.0033 0 1.9831-0.101 2.9297-0.2934-0.276-0.3898-0.52-0.8038-0.7282-1.2382-0.716 0.1195-1.4515 0.1816-2.2015 0.1816-7.33358 0-13.2786-5.945-13.2786-13.2785Z" clip-rule="evenodd"/><path fill="currentColor" fill-rule="evenodd" d="M15.6286 1C7.54943 1 1 7.54943 1 15.6286c0 8.0791 6.54943 14.6285 14.6286 14.6285" clip-rule="evenodd"/><path stroke="currentColor" stroke-width="1.8" d="M15.6286 1C7.54943 1 1 7.54943 1 15.6286c0 8.0791 6.54943 14.6285 14.6286 14.6285"/><path fill="currentColor" fill-rule="evenodd" d="M22.8729 25.114c0-1.3811 1.0901-2.5007 2.4359-2.5007 1.3459 0 2.436 1.1196 2.436 2.5007 0 1.38-1.0901 2.4997-2.436 2.4997-1.3458 0-2.4359-1.1197-2.4359-2.4997Zm7.2258-2.0373c-0.0899-0.2248-0.071-0.4785 0.0512-0.6875l0.912-1.5598c0.0898-0.1532 0.0668-0.3504-0.0574-0.4779l-1.0556-1.0832c-0.1232-0.1264-0.3153-0.1511-0.4657-0.0589l-1.5225 0.9374c-0.201 0.1237-0.4495 0.1427-0.667 0.051-0.2181-0.092-0.3797-0.2819-0.4358-0.5118l-0.4329-1.7763c-0.0428-0.1735-0.1953-0.2957-0.3696-0.2957h-1.4931c-0.1744 0-0.3268 0.1222-0.3696 0.2957l-0.433 1.7763c-0.056 0.2299-0.2177 0.4198-0.4357 0.5118-0.2176 0.0917-0.466 0.0727-0.6671-0.051l-1.5225-0.9374c-0.1503-0.0922-0.3424-0.0675-0.4656 0.0589l-1.0556 1.0832c-0.1243 0.1275-0.1473 0.3247-0.0575 0.4779l0.9121 1.5598c0.1222 0.209 0.1411 0.4627 0.0511 0.6875-0.0895 0.2239-0.2806 0.3916-0.5142 0.4514l-1.7165 0.4395c-0.1692 0.0439-0.2882 0.2003-0.2882 0.3803v1.5311c0 0.18 0.119 0.3364 0.2882 0.3804l1.7165 0.4394c0.2336 0.0599 0.4247 0.2276 0.5142 0.4515 0.09 0.2247 0.0711 0.4785-0.0511 0.6874l-0.9121 1.5599c-0.0898 0.1532-0.0668 0.3503 0.0575 0.4778l1.0556 1.0833c0.1232 0.1264 0.3153 0.151 0.4656 0.0589l1.5225-0.9374c0.2011-0.1238 0.4495-0.1428 0.6671-0.051 0.218 0.092 0.3797 0.2818 0.4357 0.5118l0.433 1.7762c0.0428 0.1736 0.1952 0.2968 0.3696 0.2968h1.4931c0.1743 0 0.3268-0.1232 0.3696-0.2968l0.4329-1.7762c0.0561-0.23 0.2177-0.4198 0.4358-0.5118 0.2175-0.0918 0.466-0.0728 0.667 0.051l1.5225 0.9374c0.1504 0.0921 0.3425 0.0675 0.4657-0.0589l1.0556-1.0833c0.1242-0.1275 0.1472-0.3246 0.0574-0.4778l-0.912-1.5599c-0.1222-0.2089-0.1411-0.4627-0.0512-0.6874 0.0896-0.2239 0.2806-0.3916 0.5142-0.4515l1.7166-0.4394c0.1691-0.044 0.2881-0.2004 0.2881-0.3804v-1.5311c0-0.18-0.119-0.3364-0.2881-0.3803l-1.7166-0.4395c-0.2336-0.0598-0.4246-0.2275-0.5142-0.4514Z" clip-rule="evenodd"/></svg>`,
1018
1174
  biggerText: `<svg xmlns="http://www.w3.org/2000/svg" version="1.2" viewBox="0 0 36 23"><g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-width="2"><path stroke-linejoin="round" d="M26.58 21.3225806V1m-7.92 4.06451613V1H34.5v4.06451613"/><path d="M22.62 21.3225806h7.92"/><path stroke-linejoin="round" d="M6.78 18.6129032V5.06451613M1.5 7.77419355V5.06451613h10.56v2.70967742"/><path d="M4.14 18.6129032h5.28"/></g></svg>`,
1019
1175
  textSpacing: `<svg xmlns="http://www.w3.org/2000/svg" width="800px" height="800px" viewBox="0 0 15 15" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.55293 0.999969C4.75295 0.999969 4.93372 1.11917 5.0125 1.30301L8.01106 8.29982C8.11984 8.55363 8.00226 8.84757 7.74844 8.95635C7.49463 9.06512 7.20069 8.94754 7.09191 8.69373L6.11613 6.41685H2.98973L2.01395 8.69373C1.90517 8.94754 1.61123 9.06512 1.35742 8.95635C1.1036 8.84757 0.986023 8.55363 1.0948 8.29982L4.09336 1.30301C4.17214 1.11917 4.35291 0.999969 4.55293 0.999969ZM4.55293 2.76929L5.75186 5.56685H3.354L4.55293 2.76929ZM11.0562 9.00214C11.2617 9.00214 11.4463 8.87633 11.5215 8.68502L14.2733 1.68299C14.3743 1.42598 14.2478 1.13575 13.9908 1.03475C13.7338 0.933747 13.4436 1.06021 13.3426 1.31722L11.0562 7.13514L8.76973 1.31722C8.66873 1.06021 8.3785 0.933747 8.1215 1.03475C7.86449 1.13575 7.73802 1.42598 7.83902 1.68299L10.5908 8.68502C10.666 8.87633 10.8506 9.00214 11.0562 9.00214ZM14.9537 12.4999C14.9537 12.606 14.9115 12.7077 14.8365 12.7828L12.8365 14.7828C12.6803 14.939 12.4271 14.939 12.2708 14.7828C12.1146 14.6265 12.1146 14.3733 12.2708 14.2171L13.588 12.8999H1.51937L2.83653 14.2171C2.99274 14.3733 2.99274 14.6265 2.83653 14.7828C2.68032 14.939 2.42705 14.939 2.27084 14.7828L0.270843 12.7828C0.195828 12.7077 0.153687 12.606 0.153687 12.4999C0.153687 12.3938 0.195828 12.2921 0.270843 12.2171L2.27084 10.2171C2.42705 10.0609 2.68032 10.0609 2.83653 10.2171C2.99274 10.3733 2.99274 10.6265 2.83653 10.7828L1.51937 12.0999L13.588 12.0999L12.2708 10.7828C12.1146 10.6265 12.1146 10.3733 12.2708 10.2171C12.4271 10.0609 12.6803 10.0609 12.8365 10.2171L14.8365 12.2171C14.9115 12.2921 14.9537 12.3938 14.9537 12.4999Z" fill="#000000"/></svg>`,
1020
1176
  pauseAnimations: `<svg xmlns="http://www.w3.org/2000/svg" version="1.2" viewBox="0 0 37 36"><g fill="none" fill-rule="evenodd"><path fill="currentColor" d="M15.8087111 23.6666667h-1.2702778c-.4429444 0-.8018333-.3598334-.8018333-.8027778v-9.7277778c0-.4429444.3588889-.8027778.8018333-.8027778h1.2702778c.4429445 0 .8027778.3598334.8027778.8027778v9.7277778c0 .4429444-.3598333.8027778-.8027778.8027778m6.6525722 0h-1.2702777c-.442 0-.8018334-.3598334-.8018334-.8027778v-9.7277778c0-.4429444.3598334-.8027778.8018334-.8027778h1.2702777c.4438889 0 .8027778.3598334.8027778.8027778v9.7277778c0 .4429444-.3588889.8027778-.8027778.8027778"/><path stroke="currentColor" stroke-linecap="round" stroke-width="1.88888889" d="M18.5 4.77777778V1m0 34v-3.7777778M31.7222222 18H35.5m-34 0h3.77777778m3.87278889-9.34943333L6.47873333 5.97967778M30.5204167 30.0204167l-2.6708889-2.6708889m-.0000945-18.69896113 2.6708889-2.67088889M6.47911111 30.0204167l2.67183333-2.6708889M23.5542889 5.78219444l1.4440555-3.49066666M12.0013722 33.7087556l1.4440556-3.4906667m17.2723778-7.1638 3.4906666 1.4440555M2.79124444 11.5013722l3.49066667 1.4440556m7.15274999-7.15860558L11.9877722 2.2971m13.0246445 31.4061778-1.4468889-3.4897222m7.14765-17.2788945L34.2029 11.4877722M2.79672222 24.5124167l3.48972222-1.4468889"/></g></svg>`,
@@ -1090,8 +1246,12 @@ function createShadowContainer() {
1090
1246
 
1091
1247
  // Cache for DOM elements to improve performance
1092
1248
  const domCache = {
1093
- body: document.body,
1094
- documentElement: document.documentElement,
1249
+ get body() {
1250
+ return document.body;
1251
+ },
1252
+ get documentElement() {
1253
+ return document.documentElement;
1254
+ },
1095
1255
  images: null,
1096
1256
  lastImageUpdate: 0,
1097
1257
  getImages: function () {
@@ -1106,6 +1266,12 @@ const domCache = {
1106
1266
 
1107
1267
  // Apply saved settings from localStorage (optimized)
1108
1268
  function applySettings() {
1269
+ // Check if body element exists
1270
+ if (!domCache.body || !domCache.documentElement) {
1271
+ console.warn('Body or document element not ready yet');
1272
+ return;
1273
+ }
1274
+
1109
1275
  const settings = [
1110
1276
  { key: 'biggerCursor', className: 'snn-bigger-cursor' },
1111
1277
  { key: 'biggerText', className: 'snn-bigger-text' },
@@ -1140,79 +1306,121 @@ function applySettings() {
1140
1306
  }
1141
1307
  });
1142
1308
 
1143
- // Apply all class changes at once
1309
+ // Apply all class changes at once - ONLY remove classes that start with 'snn-'
1144
1310
  if (bodyClassesToAdd.length > 0) {
1145
1311
  domCache.body.classList.add(...bodyClassesToAdd);
1146
1312
  }
1147
1313
  if (bodyClassesToRemove.length > 0) {
1148
- domCache.body.classList.remove(...bodyClassesToRemove);
1314
+ // Only remove our own classes, never remove classes that don't start with 'snn-'
1315
+ bodyClassesToRemove.forEach(className => {
1316
+ if (className.startsWith('snn-')) {
1317
+ domCache.body.classList.remove(className);
1318
+ }
1319
+ });
1149
1320
  }
1150
1321
  if (docClassesToAdd.length > 0) {
1151
1322
  domCache.documentElement.classList.add(...docClassesToAdd);
1152
1323
  }
1153
1324
  if (docClassesToRemove.length > 0) {
1154
- domCache.documentElement.classList.remove(...docClassesToRemove);
1325
+ // Only remove our own classes, never remove classes that don't start with 'snn-'
1326
+ docClassesToRemove.forEach(className => {
1327
+ if (className.startsWith('snn-')) {
1328
+ domCache.documentElement.classList.remove(className);
1329
+ }
1330
+ });
1155
1331
  }
1156
1332
 
1157
- // Handle font selection
1333
+ // Handle font selection - only remove widget's own font classes
1158
1334
  const fontClasses = ['snn-font-arial', 'snn-font-times', 'snn-font-verdana'];
1159
- domCache.body.classList.remove(...fontClasses);
1335
+ fontClasses.forEach(className => {
1336
+ if (domCache.body.classList.contains(className)) {
1337
+ domCache.body.classList.remove(className);
1338
+ }
1339
+ });
1160
1340
  const selectedFont = localStorage.getItem('fontSelection');
1161
1341
  if (selectedFont) {
1162
1342
  domCache.body.classList.add(`snn-font-${selectedFont}`);
1163
1343
  }
1164
1344
 
1165
- // Handle color filters
1345
+ // Handle color filters - only remove widget's own filter classes
1166
1346
  const filterClasses = ['snn-filter-protanopia', 'snn-filter-deuteranopia', 'snn-filter-tritanopia', 'snn-filter-grayscale'];
1167
- domCache.documentElement.classList.remove(...filterClasses);
1347
+ filterClasses.forEach(className => {
1348
+ if (domCache.documentElement.classList.contains(className)) {
1349
+ domCache.documentElement.classList.remove(className);
1350
+ }
1351
+ });
1168
1352
  const selectedFilter = localStorage.getItem('colorFilter');
1169
1353
  if (selectedFilter) {
1170
1354
  domCache.documentElement.classList.add(`snn-filter-${selectedFilter}`);
1171
1355
  }
1172
1356
 
1173
- // Handle saturation filters
1357
+ // Handle saturation filters - only remove widget's own saturation classes
1174
1358
  const saturationClasses = ['snn-saturation-low', 'snn-saturation-high', 'snn-saturation-none'];
1175
- domCache.documentElement.classList.remove(...saturationClasses);
1359
+ saturationClasses.forEach(className => {
1360
+ if (domCache.documentElement.classList.contains(className)) {
1361
+ domCache.documentElement.classList.remove(className);
1362
+ }
1363
+ });
1176
1364
  const selectedSaturation = localStorage.getItem('saturation');
1177
1365
  if (selectedSaturation) {
1178
1366
  domCache.documentElement.classList.add(`snn-saturation-${selectedSaturation}`);
1179
1367
  }
1180
1368
 
1181
- // Handle text alignment
1369
+ // Handle text alignment - only remove widget's own alignment classes
1182
1370
  const alignClasses = ['snn-text-align-left', 'snn-text-align-center', 'snn-text-align-right'];
1183
- domCache.body.classList.remove(...alignClasses);
1371
+ alignClasses.forEach(className => {
1372
+ if (domCache.body.classList.contains(className)) {
1373
+ domCache.body.classList.remove(className);
1374
+ }
1375
+ });
1184
1376
  const selectedAlign = localStorage.getItem('textAlign');
1185
1377
  if (selectedAlign) {
1186
1378
  domCache.body.classList.add(`snn-text-align-${selectedAlign}`);
1187
1379
  }
1188
1380
 
1189
- // Handle bigger text
1381
+ // Handle bigger text - only remove widget's own text size classes
1190
1382
  const textClasses = ['snn-bigger-text-medium', 'snn-bigger-text-large', 'snn-bigger-text-xlarge'];
1191
- domCache.body.classList.remove(...textClasses);
1383
+ textClasses.forEach(className => {
1384
+ if (domCache.body.classList.contains(className)) {
1385
+ domCache.body.classList.remove(className);
1386
+ }
1387
+ });
1192
1388
  const selectedTextSize = localStorage.getItem('biggerText');
1193
1389
  if (selectedTextSize) {
1194
1390
  domCache.body.classList.add(`snn-bigger-text-${selectedTextSize}`);
1195
1391
  }
1196
1392
 
1197
- // Handle high contrast
1393
+ // Handle high contrast - only remove widget's own contrast classes
1198
1394
  const contrastClasses = ['snn-high-contrast-medium', 'snn-high-contrast-high', 'snn-high-contrast-ultra'];
1199
- domCache.body.classList.remove(...contrastClasses);
1395
+ contrastClasses.forEach(className => {
1396
+ if (domCache.body.classList.contains(className)) {
1397
+ domCache.body.classList.remove(className);
1398
+ }
1399
+ });
1200
1400
  const selectedContrast = localStorage.getItem('highContrast');
1201
1401
  if (selectedContrast) {
1202
1402
  domCache.body.classList.add(`snn-high-contrast-${selectedContrast}`);
1203
1403
  }
1204
1404
 
1205
- // Handle Text Spacing (3 Levels)
1405
+ // Handle Text Spacing (3 Levels) - only remove widget's own spacing classes
1206
1406
  const spacingClasses = ['snn-text-spacing-light', 'snn-text-spacing-medium', 'snn-text-spacing-heavy'];
1207
- domCache.body.classList.remove(...spacingClasses);
1407
+ spacingClasses.forEach(className => {
1408
+ if (domCache.body.classList.contains(className)) {
1409
+ domCache.body.classList.remove(className);
1410
+ }
1411
+ });
1208
1412
  const selectedSpacing = localStorage.getItem('textSpacing');
1209
1413
  if (selectedSpacing) {
1210
1414
  domCache.body.classList.add(`snn-text-spacing-${selectedSpacing}`);
1211
1415
  }
1212
1416
 
1213
- // Handle Line Height (3 Levels)
1417
+ // Handle Line Height (3 Levels) - only remove widget's own line height classes
1214
1418
  const lineHeightClasses = ['snn-line-height-2em', 'snn-line-height-3em', 'snn-line-height-4em'];
1215
- domCache.body.classList.remove(...lineHeightClasses);
1419
+ lineHeightClasses.forEach(className => {
1420
+ if (domCache.body.classList.contains(className)) {
1421
+ domCache.body.classList.remove(className);
1422
+ }
1423
+ });
1216
1424
  const selectedLineHeight = localStorage.getItem('lineHeight');
1217
1425
  if (selectedLineHeight) {
1218
1426
  domCache.body.classList.add(`snn-line-height-${selectedLineHeight}`);
@@ -1283,7 +1491,14 @@ function resetAccessibilitySettings() {
1283
1491
  ];
1284
1492
  keys.forEach((key) => localStorage.removeItem(key));
1285
1493
 
1286
- // Remove all CSS classes
1494
+ // Remove only widget's own CSS classes - never touch existing body/document classes
1495
+ // Check if body and documentElement exist first
1496
+ if (!document.body || !document.documentElement) {
1497
+ console.warn('Body or document element not ready during reset');
1498
+ return;
1499
+ }
1500
+
1501
+ // Remove body classes only if they exist and start with 'snn-'
1287
1502
  const cssClasses = [
1288
1503
  'snn-bigger-cursor',
1289
1504
  'snn-bigger-text',
@@ -1292,17 +1507,30 @@ function resetAccessibilitySettings() {
1292
1507
  'snn-text-align',
1293
1508
  'snn-font-arial',
1294
1509
  'snn-font-times',
1295
- 'snn-font-verdana'
1296
- ];
1297
- cssClasses.forEach(cls => document.body.classList.remove(cls));
1298
-
1299
- const bodyClasses2 = [
1510
+ 'snn-font-verdana',
1300
1511
  'snn-high-contrast-medium',
1301
1512
  'snn-high-contrast-high',
1302
- 'snn-high-contrast-ultra'
1513
+ 'snn-high-contrast-ultra',
1514
+ 'snn-bigger-text-medium',
1515
+ 'snn-bigger-text-large',
1516
+ 'snn-bigger-text-xlarge',
1517
+ 'snn-text-spacing-light',
1518
+ 'snn-text-spacing-medium',
1519
+ 'snn-text-spacing-heavy',
1520
+ 'snn-line-height-2em',
1521
+ 'snn-line-height-3em',
1522
+ 'snn-line-height-4em',
1523
+ 'snn-text-align-left',
1524
+ 'snn-text-align-center',
1525
+ 'snn-text-align-right'
1303
1526
  ];
1304
- bodyClasses2.forEach(cls => document.body.classList.remove(cls));
1527
+ cssClasses.forEach(cls => {
1528
+ if (document.body.classList.contains(cls)) {
1529
+ document.body.classList.remove(cls);
1530
+ }
1531
+ });
1305
1532
 
1533
+ // Remove document element classes only if they exist and start with 'snn-'
1306
1534
  const documentClasses = [
1307
1535
  'snn-filter-protanopia',
1308
1536
  'snn-filter-deuteranopia',
@@ -1312,21 +1540,11 @@ function resetAccessibilitySettings() {
1312
1540
  'snn-saturation-high',
1313
1541
  'snn-saturation-none'
1314
1542
  ];
1315
- documentClasses.forEach(cls => document.documentElement.classList.remove(cls));
1316
-
1317
- const textSizeClasses = [
1318
- 'snn-bigger-text-medium',
1319
- 'snn-bigger-text-large',
1320
- 'snn-bigger-text-xlarge'
1321
- ];
1322
- textSizeClasses.forEach(cls => document.body.classList.remove(cls));
1323
-
1324
- // Clear Multi-level classes
1325
- const spacingClasses = ['snn-text-spacing-light', 'snn-text-spacing-medium', 'snn-text-spacing-heavy'];
1326
- spacingClasses.forEach(cls => document.body.classList.remove(cls));
1327
-
1328
- const lineHeightClasses = ['snn-line-height-2em', 'snn-line-height-3em', 'snn-line-height-4em'];
1329
- lineHeightClasses.forEach(cls => document.body.classList.remove(cls));
1543
+ documentClasses.forEach(cls => {
1544
+ if (document.documentElement.classList.contains(cls)) {
1545
+ document.documentElement.classList.remove(cls);
1546
+ }
1547
+ });
1330
1548
 
1331
1549
  domCache.getImages().forEach((img) => (img.style.display = ''));
1332
1550
 
@@ -1455,6 +1673,7 @@ function createActionButton(buttonText, actionFunction, iconSVG, optionsConfig =
1455
1673
  button.setAttribute('data-options-config', optionsConfig ? JSON.stringify(optionsConfig) : '');
1456
1674
  if (optionId) {
1457
1675
  button.setAttribute('data-accessibility-option-id', optionId);
1676
+ button.setAttribute('data-key', optionId); // Add data-key for voice commands
1458
1677
  }
1459
1678
 
1460
1679
  // Update initial status
@@ -1559,9 +1778,13 @@ function handleFontSelection() {
1559
1778
  const currentIndex = fonts.indexOf(currentFont);
1560
1779
  const nextIndex = (currentIndex + 1) % (fonts.length + 1); // +1 for default
1561
1780
 
1562
- // Remove all font classes in one operation
1781
+ // Remove only widget's own font classes
1563
1782
  const fontClasses = ['snn-font-arial', 'snn-font-times', 'snn-font-verdana'];
1564
- domCache.body.classList.remove(...fontClasses);
1783
+ fontClasses.forEach(className => {
1784
+ if (domCache.body.classList.contains(className)) {
1785
+ domCache.body.classList.remove(className);
1786
+ }
1787
+ });
1565
1788
 
1566
1789
  if (nextIndex === fonts.length) {
1567
1790
  // Default font
@@ -1582,9 +1805,13 @@ function handleSaturation() {
1582
1805
  const currentIndex = saturations.indexOf(currentSaturation);
1583
1806
  const nextIndex = (currentIndex + 1) % (saturations.length + 1); // +1 for default
1584
1807
 
1585
- // Remove all saturation classes in one operation
1808
+ // Remove only widget's own saturation classes
1586
1809
  const saturationClasses = ['snn-saturation-low', 'snn-saturation-high', 'snn-saturation-none'];
1587
- domCache.documentElement.classList.remove(...saturationClasses);
1810
+ saturationClasses.forEach(className => {
1811
+ if (domCache.documentElement.classList.contains(className)) {
1812
+ domCache.documentElement.classList.remove(className);
1813
+ }
1814
+ });
1588
1815
 
1589
1816
  if (nextIndex === saturations.length) {
1590
1817
  // Default saturation
@@ -1608,9 +1835,13 @@ function handleColorFilter() {
1608
1835
  const currentIndex = filters.indexOf(currentFilter);
1609
1836
  const nextIndex = (currentIndex + 1) % (filters.length + 1); // +1 for none
1610
1837
 
1611
- // Remove all filter classes in one operation
1838
+ // Remove only widget's own filter classes
1612
1839
  const filterClasses = ['snn-filter-protanopia', 'snn-filter-deuteranopia', 'snn-filter-tritanopia', 'snn-filter-grayscale'];
1613
- domCache.documentElement.classList.remove(...filterClasses);
1840
+ filterClasses.forEach(className => {
1841
+ if (domCache.documentElement.classList.contains(className)) {
1842
+ domCache.documentElement.classList.remove(className);
1843
+ }
1844
+ });
1614
1845
 
1615
1846
  if (nextIndex === filters.length) {
1616
1847
  // No filter
@@ -1631,9 +1862,13 @@ function handleTextAlign() {
1631
1862
  const currentIndex = alignments.indexOf(currentAlign);
1632
1863
  const nextIndex = (currentIndex + 1) % (alignments.length + 1); // +1 for none
1633
1864
 
1634
- // Remove all alignment classes
1865
+ // Remove only widget's own alignment classes
1635
1866
  const alignClasses = ['snn-text-align-left', 'snn-text-align-center', 'snn-text-align-right'];
1636
- domCache.body.classList.remove(...alignClasses);
1867
+ alignClasses.forEach(className => {
1868
+ if (domCache.body.classList.contains(className)) {
1869
+ domCache.body.classList.remove(className);
1870
+ }
1871
+ });
1637
1872
 
1638
1873
  if (nextIndex === alignments.length) {
1639
1874
  // Default alignment
@@ -1654,9 +1889,13 @@ function handleBiggerText() {
1654
1889
  const currentIndex = textSizes.indexOf(currentSize);
1655
1890
  const nextIndex = (currentIndex + 1) % (textSizes.length + 1); // +1 for none
1656
1891
 
1657
- // Remove all text size classes
1892
+ // Remove only widget's own text size classes
1658
1893
  const textClasses = ['snn-bigger-text-medium', 'snn-bigger-text-large', 'snn-bigger-text-xlarge'];
1659
- domCache.body.classList.remove(...textClasses);
1894
+ textClasses.forEach(className => {
1895
+ if (domCache.body.classList.contains(className)) {
1896
+ domCache.body.classList.remove(className);
1897
+ }
1898
+ });
1660
1899
 
1661
1900
  if (nextIndex === textSizes.length) {
1662
1901
  // Default text size
@@ -1677,9 +1916,13 @@ function handleHighContrast() {
1677
1916
  const currentIndex = contrastLevels.indexOf(currentContrast);
1678
1917
  const nextIndex = (currentIndex + 1) % (contrastLevels.length + 1); // +1 for none
1679
1918
 
1680
- // Remove all contrast classes
1919
+ // Remove only widget's own contrast classes
1681
1920
  const contrastClasses = ['snn-high-contrast-medium', 'snn-high-contrast-high', 'snn-high-contrast-ultra'];
1682
- domCache.body.classList.remove(...contrastClasses);
1921
+ contrastClasses.forEach(className => {
1922
+ if (domCache.body.classList.contains(className)) {
1923
+ domCache.body.classList.remove(className);
1924
+ }
1925
+ });
1683
1926
 
1684
1927
  if (nextIndex === contrastLevels.length) {
1685
1928
  // Default contrast
@@ -1700,9 +1943,13 @@ function handleTextSpacing() {
1700
1943
  const currentIndex = spacings.indexOf(currentSpacing);
1701
1944
  const nextIndex = (currentIndex + 1) % (spacings.length + 1); // +1 for none
1702
1945
 
1703
- // Remove all spacing classes
1946
+ // Remove only widget's own spacing classes
1704
1947
  const spacingClasses = ['snn-text-spacing-light', 'snn-text-spacing-medium', 'snn-text-spacing-heavy'];
1705
- domCache.body.classList.remove(...spacingClasses);
1948
+ spacingClasses.forEach(className => {
1949
+ if (domCache.body.classList.contains(className)) {
1950
+ domCache.body.classList.remove(className);
1951
+ }
1952
+ });
1706
1953
 
1707
1954
  if (nextIndex === spacings.length) {
1708
1955
  // Default
@@ -1723,9 +1970,13 @@ function handleLineHeight() {
1723
1970
  const currentIndex = heights.indexOf(currentHeight);
1724
1971
  const nextIndex = (currentIndex + 1) % (heights.length + 1); // +1 for none
1725
1972
 
1726
- // Remove all line height classes
1973
+ // Remove only widget's own line height classes
1727
1974
  const heightClasses = ['snn-line-height-2em', 'snn-line-height-3em', 'snn-line-height-4em'];
1728
- domCache.body.classList.remove(...heightClasses);
1975
+ heightClasses.forEach(className => {
1976
+ if (domCache.body.classList.contains(className)) {
1977
+ domCache.body.classList.remove(className);
1978
+ }
1979
+ });
1729
1980
 
1730
1981
  if (nextIndex === heights.length) {
1731
1982
  // Default
@@ -1754,7 +2005,24 @@ const screenReader = {
1754
2005
  if (content.trim() !== '') {
1755
2006
  window.speechSynthesis.cancel();
1756
2007
  const speech = new SpeechSynthesisUtterance(content);
1757
- speech.lang = 'en-US';
2008
+
2009
+ // Set language based on current interface language
2010
+ let speechLang = 'en-US'; // default
2011
+ switch(currentLanguage) {
2012
+ case 'de': speechLang = 'de-DE'; break;
2013
+ case 'es': speechLang = 'es-ES'; break;
2014
+ case 'it': speechLang = 'it-IT'; break;
2015
+ case 'fr': speechLang = 'fr-FR'; break;
2016
+ case 'ru': speechLang = 'ru-RU'; break;
2017
+ case 'tr': speechLang = 'tr-TR'; break;
2018
+ case 'ar': speechLang = 'ar-SA'; break;
2019
+ case 'hi': speechLang = 'hi-IN'; break;
2020
+ case 'zh-cn': speechLang = 'zh-CN'; break;
2021
+ case 'jp': speechLang = 'ja-JP'; break;
2022
+ default: speechLang = 'en-US';
2023
+ }
2024
+ speech.lang = speechLang;
2025
+
1758
2026
  speech.onerror = function (event) {
1759
2027
  console.warn('Speech synthesis error:', event.error);
1760
2028
  };
@@ -1775,10 +2043,26 @@ const screenReader = {
1775
2043
  localStorage.setItem('screenReader', isActive);
1776
2044
 
1777
2045
  try {
2046
+ // Set language based on current interface language
2047
+ let speechLang = 'en-US'; // default
2048
+ switch(currentLanguage) {
2049
+ case 'de': speechLang = 'de-DE'; break;
2050
+ case 'es': speechLang = 'es-ES'; break;
2051
+ case 'it': speechLang = 'it-IT'; break;
2052
+ case 'fr': speechLang = 'fr-FR'; break;
2053
+ case 'ru': speechLang = 'ru-RU'; break;
2054
+ case 'tr': speechLang = 'tr-TR'; break;
2055
+ case 'ar': speechLang = 'ar-SA'; break;
2056
+ case 'hi': speechLang = 'hi-IN'; break;
2057
+ case 'zh-cn': speechLang = 'zh-CN'; break;
2058
+ case 'jp': speechLang = 'ja-JP'; break;
2059
+ default: speechLang = 'en-US';
2060
+ }
2061
+
1778
2062
  if (isActive) {
1779
2063
  document.addEventListener('focusin', screenReader.handleFocus);
1780
2064
  const feedbackSpeech = new SpeechSynthesisUtterance(getTranslation('screenReaderOn'));
1781
- feedbackSpeech.lang = 'en-US';
2065
+ feedbackSpeech.lang = speechLang;
1782
2066
  feedbackSpeech.onerror = function (event) {
1783
2067
  console.warn('Speech synthesis feedback error:', event.error);
1784
2068
  };
@@ -1787,7 +2071,7 @@ const screenReader = {
1787
2071
  document.removeEventListener('focusin', screenReader.handleFocus);
1788
2072
  window.speechSynthesis.cancel();
1789
2073
  const feedbackSpeech = new SpeechSynthesisUtterance(getTranslation('screenReaderOff'));
1790
- feedbackSpeech.lang = 'en-US';
2074
+ feedbackSpeech.lang = speechLang;
1791
2075
  feedbackSpeech.onerror = function (event) {
1792
2076
  console.warn('Speech synthesis feedback error:', event.error);
1793
2077
  };
@@ -1844,7 +2128,23 @@ const voiceControl = {
1844
2128
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
1845
2129
  voiceControl.recognition = new SpeechRecognition();
1846
2130
  voiceControl.recognition.interimResults = false;
1847
- voiceControl.recognition.lang = 'en-US';
2131
+
2132
+ // Set language based on current interface language
2133
+ let recognitionLang = 'en-US'; // default
2134
+ switch(currentLanguage) {
2135
+ case 'de': recognitionLang = 'de-DE'; break;
2136
+ case 'es': recognitionLang = 'es-ES'; break;
2137
+ case 'it': recognitionLang = 'it-IT'; break;
2138
+ case 'fr': recognitionLang = 'fr-FR'; break;
2139
+ case 'ru': recognitionLang = 'ru-RU'; break;
2140
+ case 'tr': recognitionLang = 'tr-TR'; break;
2141
+ case 'ar': recognitionLang = 'ar-SA'; break;
2142
+ case 'hi': recognitionLang = 'hi-IN'; break;
2143
+ case 'zh-cn': recognitionLang = 'zh-CN'; break;
2144
+ case 'jp': recognitionLang = 'ja-JP'; break;
2145
+ default: recognitionLang = 'en-US';
2146
+ }
2147
+ voiceControl.recognition.lang = recognitionLang;
1848
2148
  voiceControl.recognition.continuous = false;
1849
2149
 
1850
2150
  voiceControl.recognition.onstart = function () {
@@ -1892,62 +2192,92 @@ const voiceControl = {
1892
2192
  console.log(`Received command: ${command}`);
1893
2193
 
1894
2194
  try {
2195
+ // Normalize the command by removing extra spaces and making it lowercase
2196
+ const normalizedCommand = command.toLowerCase().trim().replace(/\s+/g, ' ');
2197
+
2198
+ // Get voice commands for current language, fallback to English
2199
+ const languageCommands = WIDGET_CONFIG.voiceCommands[currentLanguage] || WIDGET_CONFIG.voiceCommands['en'];
2200
+
1895
2201
  // Check for show menu commands
1896
- if (WIDGET_CONFIG.voiceCommands.showMenu.some(cmd => command.includes(cmd))) {
2202
+ if (languageCommands.showMenu.some(cmd => normalizedCommand.includes(cmd))) {
1897
2203
  if (!menuCache.button) menuCache.init();
1898
2204
  if (menuCache.button) {
1899
2205
  menuCache.button.click();
2206
+ console.log('Successfully opened menu');
1900
2207
  }
1901
2208
  return;
1902
2209
  }
1903
2210
 
1904
2211
  // Check for reset all commands
1905
- if (WIDGET_CONFIG.voiceCommands.resetAll.some(cmd => command.includes(cmd))) {
2212
+ if (languageCommands.resetAll.some(cmd => normalizedCommand.includes(cmd))) {
1906
2213
  resetAccessibilitySettings();
2214
+ console.log('Successfully reset all settings');
1907
2215
  return;
1908
2216
  }
1909
2217
 
1910
2218
  // Build dynamic command map based on configuration
1911
2219
  let localStorageKey = null;
1912
-
1913
- // Check each command group
1914
- if (WIDGET_CONFIG.voiceCommands.highContrast.some(cmd => command.includes(cmd))) {
1915
- localStorageKey = 'highContrast';
1916
- } else if (WIDGET_CONFIG.voiceCommands.biggerText.some(cmd => command.includes(cmd))) {
1917
- localStorageKey = 'biggerText';
1918
- } else if (WIDGET_CONFIG.voiceCommands.textSpacing.some(cmd => command.includes(cmd))) {
1919
- localStorageKey = 'textSpacing';
1920
- } else if (WIDGET_CONFIG.voiceCommands.pauseAnimations.some(cmd => command.includes(cmd))) {
1921
- localStorageKey = 'pauseAnimations';
1922
- } else if (WIDGET_CONFIG.voiceCommands.hideImages.some(cmd => command.includes(cmd))) {
1923
- localStorageKey = 'hideImages';
1924
- } else if (WIDGET_CONFIG.voiceCommands.dyslexiaFont.some(cmd => command.includes(cmd))) {
1925
- localStorageKey = 'dyslexiaFont';
1926
- } else if (WIDGET_CONFIG.voiceCommands.biggerCursor.some(cmd => command.includes(cmd))) {
1927
- localStorageKey = 'biggerCursor';
1928
- } else if (WIDGET_CONFIG.voiceCommands.lineHeight.some(cmd => command.includes(cmd))) {
1929
- localStorageKey = 'lineHeight';
1930
- } else if (WIDGET_CONFIG.voiceCommands.textAlign.some(cmd => command.includes(cmd))) {
1931
- localStorageKey = 'textAlign';
1932
- } else if (WIDGET_CONFIG.voiceCommands.screenReader.some(cmd => command.includes(cmd))) {
1933
- localStorageKey = 'screenReader';
1934
- } else if (WIDGET_CONFIG.voiceCommands.voiceControl.some(cmd => command.includes(cmd))) {
1935
- localStorageKey = 'voiceControl';
2220
+ let matchedCommand = null;
2221
+
2222
+ // Check each command group with better matching
2223
+ for (const [key, commands] of Object.entries(languageCommands)) {
2224
+ if (key === 'showMenu' || key === 'resetAll') continue; // Already handled above
2225
+
2226
+ const isMatch = commands.some(cmd => {
2227
+ // Check for exact matches first
2228
+ if (normalizedCommand.includes(cmd.toLowerCase())) {
2229
+ matchedCommand = cmd;
2230
+ return true;
2231
+ }
2232
+ // Check for partial word matches (at least 3 characters)
2233
+ const cmdWords = cmd.toLowerCase().split(' ');
2234
+ const inputWords = normalizedCommand.split(' ');
2235
+ return cmdWords.some(cmdWord =>
2236
+ cmdWord.length >= 3 && inputWords.some(inputWord =>
2237
+ inputWord.includes(cmdWord) || cmdWord.includes(inputWord)
2238
+ )
2239
+ );
2240
+ });
2241
+
2242
+ if (isMatch) {
2243
+ localStorageKey = key;
2244
+ break;
2245
+ }
1936
2246
  }
1937
2247
 
1938
2248
  if (localStorageKey) {
1939
2249
  // Use cached menu reference if available
1940
2250
  if (!menuCache.menu) menuCache.init();
1941
- const button = menuCache.menu?.querySelector(
2251
+
2252
+ // Try to find button by data-key first (toggle buttons)
2253
+ let button = menuCache.menu?.querySelector(
1942
2254
  `.snn-accessibility-option[data-key='${localStorageKey}']`
1943
2255
  );
2256
+
2257
+ // If not found, try to find by data-accessibility-option-id (action buttons)
2258
+ if (!button) {
2259
+ button = menuCache.menu?.querySelector(
2260
+ `.snn-accessibility-option[data-accessibility-option-id='${localStorageKey}']`
2261
+ );
2262
+ }
2263
+
1944
2264
  if (button) {
1945
2265
  button.click();
2266
+ console.log(`Successfully executed command: ${command} (matched: ${matchedCommand || localStorageKey})`);
1946
2267
  } else {
1947
- console.log('Button not found for command:', command);
2268
+ console.log('Button not found for command:', command, '(key:', localStorageKey, ')');
1948
2269
  }
1949
2270
  } else {
1950
2271
  console.log('Command not recognized:', command);
2272
+ // Provide helpful suggestions
2273
+ const availableCommands = Object.values(languageCommands).flat();
2274
+ const suggestions = availableCommands.filter(cmd =>
2275
+ cmd.toLowerCase().includes(normalizedCommand.split(' ')[0]) ||
2276
+ normalizedCommand.split(' ')[0].includes(cmd.toLowerCase().split(' ')[0])
2277
+ );
2278
+ if (suggestions.length > 0) {
2279
+ console.log('Did you mean one of these?', suggestions.slice(0, 3));
2280
+ }
1951
2281
  }
1952
2282
  } catch (error) {
1953
2283
  console.warn('Voice command handling error:', error);