Accessibility fix pass for twenty-ui input components (#21776)

Re-enables the live Storybook axe gate across the twenty-ui `input`
domain (17 story files) by removing the inherited `a11y: { test: 'todo'
}` overrides. Pilot for the wider a11y fix pass; other domains follow
the same playbook.

**What changed**
- Accessible names added to icon-only buttons, `Toggle`, `Checkbox`, and
the button groups via small additive `aria-label`/`ariaLabel` props on 8
components. Where this fully fixes the story, the override is dropped so
the full gate applies.
- Color-contrast (design-token level) is deferred — not changed — via a
new shared `A11Y_DEFER_COLOR_CONTRAST` parameter (`@ui/testing`) that
disables only the `color-contrast` rule while every other axe rule stays
enforced. Grep the constant to find all deferrals when tokens are
darkened later.
- `CatalogDecorator`: unique cell ids (clears `duplicate-id-aria`) and
dimension titles switched from empty `h1/h2/h3` to `div` (clears
`empty-heading`). Both help every domain's catalogs.

**Reviewer notes**
- No visual change: edits are aria / id / story-args / axe-config only,
so Argos parity holds.
- Input axe gate goes from 12 failing to 60/60 passing; typecheck and
lint green.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21776?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Raphaël Bosi
2026-06-18 15:01:45 +02:00
committed by GitHub
parent 41b7c53dbf
commit b4113de74a
28 changed files with 103 additions and 90 deletions
@@ -17,6 +17,7 @@ export type FloatingIconButtonGroupProps = Pick<
> & {
iconButtons: {
Icon: IconComponent;
ariaLabel?: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onClick?: (event: MouseEvent<any>) => void;
isActive?: boolean;
@@ -30,7 +31,7 @@ export const FloatingIconButtonGroup = ({
}: FloatingIconButtonGroupProps) => {
return (
<div className={clsx(styles.container, className)}>
{iconButtons.map(({ Icon, onClick, isActive }, index) => {
{iconButtons.map(({ Icon, onClick, isActive, ariaLabel }, index) => {
const position: FloatingIconButtonPosition =
iconButtons.length === 1
? 'standalone'
@@ -46,6 +47,7 @@ export const FloatingIconButtonGroup = ({
applyBlur={false}
applyShadow={false}
Icon={Icon}
ariaLabel={ariaLabel}
onClick={onClick}
position={position}
size={size}
@@ -1,6 +1,7 @@
import { type Meta, type StoryObj } from '@storybook/react-vite';
import { IconCheckbox, IconNotes, IconTimelineEvent } from '@ui/icon';
import {
A11Y_DEFER_COLOR_CONTRAST,
CatalogDecorator,
type CatalogStory,
ComponentDecorator,
@@ -13,9 +14,9 @@ const meta: Meta<typeof FloatingIconButtonGroup> = {
component: FloatingIconButtonGroup,
args: {
iconButtons: [
{ Icon: IconNotes },
{ Icon: IconCheckbox },
{ Icon: IconTimelineEvent },
{ Icon: IconNotes, ariaLabel: 'Notes' },
{ Icon: IconCheckbox, ariaLabel: 'Tasks' },
{ Icon: IconTimelineEvent, ariaLabel: 'Timeline' },
],
},
argTypes: {
@@ -27,8 +28,6 @@ export default meta;
type Story = StoryObj<typeof FloatingIconButtonGroup>;
export const Default: Story = {
// TODO(a11y): violations inherited from deprecated story; fix during a11y pass
parameters: { a11y: { test: 'todo' } },
args: {
size: 'small',
},
@@ -40,8 +39,7 @@ export const Catalog: CatalogStory<Story, typeof FloatingIconButtonGroup> = {
size: { control: false },
},
parameters: {
// TODO(a11y): violations inherited from deprecated story; fix during a11y pass
a11y: { test: 'todo' },
a11y: A11Y_DEFER_COLOR_CONTRAST,
pseudo: { hover: ['.hover'], active: ['.pressed'], focus: ['.focus'] },
catalog: {
dimensions: [