Tomi Chen

Improving Mobile and Keyboard Usability in TwemojiExplorer

By Tomi Chen June 9, 2021

I recently built TwemojiExplorer, a website that gives convenient access to Twitter’s Twemoji emoji set. Since the site is primarily a tool for my personal use, I did not put too much thought into keyboard or mobile usability during the initial development process. Now that I have a bit of time, I decided to tweak a couple of small things to make the experience better. If you’re interested in how I implemented and optimized the search functionality, you can read about it here.

Most of these improvements are located in this GitHub commit if you want to follow along. You can also see a version of the site before I made these changes and see the difference!

Tabbing through elements 🔗

To access the actions for each emoji (copying SVG/codepoint), the user needs to use their mouse to hover over the emoji card. I’m using Javascript for this effect since in this case, I don’t want the actions to be displayed when Javascript is disabled. Since the only action a user can perform without Javascript is navigate to one of my favorite websites, Emojipedia, I decided to disable the action buttons when Javascript is disabled and put the Emojipedia link on the emoji image instead. Note: I would not recommend using Javascript for hover events in general. This is a special case and I have intentionally implemented it this way. Generally, you’ll want to use CSS’s :hover to keep hover events for users with Javascript disabled.

Focus styles 🔗

Because the card itself is already a <button>, it’s tabbable by default. Keyboard users can also activate the button with the on:click, and can also tab through the actions when displayed. The first thing we can do is improve the focus state styles. I’m disabling the default focus styles with:

/* 
 * The Emojipedia link should be styled as a button
 * so it has a `button` class that I'm targeting.
 */
button,
.button {
  outline: 2px solid transparent;
  outline-offset: 2px;
}

Using a transparent outline instead of removing it altogether is important for Windows high contrast mode users. It’s also how Tailwind’s outline-none utility works.

Now that we’ve removed the default focus styling, we need to add our own:

button:focus-visible,
.button:focus-visible {
  box-shadow: 0 0 0 4px hsl(213, 89%, 47%);
}

Let’s break this down:

  1. We’re using the focus-visible pseudo-class to only apply styles when the element is focused and the browser determines that there should be a visual focus indicator. When the element is tabbed into, our styles will be applied, but if it’s clicked (which still triggers the :focus class), they won’t be.
  2. We’re using a box-shadow to apply focus styles instead of outline or border since they will follow the border-radius of our buttons.
  3. I also updated the color in a later commit to improve contrast.

Tab order 🔗

If you take a look at the site before I made these changes and try to tab through the elements, you’ll notice two things:

  1. The first tabbed element is a <div> that wraps the emoji sections, and
  2. the link around the emoji image that’s meant for users with disabled Javascript is also tabbable.

I’m not entirely sure why the <div> is tabbable or if there’s a good reason for it to be, but I don’t think that should be happening, so I disabled it by adding the tabindex="-1" attribute. This takes the element out of the tab order, making it unreachable using the keyboard.

We could do the same thing for the link around the image, but I think users with Javascript disabled might still want to tab through those links. We can meet this requirement by setting the attribute using Javascript. We’re also going to use Javascript to intercept clicks and prevent the browser from navigating to the page.

<script>
  import { onMount } from 'svelte';

  let noJSAnchor;
  onMount(() => {
    noJSAnchor.tabIndex = -1;
  });
</script>

<a bind:this={noJSAnchor} on:click={(e) => {e.preventDefault()}} ><img /></a>

If Javascript is disabled, none of this will run, allowing users to navigate and tab normally.

Note: I’m using Firefox on a Mac, which has some weird default tab behaviors that prevent me from tabbing through links. You can change this by going to System Preferences > Keyboard > Shortcuts and checking the “Use keyboard navigation to move focus between controls” checkbox at the bottom. You may also need to restart Firefox for the changes to take effect.

Mobile Actions 🔗

If you try to access the old version on a mobile device, you’ll notice that tapping on the cards can also trigger the button that pops up, even if you’re only tapping once. This seems to be because phones will also trigger the mouseover and mouseleave events, even if there’s no mouse. When you tap on the card, your phone will trigger the mouseover event AND the click event in succession, which will open up the actions menu and also the buttons inside. This isn’t what we want at all!

Ideally, one tap would open the options menu, and another tap would trigger the appropriate button. Luckily, we can detect, using media queries, if a device supports hovering as well as whether there is “fine” or “coarse” control over the pointer. For more information, see this CSS-Tricks article.

We can access media queries in Javascript with [window.matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia), which returns a MediaQueryList that has a matches property we can access.

<script>
  function showActions() {
    if (window.matchMedia('(hover: hover) and (pointer: fine)').matches) {
      currentActive.set(emoji.slug)
    }
  }
  function hideActions() {
    currentActive.set(null)
  }
  function click() {
    currentActive.set(emoji.slug)
  }
</script>
<button
  on:mouseover="{showActions}"
  on:mouseleave="{hideActions}"
  on:click="{click}"
>
  <!-- ... -->
</button>

Here, we’re checking if the device’s primary input supports hovering and has fine control before showing the options menu. We’re not checking this on mouseleave since hiding the menu when the user taps away seems to be fine.

Tip: If you use Firefox and Android, you can use mobile debugging to play around with these events and media queries. There are also similar features for Safari on iOS and Chrome on Android.

Conclusion 🔗

With these few tweaks, we can improve the user experience for people on mobile devices or people who use keyboards. While there are still improvements that can be made (using arrow keys to navigate the grid?), this seems like a decent place to be. There is a responsibility on people who build websites to ensure they are accessible to everyone, and I hope this article can help you think about the decisions you make while building websites.

If you have any questions or comments, please reach out! I’d love to hear about things I’ve overlooked or any other feedback you may have.

PREV POST NEXT POST