Créer un bandeau d’actualités défilant en HTML et CSS
Parfois, lorsque nous créons un site avec un template, nous découvrons qu’un composant manque à l’interface. Nous pourrions, par exemple, avoir à afficher un fil d’actualités sous la forme d’un bandeau horizontal, alors que rien n’ait été prévu pour cela à l’origine. Dans la lignée de l’article « Organiser l’interface avec des panneaux latéraux dynamiques », nous allons voir qu’avec quelques lignes de code, il reste possible de mettre en place ce type de fonctionnalités sans avoir à dépendre d’une bibliothèque externe.
L’approche va se baser sur le couple HTML / CSS, afin d’organiser le contenu et produire le mouvement de défilement. Nous verrons dans un prochain article comment JavaScript, associé à un petit fragment JSON, nous permettra de rendre ce bandeau plus souple à maintenir. Quoiqu’il en soit, une fois le composant en place, il ne restera alors plus qu’à l’adapter visuellement à la charte graphique du template et à l’insérer dans notre page.
Ajouter un fil d’actualités dans une interface déjà construite
Proposer un fil d’actualités permet avant tout d’attirer le regard de l’internaute et d’afficher une quantité d’informations plus importante dans le même espace d’interface. Mais à y regarder de plus près, ce fil doit quand même répondre à plusieurs contraintes : en fonction de son visuel, le défilement ne doit pas être trop rapide, le contenu doit se lire facilement, il doit contenir un lien permettant d’accéder à de plus amples informations, et si le fil défile sur une seule ligne, il ne doit pas y avoir de retour chariot… bref, rien d’insurmontable, mais cela doit être pris en compte.

Dans certains cas, le fil peut être une simple ligne de texte qui défile, présentant ainsi des accès rapides à diverses informations, un peu à l’instar de ces bandeaux new-yorkais qui couvrent les façades d’immeubles, affichant dans un flux continu le cours de la Bourse et des évènements à ne pas manquer. Mais parfois, le bandeau peut être un tantinet plus sophistiqué et afficher une image, une icône, un titre et un court texte. N’oublions pas que ces éléments défilent et qu’il faut que l’internaute puisse avoir le temps de les lire.

Pour les besoins du présent article nous nous arrêterons à une version v1 qui propose simplement les bases du mécanisme. Le prochain article proposera un mode CRUD d’édition du fichier JSON, ainsi que la présentation d’une version alternative du bandeau, exposant ainsi les actualités sous forme de notes.
Comprendre le mécanisme de la boucle continue
Avant de nous lancer dans l’écriture de HTML ou de CSS, il faut mettre à plat ce que l’on cherche réellement à produire. Un bandeau qui fait défiler des actualités n’est en fait pas un simple bandeau qui se déplace de droite à gauche, car une fois que la dernière actualité est affichée un espace vide va suivre et rompre la continuité du défilement.
En réalité, au lieu d’avoir un seul et unique bandeau qui défile, il nous faut produire deux bandeaux identiques que l’on place l’un derrière l’autre. On déplace alors les deux en même temps. Pendant que le premier quitte progressivement l’espace visible, le second enchaine, et puisqu’il contient exactement le même contenu, donne cette impression de continuité.
À partir de là, plusieurs approches sont possibles. On pourrait imaginer un script qui surveille le déplacement et replace le bandeau devenu invisible derrière celui qui est encore en cours d’affichage et ainsi de suite, mais cela nécessiterait du JavaScript pour prendre en charge cette mécanique. En ce qui concerne cet article, nous allons explorer une version qui fonctionne uniquement avec du HTML et des CSS.
Nous allons donc rester sur une mécanique plus simple : le HTML propose deux bandeaux d’actualités identiques et les CSS animent cet ensemble. Lorsque l’animation arrive au bout du premier bandeau, elle reprend à son point de départ. Comme la copie visuelle occupe alors la même position que le contenu initial, le retour devient presque imperceptible.

