Aller au contenu
Démarrer un projet
Menu

Accessibilité

Focus, contrastes, décor muet, mouvement réduit, et une page lisible sans script.

Le focus

Un anneau de 2 px, vert profond sur la trame, sur tout ce qui se prend au clavier. Les champs et les questions ont le leur.

ClasseZoneValeur
.btn, a, input, select, button :focus-visiblepartout sur le siteoutline 2 px --grinn, décalé de 3 px (règle de base de grinn.css)
.tr, .tr-hdr, .tr-pied :focus-visiblesur la trameoutline-color --grinn-deep : 5,08:1 sur blanc, là où le vert seul ferait 1,70:1
.tr-form .field input, select, textarea :focus-visibleles champsoutline 2 px --grinn-deep, décalé de 2 px ; la bordure passe de --ink-3 à --ink au survol et au focus
.tr-demo__offre:focus-visibleune offre de la démonstration (accueil)l’anneau vert profond de la trame, décalé de -3 px, dedans ; la case s’allume comme au survol, avec son trait
.tr-faq__q summary:focus-visibleune questionoutline 2 px --grinn-deep, décalé de -2 px, dedans : la cellule n’a pas de marge pour un anneau extérieur
.tr-form__plus summary:focus-visiblele repli du formulaireoutline 2 px --grinn-deep, décalé de 2 px
.tr-cc__corps:focus-visible, .tr-murl__corps:focus-visibleune carte de cas, une case du mur (page Réalisations)outline 2 px --grinn-deep, décalé de -4 px, dedans : la grille coupe ce qui dépasse ; la case pâlit aussi
.tr-cc__appel-btn:focus-visiblele bouton de la cellule d’appel, sur l’encreoutline-color --grinn, le vert Grinn (10,3:1 sur l’encre)
.tr-suivant:focus-visible::afterle cas suivant (page de cas)l’outline du lien est retiré ; un anneau de 2 px --grinn-deep, décalé de -3 px, est tracé par-dessus tout le bloc, vitrine comprise
.tr-logos__pause:focus-visiblele bouton pause du bandeauoutline 2 px --grinn-deep, décalé de -4 px
.evitementle lien d’évitement« Aller au contenu », hors écran, fixé en haut à gauche au focus, encre sur blanc inversée

Vous vendez un assistant tout fait ?

Non. On construit des agents branchés sur votre métier et sur vos logiciels, jamais un assistant standard.

Faut-il nos données pour commencer ?

Non. On commence par regarder comment vous travaillez. Le périmètre technique se décide après.

Démarrer un projet

SpécimenDeux questions de la fiche IA : le summary porte le h3 et prend le focus ; la croix tourne à l’ouverture, sans script. Puis un bouton. Parcourez au clavier pour voir l’anneau.

Les cibles

Les hauteurs sont calculées depuis la feuille : police × interlignage + padding + bordure. La colonne 44 px dit si la cible atteint le repère de WCAG 2.5.5 ; toutes dépassent les 24 px de WCAG 2.2 (2.5.8).

ClasseCalculHauteur44 px
.tr .btn12,5 px × 1 + 15 + 15 de padding + 2 de bordure44,5 pxoui
.tr .btn--smmin-height 44 px (le libellé seul ferait 35,5 px)44 pxoui
.tr-cta .btn sous 480 px12,5 px × 1,35 + 13 + 13 + 244,9 pxoui
.tr-form .btn sous 600 pxidem, sur toute la largeur44,9 pxoui
.tr-hdr .btn--sml’en-tête est hors de .tr : ni la hauteur minimale ni le padding du petit bouton de la trame ; 11,5 px × 1 + 11 + 11 de padding (.btn--sm) + 2 ; sous 360 px, 11 px × 1 + 10 + 10 + 235,5 px ; 33 px sous 360 pxnon
.tr-form .field input, selectpolice --t-body, clamp(16.5px, 0.35vw + 13px, 18.5px), posée par .tr-form ; interlignage 1,6 hérité, padding 14 px, bordure 1 px56,4 px et plusoui
.tr-faq__q summarypadding clamp(18px, 2vw, 24px) + h3 de 17 à 19 px × 1,358 px et plusoui
.tr-demo__offretoute la case est le lien : min-height 128 px (150 px de 620 à 999 px, 132 px sous 620 px) ; dès 1000 px, un tiers de la hauteur de la scène128 px et plus jusqu’à 999 pxoui
.tr-logos__pause44 × 44 px44 pxoui
.tr-cc__corps, .tr-suivanttoute la carte d’un cas (page Réalisations), tout le bloc du cas suivant (page de cas) : le lien couvre la cellulela hauteur de la carte ou du blocoui
.tr-murl__corpsune case du mur des clients : min-height 96 px96 px et plusoui
.tr-fiche dd a::afterle lien d’offre de la fiche d’un cas couvre sa ligne : 16 + 11 × 1,3 + 8 + 15 × 1,45 + 1676 pxoui
.tr-rail a11,5 px × 1,35 + 6 + 627,5 pxnon
.tr-fil a11 px × 1,5, dans le texte16,5 pxnon

