70

Several SE sites, including Science Fiction & Fantasy SE, Movies & TV SE and Literature SE, support the tag >! for spoilers. This markup hides the text following the spoiler tag unless the user hovers the mouse pointer over the spoiler area. Unfortunately, if you browse websites by keyboard (e.g. due to mobility impairment), the content of the spoiler cannot be made visible.

The spoilers are not part of the focusable elements on the page, so you cannot reach them by using the TAB key. (The TAB key allows you to reach links, form elements and a few other focusable elements.)

As far as I can tell, two changes are required:

  1. Making the spoiler focusable. This can be done by adding the attribute tabindex="0" to each blockquote element that is used for spoilers (<blockquote class="spoiler" tabindex="0">).
  2. Making the text appear on focus, not just on hover. The site's CSS code contains the selector .spoiler:not(:hover), but I have not yet figured out what CSS code needs to be added to make it work for focus.

Note: As pointed out in Spoiler syntax should generate <div>, not <blockquote>, <blockquote> is not the best HTML element for spoilers.

3
  • 7
    3. Do away with intentionally making posts illegible to begin with. ;-) Commented Oct 11, 2017 at 15:53
  • 5
    @ChristianRau You have a point, but I don't see that feature go away in the near future. While it exists, it should at least be made accessible. Commented Oct 11, 2017 at 16:16
  • w3.org/TR/selectors-3/#the-user-action-pseudo-classes-hover-act about the CSS. The selector is indeed called ":focus", as you would guess. Commented May 11, 2020 at 10:17

3 Answers 3

21

I agree with this proposal. Spoilers should be accessible. Below, I've made a proof of concept for an alternative interaction model for a keyboard-accessible spoiler by extending the current spoiler behaviour. Scroll to the bottom and hit "Run code snippet" to run the proof of concept.

Here's a gif preview (inside a spoiler because it could be distracting, or visit this link if you're a keyboard-only user):

toggling the new spoiler quote a few times

