From 5ed1f2991e7a102b170f7e96d7b9d9805d95bd15 Mon Sep 17 00:00:00 2001
From: Dustin Healy <54083382+dustinhealy@users.noreply.github.com>
Date: Wed, 26 Nov 2025 06:12:04 -0800
Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=97=20fix:=20Address=20Accessibility?=
=?UTF-8?q?=20Issues=20-=20Axe=20Rating:=20Serious=20(#10521)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: add light/dark differentiation on text color for login footer links for more accessible contrast in light mode
* feat: add darker color focus ring on ThemeSelector in light mode for more accessible contrast
* feat: increase contrast on text color for rendered error messages in light and dark mode so that they pass the 4.5:1 accessibility contrast threshold against their backgrounds
* feat: add more accessible color vars to style.css for better contrast against light/dark backgrounds
* feat: un-nest DropdownMenu from ListCard and make them siblings instead for better accessibility
* feat: tweak --border-heavy in light mode so that it uses --gray-410 rather than --gray-400 so that the contrast ratio threshold is hit for accessibility
* feat: switch email and password input border to border-heavy for more accessible contrast on Login page
* fix: add proper focus ring for Action menu button in Prompts Sidenav
* fix: align light and dark focus rings with surrounding elements on preview/edit menu dropdown button in Prompt Card
* fix: remove aria-hidden on parent div with focusable child element according to accessibility guidelines
* fix: add missing aria-readonly false property that should have been in previous accessibility PR
* feat: add horizontal padding on rowRenderer's CellMeasurer div so that focus ring on rows doesnt clip behind virtualized table borders side-to-side
(still need to figure out vertical clipping on final row / a better solution to be able to get overflows to work properly within the virtualized table)
* feat: remove render prop override so that Share and Delete Buttons in Conversation dropdown can be pressed with Enter keystroke
* fix: undo additional colors and changes to --surface-hover
the initial changes came from a misunderstanding of contrast threshold requirements for hover effect accessibility
* feat: better layout for non-nested prompt card / action menu combination
* fix: add proper focus restoration behavior for Preview modal on close
* fix: undo change to --border-heavy in light mode
* fix: set borders for login input boxes back to light
* feat: add announcement for state change when link copied to clipboard in conversation share modal
* feat: add announcement to Refresh Link button
* feat: add announcement for archiving chats
* feat: make date sections in conversation history list
rather than generic
for improved screen reader support
* feat: ensure Share Link modal is accessible at high zoom percentage and low viewport width / height requirements by adding max height and overflow attributes to allow scrolling
* feat: bold toast text so that it hits font size accessibility threshold (above 14 px when bolded - change makes text 16 px bold) so that the more disruptive contrast change of the toast background color is no longer necessary.
The background color would need to achieve a 4.5:1 contrast ratio, which would significantly affect the established aesthetic of the current toast system if achieved.
* fix: do not render side nav when it is hidden to avoid keyboard navigation with screen reader
* fix: add side nav button state change announcements and don't render components that were previosuly reachable via keyboard navigation while in the side nav
* feat: add tooltip anchor for Model Select
* fix: only hide the model selector, export, and temp chat buttons when in mobile view and the sidenav is expanded
* feat: add aria-haspopup support for MenuItems and add aria-haspopup: 'dialog' for Share and Delete buttons in ConvoOptions
* feat: add label for DataTable search so that it does not rely on placeholder attribute for function identification
* feat: make X buttons on dialogs 24x24px to achieve AA compliance
* feat: add announcements for the search bar for model selector
* feat: persistent label for DataTable
* feat: make filter files text contrast compliant
* feat: add non-color visual indicator to AudioRecorder listening state
* feat: add aria-expanded attribute to tool call dropdown for screen reader
* feat: add high contrast and rounded outlines for focus indicators on Run Code and Copy Code buttons for code blocks
* fix: change Button to anchor tag in Shared Links component when linking to original conversation
* fix: allow overflow in datatable cells so that focus indicators dont get cut off
* feat: round out focus outline for link name in SharedLinks modal
* feat: add aria-controls and aria-haspopup: "dialog" to SharedLinks delete button and modal
* feat: add aria-controls for dropdown menu items on ConvoOptions for share and delete modals
* feat: add trigger ref to 2FA button and modal in settings menu so focus returns to button on modal close
* feat: add refs so that open sidebar and close sidebar buttons transfer focus to one another
* chore: formatting
* feat: make sure settings modal is accessible at 200% zoom for screen size 1366x768 viewport
* feat: round out focus outline for link names in archived chats modal
* feat: add result announcements for screen reader in DataTable search
* feat: simplify layout for checkbox / api key components for better accessibility
* feat: return focus to chat input on prompt variables modal close
* feat: add persistent labels to TextareaAutosize Inputs in Variable form
* feat: tighten max width so side scrolling not necessary at 400% zoom for VariableForm modal
* feat: add persistent labels to prompt management page
* feat: announce results found for search bars in prompts page and improve them in datatable
* feat: de-nest DashGroupItem buttons in Prompts page to allow better navigation and comply with accessibility standard
* feat: add heading for new prompt creation page for screen readers
* feat: remove non-compliant description truncation for small screen sizes by making labels static on small enough viewport width
* feat: add mobile view sidebar for prompts page
* feat: add bolded text on select for AdvancedSwitch so that there is a visual indicator of selection and it does not rely solely on color as an indication of state
* feat: add persistent labels to ModelSelector search inputs
* feat: align aria-label with visual label for speech recognition users
* feat: make MemoryCreateDialog accessible at 400% zoom (introduce max viewport height attr and make scrollable)
* feat: add persistent label to Filter input for DataTable in file attach sidebar menu
* feat: add persistent label for bookmark filter input in bookmarks sidebar menu
* feat: add alert for screen readers for invalid inputs when editting bookmarks
* feat: bold font in BookmarkForm error readout to pass contrast compliance thresholds for 14pt text
* feat: align aria-label with visual label for BookmarkForm Ttile input
* feat: add 400% zoom support for ALL modals utilizing OriginalDialog to prevent clipping
* feat: remove state change on aria label and give consistent labelling for button, offload state change notification to the announcement div and make more assertive
* feat: add aria-labels which convey that the buttons are sortable (divergence from visual text because iconography is used to signify sort functionality)
* feat: add supplemental visuals to indicate link is clickable other than color in SharedLinks
* feat: increase saturation to hit contrast threshold minimums on Link color in SharedLinks
* feat: stop DataTable from disappearing at 400% zoom in SharedLinks
* feat: increase contrast to hit contrast threshold minimums on Animated Search Input visual indicators
* feat: add aria-label for AnimatedSearchInput (doesn't require explicit labelling because of Search icon)
* fix: stop long example variable declaration from clipping at high zoom in variables info
* feat: add aria-label to bettter describe sort button functionality for vision impaired users
* chore: remove unused translation key
* chore: address ESLint comments
* fix: modify test to account for new alert on theme toggle switch for login page
* chore: interpolate translation key
---
client/src/common/types.ts | 2 +
client/src/components/Auth/Footer.tsx | 4 +-
client/src/components/Auth/LoginForm.tsx | 2 +-
.../Auth/__tests__/Registration.spec.tsx | 16 +-
.../src/components/Bookmarks/BookmarkForm.tsx | 11 +-
client/src/components/Chat/Header.tsx | 46 +++--
.../components/Chat/Input/AudioRecorder.tsx | 3 +-
.../Chat/Input/Files/Table/DataTable.tsx | 18 +-
.../components/Chat/Input/PromptsCommand.tsx | 10 +-
.../Chat/Menus/Endpoints/CustomMenu.tsx | 25 ++-
.../Chat/Menus/Endpoints/ModelSelector.tsx | 30 ++-
.../Endpoints/components/EndpointItem.tsx | 3 +-
.../Endpoints/components/SearchResults.tsx | 16 +-
.../src/components/Chat/Menus/OpenSidebar.tsx | 43 ++--
.../Chat/Messages/Content/ProgressText.tsx | 1 +
.../Conversations/Conversations.tsx | 7 +-
client/src/components/Conversations/Convo.tsx | 4 +-
.../ConvoOptions/ConvoOptions.tsx | 14 +-
.../ConvoOptions/DeleteButton.tsx | 2 +-
.../ConvoOptions/ShareButton.tsx | 19 +-
.../ConvoOptions/SharedLinkButton.tsx | 36 ++--
.../components/Messages/Content/CodeBlock.tsx | 2 +-
.../components/Messages/Content/RunCode.tsx | 2 +-
client/src/components/Nav/MobileNav.tsx | 13 +-
client/src/components/Nav/Nav.tsx | 60 +++---
client/src/components/Nav/NewChat.tsx | 15 +-
client/src/components/Nav/Settings.tsx | 4 +-
.../Account/DisableTwoFactorToggle.tsx | 5 +
.../Account/TwoFactorAuthentication.tsx | 6 +-
.../Nav/SettingsTabs/Data/SharedLinks.tsx | 43 ++--
.../General/ArchivedChatsTable.tsx | 3 +-
.../src/components/Prompts/AdvancedSwitch.tsx | 8 +-
client/src/components/Prompts/Command.tsx | 42 ++--
client/src/components/Prompts/Description.tsx | 42 ++--
.../Prompts/Groups/ChatGroupItem.tsx | 52 +++--
.../Prompts/Groups/CreatePromptForm.tsx | 15 +-
.../Prompts/Groups/DashGroupItem.tsx | 193 ++++++++++--------
.../Prompts/Groups/FilterPrompts.tsx | 30 ++-
.../Prompts/Groups/GroupSidePanel.tsx | 48 ++++-
.../components/Prompts/Groups/ListCard.tsx | 2 +-
.../Prompts/Groups/VariableForm.tsx | 42 ++--
.../src/components/Prompts/PreviewPrompt.tsx | 7 +-
.../components/Prompts/PromptVariables.tsx | 2 +-
client/src/components/Prompts/PromptsView.tsx | 72 ++++++-
.../SidePanel/Agents/Code/Action.tsx | 47 +++--
.../SidePanel/Agents/FileSearchCheckbox.tsx | 34 ++-
.../SidePanel/Agents/Search/Action.tsx | 50 +++--
.../SidePanel/Bookmarks/BookmarkTable.tsx | 12 +-
.../components/SidePanel/Files/PanelTable.tsx | 12 +-
.../SidePanel/Memories/MemoryCreateDialog.tsx | 2 +-
.../SidePanel/Memories/MemoryViewer.tsx | 2 +-
client/src/locales/en/translation.json | 21 +-
client/src/routes/Layouts/DashBreadcrumb.tsx | 32 ++-
client/src/routes/Root.tsx | 22 +-
packages/client/src/common/menus.ts | 11 +
.../src/components/AnimatedSearchInput.tsx | 7 +-
packages/client/src/components/DataTable.tsx | 38 +++-
.../client/src/components/DropdownPopup.tsx | 2 +
.../client/src/components/OriginalDialog.tsx | 4 +-
.../client/src/components/ThemeSelector.tsx | 25 ++-
packages/client/src/components/Toast.tsx | 2 +-
.../client/src/locales/en/translation.json | 6 +-
62 files changed, 935 insertions(+), 414 deletions(-)
diff --git a/client/src/common/types.ts b/client/src/common/types.ts
index bb3bdcfa6d..9b0a21098a 100644
--- a/client/src/common/types.ts
+++ b/client/src/common/types.ts
@@ -568,6 +568,8 @@ export interface ModelItemProps {
export type ContextType = {
navVisible: boolean;
setNavVisible: React.Dispatch>;
+ openSidebarRef?: React.RefObject;
+ closeSidebarRef?: React.RefObject;
};
export interface SwitcherProps {
diff --git a/client/src/components/Auth/Footer.tsx b/client/src/components/Auth/Footer.tsx
index 8d79717683..1387523779 100644
--- a/client/src/components/Auth/Footer.tsx
+++ b/client/src/components/Auth/Footer.tsx
@@ -11,7 +11,7 @@ function Footer({ startupConfig }: { startupConfig: TStartupConfig | null | unde
const privacyPolicyRender = privacyPolicy?.externalUrl && (
= ({ onSubmit, startupConfig, error,
const renderError = (fieldName: string) => {
const errorMessage = errors[fieldName]?.message;
return errorMessage ? (
-
+
{String(errorMessage)}
) : null;
diff --git a/client/src/components/Auth/__tests__/Registration.spec.tsx b/client/src/components/Auth/__tests__/Registration.spec.tsx
index 72a21b63b6..a1211ae6be 100644
--- a/client/src/components/Auth/__tests__/Registration.spec.tsx
+++ b/client/src/components/Auth/__tests__/Registration.spec.tsx
@@ -191,12 +191,16 @@ test('shows validation error messages', async () => {
await userEvent.type(getByTestId('password'), 'pass');
await userEvent.type(getByTestId('confirm_password'), 'password1');
const alerts = getAllByRole('alert');
- expect(alerts).toHaveLength(5);
- expect(alerts[0]).toHaveTextContent(/Name must be at least 3 characters/i);
- expect(alerts[1]).toHaveTextContent(/Username must be at least 2 characters/i);
- expect(alerts[2]).toHaveTextContent(/You must enter a valid email address/i);
- expect(alerts[3]).toHaveTextContent(/Password must be at least 8 characters/i);
- expect(alerts[4]).toHaveTextContent(/Passwords do not match/i);
+ expect(alerts).toHaveLength(6);
+
+ // This first alert is for the theme toggle, which is empty within this test but still picked up by getAllByRole as an alert
+ expect(alerts[0]).toHaveTextContent('');
+
+ expect(alerts[1]).toHaveTextContent(/Name must be at least 3 characters/i);
+ expect(alerts[2]).toHaveTextContent(/Username must be at least 2 characters/i);
+ expect(alerts[3]).toHaveTextContent(/You must enter a valid email address/i);
+ expect(alerts[4]).toHaveTextContent(/Password must be at least 8 characters/i);
+ expect(alerts[5]).toHaveTextContent(/Passwords do not match/i);
});
test('shows error message when registration fails', async () => {
diff --git a/client/src/components/Bookmarks/BookmarkForm.tsx b/client/src/components/Bookmarks/BookmarkForm.tsx
index bd8ffd2d76..a99b95ab96 100644
--- a/client/src/components/Bookmarks/BookmarkForm.tsx
+++ b/client/src/components/Bookmarks/BookmarkForm.tsx
@@ -100,9 +100,7 @@ const BookmarkForm = ({
- {errors.tag && {errors.tag.message}}
+ {errors.tag && (
+
+ {errors.tag.message}
+
+ )}