Le bouton courant atteint 44 px de justesse, à 44,5. Le bouton petit de la trame (Voir l’offre dans une carte, celui du mur) tient 44 px par une hauteur minimale : son libellé seul ferait 35,5 px. Celui de l’en-tête ne l’a pas : l’en-tête est posé hors de .tr, et la règle .tr .btn--sm ne le touche pas ; il garde 35,5 px, 33 px sous 360 px. Les entrées du rail font 27,5 px ; les maillons du fil d’Ariane sont des liens dans le texte, à 16,5 px. C’est écrit ici pour qu’on le sache, pas pour l’excuser.

Le décor est muet

Tout ce qui ne porte pas d’information est caché aux technologies d’assistance : la trame, les scènes, les annotations, les cellules allumées. Le texte dit tout.

ÉlémentComposantMarque
L’annotationPucearia-hidden ; elle redit ce que le texte dit
La bandeBandearia-hidden sur le bloc entier, section comprise
La cellule alluméeCelluleAllumeearia-hidden
Les cellules de la page contact, de la candidature et du piedcontact.astro, carrieres.astro, Piedaria-hidden, pointer-events none
Les cellules vivantestrame.ts (canevas .tr-vivant)aria-hidden, pointer-events none ; posées sous l’affiche de l’accueil, dans les têtes en cellule, les cases vides du mur et la tête du point de contact ; dans la cellule de la scène de l’accueil et de l’en-tête de page aussi, mais masquées derrière une illustration v2
La seconde piste du bandeau des clientsLogosaria-hidden, images à alt vide : la liste des clients ne se lit qu’une fois ; masquée en mouvement réduit
L’illustration v2Scene, et la vignette d’une offre dans Heroune image SVG, alt vide, decoding async ; lazy, sauf la scène d’un premier écran (eager) ; ses animations CSS se coupent d’elles-mêmes en mouvement réduit
Une ancienne scène en boucle (aucune en service depuis le 13 octobre)Scenearia-hidden, tabindex -1, muted, playsinline, sans contrôles, sans autoplay
La scène fixe (ancienne, si le SVG manque)Scenealt vide, loading lazy
Le canevas du dômeDomearia-hidden
Le fond quadrillé, les annotations d’un en-tête de pageEnTetePagearia-hidden
La grille de l’affiche, la cellule de la scène de l’accueilHero, variante « centre »aria-hidden
Les traits d’attache de la démonstrationHero (svg tr-demo__liens), trame.tsaria-hidden, focusable false, pointer-events none
La vignette d’une offre, et son libellé « Voir l’offre »Hero, variante « centre »alt vide ; le libellé visible est aria-hidden : le lien est nommé par son aria-label, « Voir l’offre » suivi du nom de l’offre
La flèche ↗Bouton, Cas, CarteCas, CasSuivant, Contact, Piedaria-hidden ; « (nouvel onglet) » en texte masqué quand le lien sort
La flèche → entre deux étapesPointFortaria-hidden : l’ordre est celui de la liste
La vitrine d’un casVitrinearia-hidden sur toute la boîte, matière et captures à alt vide : la carte ou la page dit tout ce qu’elle montre
Les cases vides de la grille des cas et du mur des clientsrealisations/index.astro, MurLogosaria-hidden
Le numéroEngagements, Etapes, Rubriquearia-hidden : l’ordre est déjà celui de la liste
Les cases vides du murMuraria-hidden
Le logo et le portrait en réserveMuraria-hidden
Le logo d’un client dans une carte de l’accueil ou un en-tête de page de casCas, EnTetePagealt vide : le titre, juste à côté, porte déjà le nom ; dans une carte de la page Réalisations (CarteCas), au contraire, le logo a le nom du client pour alt : il nomme le lien
Le logo d’une citation en réserveCitationCasalt vide ; le nom du client quand la citation est signée
La réserve de photoEmplacementPhotoaria-hidden ; la photo, quand elle existe, a son alt
Le champ piège du formulaireFormulaireEnvoiconteneur aria-hidden, hors de l’écran ; le champ en tabindex -1 : jamais lu ni atteint au clavier
Le point finalDesign.astro, Rubriquearia-hidden

