Improve accessibility across core flows (#86)
First incremental pass at issue #86, focused on the universal entry points and primary navigation. All changes verified in-browser with the axe-core engine (0 violations on the surfaces below) plus manual keyboard testing, on both desktop (1280px) and mobile (390px). Login / first-run setup (static/login.html) - Add a real <h1>, wrap content in <main> + <footer> landmarks. - Mark the decorative boat SVG aria-hidden. - Errors now use role="alert" so screen readers announce them. - "Remember me" checkbox is keyboard-focusable (was display:none) with an accessible name and a focus ring; dynamic 2FA field gets a linked label. - Darken the brand-red submit button so white text clears WCAG AA 4.5:1 (was ~3.2:1); add visible :focus-visible rings. App shell (static/index.html, static/style.css) - Remove invalid role="region" from the <main> chat container (it was overriding the implicit main landmark). - Add a persistent, visually-hidden <h1> inside <main> so the page always exposes one logical level-1 heading — works even on mobile where the sidebar (with the visible brand) is hidden off-canvas. - Add a reusable .a11y-visually-hidden utility. - Raise chat-title, model-picker, settings-helper and notes text contrast above 4.5:1 (were 2.8-3.9:1). Keyboard nav + dialogs (static/js/a11y.js - new) - Make the click-only <div> sidebar navigation (New Chat, Search, Brain, Calendar, Compare, Cookbook, Deep Research, Gallery, Library, Notes, Tasks, Theme, account) focusable and Enter/Space-activatable, announced as buttons (skipping role=button where a nested control would create a nested-interactive violation). Visible focus ring reused from existing .list-item:focus-visible. - Upgrade modals (.modal-content and the docked .notes-pane) to labelled role="dialog" + aria-modal, and normalise their title to heading level 2 so heading order stays valid. A MutationObserver covers runtime-rendered rows and modals. Decorative background canvases (static/js/theme.js) - Mark all 7 bg-effect canvases aria-hidden. Notes & Tasks (static/js/notes.js, static/js/tasks.js) - Label the icon-only Note/To-do toggle pills (fixes a critical button-name issue) and track aria-pressed state. - Improve Notes header-button + empty-state contrast. - Give the Tasks sort <select> an accessible name (fixes a critical select-name issue). Remaining data-dense tool modals (Tasks cards, Calendar, Gallery, Email, Cookbook, Compare, Deep Research) still have muted-text contrast to polish and are the next incremental step, per the issue's own guidance.
This commit is contained in:
@@ -265,7 +265,9 @@ body.bg-pattern-sparkles {
|
||||
transform: translateY(calc(-50% - 2px));
|
||||
font-size: 0.75em;
|
||||
line-height: 1;
|
||||
color: color-mix(in srgb, var(--fg) 40%, transparent);
|
||||
/* 70% mix keeps the chat title clearly above the WCAG AA 4.5:1
|
||||
contrast threshold (40% only reached ~2.8:1). */
|
||||
color: color-mix(in srgb, var(--fg) 70%, transparent);
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -2550,7 +2552,9 @@ body.bg-pattern-sparkles {
|
||||
background: none;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
color: color-mix(in srgb, var(--fg) 40%, transparent);
|
||||
/* 65% mix lifts the model label above the WCAG AA 4.5:1 threshold
|
||||
against the dark chat-bar (40% only reached ~2.9:1). */
|
||||
color: color-mix(in srgb, var(--fg) 65%, transparent);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: background 0.15s, color 0.15s, border-color 0.15s;
|
||||
@@ -9610,7 +9614,8 @@ details a:hover {
|
||||
margin: 0;
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
color: color-mix(in srgb, var(--fg) 50%, transparent);
|
||||
/* 65% keeps this description text above WCAG AA 4.5:1 (50% was ~3.9:1). */
|
||||
color: color-mix(in srgb, var(--fg) 65%, transparent);
|
||||
}
|
||||
|
||||
.memory-add-row {
|
||||
@@ -11152,6 +11157,17 @@ textarea.memory-add-input {
|
||||
#doc-language-icon:empty { display: none; }
|
||||
#doc-language-icon svg { display: block; }
|
||||
|
||||
/* Visually hidden but available to assistive tech (screen readers, axe).
|
||||
Use for content that should be announced/structural but not painted —
|
||||
e.g. the persistent page <h1>. */
|
||||
.a11y-visually-hidden {
|
||||
position: absolute !important;
|
||||
width: 1px !important; height: 1px !important;
|
||||
padding: 0 !important; margin: -1px !important;
|
||||
overflow: hidden !important; clip: rect(0, 0, 0, 0) !important;
|
||||
white-space: nowrap !important; border: 0 !important;
|
||||
}
|
||||
|
||||
/* ── Custom language type picker (replaces visible chrome of native <select>
|
||||
— <option>s can't render SVG). Hidden select stays as the source of truth. */
|
||||
.doc-langpicker-native-hidden {
|
||||
@@ -12929,7 +12945,9 @@ body:has(.doc-version-panel:not(.hidden)) .hamburger-btn {
|
||||
font-weight: 500;
|
||||
}
|
||||
.admin-toggle-sub {
|
||||
color: color-mix(in srgb, var(--fg) 50%, transparent);
|
||||
/* 65% mix keeps this helper text above WCAG AA 4.5:1 on the dark panel
|
||||
(50% only reached ~3.9:1). */
|
||||
color: color-mix(in srgb, var(--fg) 65%, transparent);
|
||||
font-size: 11px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
@@ -29699,7 +29717,9 @@ body.notes-mobile-mode.notes-drag-mode .note-card-pin.active {
|
||||
}
|
||||
.notes-empty-msg {
|
||||
text-align: center;
|
||||
opacity: 0.4;
|
||||
/* 0.4 dropped this empty-state text to ~2.8:1; 0.65 keeps it readable
|
||||
(WCAG AA) while staying visibly secondary. */
|
||||
opacity: 0.65;
|
||||
padding: 30px 20px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user