🚹 feat: Miscellaneous Accessibility Improvements (#10913)

* 🔱 fix: Fork Menu Accessibility Improvements (#10910)

* feat: more accessible aria-label for fork button

* fix: alignment between text and checkbox

* feat: add text change on focus for parity with on hover for keyboard accessibility

* 🤔 fix: Programmatic Expansion State for Thinking Button (#10912)

* 🙋‍♂️ feat: Accessible Default User Icon Colors (#10909)

* fix: downshift values for all non-compliant default bg-colors for user icons to achieve 4.5:1 contrast threshold minimums with text

* 🚪 feat: Open Sidebar Label Accessibility (#10893)

* feat: more accessible labelling on open / close sidebar
This commit is contained in:
Dustin Healy 2025-12-11 07:42:28 -08:00 committed by Danny Avila
parent abcf606328
commit 4a0fbb07bc
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
5 changed files with 88 additions and 34 deletions

View file

@ -65,6 +65,7 @@ export const ThinkingButton = memo(
<button
type="button"
onClick={onClick}
aria-expanded={isExpanded}
className={cn(
'group/button flex flex-1 items-center justify-start rounded-lg leading-[18px]',
fontSize,

View file

@ -75,6 +75,21 @@ const PopoverButton: React.FC<PopoverButtonProps> = ({
setActiveSetting(optionLabels[ForkOptions.DEFAULT]);
}, 175);
}}
onFocus={() => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
setActiveSetting(optionLabels[setting]);
}}
onBlur={() => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
setActiveSetting(optionLabels[ForkOptions.DEFAULT]);
}, 175);
}}
className="mx-0.5 w-14 flex-1 rounded-xl border-2 border-border-medium bg-surface-secondary text-text-secondary transition duration-200 ease-in-out hover:bg-surface-hover hover:text-text-primary"
aria-label={label}
>
@ -134,35 +149,36 @@ const CheckboxOption: React.FC<CheckboxOptionProps> = ({
const { showToast } = useToastContext();
return (
<Ariakit.HovercardProvider placement="right-start">
<div className="flex items-center">
<div className="flex h-6 w-full select-none items-center justify-start rounded-md text-sm text-text-secondary hover:text-text-primary">
<Ariakit.HovercardAnchor
render={
<div>
<Ariakit.Checkbox
id={id}
checked={checked}
onChange={(e) => {
const value = e.target.checked;
if (value && showToastOnCheck) {
showToast({
message: localize('com_ui_fork_remember_checked'),
status: 'info',
});
}
onToggle(value);
}}
className="h-4 w-4 rounded-sm border border-primary ring-offset-background transition duration-300 ease-in-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground"
aria-label={localize(labelKey)}
/>
<label htmlFor={id} className="ml-2 cursor-pointer">
{localize(labelKey)}
</label>
</div>
}
/>
</div>
<Ariakit.HovercardDisclosure className="rounded-full text-text-secondary focus:outline-none focus:ring-2 focus:ring-ring">
<div className="flex items-center justify-between">
<Ariakit.HovercardAnchor
render={
<div className="flex items-center">
<Ariakit.Checkbox
id={id}
checked={checked}
onChange={(e) => {
const value = e.target.checked;
if (value && showToastOnCheck) {
showToast({
message: localize('com_ui_fork_remember_checked'),
status: 'info',
});
}
onToggle(value);
}}
className="h-4 w-4 rounded-sm border border-primary ring-offset-background transition duration-300 ease-in-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground"
aria-label={localize(labelKey)}
/>
<label
htmlFor={id}
className="ml-2 cursor-pointer select-none text-sm text-text-secondary hover:text-text-primary"
>
{localize(labelKey)}
</label>
</div>
}
/>
<Ariakit.HovercardDisclosure className="ml-1 rounded-full text-text-secondary focus:outline-none focus:ring-2 focus:ring-ring">
<VisuallyHidden>{localize(infoKey)}</VisuallyHidden>
{chevronDown}
</Ariakit.HovercardDisclosure>
@ -331,7 +347,7 @@ export default function Fork({
}
}}
type="button"
aria-label={localize('com_ui_fork')}
aria-label={localize('com_ui_fork_open_menu')}
>
<GitFork size="19" aria-hidden="true" />
</button>

View file

@ -29,6 +29,23 @@ export default function NavToggle({
const topBarRotation = side === 'right' ? `-${rotation}` : rotation;
const bottomBarRotation = side === 'right' ? rotation : `-${rotation}`;
let sidebarLabel;
let actionKey;
if (side === 'left') {
sidebarLabel = localize('com_ui_chat_history');
} else {
sidebarLabel = localize('com_nav_control_panel');
}
if (navVisible) {
actionKey = 'com_ui_close_var';
} else {
actionKey = 'com_ui_open_var';
}
const ariaDescription = localize(actionKey, { 0: sidebarLabel });
return (
<div
className={cn(
@ -42,15 +59,13 @@ export default function NavToggle({
>
<TooltipAnchor
side={side === 'right' ? 'left' : 'right'}
aria-label={side === 'left' ? localize('com_ui_chat_history') : localize('com_ui_controls')}
aria-label={ariaDescription}
aria-expanded={navVisible}
aria-controls={side === 'left' ? 'chat-history-nav' : 'controls-nav'}
id={`toggle-${side}-nav`}
onClick={onToggle}
role="button"
description={
navVisible ? localize('com_nav_close_sidebar') : localize('com_nav_open_sidebar')
}
description={ariaDescription}
className="flex items-center justify-center"
tabIndex={0}
>