mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
🚹 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:
parent
c9005c41a8
commit
7ea9cc06f2
5 changed files with 88 additions and 34 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -440,6 +440,7 @@
|
|||
"com_nav_clear_conversation_confirm_message": "Are you sure you want to clear all conversations? This is irreversible.",
|
||||
"com_nav_close_sidebar": "Close sidebar",
|
||||
"com_nav_commands": "Commands",
|
||||
"com_nav_control_panel": "Control Panel",
|
||||
"com_nav_confirm_clear": "Confirm Clear",
|
||||
"com_nav_conversation_mode": "Conversation Mode",
|
||||
"com_nav_convo_menu_options": "Conversation Menu Options",
|
||||
|
|
@ -788,6 +789,7 @@
|
|||
"com_ui_client_secret": "Client Secret",
|
||||
"com_ui_close": "Close",
|
||||
"com_ui_close_menu": "Close Menu",
|
||||
"com_ui_close_var": "Close {{0}}",
|
||||
"com_ui_close_settings": "Close Settings",
|
||||
"com_ui_close_window": "Close Window",
|
||||
"com_ui_code": "Code",
|
||||
|
|
@ -979,6 +981,7 @@
|
|||
"com_ui_fork_info_visible": "This option forks only the visible messages; in other words, the direct path to the target message, without any branches.",
|
||||
"com_ui_fork_more_details_about": "View additional information and details about the \"{{0}}\" fork option",
|
||||
"com_ui_fork_more_info_options": "View detailed explanation of all fork options and their behaviors",
|
||||
"com_ui_fork_open_menu": "Open Fork Menu",
|
||||
"com_ui_fork_processing": "Forking conversation...",
|
||||
"com_ui_fork_remember": "Remember",
|
||||
"com_ui_fork_remember_checked": "Your selection will be remembered after usage. Change this at any time in the settings.",
|
||||
|
|
@ -1268,6 +1271,7 @@
|
|||
"com_ui_share_var": "Share {{0}}",
|
||||
"com_ui_shared_link_delete_success": "Successfully deleted shared link",
|
||||
"com_ui_shared_link_not_found": "Shared link not found",
|
||||
"com_ui_open_var": "Open {{0}}",
|
||||
"com_ui_shared_prompts": "Shared Prompts",
|
||||
"com_ui_shop": "Shopping",
|
||||
"com_ui_show_all": "Show All",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,24 @@ const useAvatar = (user: TUser | undefined) => {
|
|||
seed,
|
||||
fontFamily: ['Verdana'],
|
||||
fontSize: 36,
|
||||
backgroundType: ['solid'],
|
||||
backgroundColor: [
|
||||
'd81b60',
|
||||
'8e24aa',
|
||||
'5e35b1',
|
||||
'3949ab',
|
||||
'DB3733',
|
||||
'1B79CC',
|
||||
'027CB8',
|
||||
'008291',
|
||||
'008577',
|
||||
'58802F',
|
||||
'8A761D',
|
||||
'9C6D00',
|
||||
'B06200',
|
||||
'D1451A',
|
||||
],
|
||||
textColor: ['ffffff'],
|
||||
});
|
||||
|
||||
let avatarDataUri = '';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue