There are some good accessible components and markup patterns here:
I highly recommend digging into how this stuff works as well. Modals are very complex (e.g. they need focus traps/gaurds, keyboard navigation, etc.), so it makes sense to reach for a pre-built thing, but other components are fairly simple.
This might seem like Tailwind heresy (it’s not), but I think that when it comes to accessibility it’s best to break out of the utility class philosophy and write styles that are bound to the state of the component (i.e. aria-
attributes). I say that as someone who has heavily used and promoted Tailwind, and who has used utility CSS approaches much longer than Tailwind has been around (see Tachyons, Basscss).
Typically, Tailwind users (myself included) write code like this:
/* This is simplified and partial: don’t use this snippet as a menu tutorial. */
menuButton.addEventListener('click', event => {
menuButton.setAttribute('aria-expanded', true);
menuButton.classList.add('some-class');
menu.removeAttribute('hidden');
});
This might seem perfectly fine and logical to do, but this can be dangerous. Why? The styles are changing only as an effect of the interaction and not as an effect of the component’s state. Let me explain that a bit more. When an interaction happens in this scenario, there are two independent effects: the element’s accessible state is updated and the element’s visual state is updated. Yes, these might happen synchronously, but that means every time you change one you have to change the other or else it breaks. You, your stakeholders, and most of your users probably aren’t using a screen reader so it’ll be easy to think, “this looks right so it must be accessible.” Instead, change styles as an effect of state. Let the styling break as an effect of broken accessibility.
<!-- You can still use Tailwind for your base styles. -->
<button data-my-menu-button aria-expanded="false" class="bg-blue-9000 text-white p-2 rounded ...">
Menu
</button>
[data-my-menu-button][aria-expanded="false"] {
/* styles related to this state. */
}
[data-my-menu-button][aria-expanded="true"] {
/* styles related to this state. */
}
This way if some dev comes along and does something that breaks the accessibility, you will know because visually it won’t look correct as well. This way your great intentions of making these websites accessible aren’t set up for failure as they are maintained by people with or without the same intentions.
Adrian Roselli has a great article about this if you are interested or if I didn’t explain this well. I’m not saying don’t use Tailwind. Rather, don’t limit yourself to just using the utility class names and maybe leverage @apply
. Remember, Tailwind is utility first and not always. Keep that in mind and I think you’ll do great!