Ce mécanisme fonctionne mieux lorsque le bandeau contient suffisamment d’actualités pour occuper correctement l’espace horizontal disponible. D’une part, si le premier bandeau est plus court que la zone visible, certaines actualités apparaissent deux fois à l’écran, puisque le second bandeau est présent dans la zone d’affichage dès le départ de l’animation. Et d’autre part, un véritable blanc apparaît si les deux bandeaux placés côte à côte ne suffisent pas à remplir la largeur visible. Dans ces cas, l’illusion de continuité n’est plus vraiment efficace.
Quoiqu’il en soit, le fait de générer un second bandeau pour créer cette illusion de continuité introduit une problématique au niveau des outils d’assistance ou de la navigation par clavier. Car le système, lui, voit bien deux contenus identiques. Nous aborderons cette problématique plus loin dans cet article lors du chapitre Prévoir les pauses et les règles d’accessibilité.
Traduire ce mécanisme dans une structure HTML
La structure HTML à mettre en place pour accueillir le composant repose sur une simple imbrication de conteneurs. Ces conteneurs peuvent être de simples balises div, distinguées par un jeu de classes approprié.

- Le premier correspond à la zone visible, c’est-à-dire la fenêtre d’aperçu du fil d’actualités :
.news-actualites__viewport. - À l’intérieur, nous trouverons le conteneur des deux bandeaux :
.news-actualites__track, qui se déplacera de droite à gauche sous cette zone de visibilité. - Chaque bandeau, représenté par
.news-actualites__content, pourra ensuite contenir les éléments d’actualités,.news-actualites__item, et leurs séparateurs,.news-actualites__separator.
Nous aurons donc une structure assez simple à lire : une fenêtre visible, un ruban qui se déplace, puis deux contenus identiques placés l’un derrière l’autre.
Enfin, dans la mesure où une page peut contenir divers composants du même type, il est préférable d’englober l’ensemble dans un conteneur d’isolation, par exemple une section identifiée par la classe .news-actualites. Le code HTML final pourrait ressembler au fragment suivant.
<section class="news-actualites">
<div class="news-actualites__viewport">
<div class="news-actualites__track">
<div class="news-actualites__content">
<!-- Première série d’actualités -->
</div>
<div class="news-actualites__content">
<!-- Copie visuelle de la première série -->
</div>
</div>
</div>
</section>Pour l’instant, la structure principale d’accueil est en place, Bandeau v1.0. Il ne nous reste plus qu’à ajouter quelques actualités pour apporter une partie visible à ce code HTML.
Placer les actualités dans les deux bandeaux
Pour rester dans le cadre du présent article, et n’aborder que l’aspect mécanique et non dynamique du composant, nous allons écrire les actualités directement dans le HTML. Certes, cela présente une limite évidente : si une actualité change, il faudra alors modifier le code à la main. Mais pour comprendre le mécanisme, cette approche reste intéressante, car elle nous permet de rester focalisés sur le fonctionnement du mouvement du fil d’actualités.
Afin d’illustrer les actualités, nous allons utiliser une série de 4 articles du blog de Puce & Média :
- Une certaine idée du développement
https://www.puce-et-media.com/une-certaine-idee-du-developpement/ - Dessiner avant de coder : méthode analogique pour penser une application
https://www.puce-et-media.com/dessiner-avant-de-coder-methode-analogique-pour-penser-une-application/ - tab_options : anatomie d’une table universelle
https://www.puce-et-media.com/tab_options-anatomie-dune-table-universelle/ - Tracer des solutions : du code à l’usage
https://www.puce-et-media.com/tracer-des-solutions-du-code-a-lusage/
Intéressons-nous au premier bloc. Il doit contenir les éléments sous forme de simples ancres <a href="#">label</a> séparées par un trait de séparation <span>—</span>. Comme nous l’avons vu précédemment, les liens et les séparateurs doivent être respectivement associés aux classes .news-actualites__item et .news-actualites__separator.
<!--
Première série d’actualités.
Les liens s’ouvrent dans un nouvel onglet avec target="_blank".
L’attribut rel="noopener" évite que la page ouverte puisse accéder à la page d’origine.
-->
<div class="news-actualites__content">
<a
class="news-actualites__item"
href="https://www.puce-et-media.com/une-certaine-idee-du-developpement/"
target="_blank"
rel="noopener"
>
Une certaine idée du développement
</a>
<span class="news-actualites__separator">—</span>
<a
class="news-actualites__item"
href="https://www.puce-et-media.com/dessiner-avant-de-coder-methode-analogique-pour-penser-une-application/"
target="_blank"
rel="noopener"
>
Dessiner avant de coder : méthode analogique pour penser une application
</a>
<span class="news-actualites__separator">—</span>
<a
class="news-actualites__item"
href="https://www.puce-et-media.com/tab_options-anatomie-dune-table-universelle/"
target="_blank"
rel="noopener"
>
tab_options : anatomie d’une table universelle
</a>
<span class="news-actualites__separator">—</span>
<a
class="news-actualites__item"
href="https://www.puce-et-media.com/tracer-des-solutions-du-code-a-lusage/"
target="_blank"
rel="noopener"
>
Tracer des solutions : du code à l’usage
</a>
<span class="news-actualites__separator">—</span>
</div>
Il suffit maintenant de placer exactement le même contenu dans la seconde section correspondant à la copie visuelle de la première série, uniquement pour créer la continuité visuelle du défilement. C’est cette répétition qui permettra ensuite au CSS de déplacer le ruban sans laisser apparaître de coupure.
La structure est maintenant complète, Bandeau v1.1, nous allons pouvoir passer à la mise en forme et à l’animation au travers de quelques règles CSS.
Donner forme et mouvement au bandeau avec CSS
Notre structure HTML en place, le CSS va pouvoir prendre le relais pour donner au composant son comportement visuel. La zone visible va servir de fenêtre pour ne laisser voir que tout ou partie du ruban en fonction de son déplacement. Commençons par agir sur le conteneur principal en lui donnant la taille souhaitée pour le composant et sur la fenêtre pour qu’elle s’adapte à cet environnement en masquant ce qui en dépasse.
. news-actualites {
width: 640px;
margin : 0 auto ;
font-size: 16px;
line-height: 2;
background-color: #ffff00 ;
}
.news-actualites__viewport {
overflow: hidden;
width: 100%;
}Le track contient les deux bandeaux. En lui appliquant un affichage flex, ces deux bandeaux s’aligneront l’un à la suite de l’autre, sur le même axe horizontal. Par contre la largeur totale ainsi obtenue pourra excéder la largeur disponible, il faut donc également appliquer un width: max-content; pour pallier cette éventualité.
.news-actualites__track {
display: flex;
width: max-content;
min-width: 100%;
}L’animation est déconcertante de facilité, il suffit d’animer le ruban avec un translateX() négatif. Du fait qu’il contienne deux bandeaux strictement identiques, si on le déplace de la moitié vers la gauche, on arrive à positionner le second bandeau tel que l’était le premier au départ de l’animation. La boucle étant bouclée, l’animation peut reprendre au début, et on peut lui attribuer un mouvement infini sans coupure visible.
Donc, au départ, le ruban est à sa position initiale. À la fin de l’animation, il a été déplacé vers la gauche de 50% de sa propre largeur.
@keyframes news-defilement {
from {
transform: translateX(0);
}
to {
transform: translateX(-50%);
}
}Il ne reste plus qu’à appliquer cette animation à la règle du ruban :
.news-actualites__track {
/* autre code */
animation: news-defilement 35s linear infinite;
}Du coté des contenus, bien qu’à ce stade tout pourrait sembler fonctionner ainsi, Bandeau v1.2, il faut néanmoins nous intéresser aux trois conteneurs pour renforcer certaines de leurs propriétés. Le conteneur principal de chaque bandeau .news-actualites__content, ainsi que les deux éléments qu’il contient, .news-actualites__item et .news-actualites__separator.
Stabiliser les contenus du ruban
Bien que tout semble apparemment fonctionner comme attendu, il existe quelques écueils qu’il faut s’assurer d’éviter. Pour l’instant les contenus ne sont que des éléments de type inline ou inline-bloc, mais qu’adviendrait-il de leur positionnement si nous insérions certains éléments de type bloc, ou si nous placions des caractères ayant un font-weight différent, ou encore si nous réduisions l’espace alloué au conteneur principal. N’oublions pas que nous employons un affichage flex et que, par défaut, la valeur flex-shrink vaut 1. Les éléments peuvent donc être réduits, individuellement, si l’espace disponible devient insuffisant.
Pour éviter tout cela, ajoutons quelques règles à ces trois classes. Commençons par celle affectée aux conteneur principaux, aux bandeaux. Usons une fois encore d’un affichage flex pour éviter qu’un élément bloc vienne casser l’alignement sur une seule ligne. Assurons-nous également que l’alignement vertical ne soit pas perturbé par des graisses de caractères différentes. Enfin, évitons le rétrécissement des éléments tout en nous assurant qu’une actualité n’impose pas un retour chariot.
.news-actualites__content {
display: flex;
align-items: center;
flex-shrink: 0;
white-space: nowrap;
}En ce qui concerne les éléments eux-mêmes, commençons par l’actualité elle-même, à laquelle nous allons également bloquer le rétrécissement. En complément, nous pouvons en profiter pour surcharger les propriétés de couleurs, de gestion de liens afin d’affiner son apparence.
.news-actualites__item {
flex-shrink: 0;
color: #F88541;
text-decoration-line: underline;
text-decoration-style: dotted;
text-decoration-thickness: 1px;
text-underline-offset: 4px;
}Ensuite intéressons nous aux séparateurs. Quelques précautions sont à prendre au niveau du rétrécissement, de la couleur mais aussi des marges de positionnement.
.news-actualites__separator {
flex-shrink: 0;
color: #EAF768;
margin: 0 1.5rem;
}Il ne s’agit là que de règles minimales pour sécuriser notre composant, Bandeau v1.3, libre à vous de les compléter pour adapter le rendu final au template employé. Avant de considérer le bandeau comme terminé, il reste un point important à traiter. En effet, n’oublions pas que nous avons volontairement dupliqué les actualités pour créer l’illusion d’une boucle continue. Visuellement, cela fonctionne. Mais en ce qui concerne la navigation au clavier ou les outils d’assistance, cette copie ne doit pas être perçue comme une véritable seconde série de liens.
Prévoir les pauses et les règles d’accessibilité
En ce qui concerne les assistances visuelles, nous pouvons nous appuyer sur l’attribut aria-hidden="true" qu’il suffit d’ajouter sur le second bloc .news-actualites__content. Cela suffira pour informer les technologies d’assistance de ne pas prendre en compte le contenu concerné.
<div class="news-actualites__content" aria-hidden="true">
<!-- Ensemble des liens du clone visuel -->
</div>Par contre malgré cela les liens eux-mêmes, resteront toujours accessibles au clavier. Nous allons donc devoir, cette fois, appliquer un tabindex="-1" sur l’ensemble des liens du bandeau cloné. Cela permettra de les retirer de l’ordre naturel de navigation.
<a
class="news-actualites__item"
href="https://www.puce-et-media.com/une-certaine-idee-du-developpement/"
target="_blank"
rel="noopener"
tabindex="-1"
>
Une certaine idée du développement
</a>Un autre point qu’il peut être intéressant à prendre en compte reste le mouvement du bandeau qui défile en permanence. Suivant les contextes, cela peut rendre difficile un clic sur l’un des éléments, ou ne pas faciliter la lecture d’un des intitulés. L’idée serait de mettre en pause l’animation lorsque le pointeur survole le composant :hover, aussi bien que lorsque le focus clavier entre dans le composant :focus-within.
.news-actualites:hover .news-actualites__track {
animation-play-state: paused;
}
.news-actualites:focus-within .news-actualites__track {
animation-play-state: paused;
}Cette pause au survol améliorera considérablement l’utilisabilité du composant sur ordinateur, toutefois sur un écran tactile, la situation reste un peu différente. En fait, la notion de survol n’a pas vraiment d’équivalent, puisque l’utilisateur ne déplace pas un pointeur au-dessus du bandeau : il touche directement l’écran. Certains navigateurs peuvent suspendre temporairement l’animation lorsqu’un lien est pressé, mais ce comportement ne doit pas être considéré comme un contrôle de lecture à part entière, Bandeau v1.4.
Prévoir une version sans défilement automatique
Malgré toutes ces précautions, il ne faut pas oublier que certains utilisateurs étant gênés par les animations continues, configurent leurs ordinateurs afin de réduire le mouvement sur leurs écrans. Cela concerne généralement des personnes sensibles aux mouvements à l’écran, aux défilements permanents, aux effets de transition, ou simplement des internautes qui ont besoin d’une interface plus stable pour lire confortablement. Ce choix peut être indiqué directement depuis les préférences d’accessibilité du système d’exploitation.

