Ces derniers temps, un mini-projet me trottait en tête : un site qui propose des idées de choses rapides à dessiner. La liste serait établie au préalable par moi-même ou par des propositions externes, mais tout serait statique. Le cahier des charges me semblait suffisamment simple pour que je puisse me passer d’un langage dynamique à la fois côté serveur et client (ni PHP, ni JavaScript en somme). C’était sans compter que je voulais que les idées soient suggérées de manière aléatoire.
En y réfléchissant hier soir, je me suis demandé : « et si on pouvait générer de l’aléatoire en CSS ? » La réponse courte est « non », mais il existe également une réponse longue et elle m’a été donnée par le site CSS-Tricks : « Are There Random Numbers in CSS? ».
Attention, n’utilisez pas cette technique en production. Elle n’est ni accessible, ni très maintenable. Il s’agit avant tout de jouer avec CSS. Vous êtes prévenu‧e ! 😄
Le principe est assez simple : il consiste à superposer plusieurs label
avec
le même texte, les uns au-dessus des autres, puis à modifier leur z-index
en
boucle à l’infini grâce à une animation
. Ainsi, le clic surviendra sur un
label
que le visiteur ne saura pas prévoir. Les label
sont associés à des
champs radio
: il suffit alors d’adapter l’affichage en fonction du sélecteur
input:checked
.
Ce qui est marrant avec cette méthode, c’est que la génération d’aléatoire est déplacée de l’ordinateur au visiteur.
Pour ma part, j’ai adapté la solution parce qu’elle ne me convenait pas tout à
fait : j’ai préféré me baser sur des liens avec ancre et le sélecteur associé,
:target
. En effet, en cliquant sur un lien avec l’attribut href="#1"
par
exemple, le sélecteur CSS#1:target
devient actif. Voici une
illustration :
<p class="idee-dessin" id="1">Un renard qui fait du surf</p>
<p class="idee-dessin" id="2">Une tomate amoureuse</p>
<p class="idee-dessin" id="3">Une pendule en retard à un rendez-vous</p>
<div class="conteneur-boutons-nouvelle-idee">
<a class="bouton-nouvelle-idee" href="#1">Nouvelle idée ?</a>
<a class="bouton-nouvelle-idee" href="#2">Nouvelle idée ?</a>
<a class="bouton-nouvelle-idee" href="#3">Nouvelle idée ?</a>
</div>
Ainsi, le premier lien « Nouvelle idée ? » active le premier paragraphe, etc.
Il suffit alors de cacher en CSS tous les paragraphes, sauf celui
visé par le sélecteur :target
.
.idee-dessin {
display: none;
}
.idee-dessin:target {
display: block;
}
Il ne reste alors plus qu’à superposer les liens et animer leur z-index
:
.conteneur-boutons-nouvelle-idee {
position: relative;
}
@keyframes changeOrdre {
from { z-index: 3; }
to { z-index: 1; }
}
.bouton-nouvelle-idee {
animation: changeOrdre 3s infinite linear;
position: absolute;
}
.bouton-nouvelle-idee:nth-of-type(1) { animation-delay: -0.0s; }
.bouton-nouvelle-idee:nth-of-type(2) { animation-delay: -0.5s; }
.bouton-nouvelle-idee:nth-of-type(3) { animation-delay: -1.0s; }
.bouton-nouvelle-idee:active {
z-index: 4 !important;
}
La dernière règle est importante car les navigateurs génèrent un clic
uniquement si l’élément qui a reçu l’évènement mousedown
est le même qui
reçoit l’évènement mouseup
. Comme les z-index
changent en permanence, ce
n’est souvent pas le cas. Il faut donc forcer un peu les choses en mettant
l’élément :active
au premier plan. C’est expliqué dans l’article de
CSS-Tricks avec une solution plus compliquée, mais qui fait l’économie d’un
!important
.
Une dernière chose me chagrinait avec cette méthode, c’est qu’il était compliqué d’ajouter de nouvelles phrases à la main. J’ai donc écrit un script Python pour générer les fichiers HTML et CSSà partir d’un fichier contenant des phrases.
Le code est sur Framagit, et le résultat sur frama.io.
Avec ça je me serai bien marré, mais la prochaine fois j’utiliserai quand même JavaScript, ce sera plus simple 😅