Skip to content

Enhance/accessibility - Increase lighthouse accessibility score to 99% - #239

Merged
Samuelfaure merged 31 commits into
developfrom
enhance/accessibility
Jul 7, 2026
Merged

Enhance/accessibility - Increase lighthouse accessibility score to 99%#239
Samuelfaure merged 31 commits into
developfrom
enhance/accessibility

Conversation

@Samuelfaure

@Samuelfaure Samuelfaure commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Avant
entreprise_before
particuier_before

Après
entreprise_after
particulier_after

Reste à faire: check manuel des rendus

closes API-6742, API-6743, API-6951, API-6952, API-6953, API-6954, API-6955, API-6956, API-6957, API-6958

@Samuelfaure Samuelfaure changed the title Enhance/accessibility Enhance/accessibility - Increase lighthouse accessibility score to 99% Jun 30, 2026
@skelz0r

skelz0r commented Jun 30, 2026

Copy link
Copy Markdown
Member

@Samuelfaure y'a déjà plusieurs PRs d'ouvertes sur l'a11y, tu peux checker si ça se recoupe, et si oui, review/merge et/ou intégrer dans ta PR en mentionnant les issues linear ?

@Samuelfaure

Copy link
Copy Markdown
Contributor Author

@skelz0r Les issues linear ont comme sujet de faire un audit, ici c'est pas vraiment un audit 🤔

Je vais recouper avec les autres PRs oui 👍

@skelz0r

skelz0r commented Jul 1, 2026

Copy link
Copy Markdown
Member

Isa a fait l'audit déjà :D

@Samuelfaure
Samuelfaure force-pushed the enhance/accessibility branch from 254a23e to f32052a Compare July 1, 2026 08:28
@linear

linear Bot commented Jul 1, 2026

Copy link
Copy Markdown

Samuelfaure and others added 23 commits July 1, 2026 13:44
…ne (API-6739)

- Pictos décoratifs (_show et _others) : alt="" au lieu de l'uid technique
- <label> non associé à un contrôle → <p> (RGAA 8.9)
- Lien DataPass target="_blank" : ajout rel="noopener noreferrer" et avertissement sr-only (RGAA 13.2)

Ref: https://linear.app/pole-api/issue/API-6739
Le composant fr-sidemenu nécessite un <button class="fr-sidemenu__btn">
avec aria-controls/aria-expanded pour que le DSFR JS puisse afficher
le menu de catégories sur mobile. Sans lui, la navigation par catégorie
est inaccessible au clavier sur petits écrans.

Ref: https://linear.app/pole-api/issue/API-6738
…I-6734)

Les logos des fournisseurs dans les cartes du catalogue n'avaient pas
d'attribut alt : Rails génère alors un alt par défaut (nom de fichier)
vocalisé par les lecteurs d'écran. Ces logos sont décoratifs — le nom
du fournisseur est déjà dans le texte adjacent. alt="" explicite.

Idem pour le picto FranceConnect dans la carte API Particulier.

Ref: https://linear.app/pole-api/issue/API-6734
…I-6736)

RGAA 8.2 / 8.9 / 9.3 — <ul> et <p> imbriqués dans <p> :
- Section CGU (Entreprise et Particulier) : <ul> sorti du <p>
- Bloc deprecated (Entreprise et Particulier) : idem
- Bloc incoming : <p> imbriqué dans <p> → deux paragraphes séparés

RGAA 9.2 — nav sans aria-label :
- <nav role="navigation"> → <nav aria-label="Sommaire de la page">
  (role="navigation" redondant sur <nav> supprimé, class="" vide supprimée)

RGAA 1.1 — alt non pertinent :
- Logo fournisseur : "Logo du fournisseur de données" → "Logo de {nom}"

Ref: https://linear.app/pole-api/issue/API-6736
Typographic single quotes (U+2018/U+2019) were inadvertently introduced
in the ERB files instead of ASCII single quotes, making the compiled
Ruby code unparseable by Prism in certain contexts.
…C 8.6, 9.2, 12.1)

- admin.html.erb: corrige typo "Entreculier" → "API Entreprise / API Particulier" (NC 8.6)
- admin/editor/provider: ajout lien d'évitement DSFR (NC 12.1)
- admin: <div fr-container> → <main id="content"> (NC 9.2)
- editor/provider: <div fr-container> → <main id="content" class="fr-container ..."> (NC 9.2)
- shared/admin/_header.html.erb: correction même typo dans le titre service
…title (NC 8.5, 9.2, 12.1)

