Creating an HTML “spoilers” element with no JS
It's strangely difficult to make a “spoilers” element without JS, and even harder to do it with support for all mobile operating systems and screen readers.
A working example
You can press the hidden text to reveal it or navigate to it using the keyboard.
Why did the chicken cross the road? To get to the other side.
Why did the chicken cross the road?
<span tabindex="0" class="spoiler">To get to the other side.</span>
Why the details tag isn’t ideal
The HTML paragraph tag only allows phrasing content as its children. Notably this includes input and button but not details. The details element otherwise behaves exactly like how I want my spoilers element to behave:
- No JS
- Screen readers hide content until activated
- Doesn’t show information on hover
- Works in every browser on every OS
- Lets me customize the prompt to reveal the hidden contents (e.g. “Reveal spoilers”)
The problem is that because <details>
is not valid inside <p>
, the HTML
parser will completely rewrite your input in order to put the <details>
outside the paragraph, and orphan its following text:
<main>
<p>
hello
<details>secret</details>
world
</p>
</main>
Will be parsed as if you wrote:
<main>
<p>hello</p>
<details>secret</details>
world
<p></p>
</main>
This wouldn’t be a huge issue, but generally Markdown puts all of your text content inside paragraph tags. Given that most of my blog uses Markdown, this is a non-starter.
Handling clicks without JS
The accessibility minded among us love to complain about improper HTML like
<span onclick="...">
, but it has its uses! What I want is a button that
reveals content when clicked, so a <button>
would be correct… but I don’t
want to use JS to handle this if I don’t have to.
If you add
tabindex,
any element can be focused—not just normally interactive elements. These
elements can be focused via the keyboard or by clicking/tapping them. Then we
can use the :focus
selector to reveal the text.
What about accessibility?
I don’t think this approach is quite as accessible as using <details>
or a
custom JS solution. Testing with VoiceOver on macOS revealed the text when
navigating through the document, but unfocus it after leaving the content. It
also doesn’t give the user any indication that they’re about to see “spoilers”,
or give them an option to skip them. So it’s more or less just “text” to a
VoiceOver. I think this may be more common knowledge recently, but many UI
patterns are simply not fully accessible on the web without using JS. In my
case, I’m calling “good enough” for my blog.