This blog focuses on best-practice guidance on implementing accessibility in the menu components by using Headless UI from the creator of tailwind Labs using TailwindCSS.
Why accessibility is important?
By 2023, it is expected that the Web will offer accessible information and interaction to a large number of people. Web technologies can help to remove barriers to print, audio, and visual media, ensuring equal access and opportunity for people with disabilities. In addition to improving accessibility, this can also lead to better search engine rankings for your product by prioritizing usability. As a result, focusing on accessibility can help to enhance the user experience and overall success of your product.
Our ingredients,
- Creators of Tailwind CSS, Tailwind UI, and Refactoring UI.
- Rapidly build modern websites without ever leaving your HTML through different utility classes.
HeadlessUI is a set of completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS.
Let's build Menu Components from a headless UI
Menus are used for navigation and to provide functionality which are critical parts of web page operability.
Using For ReactJs
# npm
npm install @headlessui/react
# Yarn
yarn add @headlessui/react
Basic Example
Menu Buttons are built using the Menu component with Menu.Button
, Menu.Items
, and Menu.Item
components.
The Menu.Button
will automatically open/close the Menu.Items
when clicked, and when the menu is open, the list of items receives focus and is automatically navigable via the keyboard.
import { Menu } from "@headlessui/react";
function MyDropdown() {
return (
<Menu>
{/* Render no wrapper, instead pass in a button manually. */}
<Menu.Button as={React.Fragment}>
<button>More</button>
</Menu.Button>
<Menu.Items>
<Menu.Item>
{({ active }) => (
<a
className={`${active && "bg-blue-500"}`}
href="/account-settings"
>
Account settings
</a>
)}
</Menu.Item>
{/* ... */}
</Menu.Items>
</Menu>
);
}
The above code is an unstyled component using headless UI.
What is interesting here is if we see the generated HTML version below we can see accessibility labels and by using tailwindcss there are inline classes to make it a beautiful UI.
<div class="w-56 text-right fixed top-16">
<div class="relative inline-block text-left">
<div>
<button class="inline-flex justify-center w-full px-4 py-2 text-sm font-medium text-white bg-black rounded-md bg-opacity-20 hover:bg-opacity-30 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75" id="headlessui-menu-button-1" type="button" aria-haspopup="true">
Options <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5 ml-2 -mr-1 text-violet-200 hover:text-violet-100" aria-hidden="true"><path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
</button>
</div>
</div>
</div>
We can see in the button
tag there is
type="button" aria-haspopup="true
Also, in the dropdown content
<div class="absolute right-0 w-56 mt-2 origin-top-right bg-white divide-y divide-gray-100 rounded-md shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
aria-labelledby="headlessui-menu-button-1" id="headlessui-menu-items-10" role="menu" tabindex="0">
<div class="px-1 py-1 " role="none">
<button class="text-gray-900 group flex rounded-md items-center w-full px-2 py-2 text-sm" id="headlessui-menu-item-11" role="menuitem"
tabindex="-1">
<svg class="w-5 h-5 mr-2" aria-hidden="true" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 13V16H7L16 7L13 4L4 13Z" fill="#EDE9FE" stroke="#A78BFA" stroke-width="2"></path>
</svg>
Edit
</button>
<button class="text-gray-900 group flex rounded-md items-center w-full px-2 py-2 text-sm" id="headlessui-menu-item-12" role="menuitem"
tabindex="-1">
<svg class="w-5 h-5 mr-2" aria-hidden="true" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 4H12V12H4V4Z" fill="#EDE9FE" stroke="#A78BFA" stroke-width="2"></path>
<path d="M8 8H16V16H8V8Z" fill="#EDE9FE" stroke="#A78BFA" stroke-width="2"></path>
</svg>
Duplicate
</button>
</div>
</div>
Breaking down the code:
In the dropdown content block, we have,
<div class="..." aria-labelledby="headlessui-menu-button-1" id="headlessui-menu-items-10" role="menu" tabindex="0">...</div>
And its children div structure has
<div class="..." role="none">
<button class="..." id="headlessui-menu-item-11" role="menuitem" tabindex="-1">...</button>
</div>
Final Accessibility notes
1. Focus management
Clicking the Menu.Button
toggles the menu and focuses on the Menu.Items
component. Focus is trapped within the open menu until Escape is pressed or the user clicks outside the menu. Closing the menu returns the focus to the Menu.Button
.
2. Mouse interaction
Clicking a Menu.Button
toggles the menu. Clicking anywhere outside of an open menu will close that menu.
3. Keyboard interaction
4. Other
All relevant ARIA attributes are automatically managed. For a full reference on all accessibility features implemented in Menu, see the ARIA spec on Menu Buttons.
Reference
Please check out https://headlessui.com/react/menu for more details as all the information and the example of the code are explained better in the docs.
Conclusion
๐๐ Definitely, headless UI is my first choice for any React or VueJs project. I suggest you give a try on your project and enjoy it!
I have been writing TailwindCSS classes from the beginning of their development. And I am amazed at how this framework has evolved. Many Thanks to Adam Wathan and Steve Schoger for developing the awesome products. Make sure you guys follow them.
Feel free to share your thoughts and opinions and leave me a comment if you have any problems or questions.
Till then, Keep on Hacking, Cheers