- layouts AE et AP : support content_for(:title) pour des titres de page dynamiques (NC 8.5)
- layouts AE et AP : ajout lien d'évitement DSFR href="#main-content" (NC 12.1)
- _containerized_body AE et AP : <div> → <main id="main-content"> (NC 9.2)
… 1.1, 1.2, 7.5, 9.1)

- _modal: image loading.gif alt="" (NC 1.1)
- _loader: alt="" + role="status" + fr-sr-only "Chargement en cours" (NC 1.1, 7.5)
- _impersonate: role="alert" sur la div d'avertissement (NC 7.5)
- section_access AE/AP: pictogrammes SVG alt="" (redondants avec le texte) (NC 1.2)
  et h4 → h3 pour la colonne droite (NC 9.1)
- section_developers AE: aria-live="polite" sur le badge Hyperping (NC 7.5)

Partially solves API-6954, API-6955
- footers AE/AP: fr-sr-only "(nouvelle fenêtre)" sur le lien licence etalab
- menus AE/AP: fr-sr-only sur le lien "Demander un accès" (DataPass)
- header tools AE/AP: fr-sr-only sur le lien page de statut
- section_howto AE: fr-sr-only sur les liens DataPass et annuaire-entreprises
- section_partners AE/AP: suppression target="_blank" sur routes internes
- section_demonstrateur AP: link_to bloc avec fr-sr-only
- locales AE/AP: fr-sr-only injecté dans les liens simplifions.data.gouv.fr en YAML

Partially solves API-6952
…externe (NC 1.2, 3.1, 6.1, 7.5, 8.5, 9.1, 9.3, 13.2)

- status.html.erb: lien <a> vide → aria-label statut + fr-sr-only (NC 6.1, 13.2)
  cercle CSS-only → aria-hidden + texte sr-only (NC 3.1)
- index AE/AP: content_for :title (NC 8.5) + aria-live sur div "Aucun résultat" (NC 7.5)
- show AE/AP: content_for :title (NC 8.5) + <h3 fr-alert__title> → <p> (NC 9.1)
- _details AE: divs format/paramètres → <ul><li> (NC 9.3) + fr-sr-only liens status/tests (NC 13.2)
- _details AP: fr-sr-only sur liens externes + alt="" sur pictogramme FranceConnect (NC 1.2, 13.2)
- _property.html.erb: auto_link avec fr-sr-only sur les liens auto-détectés (NC 13.2)
- _use_cases.html.erb: fr-sr-only sur les liens "Voir en détail" (NC 13.2)

Partially solves API-6952
…NC 1.2, 7.1, 8.5, 9.1, 13.2)

- _scopes.html.erb: <span tabindex="0"> → <h3 class="fr-accordion__title"> (NC 7.1)
- show: aria-hidden="true" sur l'icône flèche (NC 1.2)
- ask_for_prolongation: aria-hidden icône + fr-sr-only sur lien DataPass (NC 1.2, 13.2)
- prolong: aria-hidden icône + fr-sr-only sur liens DataPass et CTA (NC 1.2, 13.2)
- renew: fr-sr-only "(nouvelle fenêtre)" sur le CTA (NC 13.2)
- stats: content_for :title + h4 → h2, h5 → h3 (NC 8.5, 9.1)

Partially solves API-6952
…tut, DataPass (NC 3.1, 5.3, 9.1, 13.2)

- sessions/_new: h2 → h1 class="fr-h2" (NC 9.1) + fr-sr-only sur lien ProConnect (NC 13.2)
- wizard/_header: h2 → h1 class="fr-h2" (NC 9.1) + fr-sr-only sur lien DataPass (NC 13.2)
- wizard/contacts: h4 → h3 (hiérarchie correcte sous h1) (NC 9.1)
- wizard/prolonged: h3.fr-alert__title → p.fr-alert__title (NC 9.1)
- wizard/owner + authorization_requests/_header: fr-sr-only sur liens DataPass (NC 13.2)
- authorization_requests/show: caption tableau + h2 contact_technique → h3 (NC 5.3, 9.1)
- pages/current_status: aria-hidden sur cercle + fr-sr-only statut textuel (NC 3.1, 13.2)