Here's how it works:

  • Most users will just see a totally ordinary blockquote with no special behaviour.
  • Someone who tabs into the spoiler quote will find their keyboard focus on a button that says "show this spoiler". They can use this to toggle the spoiler visibility.
  • When they hit that button, their focus is transferred to a sibling "hide this spoiler" button. The purpose of this switch is to enable screen reader users to recognise they have changed something, and that the button they are on can now hide the spoiler. (The focus switch prompts the screen reader to read out the newly focused button's text.) When they hit that button, focus is transferred back to "show this spoiler".
  • The "show this spoiler" button is only visible as long as it retains keyboard focus. If you tab away from it, the button vanishes.
  • The "hide this spoiler" button is visible for as long as the spoiler is revealed, until it's activated to hide the spoiler again.

Something additional I've done is add visibility: hidden; beside opacity: 0; for hidden spoiler quotes, and added visibility to their CSS transition list (that really works). This does two things:

  • Some readers will read out hidden spoilers anyway because opacity:0 is not a cue to them to ignore some text. This change prevents those screen readers from doing this, because they pay attention to visibility:hidden instead. This is a good practice for accessibility, recommended by Medium's development team.
  • This prevents people from tabbing into links hidden inside the spoiler quote. That's something that can happen! It really shouldn't, they're hidden and should be kept out of tab flow to avoid things being confusing.

var classShow = '.js-spoiler-button--show';
var classHide = '.js-spoiler-button--hide';
var classRevealed = 'revealed';

$(classShow).on('click', function(e) {
  $(e.target).parent().addClass(classRevealed);
  $(e.target).next(classHide).focus();
});

$(classHide).on('click', function(e) {
  $(e.target).parent().removeClass(classRevealed);
  $(e.target).prev(classShow).focus();
});
.sr-only,
.spoiler:not(.revealed) .spoiler-button:not(:focus) {
  /* from https://stackoverflow.com/a/19758620 */
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  border: 0;
}

.spoiler-button {
  /* Standard Stack Exchange button appearance */
  color: #39739d;
  background-color: #E1ECF4;
  border-color: #96bdd9;
  box-shadow: inset 0 1px 0 #f4f8fb;
}

.spoiler .spoiler-button--hide {
  display: none;
}

.spoiler.revealed .spoiler-button--hide {
  display: block;
}

.spoiler.revealed .spoiler-button--show {
  display: none;
}

/* Modified Stack Exchange CSS */

/* Old selector:
.spoiler:not(:hover) { */
.spoiler:not(:hover):not(.revealed) {
  color: rgba(255,248,220,0);
}

/* Old selector:
.spoiler:not(:hover)>* { */
.spoiler:not(:hover):not(.revealed)>*:not(.spoiler-button) {
  visibility: hidden; /* new */
  opacity: 0;
}

.spoiler>* {
  /* visibility transition is new */
  transition: opacity .5s ease-in, visibility .5s linear;
}


/* Stock Stack Exchange CSS, not modified */

body {
  font-family: sans-serif;
}

blockquote {
  quotes: none
}

blockquote:before,
blockquote:after {
  content: "";
  content: none
}

blockquote {
  margin-bottom: 10px;
  padding: 10px;
  background-color: #FFF8DC;
  border-left: 2px solid #ffeb8e
}

blockquote *:last-child {
  margin-bottom: 0
}

.spoiler {
  transition: color .5s ease-in
}
<button type="button">This button does nothing at all. Click it to put focus here, then press tab.</button>

<blockquote class="spoiler">
  <button class="spoiler-button spoiler-button--show js-spoiler-button--show">Show this spoiler</button>
  <button class="spoiler-button spoiler-button--hide js-spoiler-button--hide">Hide this spoiler</button>

  <!-- Spoiler content goes here as usual. -->
  <p>This is a spoiler! <a href="https://example.com/inside-accessible-spoiler">Test link inside the spoiler.</a><p>
</blockquote>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Old spoiler for comparison:

This is a spoiler! Test link inside the spoiler.

You can tab to the link inside this spoiler even while it's hidden. Keep an eye out for the tooltip in the corner of your browser window showing https://example.com/inside-old-spoiler. Note that in the snippet above, the URL is https://example.com/inside-accessible-spoiler and that link can't be tabbed to while the spoiler is hidden.

0
17

Implement spoilers with the details and summary elements

The current implementation for spoilers relies on the blockquote element, which is semantically not appropriate for this type of content, and pointer interaction. Making this implementation accessible seems to require extra HTML code and JavaScript event listeners.

There seems to be a simpler way, using native HTML elements that are well supported and keyboard accessible in mainstream browsers, namely the details and summary elements. The summary creates a button-like label to toggle the content of the details element open or closed.

I have tested these elements in a range of browsers and they appear to be well supported in mainstream browsers. (The now outdated Internet Explorer 11 and the very rare Otter browser don't really support these elements.) For an overview of test results, see my webpage HTML5 Summary & Details: Browser Support. Other details about accessibility support can be viewed at a11ySupport.io.

Update 06.11.2025 below.

Making the current implementation more accessible

The most recent changes resulted in the following type of code (in the state before the spoiler is revealed; example from Literature SE):

<blockquote class="spoiler" data-spoiler="Reveal spoiler" 
  tabindex="0" 
  aria-label="Reveal spoiler">
  <p><!--hidden content-->
  </p>
</blockquote>

When keyboard focus lands on the blockquote, NVDA (with Firefox) announces the element as follows:

Reveal spoiler blockquote reveal spoiler

Screen reader users know how to work with interactive interactive elements such as links (press Enter), buttons (press Enter or space bar), check boxes, radio buttons, menus etc, but blockquote is not an interactive element, so screen reader users need to guess the expected keyboard interaction.

If it's too difficult to get rid of the blockquote element (see kristinalustig's answer), there should at least be a button:

<blockquote class="spoiler">
  <button>Reveal spoiler</button>
  <!-- button does not need tabindex and aria-label;
    event listeners would be on the button, not the blockquote
  -->
  <p><!--hidden content-->
  </p>
</blockquote>
1
9

As a part of the community asks sprint last month, we implemented a basic fix for spoiler accessibility.

Spoilers can now be tabbed to, and both enter and space work to reveal the spoiler. We also added an aria-label to the element.

I am not a keyboard-only user or a screenreader user, although I tested these changes with both modes of interaction. I would be happy to hear any feedback on this updated implementation from someone who is more comfortable with these modes.

I know that blockquote still is not the ideal element type for a spoiler, but I had to find a balance, and making more fundamental changes to how spoilers work was out of scope.

We hope this helps and we appreciate your patience!

2
  • Thanks for the effort, but see the update to my answer. Commented Nov 6, 2025 at 17:18
  • 1
    I'm going to look into potential further improvements as soon as I get a bit of spare time. I truly appreciate your taking the time to test it and provide more feedback! Commented Nov 6, 2025 at 22:09

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.