Accessible Inline Spoiler
by Kai Kubasta
HTML
<h1>Accessible Inline Spoiler</h1>
<p>I never thought that
<button
class="spoiler"
aria-role="switch"
aria-pressed="false"
aria-live="polite"
aria-label="Spoiler"
data-showText="Click to reveal text"
data-hideText="Click to hide text"
>
<span
class="spoiler__text"
data-text="Gandalf is Harry Potter's father">
</span>
</button>.
</p>
CSS
body {
background-color: white;
color: black;
font: 1rem/1.5 sans-serif;
padding: 2rem;
margin: 0;
}
h1 {
margin: 0;
}
a {
color: blue;
}
button {
color: black;
}
*:focus {
outline: 2px solid blue;
}
.spoiler {
background: transparent;
font: 1rem/1.5 sans-serif;
margin: 0;
padding: 0;
border: 0;
appearance: none;
cursor: pointer;
}
.spoiler:focus {
outline-offset: 2px;
}
[aria-pressed="false"] .spoiler__text {
background-color: currentColor;
}
[aria-pressed="false"] .spoiler__text::before {
content: attr(data-text);
}
[aria-pressed="true"] .spoiler__text {
text-decoration: underline;
text-decoration-style: dotted;
text-decoration-thickness: 2px;
text-decoration-color: gray;
}
JavaScript
const $spoilers = document.querySelectorAll('.spoiler');
$spoilers.forEach(($spoiler) => {
const label = $spoiler.getAttribute('aria-label');
const showText = $spoiler.dataset.showtext;
const hideText = $spoiler.dataset.hidetext;
const $text = $spoiler.querySelector('.spoiler__text');
const spoiler = $text.dataset.text;
$spoiler.title = showText;
$spoiler.addEventListener('click', () => {
if($spoiler.getAttribute('aria-pressed') === 'false') {
$spoiler.setAttribute('aria-pressed', 'true');
$spoiler.removeAttribute('aria-label');
$spoiler.title = hideText;
$text.innerText = spoiler;
} else {
$spoiler.setAttribute('aria-pressed', 'false');
$spoiler.setAttribute('aria-label', label);
$spoiler.title = showText;
$text.innerText = '';
}
});
});