Les titres

Un h1 par page, un h2 par section, des h3 dans les cellules. Chaque section est nommée par son titre (aria-labelledby) ou, sans titre, par un aria-label.

NiveauOùQui le pose
h1un par pageHero (id tr-titre), EnTetePage, ou le gabarit Design : la section d’ouverture le nomme par aria-labelledby
h2une par sectionTete le pose avec son id, que Section reprend en aria-labelledby ; Logos en fait une étiquette (h2.tr-etq) quand il en a une ; le mur, la cellule IA, le point fort et le cas suivant d’une page de cas posent le leur ; la grille des cas de la page Réalisations a un h2 masqué (sr-only) ; ici, chaque Rubrique
h3dans les cellulesCarte, Cas, CarteCas, AppelCas, les rôles, Engagements, Etapes, Tarifs, les colonnes du pied, et la question d’une Faq (dans son summary)
sans titreune section muetteSection prend un aria-label (la citation d’une page de cas) ; la bande n’est pas une section lisible ; le bandeau de la page Réalisations, sans étiquette, n’a pas de nom : sa section n’est pas un repère

Sans script, sans mouvement

La page se lit sans JavaScript et sans animation : c’est une condition de départ, pas un repli.

Page.astro pose la classe .js sur l’élément html avant le rendu, par un script inline. Toutes les règles de départ (opacité 0, lignes à 0 %, carrés à 0) n’existent que sous cette classe : sans script, tout est affiché et tracé. Aucune vidéo n’est plus posée depuis le 13 octobre (la section IA de l’accueil a reçu son schéma v2) ; les illustrations v2 s’animent quand même : leur mouvement est écrit dans le SVG, et la boîte du schéma IA flotte par grinn.css, ses étapes éteintes. Les questions s’ouvrent avec details, le menu est un nav ordinaire. Le formulaire, lui, passe la main à la messagerie du visiteur, après la validation du navigateur (Formulaire, rubrique 7).

En mouvement réduit (prefers-reduced-motion: reduce), grinn.css coupe toutes les transitions et les boucles de la trame, arrête le bandeau des clients (sa seconde piste masquée, la rangée défilable à la main), masque la barre de lecture, chaque illustration v2 coupe ses propres animations (son SVG pose animation: none sur tout), la boîte du schéma IA ne flotte pas et ses étapes ne s’allument pas, et les scripts suivent : grinn.js affiche tout d’emblée, trame.ts ne lance aucune ancienne scène en boucle (il n’y en a plus en service), ne fait pas flotter la scène hero ni circuler les carrés de son anneau, trace la grille sans attendre, coupe la parallaxe et les cellules sous la souris, dessine le dôme en une seule image, et ne fait pas tourner les offres du premier écran (leurs traits restent tracés ; le survol et le focus d’une offre allument encore son trait, sans transition ni flux).

Sans script, la démonstration de l’accueil garde ses cases et leurs liens, la scène hero sans flottement, sans traits d’attache ni anneau : rien ne manque pour choisir une offre.

