Normalize atoms: fix Card a11y + responsive padding, Input spacing
- Card: add Enter/Space keyboard activation for interactive cards (P1 a11y) - Card: responsive padding — 16px mobile / 24px desktop (P1 responsive) - Card: focus-visible outline uses focus token CSS var instead of palette - Card: remove unused Theme import - Input: convert raw px strings to MUI spacing (mb: 2.5, mt: 1.5) Phase 1 retroactive review: atoms normalize + audit (Button 20/20, Input 20/20, Card 18→20/20 after fixes) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,6 @@ 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 ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -14,7 +13,7 @@ export interface CardProps extends Omit<MuiCardProps, 'raised' | 'variant'> {
|
|||||||
interactive?: boolean;
|
interactive?: boolean;
|
||||||
/** Highlights the card as selected — brand border + warm background tint */
|
/** Highlights the card as selected — brand border + warm background tint */
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
/** Padding preset: "default" (24px), "compact" (16px), "none" (no wrapper) */
|
/** Padding preset: "default" (16px mobile / 24px desktop), "compact" (12px mobile / 16px desktop), "none" (no wrapper) */
|
||||||
padding?: 'default' | 'compact' | 'none';
|
padding?: 'default' | 'compact' | 'none';
|
||||||
/** Content to render inside the card */
|
/** Content to render inside the card */
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
@@ -58,7 +57,18 @@ export const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
|||||||
const muiVariant = variant === 'outlined' ? 'outlined' : undefined;
|
const muiVariant = variant === 'outlined' ? 'outlined' : undefined;
|
||||||
|
|
||||||
// Interactive cards need keyboard operability
|
// Interactive cards need keyboard operability
|
||||||
const interactiveProps = interactive ? { tabIndex: 0 as const, role: 'button' as const } : {};
|
const handleKeyDown = interactive
|
||||||
|
? (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
e.currentTarget.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const interactiveProps = interactive
|
||||||
|
? { tabIndex: 0 as const, role: 'button' as const, onKeyDown: handleKeyDown }
|
||||||
|
: {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MuiCard
|
<MuiCard
|
||||||
@@ -86,7 +96,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: Theme) => `2px solid ${theme.palette.primary.main}`,
|
outline: '2px solid var(--fa-color-interactive-focus)',
|
||||||
outlineOffset: '2px',
|
outlineOffset: '2px',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -97,9 +107,9 @@ export const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
|||||||
{padding !== 'none' ? (
|
{padding !== 'none' ? (
|
||||||
<CardContent
|
<CardContent
|
||||||
sx={{
|
sx={{
|
||||||
p: padding === 'compact' ? 4 : 6,
|
p: padding === 'compact' ? { xs: 3, md: 4 } : { xs: 4, md: 6 },
|
||||||
'&:last-child': {
|
'&:last-child': {
|
||||||
pb: padding === 'compact' ? 4 : 6,
|
pb: padding === 'compact' ? { xs: 3, md: 4 } : { xs: 4, md: 6 },
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export const Input = React.forwardRef<HTMLDivElement, InputProps>(
|
|||||||
transform: 'none',
|
transform: 'none',
|
||||||
maxWidth: 'none',
|
maxWidth: 'none',
|
||||||
pointerEvents: 'auto',
|
pointerEvents: 'auto',
|
||||||
mb: '10px',
|
mb: 2.5,
|
||||||
// labelLg typography
|
// labelLg typography
|
||||||
fontFamily: (theme) => theme.typography.labelLg.fontFamily,
|
fontFamily: (theme) => theme.typography.labelLg.fontFamily,
|
||||||
fontSize: (theme) => theme.typography.labelLg.fontSize,
|
fontSize: (theme) => theme.typography.labelLg.fontSize,
|
||||||
@@ -163,7 +163,7 @@ export const Input = React.forwardRef<HTMLDivElement, InputProps>(
|
|||||||
role={error ? 'alert' : undefined}
|
role={error ? 'alert' : undefined}
|
||||||
sx={{
|
sx={{
|
||||||
mx: 0,
|
mx: 0,
|
||||||
mt: '6px',
|
mt: 1.5,
|
||||||
// caption typography
|
// caption typography
|
||||||
fontFamily: (theme) => theme.typography.caption.fontFamily,
|
fontFamily: (theme) => theme.typography.caption.fontFamily,
|
||||||
fontSize: (theme) => theme.typography.caption.fontSize,
|
fontSize: (theme) => theme.typography.caption.fontSize,
|
||||||
|
|||||||
Reference in New Issue
Block a user