diff --git a/.gitignore b/.gitignore index 8ec11ab..cba02b2 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,11 @@ output.txt.txt !docs/*.png !docs/*.gif !docs/*.webp +# …and curated docs/ subfolder assets (e.g. accessibility before/after shots). +!docs/**/*.png +!docs/**/*.jpg +!docs/**/*.gif +!docs/**/*.webp # Reports and temp files reports/ diff --git a/docs/a11y/focus-after.png b/docs/a11y/focus-after.png new file mode 100644 index 0000000..7c9938a Binary files /dev/null and b/docs/a11y/focus-after.png differ diff --git a/docs/a11y/focus-before.png b/docs/a11y/focus-before.png new file mode 100644 index 0000000..d5cf76b Binary files /dev/null and b/docs/a11y/focus-before.png differ diff --git a/docs/a11y/login-after.png b/docs/a11y/login-after.png new file mode 100644 index 0000000..cc2571d Binary files /dev/null and b/docs/a11y/login-after.png differ diff --git a/docs/a11y/login-before.png b/docs/a11y/login-before.png new file mode 100644 index 0000000..bb76ea4 Binary files /dev/null and b/docs/a11y/login-before.png differ diff --git a/static/index.html b/static/index.html index 8b232f2..5c68606 100644 --- a/static/index.html +++ b/static/index.html @@ -921,7 +921,12 @@ -
+
+ +

Odysseus

Odysseus Chat
Rename
Copy Chat
PDF
Save to Documents
@@ -2254,6 +2259,7 @@ + diff --git a/static/js/a11y.js b/static/js/a11y.js new file mode 100644 index 0000000..814472d --- /dev/null +++ b/static/js/a11y.js @@ -0,0 +1,165 @@ +// Accessibility enhancements for keyboard + screen-reader users. +// +// Several primary controls in Odysseus are authored as click-only
s +// (most notably the whole sidebar navigation: New Chat, Search, Brain, +// Calendar, Compare, Cookbook, Deep Research, Gallery, Library, Notes, +// Tasks, Theme, plus the account row).
s are not in the tab order and +// are not announced as buttons, so keyboard and screen-reader users cannot +// reach or operate them. +// +// This module enhances those rows in place — making them focusable +// (tabindex=0), announcing them as buttons when it's safe to do so, and +// activating them with Enter / Space — without changing how they look or +// how they behave for mouse users. The visible focus ring already exists in +// style.css (`.list-item:focus-visible`); it simply never fired because the +// rows were never focusable. + +(function () { + 'use strict'; + + // Click-as-button rows we want reachable by keyboard. + var ROW_SELECTOR = ['#sidebar .list-item', '#user-bar-profile'].join(','); + + // Native interactive descendants. If a row contains one of these we must + // NOT give the row role="button" — a button inside a button is invalid + // (axe "nested-interactive") and confuses screen readers. Such rows still + // become focusable + Enter/Space-activatable, just without the role. + var NESTED_INTERACTIVE = + 'a[href],button,input,select,textarea,[contenteditable="true"],[tabindex]:not([tabindex="-1"])'; + + function enhanceRow(el) { + if (!el || el.nodeType !== 1 || el.dataset.a11yEnhanced === '1') return; + var tag = el.tagName; + // Leave genuine native controls alone. + if (tag === 'BUTTON' || tag === 'A' || tag === 'INPUT' || + tag === 'SELECT' || tag === 'TEXTAREA') return; + + el.dataset.a11yEnhanced = '1'; + if (!el.hasAttribute('tabindex')) el.setAttribute('tabindex', '0'); + el.setAttribute('data-a11y-activatable', '1'); + + if (!el.querySelector(NESTED_INTERACTIVE) && !el.hasAttribute('role')) { + el.setAttribute('role', 'button'); + } + + // Guarantee an accessible name. Visible text normally supplies it; fall + // back to the title attribute for icon-only rows. + if (!el.getAttribute('aria-label') && + !(el.textContent || '').trim() && + el.getAttribute('title')) { + el.setAttribute('aria-label', el.getAttribute('title')); + } + } + + function enhanceAll(root) { + (root || document).querySelectorAll(ROW_SELECTOR).forEach(enhanceRow); + } + + // ---- Modal dialogs ----------------------------------------------------- + // Odysseus modals are plain
-
+