🎨 style: Unify Styles across Themes and Improve Accessibility (#7783)

* style: update button styles for improved hover effects and accessibility

* style: enhance CustomMenuItem styling for improved visual feedback

* style: improved accessibility and visual consistency

* chore: add missing localization in ActionsPanel

---------

Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
Marco Beretta 2025-06-08 00:22:08 +02:00 committed by GitHub
parent c22d74d41e
commit cd7dd576c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 38 additions and 66 deletions

View file

@ -159,7 +159,7 @@ export const CustomMenuItem = React.forwardRef<HTMLDivElement, CustomMenuItemPro
blurOnHoverEnd: false,
...props,
className: cn(
'flex cursor-default items-center gap-2 rounded-lg p-2 outline-none! scroll-m-1 scroll-mt-[calc(var(--combobox-height,0px)+var(--label-height,4px))] aria-disabled:opacity-25 border-l-2 border-transparent data-[active-item]:border-black data-[active-item]:rounded-l-none data-[active-item]:bg-black/[0.075] data-[active-item]:text-black dark:data-[active-item]:bg-white/10 dark:data-[active-item]:text-white dark:data-[active-item]:border-white sm:py-1 sm:text-sm min-w-0 w-full',
'relative flex cursor-default items-center gap-2 rounded-lg p-2 outline-none! scroll-m-1 scroll-mt-[calc(var(--combobox-height,0px)+var(--label-height,4px))] aria-disabled:opacity-25 data-[active-item]:bg-black/[0.075] data-[active-item]:text-black dark:data-[active-item]:bg-white/10 dark:data-[active-item]:text-white sm:py-1 sm:text-sm min-w-0 w-full before:absolute before:left-0 before:top-1 before:bottom-1 before:w-0.5 before:bg-transparent before:rounded-full data-[active-item]:before:bg-black dark:data-[active-item]:before:bg-white',
props.className,
),
};

View file

