Carrousel héros de site web : des photos qui défilent, et les tests qui les gardent honnêtes
Un emplacement photo en haut d'une page où deux photos ou plus prennent tour à tour la place toutes les quelques secondes, avec un point pour chacune. Cela a l'air facile et ne l'est pas : une légende sur une photo fait sauter la page à chaque changement ; des points blancs disparaissent sur une photo pâle ; un téléphone a besoin de cibles plus grandes qu'une souris ; et le contenu en mouvement a sa propre règle d'accessibilité. Cette compétence livre le composant et, plus important encore, les tests qui détectent tous ces problèmes.
Pourquoi il est construit ainsi (dites-le au propriétaire, avec vos propres mots)
- La page ne saute jamais. Toutes les photos partagent un seul emplacement, rognées à une même forme, donc la page est aussi haute pour la première photo que pour la dernière. Il n'y a pas de légendes ou de crédits sur les photos : ce sont eux qui faisaient sauter la page sur le site d'origine de ce composant. Les crédits appartiennent à la page des crédits d'images du site.
- Les points peuvent être lus sur n'importe quelle photo. Ils reposent sur une petite pastille sombre, donc ils se voient sur un ciel pâle comme sur un mur sombre. Un test mesure cela sur les pixels d'une photo pâle et d'une photo sombre.
- Cela fonctionne sans JavaScript. Les photos se présentent alors les unes sous les autres et aucun bouton n'est affiché.
- C'est poli. Cela s'arrête lorsque la souris passe dessus, lorsque l'onglet est caché, lorsque le focus clavier y entre, et dès le départ pour les visiteurs qui demandent à leur appareil moins de mouvement.
- Rien n'est stocké ni envoyé. Pas de cookie, pas de stockage, pas de requête vers un autre site.
Et les points faibles honnêtes, pour que le propriétaire les entende de vous en premier :
- Les photos qui tournent ne sont pas toujours le meilleur design. Une seule photo forte est plus calme, se charge plus vite et ne peut irriter personne. Dites-le ; le propriétaire décide.
- Chaque photo supplémentaire coûte du temps de téléchargement au visiteur sur un téléphone. Trois photos à quatre tailles chacune, c'est bien ; dix, ce ne l'est pas. Limitez-vous à une poignée.
- Le mode pause "points" échange un contrôle visible contre une photo plus propre (la section "Pause" ci-dessous).
0. Est-ce le bon outil ? (à demander avant l'installation)
- Le propriétaire est-il sûr de vouloir une rotation ? Une photo n'a besoin de rien de tout cela. Demandez une fois, clairement.
- Combien de photos, et ont-elles la même forme ? Deux à cinq. N'importe quelle forme fonctionne (chaque photo est rognée à la forme du carrousel), mais une photo avec son sujet au bord le perd dans le rognage : regardez chacune à la forme du carrousel (16/9 par défaut) et à celle d'un téléphone.
- Les photos pointent-elles vers quelque part ? N'importe quelle diapositive peut pointer vers une page (un projet, par exemple) ; mélanger des diapositives avec et sans lien est possible (la page CI le fait).
- Quel mode pause ? Lisez "Pause" ci-dessous au propriétaire et laissez-le choisir. Par défaut : le bouton.
1. Ce qu'il ajoute au site
| Fichier | Depuis le dossier templates/ de cette compétence | Ce que c'est |
|---|---|---|
src/components/HeroCarousel.astro | HeroCarousel.astro | le balisage, les points, les styles |
src/components/hero-carousel-text.ts | hero-carousel-text.ts | les mots qu'il dit, en anglais et en allemand |
public/js/hero-carousel.js | hero-carousel.js | le script : place les diapositives au même endroit, fait tourner, gère les contrôles |
tests/hero-carousel.spec.ts | hero-carousel.spec.ts | le carrousel tel qu'un visiteur le rencontre (liste ci-dessous) |
Installez les quatre. Les modèles ne contiennent aucun fichier image : les photos sont celles du site, passées en propriétés.
Comment cela fonctionne, en un paragraphe. Le serveur envoie chaque diapositive, l'une sous l'autre, et les contrôles sont masqués. Le script, un fichier propre au site sous public/js/ plutôt qu'un script en ligne (afin qu'une directive script-src 'self' stricte le laisse s'exécuter), place ensuite les diapositives dans une seule cellule de grille, affiche les contrôles et marque la diapositive en cours d'affichage. La diapositive en cours est identifiée par un attribut de données ; le CSS fait le reste, y compris le fondu d'une seconde. Une diapositive n'est affichée que lorsque son image est complète, donc une image tardive ou défaillante ne laisse jamais un emplacement vide.
Ce que le test vérifie (vue du visiteur, sur la page telle que construite) : le balisage et le nom de chaque point ; l'intervalle à la milliseconde près sur une horloge figée ; le bouton Pause ou le point qui l'arrête, avec les libellés et la région live ; le focus clavier (l'arrête, reste arrêté, Reprendre) ; le pointeur qui le retient et un nouvel intervalle après le départ ; le mouvement réduit ; un onglet caché ; une deuxième image tardive et une défaillante ; la page sans JavaScript ; aucun saut de hauteur à 1280, 768 et 390 px ; le fait qu'une diapositive non affichée ne puisse être atteinte au clavier ni dans l'arbre d'accessibilité ; le fait qu'un lien de photo fonctionne et qu'un point ne l'ouvre jamais ; des cibles de 32 px (44 sur écrans tactiles) ; les points sur la pastille au-dessus de chaque photo, lus à partir des pixels rendus ; les couleurs forcées ; axe dans les deux thèmes ; rien stocké ni envoyé ; le script est un fichier, pas en ligne ; le fondu s'exécute sur une image à mi-chemin et se stabilise, et ne s'exécute pas en mouvement réduit.
2. Installation (sur une branche, en tant que pull request : AGENTS.md §2)
- Copiez les quatre fichiers aux emplacements du §1 (
mkdir -p public/jsd'abord). - Placez le carrousel sur une page, à l'intérieur du
<Base>de cette page, près du haut :--- import HeroCarousel from '../components/HeroCarousel.astro'; import first from '../assets/hero-1.jpg'; import second from '../assets/hero-2.jpg'; --- <HeroCarousel label="Photos de notre travail" slides={[ { image: first, alt: 'Ce que montre la première photo', label: 'Nom court', href: '/projets/un' }, { image: second, alt: 'Ce que montre la seconde', label: 'Autre nom' }, ]} />label: ce qu'est le carrousel, pour un lecteur d'écran. Requis.- chaque diapositive :
image(une photo importée),alt(une phrase qui dit ce qu'elle contient, au moins 10 caractères : la construction s'arrête en dessous),label(le nom court du point),href(optionnel). pause:"button"(par défaut) ou"dots".swap:"instant"(par défaut) ou"fade".intervalMs: par défaut 6000, jamais en dessous de 2000.ratio: par défaut"16 / 9".lang: celui de la page. Photos : JPEG ou PNG, environ 2 400 px de large, limitées à ce dont la page a besoin (la construction crée quatre tailles de chacune, AVIF avec repli WebP). La première photo se charge en premier (haute priorité) ; les autres se chargent à basse priorité et conservent leur place.
- Langue. L'anglais et l'allemand sont intégrés, et le composant prend la langue de la page (
Base.astrola transmet commeAstro.locals.lang). Toute autre langue : "Ajouter une langue" ci-dessous, d'abord. - Dans
tests/hero-carousel.spec.ts, définissezPAGE(la page avec le carrousel),PAUSEetSWAPà ce que la page transmet (les valeurs par défaut du composant sontbuttonetinstant; le test en a besoin explicitement). Le test échoue tant que l'un est vide.LANGseulement si la page transmetlang. Photos pour le test de contraste : le test lit les vraies photos du carrousel, donc il rencontre celles du propriétaire. Si chacune d'elles est sombre, le test "points blancs sur la pastille" ne peut pas échouer ; pour le voir échouer, insérez-en une pâle manuellement une fois (la vérification de mutation au §3 le fait). npm run check && npm test.
Pause (lisez ceci au propriétaire avant qu'il ne choisisse)
Le contenu en mouvement qui démarre tout seul, dure plus de cinq secondes et se trouve à côté d'autre contenu relève de WCAG 2.2.2, Niveau A, qui exige "un mécanisme permettant à l'utilisateur de le mettre en pause, de l'arrêter ou de le masquer" (lisez à l'adresse https://www.w3.org/WAI/WCAG22/Understanding/pause-stop-hide.html). La même page définit pause comme "arrêté à la demande de l'utilisateur et non repris jusqu'à ce que l'utilisateur le demande", et ne dit pas que le mécanisme doit être visible. Elle ne traite pas des indicateurs de diapositive.
pause="button"(par défaut). Un bouton Pause à côté des points (Reprendre quand arrêté). Visible, nommé, la lecture que personne ne conteste, et celui qu'un visiteur trouve. Son prix : un petit mot sur la photo.pause="dots". Pas de bouton. Choisir un point montre cette photo et arrête la rotation jusqu'à ce que le visiteur recharge ; le focus clavier qui y entre l'arrête aussi, et le mouvement réduit le démarre arrêté. C'est probablement un mécanisme au sens de 2.2.2, mais rien ne dit à un visiteur qu'un point l'arrête, et il n'y a aucun moyen de reprendre. Le premier site à utiliser ce mode l'a choisi pour la photo plus propre, sachant cela.
Cette compétence ne tire aucune conclusion de conformité : l'acceptation du mode "points" est du ressort de l'auditeur du site. Si le propriétaire le choisit, écrivez une ligne dans la documentation du site le disant, avec la date.
Ajouter une langue
Sur un nouveau site dans cette langue, ou lorsqu'un site l'acquiert plus tard.
- Lisez d'abord la voix du site (
CONTENT_GUIDE.md; en allemand le registre, "du" ou "Sie"). Les textes sont une poignée de mots : là où la langue le permet, ne vous adressez à personne. - Dans
src/components/hero-carousel-text.ts, copiez l'entréeendeTEXTsous le code à deux lettres de la langue (fr, pasfr-CA) et traduisez chaque valeur ;npm run checkéchoue tant qu'une valeur manque. - La page et son test, comme aux étapes 3 et 4 du §2.
3. Essayez-le une fois (le propriétaire), et les vérifications derrière cette compétence
Après npm run build && npm run preview, ou sur l'adresse de prévisualisation de la pull request, le propriétaire :
- Attend : les photos changent toutes seules, le point affiché suit.
- Déplace la souris dessus : il s'arrête. L'enlève : il reprend, après un intervalle complet.
- Sur un téléphone : tape un point (il montre sa photo) ; pas de défilement latéral ; les points sont faciles à toucher.
- En mode bouton : appuie sur Pause, voit qu'il dit Reprendre, appuie à nouveau.
Les vérifications avec lesquelles cette compétence a été construite, afin qu'une modification d'un modèle puisse être jugée : le test porte un test pour chacune de ces vérifications, et chacun échoue lorsque la chose qu'il protège est supprimée. Exécutez manuellement une fois lorsque les modèles changent (le CI les installe dans un site frais et exécute le test, dans les trois combinaisons des paramètres qu'il utilise) :
Supprimez ceci de HeroCarousel.astro | Ce test échoue |
|---|---|
les règles de grille (display: grid, grid-area) | "les diapositives partagent un même emplacement …" |
aspect-ratio sur la photo | le même test (une photo 4:3 est plus haute) |
le background de la pastille | "rendu sur chaque photo …" (blanc sur une photo pâle est environ 1:1) |
border-width: 5px sur le point affiché | le test des couleurs forcées |
les règles transition | le test du fondu ("une image à mi-chemin") |
pointer-events: none sur les diapositives non affichées | "le lien de la photo mène à sa page …" (une diapositive en fondu prend le clic) |
l'inert et aria-hidden que le script définit sur les diapositives non affichées (hero-carousel.js) | les tests de la touche Tab et de l'arbre d'accessibilité (le lien de la diapositive qui part reste accessible et lisible pendant qu'elle fond) |
la fonction recover() du script (une photo défaillante affichée est remplacée par la première autre photo présente, maintenant ou à son arrivée) | "une première image qui échoue …" |
4. La page de confidentialité, et le retour en arrière
Le carrousel ne stocke rien et n'envoie rien : rien à ajouter à la page de confidentialité.
Pour le supprimer : supprimez les quatre fichiers, le bloc <HeroCarousel> et la ligne import HeroCarousel (avec les imports de photos qu'elle utilisait seulement) de la page ; remettez une photo. Rien d'autre n'y fait référence, à part les suites PAGES qui ne voient que la page.
5. Non inclus dans cette compétence (afin que personne ne suppose)
Flèches précédent/suivant, gestes de balayage, légendes sur les photos, crédits sur les photos, diapositives vidéo, plus d'un carrousel sur une page (ses id sont fixes), chargement différé des photos suivantes. Chacun peut être ajouté plus tard ; aucun n'est nécessaire pour la première version.