Pseudo-classe :target en CSS

Rappels sur les fragments et les identifiants

En ajoutant un fragment à une URL, on peut accéder directement à un élément spécifique sur la page : celui dont l'identifiant correspond au fragment demandé.

Par exemple, le bloc suivant a pour identifiant toto :

Je suis le bloc d'identifiant toto !

Et le lien suivant pointe vers l'URL target.html#toto, c'est-à-dire vers l'élément toto de cette page : je suis un lien vers toto. Cliquer dessus pour voir le résultat.

On peut ainsi pointer vers un élément précis de n'importe quelle page web (à condition que cet élément ait un identifiant). Par exemple, ce lien pointe vers la section « Locomotion » de la page « Rorqual boréal » de Wikipédia.

Quand on veut pointer vers un élément sur la page courante, par exemple toto sur cette page, il suffit de mettre la partie « fragment » dans l'URL : je pointe vers l'URL #toto, je suis donc aussi un lien vers toto.

La pseudo-classe :target

Normalement, quand on lui demande d'aller à une URL comportant un fragment, le navigateur essaie de placer l'élément visé tout en haut de la fenêtre. Cependant, ce n'est pas toujours possible si la page n'est pas assez longue (par exemple si elle fait moins d'un écran de hauteur) ou si la cible est proche du bas de la page.

Dans ce cas, la cible précise de l'URL peut ne pas être claire : la personne qui a cliqué sur le lien peut croire que le lien porte sur la page entière. Par exemple, il n'est pas clair si ce lien pointe vers « Annexes », « Articles connexes », « Références taxonomiques » ou « Liens externes ».

La pseudo-classe :target vise à résoudre ce problème. Elle permet de sélectionner l'élément visé par l'URL courante. Par exemple, le CSS

#titi:target {
	background-color: tomato;
}
va mettre l'élément d'identifiant titi en rouge si l'URL courante du navigateur comporte le fragment #titi. Tester en cliquant sur ce lien vers titi.

Je suis le bloc d'identifiant titi !

Il est ainsi possible de mettre en évidence l'élément visé par un lien (typiquement un titre de section), ce qui permet d'éviter le problème expliqué plus haut.


Le cas d'utilisation de :target peut paraître très spécifique, mais cette pseudo-classe est en fait très puissante : en effet, il est possible d'appliquer n'importe quel code CSS à l'élément visé, ce qui augmente énormément la flexibilité de l'interface utilisateur sans nécessiter de JavaScript.

L'exemple typique de « détournement » de :target est de l'utiliser pour faire une lightbox, c'est-à-dire d'ouvrir une image en grand quand on clique dessus.

Faire apparaître un élément au clic

Pour commencer, on va simplement faire apparaître un élément lors d'un clic sur un lien. L'idée de base est très simple : on met l'élément à l'endroit où l'on veut qu'il apparaisse dans le HTML, et on fait un lien vers cet élément :

<div>
	<p class="hidden" id="tutu">
		Je suis <code>tutu</code>, l'élément caché !
	</p>
	<a href="#tutu">Cliquer ici pour faire apparaître tutu…</a>
</div>

Dans le CSS, on cache par défaut l'élément qu'on veut faire apparaître (de classe hidden) mais on le fait apparaître s'il est cible de l'URL courante.

.hidden {
	display: none;
}

.hidden:target {
	display: block;
	background-color: gold;
}

Quand on clique sur le lien, le fragment #tutu s'ajoute à l'URL courante, l'élément tutu est donc ciblé par l'URL courante, et comme il est de classe hidden, la deuxième règle CSS s'applique : l'élément tutu apparaît. Tester :

Cliquer ici pour faire apparaître tutu…

Une lightbox en CSS

Pour faire une lightbox, il suffit d'appliquer cette idée, avec quelques différences :

<div>
	<a href="#loup1" id="loup1-mini">
		<img src="img/miniature.jpg" alt="Image d'un loup gris." />
	</a>
	<a class="lightbox" href="#loup1-mini" id="loup1">
		<img src="img/grande.jpg" alt="Image d'un loup gris." />
	</a>
</div>
.lightbox {
	display: none;
}

.lightbox:target {
	display: block;
	position: fixed;
	top: 10px;
	left: 10px;
}

À part ces détails, le fonctionnement est exactement le même qu'auparavant. Cliquer sur l'image pour tester :

Image d'un loup gris. Image d'un loup gris.

Un peu d'habillage

Le CSS ci-dessous ajoute un peu d'habillage à notre lightbox : on fait en sorte que le « lien retour » recouvre tout l'écran et on lui met un fond sombre (mais légèrement transparent). Le passage entre mode normal et mode lightbox est ainsi très clair ; de plus, on peut cliquer n'importe où sur la fenêtre pour revenir au mode normal.

Dans cet exemple, j'ai ajouté aussi des dimensions maximales pour l'image elle-même : quelle que soit la taille ou l'orientation de l'écran, l'image ne dépassera pas. Attention : il est donc impossible de zoomer sur l'image, ce qui n'est pas forcément idéal ! Une solution est présentée dans la section suivante.

.lightbox {
	display: none;
}

.lightbox:target {
	display: block;
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	background-color: rgba(0, 0, 0, .8);
}

.lightbox img {
	display: block;
	margin: auto;
	max-height: 100%;
	max-width: 100%;
}
Image d'un loup gris. Image d'un loup gris.

À noter que le margin:auto sur l'image centre l'image horizontalement, mais pas verticalement : en CSS « classique » auto vaut toujours 0 pour les marges verticales. Il y a ainsi plusieurs aspects pour lesquels CSS ne traite pas de la même façon les dimensions horizontales et verticales, pour des raisons a priori principalement « historiques ». Voir la section suivante pour une solution moderne.

Version plus ergonomique (et centrage vertical)

Dans cette version, pour garantir une ergonomie minimale, j'ai fait en sorte que l'image soit zoomable si jamais l'écran est trop petit. Pour ce faire, j'ai mis la grande image dans un bloc de classe viewer.

<div>
	<a href="#loup4" id="loup4-mini">
		<img src="img/miniature.jpg" alt="Image d'un loup gris." />
	</a>
	<a class="lightbox" href="#loup4-mini" id="loup4">
		<div class="viewer">
			<img src="img/grande.jpg" alt="Image d'un loup gris." />
		</div>
	</a>
</div>

Dans le CSS, les contraintes sur les dimensions maximales sont appliquées sur le viewer, qui est en overflow:auto ; aucune contrainte n'est appliquée sur l'image elle-même. Ainsi, s'il y a suffisamment d'espace, il n'y aura aucune différence par rapport au cas précédent, mais si l'écran est trop petit, l'internaute pourra se déplacer dans l'image.

NB : le viewer ne prend pas tout l'espace disponible pour limiter la confusion due à des ascenseurs multiples.

Au passage, j'ai centré le viewer verticalement en mettant tout simplement son conteneur en display:grid et lui-même en margin:auto. En effet, pour un item de grille, margin:auto fonctionne comme on le souhaite dans les deux dimensions : les personnes qui ont conçu les spécs ont profité de ce contexte « moderne » pour modifier le comportement de margin: auto sans casser tous les sites web existants. Ça marche d'ailleurs aussi avec le conteneur en display:flex, pour les mêmes raisons.

.lightbox {
	display: none;
}

.lightbox:target {
	display: grid;
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	background-color: rgba(0, 0, 0, .8);
}

.lightbox .viewer {
	margin: auto;
	max-height: 90%;
	max-width: 90%;
	overflow: auto;
}
Image d'un loup gris.
Image d'un loup gris.