La requête média prefers-reduced-motion permet de tenir compte de cette préférence. Par contre, si l’utilisateur a demandé une réduction des animations, il nous faut désactiver le défilement automatique. Du coup, la présentation du bandeau, va être altérée.
@media (prefers-reduced-motion: reduce) {
.news-actualites__track {
animation: none;
}
}Il ne suffit pas pour autant de couper l’animation sans réfléchir à la suite. Car, si le bandeau ne défile plus alors que son contenu reste plus large que la zone visible, toute une partie des liens va se trouver hors champ. Nous pourrions bien autoriser un défilement horizontal manuel avec overflow-x: auto, mais cela reviendrait à remplacer un mouvement automatique par une manipulation supplémentaire, Bandeau v1.5 [Pensez à désactiver les animations Windows au préalable].

Il est sans doute plus confortable d’accepter que le composant change de forme de présentation. Pour cela, lorsque l’utilisateur a demandé une réduction des animations, le bandeau peut devenir une simple liste verticale de liens. Ce qui implique que : la copie visuelle n’a alors plus d’utilité, les séparateurs peuvent être masqués, et chaque actualité reste directement lisible sans mouvement imposé.
@media (prefers-reduced-motion: reduce) {
/* On ne masque plus le dépassement : le bandeau peut prendre plus de hauteur. */
.news-actualites__viewport {
overflow: visible;
}
/*
* Le ruban ne défile plus.
* On annule aussi toute transformation éventuelle pour revenir à une position stable.
*/
.news-actualites__track {
animation: none;
transform: none;
width: 100%;
}
/*
* Les actualités ne sont plus présentées sur une seule ligne.
* Elles passent en colonne pour rester lisibles sans animation.
*/
.news-actualites__content {
flex-direction: column;
align-items: flex-start;
white-space: normal;
width: 100%;
}
/*
* La copie visuelle ne sert plus à créer la boucle.
* Elle est donc masquée pour ne garder qu’une seule série d’actualités.
*/
.news-actualites__content[aria-hidden="true"] {
display: none;
}
/* Les séparateurs horizontaux n’ont plus d’utilité dans une liste verticale. */
.news-actualites__separator {
display: none;
}
/* Chaque lien devient un élément de liste lisible sur sa propre ligne. */
.news-actualites__item {
display: block;
padding-left: 1rem;
}
}Cette adaptation ne modifie pas définitivement le composant, Bandeau v1.6. Elle ne s’applique que lorsque l’utilisateur a demandé à son système de réduire les animations. Dès lors que cette préférence est désactivée, ou que le navigateur ne la reçoit plus, les règles placées dans @media (prefers-reduced-motion: reduce) ne sont plus appliquées. Le bandeau retrouve alors son comportement initial, avec son ruban horizontal et son défilement automatique.

Libre ensuite à chacun d’adapter cette version sans animation au contexte du site ou au template employé. Pour cet article, nous avons opté pour une présentation verticale assez simple, parce qu’elle rend tous les liens immédiatement lisibles. Mais rien n’empêche d’imaginer une autre mise en forme, l’essentiel est de conserver l’idée de départ : lorsque le mouvement automatique disparaît, les actualités doivent rester accessibles.
Conclusion, le ruban défile, le code reste simple
Ce premier bandeau reste volontairement simple. Tout repose sur une structure HTML écrite à la main et sur quelques règles CSS capables d’organiser le ruban, de le faire défiler et d’adapter son comportement lorsque le mouvement doit être réduit. Aucun JavaScript n’est nécessaire pour obtenir cette première version. Le composant peut donc être intégré assez facilement dans un template existant, à condition de garder en tête que chaque actualité devra être ajoutée, modifiée ou supprimée directement dans le code.
La prochaine étape consiste donc à faire intervenir JavaScript, non pas pour remplacer le CSS dans l’animation, mais pour générer automatiquement le contenu du bandeau à partir d’un fichier JSON. Nous pourrons explorer cette approche dans « Alimenter un bandeau d’actualités avec JavaScript et JSON ».
