Panneau dépliant
Le panneau dépliant permet d'afficher ou de masquer du contenu supplémentaire via un clic sur un bouton. Contrairement à l'accordéon, le panneau dépliant ne fait pas partie d'une liste d'éléments identiques.
Règles d’implémentation pour l’accessibilité
- Le titre du panneau dépliant est contenu dans un élément
button
avec un attributaria-controls
qui fait référence à l’id
de l’élément qu’il contrôle et un attributaria-expanded
qui prend la valeurtrue
quand la zone de contenu est visible etfalse
quand la zone de contenu est masquée. - La zone de contenu détaillé du panneau a
- un
id
avec la valeur dearia-controls
du bouton qui permet de voir ou de masquer le contenu ; - un
tabindex
avec la valeur-1
quand le contenu est masqué afin de s’assurer que la prise de focus n’est pas possible ; la valeur detabindex
passe à0
lorsque le contenu est affiché et le focus clavier est positionné sur le contenu affiché. - le rôle
region
. La région de rôle est particulièrement utile pour la perception de la structure par les utilisateurs de lecteurs d’écran lorsque les panneaux contiennent des éléments d’en-tête ou un accordéon imbriqué.
- un
Visuel
Le contenu du panneau dépliant.
Extraits de code
Code HTML
L’attribut aria-label
contiendra au départ le texte « Déplier » suivi du contenu de l’élément button
.
Le script javascript présenté après remplacera le mot « Déplier » par « Replier » suivi du contenu de l’élément button
afin que la vocalisation du bouton soit la plus précise possible (le contenu de aria-label
est lu à la place du contenu du button
).
<button type="button" class="depliant" aria-expanded="false" aria-controls="panno" aria-label="Déplier Panneau dépliant">
Panneau dépliant
</button>
<div id="panno" role="region" tabindex="-1">
<p>Le contenu du panneau dépliant.</p>
</div>
Code CSS
Pour adapter la couleur de la barre de navigation et des liens, modifier les codes couleurs dans la déclaration :root
.
L’image d’arrière-plan utilisé pour symboliser la flèche est une icône de la police symbolique Fontawesome.
:root {
--FRONT-color: #333;
--BACKGROUND-color : #C3C3C3;
--BACKGROUND-LIGHT-color: #E3E3E3;
--FOCUS-color : #F9C233;
}
button.depliant {
font-weight: normal;
color: var(--FRONT-color);
background-color: var(--BACKGROUND-color);
border: 0;
border-radius: .25em;
white-space: nowrap;
font-size: 1em;
width: 100%;
text-align: left;
margin: 0.5em 0 0 0;
}
button.depliant::after {
--icon-size: 1.25rem;
content: "";
background-color: currentColor;
float: right;
flex: 0 0 auto;
height: var(--icon-size);
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
vertical-align: calc((0.55em - var(--icon-size)) * 0.5);
width: var(--icon-size);
-webkit-mask-image: url(../images/caret-right-fill.svg);
mask-image: url(../images/caret-right-fill.svg);
transform: rotate(90deg);
transition: transform .2s ease-out;
}
button[aria-expanded=false].depliant + div {
display: none;
}
button[aria-expanded=true].depliant::after {
transform: rotate(-90deg);
}
button[aria-expanded="true"].depliant {
border-radius: .25em .25em 0 0 ;
}
button[aria-expanded=true].depliant + div {
display: block;
background-color: var(--BACKGROUND-LIGHT-color);
overflow: auto;
border-radius: 0 0 .25em .25em;
padding: 1em;
}
button[aria-expanded=true],
button.depliant:focus,
button.depliant:hover {
background-color: var(--FOCUS-color);
outline: 3px solid var(--FOCUS-color);
font-weight: bold;
color: var(--BLACK-color);
cursor: pointer;
}
button[aria-expanded="false"].depliant,
button.depliant + div {
margin-bottom: 1.5em;
}
button.depliant + div:focus {
outline: 3px solid var(--FOCUS-color);
}
button.depliant + div a:focus{
outline: 0 ;
}
Code javascript
/************ Panneau dépliant ***********/
function PlierDeplier() {
var lesButtons = document.querySelectorAll('button.depliant');
var i;
for (i = 0; i < lesButtons.length; i++) {
//initialisation aria-label
var textBoutonsDeplier = "Déplier " + lesButtons[i].innerText;
lesButtons[i].setAttribute('aria-label', textBoutonsDeplier);
// apparition/disparition du sous-menu au clic
lesButtons[i].addEventListener('click', function (e) {
var leBouton = e.target;
var lePanneau = leBouton.nextElementSibling;
// La boucle "for" ci-après referme tous les panneaux ouverts.
// Supprimer cette boucle "for" pour laisser les panneaux déjà ouverts dans cet état (ouvert).
for (j = 0; j < lesButtons.length; j++) {
if (leBouton !== lesButtons[j])
lesButtons[j].setAttribute('aria-expanded', 'false')
}
var stateButton = leBouton.getAttribute('aria-expanded') === 'false' ? true : false;
leBouton.setAttribute('aria-expanded', stateButton);
var textBoutonDeplier = "Déplier " + leBouton.innerText;
var textBoutonReplier = "Replier " + leBouton.innerText;
var labelButton = leBouton.getAttribute('aria-expanded') === 'false' ? textBoutonDeplier : textBoutonReplier;
leBouton.setAttribute('aria-label', labelButton);
var indexPanneau = leBouton.getAttribute('aria-expanded') === 'false' ? -1 : 0 ;
lePanneau.setAttribute('tabindex', indexPanneau);
lePanneau.focus();
});
}
}
if (document.querySelectorAll('button.depliant')) {
PlierDeplier();
}
Utilisation des éléments HTML details
et summary
Il existe deux éléments HTML qui permettent d’obtenir l’aeffet « panneau dépliant ».
Inconvénient : ils ne sont pas bien interprétés par les synthèse vocale.
On pourra toutefois les utiliser en rajoutant du code javascript.
Visuel
Afficher plus d'informations
Les informations supplémentaires à afficher
Code HTML
<details>
<summary class="details-summary">
<span>Afficher plus d'informations</span>
</summary>
<div class="details-contenu" role="region" tabindex="-1">
<p>Les informations supplémentaires à afficher</p>
</div>
</details>
Code CSS
summary {
cursor: pointer;
font-weight: bold;
font-size: 1rem;
color: black;
margin-bottom: 0.5rem;
display: inline-block;
}
summary::before {
--icon-size: 1rem;
background-color: currentColor;
display: inline-block;
flex: none;
margin-left: .25em;
height: var(--icon-size);
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
vertical-align: calc((.75em - var(--icon-size)) * 1);
width: var(--icon-size);
content: "";
-webkit-mask-image: url(../images/caret-right-fill.svg);
mask-image: url(../images/caret-right-fill.svg);
transition: transform .2s ease-out;
}
details[open] > summary::before {
transform: rotate(90deg);
}
summary span {
border-bottom: 1px solid currentColor;
}
summary:focus,
summary:hover {
outline: 3px solid black;
}
Code javascript
/** Panneau dépliant avec details et summary */
function allyDetails(detailsElements) {
for (var i = 0; i < detailsElements.length; i++) {
var detail = detailsElements[i];
detail.setAttribute("id", "det-" + i);
var libelle = document.getElementById("det-" + i).getElementsByClassName("details-summary")[0];
libelle.setAttribute("aria-controls", "det-" + i);
libelle.setAttribute("aria-expanded", false);
}
}
function handleClickOnDetails() {
let allDetails = document.querySelectorAll("details");
for (const item of allDetails) {
var libelle = item.getElementsByClassName("details-summary")[0];
var contenu = item.getElementsByClassName("details-contenu")[0];
if (this === item && this.open === false) {
libelle.setAttribute("aria-expanded", true);
contenu.setAttribute("tabindex", 0);
}
if (this === item && this.open === true) {
libelle.setAttribute("aria-expanded", false);
contenu.setAttribute("tabindex", -1);
}
}
}
const detailsElements = document.querySelectorAll("details");
if(detailsElements) {
allyDetails(detailsElements);
detailsElements.forEach(item => {item.addEventListener("click", handleClickOnDetails);});
}