☰ fix: Side Panel Accessibility Improvements (#10830)

* fix: pad cards so focus outline doesn't clip in prompts list

* feat: pad snippet top for space between text and button

* fix: prompt menu focus outline clipping with overflow-visible

* fix: clipping in AgentBuilder for advanced and admin buttons

* fix: clipping in memory panel for admin settings

* fix: better contrast thresholds on focus outlines for admin settings and advanced buttons in agent builder

* fix: better contrast thresholds on focus outlines for admin settings  button in memory panel

* fix: clipping on focus outline for manage files button in files panel

* fix: focus outline clipping table cells for files panel table

* fix: clipping on new bookmark button in bookmarks panel

* fix: clipping on Admin Settings button in MCP Settings panel

* fix: better contrast threshold outline and aria-label for Admin Settings button in MCP Settings panel

* fix: misaligned globe because of new unnested menu button positioning

* fix: localize global group aria-label

* fix: screen reader not reading out proper prompt name for dropdown menu button
This commit is contained in:
Dustin Healy 2025-12-09 17:17:23 -08:00 committed by Danny Avila
parent 9e67eee294
commit 6fc6471010
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
15 changed files with 32 additions and 23 deletions

View file

@ -55,7 +55,7 @@ function ChatGroupItem({
return (
<>
<div className="relative my-2 items-stretch justify-between rounded-xl border border-border-light shadow-sm transition-all duration-300 ease-in-out hover:bg-surface-tertiary hover:shadow-lg">
<div className="relative my-2 items-stretch justify-between rounded-xl border border-border-light px-1 shadow-sm transition-all duration-300 ease-in-out hover:bg-surface-tertiary hover:shadow-lg">
<ListCard
name={group.name}
category={group.category ?? ''}
@ -65,13 +65,15 @@ function ChatGroupItem({
? group.oneliner
: (group.productionPrompt?.prompt ?? '')
}
>
<div className="flex flex-row items-center gap-2">
{groupIsGlobal === true && (
<EarthIcon className="icon-md text-green-400" aria-label="Global prompt group" />
)}
></ListCard>
{groupIsGlobal === true && (
<div className="absolute right-14 top-[16px]">
<EarthIcon
className="icon-md text-green-400"
aria-label={localize('com_ui_sr_global_prompt')}
/>
</div>
</ListCard>
)}
<div className="absolute right-0 top-0 mr-1 mt-2.5 items-start pl-2">
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild>
@ -79,7 +81,7 @@ function ChatGroupItem({
ref={triggerButtonRef}
id={`prompt-actions-${group._id}`}
type="button"
aria-label={localize('com_ui_sr_actions_menu', { name: group.name })}
aria-label={localize('com_ui_sr_actions_menu', { 0: group.name })}
onClick={(e) => {
e.stopPropagation();
}}

View file

@ -55,7 +55,7 @@ export default function GroupSidePanel({
/>
</div>
)}
<div className="flex flex-1 flex-col gap-2 overflow-y-auto">
<div className="flex flex-1 flex-col gap-2 overflow-visible">
{children}
<div className={cn('relative flex h-full flex-col', isChatRoute ? '' : 'px-2 md:px-0')}>
<List

View file

@ -50,7 +50,7 @@ export default function ListCard({
</div>
<div
id={`card-snippet-${name}`}
className="ellipsis max-w-full select-none text-balance text-sm text-text-secondary"
className="ellipsis max-w-full select-none text-balance pt-1 text-sm text-text-secondary"
>
{snippet}
</div>

View file

@ -152,7 +152,7 @@ const AdminSettings = () => {
<Button
size={'sm'}
variant={'outline'}
className="btn btn-neutral border-token-border-light relative h-9 w-full gap-1 rounded-lg font-medium"
className="btn btn-neutral border-token-border-light relative h-9 w-full gap-1 rounded-lg font-medium focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-text-primary"
aria-label={localize('com_ui_admin_settings')}
>
<ShieldEllipsis className="cursor-pointer" aria-hidden="true" />
@ -218,7 +218,7 @@ const AdminSettings = () => {
type="button"
onClick={handleSubmit(onSubmit)}
disabled={isSubmitting || isLoading}
className="btn rounded bg-green-500 font-bold text-white transition-all hover:bg-green-600"
className="btn rounded bg-green-500 font-bold text-white transition-all hover:bg-green-600 focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring-primary"
>
{localize('com_ui_save')}
</button>

View file

@ -15,7 +15,7 @@ const AdvancedButton: React.FC<AdvancedButtonProps> = ({ setActivePanel }) => {
<Button
size={'sm'}
variant={'outline'}
className="btn btn-neutral border-token-border-light relative h-9 w-full gap-1 rounded-lg font-medium"
className="btn btn-neutral border-token-border-light relative h-9 w-full gap-1 rounded-lg font-medium focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-text-primary"
onClick={() => setActivePanel(Panel.advanced)}
aria-label={localize('com_ui_advanced')}
>

View file

@ -476,7 +476,7 @@ export default function AgentPanel() {
<FormProvider {...methods}>
<form
onSubmit={handleSubmit(onSubmit)}
className="scrollbar-gutter-stable h-auto w-full flex-shrink-0 overflow-x-hidden"
className="scrollbar-gutter-stable h-auto w-full flex-shrink-0 overflow-x-visible"
aria-label="Agent configuration form"
>
<div className="mx-1 mt-2 flex w-full flex-wrap gap-2">

View file

@ -6,7 +6,7 @@ const BookmarkPanel = () => {
const { data } = useConversationTagsQuery();
return (
<div className="h-auto max-w-full overflow-x-hidden">
<div className="h-auto max-w-full overflow-x-visible">
<BookmarkContext.Provider value={{ bookmarks: data || [] }}>
<BookmarkTable />
</BookmarkContext.Provider>

View file

@ -7,7 +7,7 @@ export default function FilesPanel() {
const { data: files = [] } = useGetFiles<TFile[]>();
return (
<div className="h-auto max-w-full overflow-x-hidden">
<div className="h-auto max-w-full overflow-x-visible">
<DataTable columns={columns} data={files} />
</div>
);

View file

@ -241,6 +241,11 @@ export default function DataTable<TData, TValue>({ columns, data }: DataTablePro
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}}
className={
isFilenameCell
? 'focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-text-primary'
: ''
}
data-skip-refocus="true"
key={cell.id}
role={isFilenameCell ? 'button' : undefined}

View file

@ -151,7 +151,8 @@ const MCPAdminSettings = () => {
<Button
size={'sm'}
variant={'outline'}
className="btn btn-neutral border-token-border-light relative h-9 w-full gap-1 rounded-lg font-medium"
className="btn btn-neutral border-token-border-light relative h-9 w-full gap-1 rounded-lg font-medium focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-text-primary"
aria-label={localize('com_ui_admin_settings')}
>
<ShieldEllipsis className="cursor-pointer" aria-hidden="true" />
{localize('com_ui_admin_settings')}
@ -216,7 +217,7 @@ const MCPAdminSettings = () => {
type="button"
onClick={handleSubmit(onSubmit)}
disabled={isSubmitting || isLoading}
className="btn rounded bg-green-500 font-bold text-white transition-all hover:bg-green-600"
className="btn rounded bg-green-500 font-bold text-white transition-all hover:bg-green-600 focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-text-primary"
>
{localize('com_ui_save')}
</button>

View file

@ -22,7 +22,7 @@ export default function MCPBuilderPanel() {
const configDialogProps = getConfigDialogProps();
return (
<div className="flex h-full w-full flex-col overflow-hidden">
<div className="flex h-full w-full flex-col overflow-visible">
<div role="region" aria-label="MCP Builder" className="mt-2 space-y-2">
{/* Admin Settings Button */}
<MCPAdminSettings />

View file

@ -141,7 +141,7 @@ const AdminSettings = () => {
<Button
size={'sm'}
variant={'outline'}
className="btn btn-neutral border-token-border-light relative h-9 w-full gap-1 rounded-lg font-medium"
className="btn btn-neutral border-token-border-light relative h-9 w-full gap-1 rounded-lg font-medium focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-text-primary"
aria-label={localize('com_ui_admin_settings')}
>
<ShieldEllipsis className="cursor-pointer" aria-hidden="true" />
@ -206,7 +206,7 @@ const AdminSettings = () => {
<button
type="submit"
disabled={isSubmitting || isLoading}
className="btn rounded bg-green-500 font-bold text-white transition-all hover:bg-green-600"
className="btn rounded bg-green-500 font-bold text-white transition-all hover:bg-green-600 focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-text-primary"
>
{localize('com_ui_save')}
</button>

View file

@ -239,7 +239,7 @@ export default function MemoryViewer() {
}
return (
<div className="flex h-full w-full flex-col overflow-hidden">
<div className="flex h-full w-full flex-col">
<div role="region" aria-label={localize('com_ui_memories')} className="mt-2 space-y-2">
<div className="relative">
<Input

View file

@ -1285,6 +1285,7 @@
"com_ui_special_variables_more_info": "You can select special variables from the dropdown: `{{current_date}}` (today's date and day of week), `{{current_datetime}}` (local date and time), `{{utc_iso_datetime}}` (UTC ISO datetime), and `{{current_user}}` (your account name).",
"com_ui_speech_while_submitting": "Can't submit speech while a response is being generated",
"com_ui_sr_actions_menu": "Open actions menu for \"{{0}}\" prompt",
"com_ui_sr_global_prompt": "Global prompt group",
"com_ui_stack_trace": "Stack Trace",
"com_ui_status_prefix": "Status:",
"com_ui_stop": "Stop",

View file

@ -39,7 +39,7 @@ const AccordionContent = React.forwardRef<
>(({ className = '', children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
className="overflow-x-visible text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}
>
<div className={cn('pb-4 pt-0', className)}>{children}</div>