Partially solves API-6952
….3, 6.1, 8.5, 9.3, 13.2)

Titres de page (NC 8.5):
- home AE/AP, accessibility, mentions, errors, profile, changelogs: content_for :title

Cas d'usages:
- _others: div.use_cases → ul.fr-raw-list + li (NC 9.3)
  + aria-label contextuel sur les liens "En savoir plus" (NC 6.1)
- _simplifions_card: fr-sr-only "(ouverture dans un nouvel onglet)" (NC 13.2)
- _endpoints_table: <caption class="fr-sr-only"> (NC 5.3)
- index AE/AP: content_for :title (NC 8.5)

Partially solves API-6952
Partner logo alt describes link destination rather than the image itself.
Decorative pictos alongside headings get empty alt.

Closes https://linear.app/pole-api/issue/API-6954
Adds external_link_to helper that appends a fr-sr-only "(nouvelle fenêtre)"
span and enforces target=_blank + rel=noopener noreferrer. Migrates all
external links in shared layout, footer, header, newsletter and home sections.
Also removes target=_blank from the internal partner link and fixes the
taget=_blank typo in the footer.

Closes https://linear.app/pole-api/issue/API-6952
… (RGAA 6.1)

Two issues fixed:
- _donnees_personnelles partials (both APIs) had `href` without `mailto:` prefix,
  making the links non-functional
- accessibility.html.erb, mentions.html.erb and _donnees_personnelles partials
  use raw email addresses as link text; added `title` attributes so screen
  readers announce the purpose when reading link lists out of context

Closes https://linear.app/pole-api/issue/API-6953
Two issues on api_entreprise home:
- _section_access: right-column cards used <h4> creating an h2→h4 jump
  relative to a preceding sibling h2; changed to <h3> (same level as
  the info items in the left column). Same fix applied to api_particulier.
- _section_howto: "Accès à mon compte" sub-block used <h2> at the same
  level as the section title; demoted to <h3>.

DSFR visual classes (fr-h4 etc.) are unaffected — only semantic level changed.

Closes https://linear.app/pole-api/issue/API-6955
… 8.7)

- "newsletter" → "lettre d'information" (DSFR official term) in
  api_particulier newsletter_title and api_entreprise iframe title
- "changelog" in newsletter section titles wrapped with <span lang="en">
- "Swagger" and "OpenAPI" in section_developers wrapped with <span lang="en">;
  swagger_description values now rendered with .html_safe to allow HTML
- "back-office" in api_entreprise section_use_cases description wrapped
  with <span lang="en">

