Menu

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.


Thanks for reading

Let's talk about this post. I love making friends and chatting.

Subscribe to my RSS feed to stay up to date. What is RSS?