Fix P1 accessibility issues in Button and Card
Button:
- Add aria-busy={loading} for assistive technology
- Add visually-hidden "Loading" text for screen readers
- Mark CircularProgress as aria-hidden (decorative)
Card:
- Add tabIndex={0} and role="button" when interactive
- Fix Record<string, any> → Theme type for type safety
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|||||||
variant={variant}
|
variant={variant}
|
||||||
size={size}
|
size={size}
|
||||||
disabled={loading || disabled}
|
disabled={loading || disabled}
|
||||||
|
aria-busy={loading || undefined}
|
||||||
sx={[
|
sx={[
|
||||||
underline &&
|
underline &&
|
||||||
variant === 'text' && {
|
variant === 'text' && {
|
||||||
@@ -63,12 +64,30 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
{loading && (
|
{loading && (
|
||||||
|
<>
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
size={16}
|
size={16}
|
||||||
color="inherit"
|
color="inherit"
|
||||||
thickness={3}
|
thickness={3}
|
||||||
|
aria-hidden
|
||||||
sx={{ ml: 1 }}
|
sx={{ ml: 1 }}
|
||||||
/>
|
/>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
padding: 0,
|
||||||
|
margin: -1,
|
||||||
|
overflow: 'hidden',
|
||||||
|
clip: 'rect(0, 0, 0, 0)',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
borderWidth: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Loading
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</MuiButton>
|
</MuiButton>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import MuiCard from '@mui/material/Card';
|
import MuiCard from '@mui/material/Card';
|
||||||
import type { CardProps as MuiCardProps } from '@mui/material/Card';
|
import type { CardProps as MuiCardProps } from '@mui/material/Card';
|
||||||
import CardContent from '@mui/material/CardContent';
|
import CardContent from '@mui/material/CardContent';
|
||||||
|
import type { Theme } from '@mui/material/styles';
|
||||||
|
|
||||||
// ─── Types ───────────────────────────────────────────────────────────────────
|
// ─── Types ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -56,11 +57,17 @@ export const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
|||||||
// Map FA variant names to MUI Card variant
|
// Map FA variant names to MUI Card variant
|
||||||
const muiVariant = variant === 'outlined' ? 'outlined' : undefined;
|
const muiVariant = variant === 'outlined' ? 'outlined' : undefined;
|
||||||
|
|
||||||
|
// Interactive cards need keyboard operability
|
||||||
|
const interactiveProps = interactive
|
||||||
|
? { tabIndex: 0 as const, role: 'button' as const }
|
||||||
|
: {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MuiCard
|
<MuiCard
|
||||||
ref={ref}
|
ref={ref}
|
||||||
variant={muiVariant}
|
variant={muiVariant}
|
||||||
elevation={0}
|
elevation={0}
|
||||||
|
{...interactiveProps}
|
||||||
sx={[
|
sx={[
|
||||||
// Selected state: brand border + warm background
|
// Selected state: brand border + warm background
|
||||||
// Border width is always 2px (set in theme) — only colour changes here
|
// Border width is always 2px (set in theme) — only colour changes here
|
||||||
@@ -81,7 +88,7 @@ export const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
|||||||
// Focus-visible for keyboard accessibility on interactive cards
|
// Focus-visible for keyboard accessibility on interactive cards
|
||||||
interactive && {
|
interactive && {
|
||||||
'&:focus-visible': {
|
'&:focus-visible': {
|
||||||
outline: (theme: Record<string, any>) =>
|
outline: (theme: Theme) =>
|
||||||
`2px solid ${theme.palette.primary.main}`,
|
`2px solid ${theme.palette.primary.main}`,
|
||||||
outlineOffset: '2px',
|
outlineOffset: '2px',
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user