🤲 a11y: Sidebar Text Contrast (#3665)

* fix: use theme class for valid contrast for light/dark, conversation group names, fix type issues

* fix: searchbar text contrast styling, closes #3469

* style: add theming for prompt cards, fix a11y contrast issues

* a11y: address placeholder text contrast issues in LLM controls section

* chore: Add conditional check for pull request repository in a11y workflow

* style: Update text color contrast to WCAG standard for placeholder and focus states in AssistantPanel components
This commit is contained in:
Danny Avila 2024-08-16 13:50:47 -04:00 committed by GitHub
parent f8a5dad265
commit 91fc89076f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 62 additions and 51 deletions

View file

@ -2,7 +2,6 @@ import { useState, useMemo } from 'react';
import { Menu as MenuIcon, Edit as EditIcon, EarthIcon, TextSearch } from 'lucide-react';
import type { TPromptGroup } from 'librechat-data-provider';
import {
Button,
DropdownMenu,
DropdownMenuItem,
DropdownMenuGroup,
@ -29,12 +28,12 @@ export default function ChatGroupItem({
const [isVariableDialogOpen, setVariableDialogOpen] = useState(false);
const onEditClick = useCustomLink<HTMLDivElement>(`/d/prompts/${group._id}`);
const groupIsGlobal = useMemo(
() => instanceProjectId && group.projectIds?.includes(instanceProjectId),
() => instanceProjectId != null && group.projectIds?.includes(instanceProjectId),
[group, instanceProjectId],
);
const isOwner = useMemo(() => user?.id === group.author, [user, group]);
const onCardClick = () => {
const onCardClick: React.MouseEventHandler<HTMLButtonElement> = () => {
const text = group.productionPrompt?.prompt ?? '';
if (!text) {
return;
@ -53,23 +52,26 @@ export default function ChatGroupItem({
name={group.name}
category={group.category ?? ''}
onClick={onCardClick}
snippet={group.oneliner ? group.oneliner : group.productionPrompt?.prompt ?? ''}
snippet={
typeof group.oneliner === 'string' && group.oneliner.length > 0
? group.oneliner
: group.productionPrompt?.prompt ?? ''
}
>
<div className="flex flex-row items-center gap-2">
{groupIsGlobal && <EarthIcon className="icon-md text-green-400" />}
{groupIsGlobal === true && <EarthIcon className="icon-md text-green-400" />}
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild>
<Button
id="promtps-menu-trigger"
aria-label="promtps-menu-trigger"
variant="outline"
<button
id="prompts-menu-trigger"
aria-label="prompts-menu-trigger"
onClick={(e) => {
e.stopPropagation();
}}
className="z-50 h-7 w-7 p-0 transition-all duration-300 ease-in-out hover:border-white dark:bg-gray-800 dark:hover:border-gray-400 dark:focus:border-gray-500"
className="z-50 inline-flex h-7 w-7 items-center justify-center rounded-md border border-border-medium bg-transparent p-0 text-sm font-medium transition-all duration-300 ease-in-out hover:border-border-heavy hover:bg-surface-secondary focus:border-border-heavy focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
>
<MenuIcon className="icon-md dark:text-gray-300" />
</Button>
<MenuIcon className="icon-md text-text-secondary" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent
className="z-50 mt-2 w-36 rounded-lg"
@ -81,7 +83,7 @@ export default function ChatGroupItem({
e.stopPropagation();
setPreviewDialogOpen(true);
}}
className="w-full cursor-pointer rounded-lg disabled:cursor-not-allowed dark:text-gray-300 dark:hover:bg-gray-700 dark:focus:bg-gray-700"
className="w-full cursor-pointer rounded-lg text-text-secondary hover:bg-surface-hover focus:bg-surface-hover disabled:cursor-not-allowed"
>
<TextSearch className="mr-2 h-4 w-4" />
<span>{localize('com_ui_preview')}</span>
@ -90,7 +92,7 @@ export default function ChatGroupItem({
<DropdownMenuGroup>
<DropdownMenuItem
disabled={!isOwner}
className="cursor-pointer rounded-lg disabled:cursor-not-allowed dark:text-gray-300 dark:hover:bg-gray-700 dark:focus:bg-gray-700"
className="cursor-pointer rounded-lg text-text-secondary hover:bg-surface-hover focus:bg-surface-hover disabled:cursor-not-allowed"
onClick={(e) => {
e.stopPropagation();
onEditClick(e);

View file

@ -152,7 +152,7 @@ export default function FilterPrompts({
setDisplayName(e.target.value);
setName(e.target.value);
}}
className="w-full border-border-light"
className="w-full border-border-light placeholder:text-text-secondary"
/>
</div>
);

View file

@ -10,27 +10,25 @@ export default function ListCard({
category: string;
name: string;
snippet: string;
onClick?: React.MouseEventHandler<HTMLDivElement>;
onClick?: React.MouseEventHandler<HTMLButtonElement>;
children?: React.ReactNode;
}) {
return (
<div
<button
onClick={onClick}
className="relative my-2 flex w-full cursor-pointer flex-col gap-2 rounded-2xl border px-3 pb-4 pt-3 text-start align-top
text-[15px] shadow-[0_0_2px_0_rgba(0,0,0,0.05),0_4px_6px_0_rgba(0,0,0,0.02)] transition-all duration-300 ease-in-out hover:bg-gray-100 dark:border-gray-700 dark:hover:bg-gray-700"
className="relative my-2 flex w-full cursor-pointer flex-col gap-2 rounded-2xl border border-border-light px-3 pb-4 pt-3 text-start
align-top text-[15px] shadow-[0_0_2px_0_rgba(0,0,0,0.05),0_4px_6px_0_rgba(0,0,0,0.02)] transition-all duration-300 ease-in-out hover:bg-surface-tertiary"
>
<div className="flex w-full justify-between">
<div className="flex flex-row gap-2">
<CategoryIcon category={category} className="icon-md" />
<h3 className="break-word select-none text-balance text-sm font-semibold text-gray-800 dark:text-gray-200">
<h3 className="break-word select-none text-balance text-sm font-semibold text-text-primary">
{name}
</h3>
</div>
<div>{children}</div>
</div>
<div className="ellipsis select-none text-balance text-sm text-gray-600 dark:text-gray-400">
{snippet}
</div>
</div>
<div className="ellipsis select-none text-balance text-sm text-text-secondary">{snippet}</div>
</button>
);
}