La page est en français (lang="fr"), sans zoom bloqué (width=device-width, initial-scale=1), et commence par le lien d’évitement vers le contenu.

Les contrastes

Les ratios sont calculés et vérifiés dans la page Couleurs, sur les paires que la trame emploie. Ils ne sont pas recalculés ici.

Toutes les paires de texte passent 4,5:1. Dans la démonstration de l’accueil, « Voir l’offre » passait au vert profond sur le vert clair au survol, à 4,497:1 : il passe à l’encre depuis le 12 octobre. La plus juste est le vert profond sur la cellule pâle, à 4,81:1. Le détail, paire par paire, avec la pastille et l’emploi : Couleurs, rubrique 04.

Ce qui reste à faire

Ce que le code ne fait pas encore, dit en clair. Rien ici n’est caché derrière une réserve.

  • Le formulaire part par le relais depuis le 12 octobre (Formulaire, rubrique 6) : bilan des erreurs en tête, liens vers les champs, champ refusé marqué, focus sur le premier. Les messages d’état sont annoncés poliment (aria-live polite), pas en alerte, pour ne pas couper l’annonce du champ qui reçoit le focus : à valider avec un lecteur d’écran.
  • Les logos du bandeau ont le nom du client pour alternative, ceux du mur des clients et des cartes de la page Réalisations aussi ; un client sans fichier reste en texte, en Outfit. Sur la page Réalisations, le badge « cas » se lit à la suite du nom, dans le bandeau comme dans le nom des liens du mur.
  • Le bandeau des clients défile plus de cinq secondes. Le survol l’arrête, et un bouton pause (44 × 44 px, aria-pressed, libellé qui change) l’arrête au clavier et au doigt, comme le demande le critère 2.2.2 de WCAG. Sans script, le bouton reste caché et seul le survol arrête la rangée ; en mouvement réduit, elle ne défile pas.
  • La photo de groupe est affichée depuis le 12 octobre (public/assets/equipe/groupe.webp), avec son alt : « L’équipe Grinn, à Nantes » sur l’accueil et la page de l’équipe, « L’équipe Grinn » sur la page carrières. C’est le seul alt de photo à relire. Les photos des réalisations (les cartes des pages d’offre), des lieux et les portraits sont en réserve : une cellule marquée aria-hidden, avec le format attendu. Les captures des cas, dans leur vitrine (les cartes de l’accueil depuis le 13 octobre, la page Réalisations, les pages de cas), sont décoratives : la carte ou la page dit ce qu’elles montrent.
  • Le bouton de l’en-tête (35,5 px, 33 px sous 360 px), les entrées du rail (27,5 px) et les maillons du fil d’Ariane font moins de 44 px.
  • Le tour des offres du premier écran dure 21,6 s (deux passages, six offres, 1,8 s chacune), et le trait de l’offre allumée défile tant qu’elle l’est ; il n’a pas de bouton pour s’arrêter. Le survol et le focus dans la démonstration l’arrêtent, mais il reprend quand ils en sortent ; il s’arrête seul après deux passages, et le mouvement réduit l’empêche. Le critère 2.2.2 de WCAG demande un moyen de mettre en pause ce qui bouge plus de cinq secondes : à trancher.
  • Les illustrations v2 bougent sans fin, par le CSS de leur SVG : l’image flotte (5 s), le vert respire, les nœuds s’allument ; sur l’accueil, trame.ts fait en plus flotter la scène hero et circuler les carrés de son anneau, et grinn.css fait flotter la boîte du schéma IA (5 s) pendant que ses trois étapes s’allument tour à tour (6,6 s). Rien ne les arrête, sinon le mouvement réduit, qui les fige. Le critère 2.2.2 de WCAG demande un moyen de mettre en pause ce qui bouge plus de cinq secondes à côté d’autre contenu : à trancher, avec le tour des offres.
  • Aucun test avec un lecteur d’écran n’a été fait. Ce que ces pages disent vient du code, pas d’un parcours réel.

RègleLa page se lit sans script, sans mouvement, sans souris et sans image ; le décor se tait, le texte dit tout.