Saturday, January 20, 2018

Getting the light show started

Anyone who's followed me for very long knows that one of my big concerns is accessibility. Today I'm going to tell you exactly how you can violate accessibility rules and possibly trigger epileptic seizures in a portion of the population at the same time. I'm sharing this information because this should NEVER happen, and yet it does (this means I've seen it in the wild).

Let's take, as a hypothetical case, a design where when you hover over something you want to show a callout - a very common design. In browser-based terms, there are two ways you can handle hovering interactions - using the CSS pseudoclass (which you should only use with focus and active, by the way) and with JavaScript using the mouse events.

Using CSS it's relatively straightforward - add the two elements (we'll call them the target and callout) to the DOM as siblings, placing the callout after the target, and hide the callout by default and use a sibling selector with the hover pseudoclass to show the callout. By the way, this shifts the show/hide functionality to the GPU and keeps it out of the script stack - which means it'll provide better performance. This is the best way to implement this little trick.

Unfortunately, more than a few engineers in our industry don't like CSS and prefer to do everything in JavaScript. In this case, we would still have the two DOM nodes and we would add event listeners to show the callout when we mouseover the target and hide the callout when we mouseover it. This method keeps the show/hide in the script stack, meaning it'll likely get janky at some point.

Here's where the bad stuff comes into play.

If the target and callout overlap (they way they sometimes will - especially in a responsive design), they will 'flash' as the 'hover' events fire on each element. This will be bad, very bad, especially if there is significant contrast between the colors, unless you're trying to simulate a nightclub or light show and don't really care about the likelihood of triggering photosensitive epilepsy.

If you want to see how this works, run the code samples below in a browser, but be warned, the target/callout flashes more than three times per second.

CSS
<!DOCTYPE html> <html> <body> <style type="text/css"> #callout, #target {   border: 1px solid black;   height: 10rem;   left: 20px;   position: absolute;   top: 100px;   width: 90%; } #callout {   z-index: -1; } #target:hover + #callout {   z-index: 10; } </style> <div id="parent">   <p id="target" style="background-color: #0f0;">target</p>   <p id="callout" style="background-color: #f00;">callout</p> </div> </body> </html>


JavaScript
<!DOCTYPE html> <html> <body> <style type="text/css"> #callout, #target {   border: 1px solid black;   height: 10rem;   left: 20px;   position: absolute;   top: 100px;   width: 90%; } #callout {   z-index: -1; } </style>
<div id="parent"> <p id="target" style="background-color: #0f0;">target</p> <p id="callout" style="background-color: #f00;">callout</p> </div>
<script> var callout = document.getElementById('callout'); var target = document.getElementById('target'); target.addEventListener('mouseover', function() {     callout.style.zIndex = '10'; }); callout.addEventListener('mouseover', function() {     callout.style.zIndex = '-1'; }); </script> </body> </html>


Notes

  1. A callout is those little bubbles that have a triangular-ish thing connecting the bubble to an item as a visual context clue.
  2. The events mouseenter, mouseleave, mousemove, mouseout,  and mouseover are the events applicable to a 'hover'

No comments:

Post a Comment