fix: improve accessibility and styling in sidebar components

This commit is contained in:
Anish Sarkar 2026-03-17 12:42:25 +05:30
parent 591a39ee56
commit 440f5aec72
5 changed files with 25 additions and 42 deletions

View file

@ -369,7 +369,7 @@ export function LayoutShell({
aria-valuenow={50}
tabIndex={0}
onMouseDown={onResizeMouseDown}
className="hidden md:block h-full cursor-col-resize z-30"
className="hidden md:block h-full cursor-col-resize z-30 focus:outline-none"
style={{ width: 8, marginLeft: -8, marginRight: -8 }}
/>
)}

View file

@ -31,13 +31,12 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { Skeleton } from "@/components/ui/skeleton";
import { Spinner } from "@/components/ui/spinner";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/animated-tabs";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { useDebouncedValue } from "@/hooks/use-debounced-value";
import { useLongPress } from "@/hooks/use-long-press";
@ -300,14 +299,11 @@ export function AllPrivateChatsSidebar({
<Tabs
value={showArchived ? "archived" : "active"}
onValueChange={(value) => setShowArchived(value === "archived")}
className="shrink-0 mx-4"
className="shrink-0 mx-4 mt-2"
>
<TabsList className="w-full h-auto p-0 bg-transparent rounded-none border-b">
<TabsTrigger
value="active"
className="group/tab flex-1 rounded-none border-b-2 border-transparent px-1 py-2 text-xs font-medium data-[state=active]:border-primary data-[state=active]:bg-transparent data-[state=active]:shadow-none"
>
<span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted group-data-[state=active]/tab:bg-muted transition-colors">
<TabsList stretch showBottomBorder size="sm">
<TabsTrigger value="active">
<span className="inline-flex items-center gap-1.5">
<MessageCircleMore className="h-4 w-4" />
<span>Active</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">
@ -315,11 +311,8 @@ export function AllPrivateChatsSidebar({
</span>
</span>
</TabsTrigger>
<TabsTrigger
value="archived"
className="group/tab flex-1 rounded-none border-b-2 border-transparent px-1 py-2 text-xs font-medium data-[state=active]:border-primary data-[state=active]:bg-transparent data-[state=active]:shadow-none"
>
<span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted group-data-[state=active]/tab:bg-muted transition-colors">
<TabsTrigger value="archived">
<span className="inline-flex items-center gap-1.5">
<ArchiveIcon className="h-4 w-4" />
<span>Archived</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">
@ -383,7 +376,6 @@ export function AllPrivateChatsSidebar({
disabled={isBusy}
className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden"
>
<MessageCircleMore className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="truncate">{thread.title || "New Chat"}</span>
</button>
) : (
@ -395,7 +387,6 @@ export function AllPrivateChatsSidebar({
disabled={isBusy}
className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden"
>
<MessageCircleMore className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="truncate">{thread.title || "New Chat"}</span>
</button>
</TooltipTrigger>
@ -458,7 +449,6 @@ export function AllPrivateChatsSidebar({
</>
)}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => handleDeleteThread(thread.id)}>
<Trash2 className="mr-2 h-4 w-4" />
<span>{t("delete") || "Delete"}</span>

View file

@ -31,13 +31,12 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { Skeleton } from "@/components/ui/skeleton";
import { Spinner } from "@/components/ui/spinner";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/animated-tabs";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { useDebouncedValue } from "@/hooks/use-debounced-value";
import { useLongPress } from "@/hooks/use-long-press";
@ -300,14 +299,11 @@ export function AllSharedChatsSidebar({
<Tabs
value={showArchived ? "archived" : "active"}
onValueChange={(value) => setShowArchived(value === "archived")}
className="shrink-0 mx-4"
className="shrink-0 mx-4 mt-2"
>
<TabsList className="w-full h-auto p-0 bg-transparent rounded-none border-b">
<TabsTrigger
value="active"
className="group/tab flex-1 rounded-none border-b-2 border-transparent px-1 py-2 text-xs font-medium data-[state=active]:border-primary data-[state=active]:bg-transparent data-[state=active]:shadow-none"
>
<span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted group-data-[state=active]/tab:bg-muted transition-colors">
<TabsList stretch showBottomBorder size="sm">
<TabsTrigger value="active">
<span className="inline-flex items-center gap-1.5">
<MessageCircleMore className="h-4 w-4" />
<span>Active</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">
@ -315,11 +311,8 @@ export function AllSharedChatsSidebar({
</span>
</span>
</TabsTrigger>
<TabsTrigger
value="archived"
className="group/tab flex-1 rounded-none border-b-2 border-transparent px-1 py-2 text-xs font-medium data-[state=active]:border-primary data-[state=active]:bg-transparent data-[state=active]:shadow-none"
>
<span className="w-full inline-flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-lg hover:bg-muted group-data-[state=active]/tab:bg-muted transition-colors">
<TabsTrigger value="archived">
<span className="inline-flex items-center gap-1.5">
<ArchiveIcon className="h-4 w-4" />
<span>Archived</span>
<span className="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-full bg-primary/20 text-muted-foreground text-xs font-medium">
@ -383,7 +376,6 @@ export function AllSharedChatsSidebar({
disabled={isBusy}
className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden"
>
<MessageCircleMore className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="truncate">{thread.title || "New Chat"}</span>
</button>
) : (
@ -395,7 +387,6 @@ export function AllSharedChatsSidebar({
disabled={isBusy}
className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden"
>
<MessageCircleMore className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="truncate">{thread.title || "New Chat"}</span>
</button>
</TooltipTrigger>
@ -458,7 +449,6 @@ export function AllSharedChatsSidebar({
</>
)}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => handleDeleteThread(thread.id)}>
<Trash2 className="mr-2 h-4 w-4" />
<span>{t("delete") || "Delete"}</span>

View file

@ -168,7 +168,7 @@ export function Sidebar({
<button
type="button"
onClick={onViewAllSharedChats}
className="text-[10px] text-muted-foreground/70 hover:text-muted-foreground transition-colors whitespace-nowrap cursor-pointer bg-transparent border-none p-0"
className="text-[10px] text-muted-foreground/70 hover:text-muted-foreground transition-colors whitespace-nowrap cursor-pointer bg-transparent border-none p-0 focus:outline-none"
>
{!disableTooltips && isSharedChatsPanelOpen ? t("hide") : t("show_all")}
</button>
@ -224,7 +224,7 @@ export function Sidebar({
<button
type="button"
onClick={onViewAllPrivateChats}
className="text-[10px] text-muted-foreground/70 hover:text-muted-foreground transition-colors whitespace-nowrap cursor-pointer bg-transparent border-none p-0"
className="text-[10px] text-muted-foreground/70 hover:text-muted-foreground transition-colors whitespace-nowrap cursor-pointer bg-transparent border-none p-0 focus:outline-none"
>
{!disableTooltips && isPrivateChatsPanelOpen ? t("hide") : t("show_all")}
</button>

View file

@ -77,14 +77,14 @@ const XScrollable = forwardRef<
const dragging = useRef(false);
const startX = useRef(0);
const startScrollLeft = useRef(0);
const [scrollPos, setScrollPos] = useState<"start" | "middle" | "end">("start");
const [scrollPos, setScrollPos] = useState<"start" | "middle" | "end" | "none">("none");
const updateScrollPos = useCallback(() => {
const el = scrollRef.current;
if (!el) return;
const canScroll = el.scrollWidth > el.clientWidth + 1;
if (!canScroll) {
setScrollPos("start");
setScrollPos("none");
return;
}
const atStart = el.scrollLeft <= 2;
@ -130,9 +130,12 @@ const XScrollable = forwardRef<
updateScrollPos();
}, [updateScrollPos]);
const maskStart = scrollPos === "start" ? "black" : "transparent";
const maskEnd = scrollPos === "end" ? "black" : "transparent";
const maskImage = `linear-gradient(to right, ${maskStart}, black 24px, black calc(100% - 24px), ${maskEnd})`;
const needsMask = scrollPos !== "none";
const maskStart = scrollPos === "start" || scrollPos === "none" ? "black" : "transparent";
const maskEnd = scrollPos === "end" || scrollPos === "none" ? "black" : "transparent";
const maskImage = needsMask
? `linear-gradient(to right, ${maskStart}, black 24px, black calc(100% - 24px), ${maskEnd})`
: undefined;
return (
// biome-ignore lint/a11y/noStaticElementInteractions: drag-scroll container needs mouse events