@ -68,7 +68,7 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
/>
<div className="flex items-center gap-2">
<TooltipAnchor
description={localize('com_ui_save')}
description={localize('com_ui_download')}
render={
<Button onClick={() => downloadImage()} variant="ghost" className="h-10 w-10 p-0">
<ArrowDownToLine className="size-6" />
@ -108,7 +108,7 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
alt="Image"
className="max-h-full max-w-full object-contain"
style={{
maxHeight: 'calc(100vh - 6rem)', // Account for header and padding
maxHeight: 'calc(100vh - 6rem)',
maxWidth: '100%',
}}
/>
@ -117,7 +117,7 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
{/* Side Panel */}
<div
className={`shadow-l-lg fixed right-0 top-0 z-20 h-full w-80 transform rounded-l-2xl border-l border-border-light bg-surface-secondary transition-transform duration-500 ease-in-out ${
className={`shadow-l-lg fixed right-0 top-0 z-20 h-full w-80 transform rounded-l-2xl border-l border-border-light bg-surface-primary transition-transform duration-500 ease-in-out ${
isPromptOpen ? 'translate-x-0' : 'translate-x-full'
}`}
>
@ -132,7 +132,7 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
<div className="space-y-6">
{/* Prompt Section */}
<div>
<h4 className="mb-2 text-sm font-medium text-text-secondary">
<h4 className="mb-2 text-sm font-medium text-text-primary">
{localize('com_ui_prompt')}
</h4>
<div className="rounded-md bg-surface-tertiary p-3">
@ -144,20 +144,18 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
{/* Generation Settings */}
<div>
<h4 className="mb-3 text-sm font-medium text-text-secondary">
<h4 className="mb-3 text-sm font-medium text-text-primary">
{localize('com_ui_generation_settings')}
</h4>
<div className="space-y-3">
<div className="flex items-center justify-between">
<span className="text-sm text-text-secondary">{localize('com_ui_size')}:</span>
<span className="text-sm text-text-primary">{localize('com_ui_size')}:</span>
<span className="text-sm font-medium text-text-primary">
{args?.size || 'Unknown'}
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-text-secondary">
{localize('com_ui_quality')}:
</span>
<span className="text-sm text-text-primary">{localize('com_ui_quality')}:</span>
<span
className={`rounded px-2 py-1 text-xs font-medium capitalize ${
args?.quality === 'high'
@ -171,7 +169,7 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-text-secondary">
<span className="text-sm text-text-primary">
{localize('com_ui_file_size')}:
</span>
<span className="text-sm font-medium text-text-primary">

View file

@ -178,17 +178,6 @@ export default function OpenAIImageGen({
<div className="relative my-2.5 flex size-5 shrink-0 items-center gap-2.5">
<ProgressText progress={progress} error={cancelled} toolName={toolName} />
</div>
{/* {showInfo && hasInfo && (
<ToolCallInfo
key="tool-call-info"
input={args ?? ''}
output={output}
function_name={function_name}
pendingAuth={authDomain.length > 0 && !cancelled && initialProgress < 1}
/>
)} */}
<div className="relative mb-2 flex w-full justify-start">
<div ref={containerRef} className="w-full max-w-lg">
{dimensions.width !== 'auto' && progress < 1 && (

View file

@ -216,18 +216,12 @@ function FeedbackButtons({
function buttonClasses(isActive: boolean, isLast: boolean) {
return cn(
'hover-button rounded-lg p-1.5',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-black dark:focus-visible:ring-white',
'hover:bg-gray-100 hover:text-gray-500',
'data-[state=open]:active data-[state=open]:bg-gray-100 data-[state=open]:text-gray-500',
isActive ? 'text-gray-500 dark:text-gray-200 font-bold' : 'dark:text-gray-400/70',
'dark:hover:bg-gray-700 dark:hover:text-gray-200',
'data-[state=open]:dark:bg-gray-700 data-[state=open]:dark:text-gray-200',
'disabled:dark:hover:text-gray-400',
isLast
? ''
: 'data-[state=open]:opacity-100 md:opacity-0 md:group-focus-within:opacity-100 md:group-hover:opacity-100',
'md:group-focus-within:visible md:group-hover:visible md:group-[.final-completion]:visible',
'hover-button rounded-lg p-1.5 text-text-secondary-alt transition-colors duration-200',
'hover:text-text-primary hover:bg-surface-hover',
'md:group-hover:visible md:group-focus-within:visible md:group-[.final-completion]:visible',
!isLast && 'md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100',
'focus-visible:ring-2 focus-visible:ring-black dark:focus-visible:ring-white focus-visible:outline-none',
isActive && 'active text-text-primary bg-surface-hover',
);
}

View file

@ -211,14 +211,12 @@ export default function Fork({
});
const buttonStyle = cn(
'hover-button rounded-lg p-1.5',
'hover:bg-gray-100 hover:text-gray-500',
'dark:text-gray-400/70 dark:hover:bg-gray-700 dark:hover:text-gray-200',
'disabled:dark:hover:text-gray-400',
'hover-button rounded-lg p-1.5 text-text-secondary-alt transition-colors duration-200',
'hover:text-text-primary hover:bg-surface-hover',
'md:group-hover:visible md:group-focus-within:visible md:group-[.final-completion]:visible',
!isLast && 'md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100',
'focus-visible:ring-2 focus-visible:ring-black dark:focus-visible:ring-white focus-visible:outline-none',
isActive && 'active text-gray-700 dark:text-gray-200 bg-gray-100 bg-gray-700',
isActive && 'active text-text-primary bg-surface-hover',
);
const forkConvo = useForkConvoMutation({

View file

@ -77,21 +77,13 @@ const HoverButton = memo(
className = '',
}: HoverButtonProps) => {
const buttonStyle = cn(
'hover-button rounded-lg p-1.5',
'hover:bg-gray-100 hover:text-gray-500',
'dark:text-gray-400/70 dark:hover:bg-gray-700 dark:hover:text-gray-200',
'disabled:dark:hover:text-gray-400',
'hover-button rounded-lg p-1.5 text-text-secondary-alt transition-colors duration-200',
'hover:text-text-primary hover:bg-surface-hover',
'md:group-hover:visible md:group-focus-within:visible md:group-[.final-completion]:visible',
!isLast && 'md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100',
!isVisible && 'opacity-0',
'focus-visible:ring-2 focus-visible:ring-black dark:focus-visible:ring-white focus-visible:outline-none',
isActive && isVisible && 'active text-gray-700 dark:text-gray-200 bg-gray-100 bg-gray-700',
isActive && isVisible && 'active text-text-primary bg-surface-hover',
className,
);

View file

@ -128,7 +128,7 @@ export default function ActionsPanel({
selectHandler: () => {
if (!agent_id) {
return showToast({
message: 'No agent_id found, is the agent created?',
message: localize('com_agents_no_agent_id_error'),
status: 'error',
});
}

View file

@ -20,6 +20,7 @@
"com_agents_missing_provider_model": "Please select a provider and model before creating an agent.",
"com_agents_name_placeholder": "Optional: The name of the agent",
"com_agents_no_access": "You don't have access to edit this agent.",
"com_agents_no_agent_id_error": "No agent ID found. Please ensure the agent is created first.",
"com_agents_not_available": "Agent Not Available",
"com_agents_search_info": "When enabled, allows your agent to search the web for up-to-date information. Requires a valid API key.",
"com_agents_search_name": "Search agents by name",
@ -970,4 +971,4 @@
"com_ui_zoom": "Zoom",
"com_user_message": "You",
"com_warning_resubmit_unsupported": "Resubmitting the AI message is not supported for this endpoint."
}
}

View file

@ -2720,6 +2720,20 @@ html {
.shimmer {
display: inline-block;
position: relative;
background: linear-gradient(
90deg,
rgb(33, 33, 33) 25%,
rgba(129, 130, 134, 0.18) 50%,
rgb(33, 33, 33) 75%
);
background-size: 200% 100%;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer 4s linear infinite;
}
.dark .shimmer {
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0.8) 25%,
@ -2733,20 +2747,6 @@ html {
animation: shimmer 4s linear infinite;
}
:global(.dark) .shimmer {
background: linear-gradient(
90deg,
rgba(255, 255, 255) 25%,
rgba(129, 130, 134, 0.18) 50%,
rgb(255, 255, 255) 75%
);
background-size: 200% 100%;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer 4s linear infinite;
}
.custom-style-2 {
padding: 12px;
}