Sprint 1 — Quick A11y wins: - progress.tsx: role=progressbar + aria-value* + aria-label - switch.tsx: role=switch + aria-checked - skeleton.tsx: aria-hidden=true - alert.tsx, Toast.tsx, SelectTrigger.tsx: aria-labels on close buttons - PostCard.tsx: alt on images + aria-labels on icon buttons - ProductCard.tsx: aria-labels on play/view buttons - modal.tsx: role=dialog + aria-modal + aria-labelledby - input.tsx: error state + aria-invalid + aria-describedby - FAB.tsx: forward aria-label from label prop Sprint 2 — Structural A11y + View States: - tabs/: full ARIA tablist/tab/tabpanel + arrow key navigation - radio-group.tsx: role=radio + arrow key navigation - select/: aria-activedescendant + full keyboard navigation - List.tsx + card.tsx: focus-visible states on interactive elements - DashboardPage, LibraryPage, LiveView, QueueView: error states - WishlistView, AdminDashboard, AnalyticsView, SellerDashboard: loading/empty states Co-authored-by: Cursor <cursoragent@cursor.com>
35 lines
964 B
TypeScript
35 lines
964 B
TypeScript
import * as React from 'react';
|
|
import { cn } from '@/lib/utils';
|
|
import type { TabsContentProps } from './types';
|
|
|
|
export const TabsContent = React.forwardRef<HTMLDivElement, TabsContentProps>(
|
|
(
|
|
{ className, value: contentValue, activeValue, tabsId, children, ...props },
|
|
ref,
|
|
) => {
|
|
if (activeValue !== contentValue) {
|
|
return null;
|
|
}
|
|
|
|
const panelId = tabsId ? `${tabsId}-panel-${contentValue}` : undefined;
|
|
const tabId = tabsId ? `${tabsId}-tab-${contentValue}` : undefined;
|
|
|
|
return (
|
|
<div
|
|
ref={ref}
|
|
id={panelId}
|
|
role="tabpanel"
|
|
aria-labelledby={tabId}
|
|
tabIndex={0}
|
|
className={cn(
|
|
'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
},
|
|
);
|
|
TabsContent.displayName = 'TabsContent';
|