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>
93 lines
2.3 KiB
TypeScript
93 lines
2.3 KiB
TypeScript
import * as React from 'react';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
/**
|
|
* SkeletonProps - Propriétés du composant Skeleton
|
|
*
|
|
* @interface SkeletonProps
|
|
* @extends React.HTMLAttributes<HTMLDivElement>
|
|
*/
|
|
export interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
/**
|
|
* Variant de forme du skeleton
|
|
*
|
|
* - `text`: Forme de texte (arrondi, hauteur fixe)
|
|
* - `circular`: Forme circulaire (pour avatars)
|
|
* - `rectangular`: Forme rectangulaire arrondie (par défaut)
|
|
*
|
|
* @default 'rectangular'
|
|
*/
|
|
variant?: 'text' | 'circular' | 'rectangular';
|
|
|
|
/**
|
|
* Largeur du skeleton (string CSS ou nombre)
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* <Skeleton width="100%" height={20} />
|
|
* <Skeleton width={200} height={100} />
|
|
* ```
|
|
*/
|
|
width?: string | number;
|
|
|
|
/**
|
|
* Hauteur du skeleton (string CSS ou nombre)
|
|
*/
|
|
height?: string | number;
|
|
}
|
|
|
|
/**
|
|
* Skeleton - Composant de placeholder animé avec design system Kodo
|
|
*
|
|
* Composant de skeleton pour afficher des placeholders pendant le chargement.
|
|
* Utilise une animation pulse pour indiquer le chargement.
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* // Skeleton rectangulaire
|
|
* <Skeleton width="100%" height={100} />
|
|
*
|
|
* // Skeleton de texte
|
|
* <Skeleton variant="text" width="80%" />
|
|
*
|
|
* // Skeleton circulaire (avatar)
|
|
* <Skeleton variant="circular" width={40} height={40} />
|
|
* ```
|
|
*
|
|
* @component
|
|
* @param {SkeletonProps} props - Propriétés du composant
|
|
* @returns {JSX.Element} Élément div avec animation pulse
|
|
*/
|
|
|
|
const Skeleton = React.forwardRef<HTMLDivElement, SkeletonProps>(
|
|
({ variant = 'rectangular', width, height, className, ...props }, ref) => {
|
|
const baseClasses = 'relative overflow-hidden bg-muted/50';
|
|
|
|
const variantClasses = {
|
|
text: 'rounded h-4 w-full',
|
|
circular: 'rounded-full',
|
|
rectangular: 'rounded-lg',
|
|
};
|
|
|
|
const style = {
|
|
width,
|
|
height,
|
|
};
|
|
|
|
return (
|
|
<div
|
|
ref={ref}
|
|
className={cn(baseClasses, variantClasses[variant], className)}
|
|
style={style}
|
|
aria-hidden="true"
|
|
{...props}
|
|
>
|
|
{/* Shimmer overlay — sweeping gradient for premium loading feel */}
|
|
<div className="absolute inset-0 skeleton-shimmer" />
|
|
</div>
|
|
);
|
|
},
|
|
);
|
|
Skeleton.displayName = 'Skeleton';
|
|
|
|
export { Skeleton };
|