FSHSP-106 fix(a11y): clear the mechanical axe violations (151 → 79) - #87
Open
LBU4SH wants to merge 8 commits into
Open
FSHSP-106 fix(a11y): clear the mechanical axe violations (151 → 79)#87LBU4SH wants to merge 8 commits into
LBU4SH wants to merge 8 commits into
Conversation
ui-input forwarded aria-expanded to the native input, but that attribute is not allowed on the implicit textbox role: screen readers dropped it, so a ui-datepicker never announced that a calendar could open, nor its state. Promote the input to role="combobox" (the ARIA 1.2 role that accepts aria-expanded) only when ariaExpanded is provided, leaving plain text fields announced as before. ui-datepicker is the only consumer of these attributes in the kit. Clears the 25 aria-allowed-attr violations of the a11y job (136 -> 111 occurrences, 122 -> 97 failing stories), with no new violation and no cascade on aria-required-children, verified by a full rebuild + axe pass.
`min`/`max` are declared with `numberAttribute`, which turns an unset input into NaN rather than null. The spinbutton then rendered aria-valuemin="NaN" and aria-valuemax="NaN" — an invalid ARIA value, so the bound was not just useless but discarded by assistive tech. Emit both attributes only for a finite bound: no bound, no attribute. Clears 11 of the aria-valid-attr-value violations of the a11y job.
The tags listbox only got an accessible name when no visible `label` was set: with a label, the name went to the inner <input> and the listbox announced nothing. Fall back to `label` for the listbox name. In ui-autocomplete the `role="listbox"` also spanned the whole box, including the combobox <input> — a listbox may contain only options/groups. Wrap the option tags alone, with `display: contents` so the layout is untouched (same treatment ui-input-tags already had). Clears 14 aria-input-field-name and 3 aria-required-children violations.
Both pickers rendered `role="gridcell"` buttons straight inside `role="grid"`, with no `role="row"` in between — required by the grid pattern, and present on the day grid. Screen readers got no row structure to walk in the month and year views, and axe flagged the whole grid as invalid. Chunk the cells into the rows the CSS actually renders (3 columns for months, 2 for years) and mark the wrappers `display: contents`, so the rendered grid is byte-for-byte the same. Clears 2 aria-required-parent (22 nodes) and 2 aria-required-children.
aria-label is prohibited on a role-less <span>: the name the consumer passed was simply dropped, so these components had no accessible name at all. Take role="img" when an aria-label is provided — and role="group" for a removable chip, whose × button must not end up inside an img, which hides its children from assistive tech. No label, no role: unlabelled usage is unchanged. Clears the 4 aria-prohibited-attr violations (ui-avatar's badge included, it is a ui-badge instance).
…hable A scrolling region has to be focusable, or its overflowing content can only be reached with a mouse. ui-table: the viewport takes tabindex=0 whenever `scrollable` is on. ui-modal: the body region is always `overflow-y: auto`, but an unconditional tab stop inserts itself into the focus order of every dialog — a focus-trap unit test caught exactly that. It therefore becomes focusable only once it actually overflows AND holds nothing focusable itself; since the content is projected, neither is derivable from the inputs, so both are measured (ResizeObserver). Clears the 6 scrollable-region-focusable violations.
`hasProjectedContent` is detected from the `.ui-button-content` element, but that
element was only rendered `@if (label() || !isIconOnly())` — and `isIconOnly()`
derives from that very detection. A ui-button given an `icon` and nothing but
projected content therefore never rendered the content, stayed in icon-only mode,
and ended up with no accessible name at all. The detection could never succeed:
the element it inspects did not exist.
Always render the wrapper. The SCSS already carried
`.ui-button-content:empty { display: none; }` ("empty → removed, avoids phantom
gap") precisely for this, so a genuine icon-only button is unchanged.
Clears the button-name violation on the Projected Content story — which was not
a story oversight but this bug, rendering an empty button.
…ble headers axe's empty-table-header wants text that is actually present: an aria-label on the <th> does not satisfy it (measured — the violation survived that attempt). The kit had no screen-reader-only utility, while four components hand-roll the same block in their own SCSS (ui-datepicker, ui-select, ui-file-upload, ui-read-only), so add it once to the published helpers sheet. Use it to name the selection and row-expansion columns of the ui-table stories. Verified: both stories now report zero violations, and the headers still measure 56px and 68px — the absolutely-positioned span adds nothing to the column.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Premier lot de FSHSP-106 : toutes les violations axe qui ne demandent aucune décision de design — donc rien qui touche aux couleurs, qui sont bloquées sur les variables Figma.
Mesure
Baseline re-mesurée sur
main(605 stories) : 151 occurrences / 135 stories — pas les 136 du ticket, écrit avant que le datepicker gagne 18 stories.aria-allowed-attraria-input-field-namescrollable-region-focusablearia-prohibited-attraria-required-parentempty-table-headerbutton-namearia-valid-attr-valuearia-required-childrennested-interactivecolor-contrast77 stories en échec au lieu de 135, aucune story devenue rouge, aucune erreur de chargement. Lint et les 154 tests unitaires passent.
Un commit par règle, relisible indépendamment.
Deux trouvailles qui ne sont pas des correctifs d'attribut
ui-buttonavalait son contenu projeté (af31015).hasProjectedContentest détecté depuis.ui-button-content, dont le rendu était conditionné àisIconOnly()— qui dérive de cette détection. Unui-buttonavec une icône et du seul contenu projeté ne rendait donc jamais ce contenu, restait en mode icône seule et se retrouvait sans nom accessible. La détection ne pouvait pas aboutir : l'élément qu'elle inspecte n'existait pas. C'était l'origine réelle dubutton-name.Le kit n'avait aucun utilitaire
sr-only(fca6ba9), alors queui-datepicker,ui-select,ui-file-uploadetui-read-onlyrecopient le même bloc dans leur SCSS. Unaria-labelsur un<th>vide ne satisfait pasempty-table-header— axe veut du texte réellement présent, mesuré et confirmé.Point à trancher en revue
fca6ba9est le seul commit qui élargit la surface CSS publiée (.sr-onlydans les helpers, d'où le typefeatet l'entrée enAdded). Il s'annule seul, sans toucher aux sept autres, si vous préférez le sortir en ticket dédié.Réserve
Le
role="combobox"deui-input(079c575) n'a pas été testé au lecteur d'écran. axe valide la légalité de l'attribut, pas l'expérience : reste à vérifier que NVDA/VoiceOver, en mode formulaire, ne captent pas les flèches dont la grille du datepicker a besoin. À faire avant de retirercontinue-on-errordu joba11y.Reste sur le ticket
nested-interactive(11) +aria-required-children(1) — pattern chips-dans-combobox deui-input-tags/ui-autocomplete, etui-stepperverticalaria-valid-attr-value(2) —aria-controlsvers un panneau non monté, demande un registre des panneaux renduscolor-contrast(65) — passe designer, détail des 7 paires de couleurs en commentaire sur FSHSP-106🤖 Generated with Claude Code