Closes https://linear.app/pole-api/issue/API-6957
Covers API Entreprise and API Particulier. Skip link targets real ids
already present in the DOM (#contenu, #navigation-header-menu, #footer).

Non-regression: landmarks asserted on home, cas usages, catalogue, FAQ
and an authenticated account page for both APIs (65 examples).

Closes https://linear.app/pole-api/issue/API-6743
Two-layer strategy:
- Views call `content_for(:page_title)` for dynamic titles (endpoint name,
  cas_usage name, blog post title, authorization request intitulé)
- Static pages declare `page_title:` i18n keys under their controller/action;
  empty string = use site name only
- `full_page_title` helper: checks content_for first, falls back to i18n,
  raises in local/test env if neither is set — guarantees no silent gaps

Added `AbstractBlogPost#title` to parse the first `# ` heading from markdown.

Closes https://linear.app/pole-api/issue/API-6951
@Samuelfaure
Samuelfaure force-pushed the enhance/accessibility branch from f4c5f67 to 5e8696a Compare July 1, 2026 11:56
…g_posts, redoc v2/v3 (NC 8.5)

Adds fallback page_title keys so static_page_title does not raise in
Rails.env.local? when content_for(:page_title) is a no-op (nil intitule,
nil blog post title, or view that never sets the key).

- authorization_requests#show — both AE and AP
- blog_posts#show — both AE and AP
- pages#redoc_v2 and pages#redoc_v3 — AP only
…el (NC 1.2, 13.2)

AP _section_partners had kept the old decorative alt "Logo fournisseur de
données - …" while AE was already updated to the link-purpose label "Voir
les API de …" (RGAA 6.1).

Three different sr-only strings were used for the same semantic action
(link opens in a new window): "(ouvre une nouvelle fenêtre)",
"(ouverture dans un nouvel onglet)", and "(nouvelle fenêtre)". Normalised
to the canonical "(nouvelle fenêtre)" from external_link_to across all
templates.
…sing container

external_link_to reassigned html_options to {} in the block-given branch
when href was not a Hash, silently discarding any options passed as the
third positional argument (aria-*, data-*, class). Changed to only
override html_options when href is actually a Hash (the intended block-form
calling convention).

Admin layout <main> had no CSS classes while editor and provider layouts
both use fr-container fr-mb-5w fr-mt-5w, causing admin pages to render
full-width without layout constraints.
@Samuelfaure

Copy link
Copy Markdown
Contributor Author
image slight increase 👍

@Samuelfaure
Samuelfaure requested review from Un3x and skelz0r and removed request for skelz0r July 1, 2026 12:21
@Samuelfaure
Samuelfaure marked this pull request as ready for review July 1, 2026 12:21
@Samuelfaure
Samuelfaure requested a review from skelz0r July 1, 2026 12:21
@Isalafont

Copy link
Copy Markdown
Collaborator

J'ai fait un Pré-audit assistée par IA (20% du RGAA environ), il restera encore toute la partie audit "humaine" à faire mais c'est top cela va me permettre d'aller plus vite sur les pages. 👍🏻

@Isalafont Isalafont left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super travail !!
Cela couvre une bonne partie des problèmes détectés et cela va grandement me faciliter l'audit "humaine" page par page restante à faire 🚀

J'ai ajouté des petits commentaires et des suggestions. N'hésites pas si tu veux que l'on en discute car tu connais le site bien mieux que moi et tu saura mieux m'orienter vers la bonne solution à te conseiller.

Je ne sais plus si dans les tickets que tu as mergé, il y avait celui du plan du site inclus ?

Ce que je peux te suggérer, mais c'est à toi de me dire, vu que la PR est déjà volumineuse, on peut très bien se dire que pour les premiers commentaires suggéré, on créé des tickets dédiés pour n'avoir à traiter que ces points séparément dans une autre PR.
Si tu préfères cela, on pourra merger celle ci. A toi de me dire !

Comment thread site/app/views/layouts/api_entreprise/application.html.erb Outdated
Comment thread site/app/views/shared/api_particulier/_containerized_body.html.erb Outdated
Comment thread site/app/views/shared/api_entreprise/_containerized_body.html.erb Outdated
Comment thread site/app/views/layouts/api_entreprise/application.html.erb
Comment thread site/config/locales/api_particulier/fr.yml Outdated
Comment thread site/config/locales/api_particulier/fr.yml
Comment thread site/app/views/shared/endpoints/_property.html.erb
@Samuelfaure

Samuelfaure commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

@Isalafont La PR plan du site est ici #204 ça ne chevauchait pas sur celle ci donc j'ai pas touché

Je vais faire les quick-wins dans ta review et si y'a de gros morceaux on corrigera après 👍 comme ça on ship

J'ai tout résolu au final

Address review feedback from PR #239: containerized_body partials
opened a second <main> nested inside the layout's <main id="contenu">,
breaking the unique-landmark rule. Also add tabindex="-1" to the
three skip-link targets (#contenu, nav menu, footer) so Firefox/Safari
actually move focus on activation, matching Chrome's native behavior.
… (NC 8.5)

Tab title read "Mes demandes" while the H1 reads "Habilitations ...",
a wording mismatch that could confuse users switching between tabs.
…s (NC 13.2)

auto_link's wrapper never declared or forwarded its block, so Ruby
silently dropped it — the accessible "(nouvelle fenêtre)" suffix added
in shared/endpoints/_property.html.erb was dead code even though the
underlying anchored gem supports blocks and target="_blank" rendered
fine on its own.
@Samuelfaure
Samuelfaure requested a review from Isalafont July 6, 2026 08:46
@Samuelfaure
Samuelfaure merged commit cf6edad into develop Jul 7, 2026
10 checks passed
@Samuelfaure
Samuelfaure deleted the enhance/accessibility branch July 7, 2026 11:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants