Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,15 @@ axe-core runs inside the visual-regression Playwright loop (`test/regressions/in
Key files:

- `test/regressions/demoMeta.ts``SCREENSHOT_RULES` and `A11Y_RULES` arrays, matched last-wins (no inheritance: overrides restate every field) against `docs/data/material/components/{slug}/{Demo}` (minimatch globs).
- `test/regressions/a11y/axe.ts` — asserts `color-contrast` and `link-in-text-block` unless listed in `skipAssertions`.
- `test/regressions/a11y/axe.ts` — asserts `color-contrast` and `link-in-text-block` by default, or all exercised axe rules when the matching a11y rule sets `assertions: 'all'`; `skipAssertions` suppresses selected rule assertions.
- `test/regressions/a11y/a11yReporter.ts` — writes one file per slug at `docs/data/material/components/{slug}/{slug}.a11y.json`. Each file is keyed by demo name, then by axe rule ID. Each rule records a `status` (`pass`, `fail`, or `incomplete`) and WCAG tags.

Enroll a component (slug-wide, or narrow with brace-glob):

```ts
// test/regressions/demoMeta.ts
{ test: 'docs/data/material/components/alert/*', enabled: true, skipAssertions: ['color-contrast'] },
{ test: 'docs/data/material/components/buttons/{BasicButtons,ColorButtons}', enabled: true },
{ test: 'docs/data/material/components/buttons/{BasicButtons,ColorButtons}', enabled: true, assertions: 'all' },
```

Override a specific demo: append a per-demo rule _after_ the slug-wide rule (last-match-wins; the override must restate every field it wants):
Expand Down
40 changes: 40 additions & 0 deletions docs/data/material/components/buttons/ButtonA11yColorMatrix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';

const variants = ['text', 'outlined', 'contained'];
const colors = [
'primary',
'secondary',
'success',
'error',
'info',
'warning',
'inherit',
];

export default function ButtonA11yColorMatrix() {
return (
<Box
sx={{
color: 'text.primary',
display: 'grid',
gap: 1,
'& > div': {
display: 'flex',
flexWrap: 'wrap',
gap: 1,
},
}}
>
{variants.map((variant) => (
<div key={variant}>
{colors.map((color) => (
<Button key={`${variant}-${color}`} variant={variant} color={color}>
{color} {variant}
</Button>
))}
</div>
))}
</Box>
);
}
40 changes: 40 additions & 0 deletions docs/data/material/components/buttons/ButtonA11yColorMatrix.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';

const variants = ['text', 'outlined', 'contained'] as const;
const colors = [
'primary',
'secondary',
'success',
'error',
'info',
'warning',
'inherit',
] as const;

export default function ButtonA11yColorMatrix() {
return (
<Box
sx={{
color: 'text.primary',
display: 'grid',
gap: 1,
'& > div': {
display: 'flex',
flexWrap: 'wrap',
gap: 1,
},
}}
>
{variants.map((variant) => (
<div key={variant}>
{colors.map((color) => (
<Button key={`${variant}-${color}`} variant={variant} color={color}>
{color} {variant}
</Button>
))}
</div>
))}
</Box>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{variants.map((variant) => (
<div key={variant}>
{colors.map((color) => (
<Button key={`${variant}-${color}`} variant={variant} color={color}>
{color} {variant}
</Button>
))}
</div>
))}
20 changes: 20 additions & 0 deletions docs/data/material/components/buttons/ButtonA11yNonNative.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from 'react';
import Stack from '@mui/material/Stack';
import Button from '@mui/material/Button';

const CustomDivButton = React.forwardRef(function CustomDivButton(props, ref) {
return <div ref={ref} {...props} />;
});

export default function ButtonA11yNonNative() {
return (
<Stack spacing={2} direction="row">
<Button component={CustomDivButton} nativeButton={false}>
Non-native action
</Button>
<Button component={CustomDivButton} nativeButton={false} disabled>
Disabled non-native
</Button>
</Stack>
);
}
23 changes: 23 additions & 0 deletions docs/data/material/components/buttons/ButtonA11yNonNative.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';
import Stack from '@mui/material/Stack';
import Button from '@mui/material/Button';

const CustomDivButton = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(function CustomDivButton(props, ref) {
return <div ref={ref} {...props} />;
});

export default function ButtonA11yNonNative() {
return (
<Stack spacing={2} direction="row">
<Button component={CustomDivButton} nativeButton={false}>
Non-native action
</Button>
<Button component={CustomDivButton} nativeButton={false} disabled>
Disabled non-native
</Button>
</Stack>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Button component={CustomDivButton} nativeButton={false}>
Non-native action
</Button>
<Button component={CustomDivButton} nativeButton={false} disabled>
Disabled non-native
</Button>
58 changes: 58 additions & 0 deletions docs/data/material/components/buttons/ButtonA11ySemanticStates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { styled } from '@mui/material/styles';
import Button from '@mui/material/Button';
import Stack from '@mui/material/Stack';
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
import DeleteIcon from '@mui/icons-material/Delete';
import SaveIcon from '@mui/icons-material/Save';
import SendIcon from '@mui/icons-material/Send';

const VisuallyHiddenInput = styled('input')({
clip: 'rect(0 0 0 0)',
clipPath: 'inset(50%)',
height: 1,
overflow: 'hidden',
position: 'absolute',
bottom: 0,
left: 0,
whiteSpace: 'nowrap',
width: 1,
});

export default function ButtonA11ySemanticStates() {
return (
<Stack spacing={2}>
<Stack direction="row" spacing={2}>
<Button>Native action</Button>
<Button href="#button-a11y-link">Link action</Button>
<Button disabled>Disabled action</Button>
</Stack>
<Stack direction="row" spacing={2}>
<Button variant="outlined" startIcon={<DeleteIcon />}>
Delete file
</Button>
<Button variant="contained" endIcon={<SendIcon />}>
Send message
</Button>
<Button
loading
loadingPosition="start"
startIcon={<SaveIcon />}
variant="outlined"
>
Save changes
</Button>
</Stack>
<Button
component="label"
role={undefined}
tabIndex={-1}
variant="contained"
startIcon={<CloudUploadIcon />}
sx={{ alignSelf: 'flex-start' }}
>
Upload receipt
<VisuallyHiddenInput type="file" multiple />
</Button>
</Stack>
);
}
58 changes: 58 additions & 0 deletions docs/data/material/components/buttons/ButtonA11ySemanticStates.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { styled } from '@mui/material/styles';
import Button from '@mui/material/Button';
import Stack from '@mui/material/Stack';
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
import DeleteIcon from '@mui/icons-material/Delete';
import SaveIcon from '@mui/icons-material/Save';
import SendIcon from '@mui/icons-material/Send';

const VisuallyHiddenInput = styled('input')({
clip: 'rect(0 0 0 0)',
clipPath: 'inset(50%)',
height: 1,
overflow: 'hidden',
position: 'absolute',
bottom: 0,
left: 0,
whiteSpace: 'nowrap',
width: 1,
});

export default function ButtonA11ySemanticStates() {
return (
<Stack spacing={2}>
<Stack direction="row" spacing={2}>
<Button>Native action</Button>
<Button href="#button-a11y-link">Link action</Button>
<Button disabled>Disabled action</Button>
</Stack>
<Stack direction="row" spacing={2}>
<Button variant="outlined" startIcon={<DeleteIcon />}>
Delete file
</Button>
<Button variant="contained" endIcon={<SendIcon />}>
Send message
</Button>
<Button
loading
loadingPosition="start"
startIcon={<SaveIcon />}
variant="outlined"
>
Save changes
</Button>
</Stack>
<Button
component="label"
role={undefined}
tabIndex={-1}
variant="contained"
startIcon={<CloudUploadIcon />}
sx={{ alignSelf: 'flex-start' }}
>
Upload receipt
<VisuallyHiddenInput type="file" multiple />
</Button>
</Stack>
);
}
40 changes: 40 additions & 0 deletions docs/data/material/components/buttons/ButtonA11yTextSpacing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Stack from '@mui/material/Stack';
import SaveIcon from '@mui/icons-material/Save';
import SendIcon from '@mui/icons-material/Send';

export default function ButtonA11yTextSpacing() {
return (
<Stack spacing={2} sx={{ maxWidth: 320 }}>
<Button
variant="contained"
sx={{
justifyContent: 'flex-start',
lineHeight: 1.5,
letterSpacing: '0.12em',
whiteSpace: 'normal',
wordSpacing: '0.16em',
}}
>
Review accessibility settings before continuing
</Button>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
<Button
variant="outlined"
startIcon={<SaveIcon />}
sx={{ whiteSpace: 'normal' }}
>
Save a longer action label
</Button>
<Button
variant="contained"
endIcon={<SendIcon />}
sx={{ whiteSpace: 'normal' }}
>
Send confirmation message
</Button>
</Box>
</Stack>
);
}
40 changes: 40 additions & 0 deletions docs/data/material/components/buttons/ButtonA11yTextSpacing.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Stack from '@mui/material/Stack';
import SaveIcon from '@mui/icons-material/Save';
import SendIcon from '@mui/icons-material/Send';

export default function ButtonA11yTextSpacing() {
return (
<Stack spacing={2} sx={{ maxWidth: 320 }}>
<Button
variant="contained"
sx={{
justifyContent: 'flex-start',
lineHeight: 1.5,
letterSpacing: '0.12em',
whiteSpace: 'normal',
wordSpacing: '0.16em',
}}
>
Review accessibility settings before continuing
</Button>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
<Button
variant="outlined"
startIcon={<SaveIcon />}
sx={{ whiteSpace: 'normal' }}
>
Save a longer action label
</Button>
<Button
variant="contained"
endIcon={<SendIcon />}
sx={{ whiteSpace: 'normal' }}
>
Send confirmation message
</Button>
</Box>
</Stack>
);
}
Loading
Loading