通过 headless UI tailwindcss 实现完全可访问的菜单组件

2025-06-09

通过 headless UI tailwindcss 实现完全可访问的菜单组件

本博客重点介绍使用 tailwindcss 的 tailwind Labs 创建者提供的使用Headless UI在菜单组件中实现可访问性的最佳实践指导。

为什么可访问性很重要?

正如2021年一样,网络应该让更多人能够访问信息并进行互动。此外,通过网络技术,残障人士在阅读印刷、音频和视觉媒体方面遇到的障碍可以更容易克服,因此也应该为他们提供平等的访问渠道和机会。此外,这有助于在谷歌搜索结果中呈现最佳效果,并根据可用性进行排名,从而提升您的用户体验和产品。

我们将使用,

无头用户界面

  • Tailwind CSS、Tailwind UI 和 Refactoring UI 的创建者。

Tailwindcss

  • 通过不同的实用程序类,无需离开 HTML 即可快速构建现代网站。

HeadlessUI Dev 是一组完全无样式、完全可访问的 UI 组件,旨在与 Tailwind CSS 完美集成。

无头用户界面


让我们从无头 UI 构建菜单组件

菜单用于导航并提供功能,是网页可操作性的关键部分。

无头 UI 菜单


用于 ReactJs



# npm
npm install @headlessui/react

# Yarn
yarn add @headlessui/react


Enter fullscreen mode Exit fullscreen mode

基本示例

菜单按钮是使用菜单Menu.Button、、Menu.ItemsMenu.Item组件构建的。

单击 Menu.Button 时将自动打开/关闭 Menu.Items,当菜单打开时,项目列表将获得焦点并可通过键盘自动导航。



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>
  );
}


Enter fullscreen mode Exit fullscreen mode

上述代码是使用 headlessUI 实现的无样式组件。

有趣的是,如果我们看到下面生成的 HTML 版本,我们可以看到可访问性标签,并且通过使用 tailwindcss,可以使用内联类使其成为漂亮的 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>


Enter fullscreen mode Exit fullscreen mode

我们可以在button标签中看到



type="button" aria-haspopup="true


Enter fullscreen mode Exit fullscreen mode

此外,在下拉内容中



<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>


Enter fullscreen mode Exit fullscreen mode

分解代码:

在下拉内容块中,我们有



<div class="..." aria-labelledby="headlessui-menu-button-1" id="headlessui-menu-items-10" role="menu" tabindex="0">...</div>


Enter fullscreen mode Exit fullscreen mode

并且它的子 div 结构具有



<div class="..." role="none">
        <button class="..." id="headlessui-menu-item-11" role="menuitem" tabindex="-1">...</button>
</div>



Enter fullscreen mode Exit fullscreen mode

最终可访问性说明

1. 焦点管理:
点击Menu.Button可切换菜单并聚焦于Menu.Items组件。焦点会一直停留在打开的菜单中,直到按下 Esc 键或用户点击菜单外部。关闭菜单后,焦点将返回到Menu.Button

2. 鼠标交互
:点击Menu.Button可切换菜单。点击已打开菜单之外的任意位置将关闭该菜单。

3. 键盘交互
键盘交互

4. 其他
所有相关的 ARIA 属性均自动管理。
有关菜单中实现的所有辅助功能的完整参考,请参阅菜单按钮的 ARIA 规范


参考

请查看https://headlessui.dev/react/menu了解更多详细信息,因为所有信息和代码示例在文档中都有更好的解释。


结论

👏👏 毫无疑问,https://headlessui.dev/是我开发 React 或 VueJs 项目的首选。我建议你在自己的项目中尝试一下,享受它带来的乐趣!

我从 tailwindcss 开发伊始就一直在编写它的类。这个框架的演变速度让我惊叹不已。非常感谢Adam WathanSteve Schoger开发了如此优秀的产品。请务必关注他们。

如果您有任何问题或疑问,请随时分享您的想法和意见并给我留言。

到那时,
继续黑客攻击,干杯

鏂囩珷鏉ユ簮锛�https://dev.to/integridsolutions/filled-accessible-menu-components-by-headless-ui-tailwindcss-4ml7
PREV
第一次参加编程面试?常见问题及解答。
NEXT
2020 年前端路线图