Accordéon A11y
by oilvier
HTML
<div class="accordion" data-accordion="form" data-accordion-icon>
<h2 class="accordion__trigger" data-accordion-trigger data-accordion-open>
<span class="accordion__ttl">
A
</span>
</h2>
<div class="accordion__content" data-accordion-content>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nummus in Croesi divitiis obscuratur, pars est tamen divitiarum. Nam si propter voluptatem, quae est ista laus, quae possit e macello peti? Sit, inquam, tam facilis, quam vultis, comparatio voluptatis, quid de dolore dicemus? Quem si tenueris, non modo meum Ciceronem, sed etiam me ipsum abducas licebit. Quid de Platone aut de Democrito loquar? Duo Reges: constructio interrete. Qui ita affectus, beatum esse numquam probabis; </p>
</div>
<h2 class="accordion__trigger" data-accordion-trigger>
<span class="accordion__ttl">
B
</span>
</h2>
<div class="accordion__content" data-accordion-content>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Si enim ad populum me vocas, eum. Bonum integritas corporis: misera debilitas. Sedulo, inquam, faciam. Quod dicit Epicurus etiam de voluptate, quae minime sint voluptates, eas obscurari saepe et obrui. At ego quem huic anteponam non audeo dicere; Nam memini etiam quae nolo, oblivisci non possum quae volo. Duo Reges: constructio interrete. Quia nec honesto quic quam honestius nec turpi turpius. </p>
</div>
</div>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">><symbol viewBox="0 0 20 13.9" id="ico-pointer" xmlns="http://www.w3.org/2000/svg"><path d="M20 7L8.2 13.9V0L20 7zm-9.8 3.4l5.9-3.5-5.9-3.5v7z"/><circle cx="3" cy="6.5" r="3"/></symbol><symbol viewBox="0 0 14.2 25" id="ico-arrow-right" xmlns="http://www.w3.org/2000/svg"><path d="M13.8 13.7L3 24.5c-.7.7-1.8.7-2.5 0s-.7-1.8 0-2.5l9.5-9.6L.5 3C-.2 2.3-.2 1.2.5.5S2.3-.2 3 .5l10.8 10.8c.3.3.5.8.5 1.3 0 .3-.2.7-.5 1.1z"/></symbol></svg>
SCSS
.accordion {}
.accordion__ttl,
.accordion__trigger > button > span {display:flex; align-items:baseline;
& > svg {flex-shrink:0;}
}
.accordion__trigger {display:flex; align-items:center; padding:15px;
& > button {flex:1 1 100%; margin:-15px; border:0; padding:15px; background-color:tomato; text-align:left; transition:background 0.3s ease-out;
& > span {justify-content:space-between;
& > svg {width:9px; height:15px; transition:transform 0.2s ease;}
}
&[aria-expanded="true"] {background-color:transparent;
& > span > svg {transform:rotate(90deg);}
}
&:focus {background-color:firebrick; outline:none;
& > span {text-decoration:underline;}
}
}
}
.accordion__ttl {
& > svg {margin-right:10px; fill:blue;}
}
.accordion__content {padding:0 15px;}
p {margin:0 0 20px;}
/**
* [1] Corrige la couleur non héritée
* [2] Corrige les propriétés de font non héritées
* [3] Uniformise les marges sur Firefox, Safari et Chrome
*/
button,
input,
select,
option,
optgroup,
textarea {font:inherit;/* [1] */ color:inherit;/* [2] */ margin:0;/* [3] */}
/**
* Retire le padding et la bordure dans Firefox
*/
button::-moz-focus-inner,
input::-moz-focus-inner {border:0; padding:0;}
JavaScript
/*=========================================================
= Gestion d'un système d'accordéons =
=========================================================*/
// On récupère tous les titres de la page
var $trigger = $('[data-accordion-trigger]'),
content = $('[data-accordion-content]');
// On masque les contenus
content.attr('hidden', true);
$trigger.each(function() {
var $this = $(this),
isOpen = $this.attr('data-accordion-open'), // Si un élément doit rester ouvert au chargement
hasIcon = $this.parent('[data-accordion]').attr('data-accordion-icon'), // Si on doit ajouter des icônes pour signifier l'état
isCloseable = $this.parent('[data-accordion]').attr('data-accordion-closeable'), // Si on peut refermer l'accordéon
// Gestion d'un texte spécifique pour remplacer le titre usuel (≠ du texte affiché par défaut lorsque pas de JS)
openLabel = $this.find('[data-accordion-open-label]').attr('data-accordion-open-label'), // Pour ouvrir
closeLabel = $this.find('[data-accordion-close-label]').attr('data-accordion-close-label'); // Pour fermer
// On ajoute un bouton sur chaque titre
$this.wrapInner('<button type="button" aria-expanded="false"><span></span></button>');
var button = $this.find('button');
// On modifie le texte du bouton
button.find('[data-accordion-open-label]').text(openLabel);
// Si un accordéon doit être ouvert par défaut
if(typeof isOpen !== typeof undefined && isOpen !== false) {
button.attr('aria-expanded', true);
button.find('[data-accordion-close-label]').text(closeLabel);
button.parents('[data-accordion-trigger]').next('[data-accordion-content]').removeAttr('hidden');
}
// Si un message d'erreur de validation de formulaire est présent
// on ouvre l'accordéon correspondant
if( $this.next('[data-accordion-content]').find('.error').length ) {
$trigger.find('button').attr('aria-expanded', 'false');
content.attr('hidden', true);
button.attr('aria-expanded',...