ドロップダウンメニュー

メニューは、キーボードナビゲーションを強力にサポートした、カスタムでアクセシブルなドロップダウンコンポーネントを簡単に構築できる方法を提供します。

インストール

開始するには、npm を介して Headless UI をインストールします。

npm install @headlessui/react

基本例

メニューは、MenuMenuButtonMenuItems、およびMenuItemコンポーネントを使用して構築されます。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'

function Example() {
  return (
    <Menu>
      <MenuButton>My account</MenuButton>
      <MenuItems anchor="bottom">
        <MenuItem>
          <a className="block data-[focus]:bg-blue-100" href="/settings">
            Settings
          </a>
        </MenuItem>
        <MenuItem>
          <a className="block data-[focus]:bg-blue-100" href="/support">
            Support
          </a>
        </MenuItem>
        <MenuItem>
          <a className="block data-[focus]:bg-blue-100" href="/license">
            License
          </a>
        </MenuItem>
      </MenuItems>
    </Menu>
  )
}

MenuButtonは、クリックされたときにMenuItemsを自動的に開閉し、メニューが開かれると、アイテムのリストがフォーカスを受け取り、キーボードで操作できるようになります。

スタイリング

Headless UI は、キーボードで現在フォーカスされているメニュー項目、ポップオーバーが開いているか閉じているか、現在選択されているリストボックスオプションなど、各コンポーネントに関する多くの状態を追跡します。

しかし、コンポーネントはヘッドレスで、すぐに使える状態では完全にスタイルが適用されていないため、自分で各状態のスタイルを提供するまで、UIでこの情報を見ることはできません。

データ属性の使用

Headless UI コンポーネントのさまざまな状態をスタイル設定する最も簡単な方法は、各コンポーネントが公開するdata-*属性を使用することです。

たとえば、MenuButtonコンポーネントはdata-active属性を公開しており、これはメニューが現在開いているかどうかを示し、MenuItemコンポーネントはdata-focus属性を公開しており、これはマウスまたはキーボードでメニュー項目が現在フォーカスされているかどうかを示します。

<!-- Rendered `MenuButton`, `MenuItems`, and `MenuItem` -->
<button data-active>Options</button>
<div data-open>
  <a href="/settings">Settings</a>
  <a href="/support" data-focus>Support</a>
  <a href="/license">License</a>
</div>

これらのデータ属性の存在に基づいて条件付きでスタイルを適用するには、CSS 属性セレクターを使用します。Tailwind CSS を使用している場合、データ属性修飾子を使用すると簡単になります。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'

const links = [
  { href: '/settings', label: 'Settings' },
  { href: '/support', label: 'Support' },
  { href: '/license', label: 'License' },
]

function Example() {
  return (
    <Menu>
<MenuButton className="data-[active]:bg-blue-200">My account</MenuButton>
<MenuItems anchor="bottom"> {links.map((link) => (
<MenuItem key={link.href} className="block data-[focus]:bg-blue-100">
<a href={link.href}>{link.label}</a> </MenuItem> ))} </MenuItems> </Menu> ) }

利用可能なすべてのデータ属性の一覧については、コンポーネントAPIを参照してください。

レンダープロップスの使用

各コンポーネントは、レンダープロップスを介して現在の状態に関する情報を公開しており、これを使用して条件付きで異なるスタイルを適用したり、異なるコンテンツをレンダリングしたりできます。

たとえば、MenuButtonコンポーネントはactive状態を公開しており、これはメニューが現在開いているかどうかを示し、MenuItemコンポーネントはfocus状態を公開しており、これはマウスまたはキーボードでメニュー項目が現在フォーカスされているかどうかを示します。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
import clsx from 'clsx'
import { Fragment } from 'react'

const links = [
  { href: '/settings', label: 'Settings' },
  { href: '/support', label: 'Support' },
  { href: '/license', label: 'License' },
]

function Example() {
  return (
    <Menu>
<MenuButton as={Fragment}>
{({ active }) => <button className={clsx(active && 'bg-blue-200')}>My account</button>}
</MenuButton>
<MenuItems anchor="bottom"> {links.map((link) => (
<MenuItem key={link.href} as={Fragment}>
{({ focus }) => (
<a className={clsx('block', focus && 'bg-blue-100')} href={link.href}>
{link.label}
</a>
)}
</MenuItem>
))} </MenuItems> </Menu> ) }

利用可能なすべてのレンダープロップスのリストについては、コンポーネントAPIを参照してください。

ボタンとの使用

リンクに加えて、MenuItemでボタンを使用することもできます。これは、ダイアログを開いたり、フォームを送信したりするなどのアクションをトリガーする場合に便利です。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'

function Example() {
  function showSettingsDialog() {
    alert('Open settings dialog!')
  }

  return (
    <Menu>
      <MenuButton>My account</MenuButton>
      <MenuItems anchor="bottom">
<MenuItem>
<button onClick={showSettingsDialog} className="block w-full text-left data-[focus]:bg-blue-100">
Settings
</button>
</MenuItem>
<MenuItem> <a className="block data-[focus]:bg-blue-100" href="/support"> Support </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/license"> License </a> </MenuItem>
<form action="/logout" method="post">
<MenuItem>
<button type="submit" className="block w-full text-left data-[focus]:bg-blue-100">
Sign out
</button>
</MenuItem>
</form>
</MenuItems> </Menu> ) }

アイテムの無効化

disabledプロップを使用してMenuItemを無効にし、選択できないようにします。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'

function Example() {
  return (
    <Menu>
      <MenuButton>My account</MenuButton>
      <MenuItems anchor="bottom">
        <MenuItem>
          <a className="block data-[focus]:bg-blue-100" href="/settings">
            Settings
          </a>
        </MenuItem>
        <MenuItem>
          <a className="block data-[focus]:bg-blue-100" href="/support">
            Support
          </a>
        </MenuItem>
        <MenuItem>
          <a className="block data-[focus]:bg-blue-100" href="/license">
            License
          </a>
        </MenuItem>
<MenuItem disabled>
<a className="block data-[disabled]:opacity-50" href="/invite-a-friend">
Invite a friend (coming soon!)
</a>
</MenuItem>
</MenuItems> </Menu> ) }

アイテムの区切り

MenuSeparatorコンポーネントを使用して、メニュー内のアイテム間に視覚的な区切りを追加します。

import { Menu, MenuButton, MenuItem, MenuItems, MenuSeparator } from '@headlessui/react'

function Example() {
  return (
    <Menu>
      <MenuButton>My account</MenuButton>
      <MenuItems anchor="bottom">
        <MenuItem>
          <a className="block data-[focus]:bg-blue-100" href="/settings">
            Settings
          </a>
        </MenuItem>
<MenuSeparator className="my-1 h-px bg-black" />
<MenuItem> <a className="block data-[focus]:bg-blue-100" href="/support"> Support </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/license"> License </a> </MenuItem> </MenuItems> </Menu> ) }

アイテムのグループ化

MenuSectionMenuHeading、およびMenuSeparatorコンポーネントを使用して、ラベル付きのセクションにアイテムをグループ化します。

import { Menu, MenuButton, MenuHeading, MenuItem, MenuItems, MenuSection, MenuSeparator } from '@headlessui/react'

function Example() {
  return (
    <Menu>
      <MenuButton>My account</MenuButton>
      <MenuItems anchor="bottom">
<MenuSection>
<MenuHeading className="text-sm opacity-50">Settings</MenuHeading>
<MenuItem> <a className="block data-[focus]:bg-blue-100" href="/profile"> My profile </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/notifications"> Notifications </a> </MenuItem>
</MenuSection>
<MenuSeparator className="my-1 h-px bg-black" />
<MenuSection>
<MenuHeading className="text-sm opacity-50">Support</MenuHeading>
<MenuItem> <a className="block data-[focus]:bg-blue-100" href="/support"> Documentation </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/license"> License </a> </MenuItem>
</MenuSection>
</MenuItems> </Menu> ) }

ドロップダウン幅の設定

MenuItemsドロップダウンには、デフォルトでは幅が設定されていませんが、CSSを使用して追加できます。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'

function Example() {
  return (
    <Menu>
      <MenuButton>My account</MenuButton>
<MenuItems anchor="bottom" className="w-52">
<MenuItem> <a className="block data-[focus]:bg-blue-100" href="/settings"> Settings </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/support"> Support </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/license"> License </a> </MenuItem> </MenuItems> </Menu> ) }

ドロップダウンの幅をMenuButtonの幅と一致させたい場合は、MenuItems要素で公開されている--button-width CSS 変数を使用します。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'

function Example() {
  return (
    <Menu>
      <MenuButton>My account</MenuButton>
<MenuItems anchor="bottom" className="w-[var(--button-width)]">
<MenuItem> <a className="block data-[focus]:bg-blue-100" href="/settings"> Settings </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/support"> Support </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/license"> License </a> </MenuItem> </MenuItems> </Menu> ) }

ドロップダウンの位置決め

MenuItemsanchorプロップを追加して、MenuButtonを基準にドロップダウンを自動的に配置します。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'

function Example() {
  return (
    <Menu>
      <MenuButton>My account</MenuButton>
<MenuItems anchor="bottom start">
<MenuItem> <a className="block data-[focus]:bg-blue-100" href="/settings"> Settings </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/support"> Support </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/license"> License </a> </MenuItem> </MenuItems> </Menu> ) }

toprightbottom、またはleftの値を使用して、適切なエッジに沿ってドロップダウンを中央揃えするか、startまたはendと組み合わせて、top startまたはbottom endなど、特定のコーナーにドロップダウンを配置します。

ボタンとドロップダウンの間の間隔を制御するには、--anchor-gap CSS 変数を使用します。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'

function Example() {
  return (
    <Menu>
      <MenuButton>My account</MenuButton>
<MenuItems anchor="bottom start" className="[--anchor-gap:4px] sm:[--anchor-gap:8px]">
<MenuItem> <a className="block data-[focus]:bg-blue-100" href="/settings"> Settings </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/support"> Support </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/license"> License </a> </MenuItem> </MenuItems> </Menu> ) }

さらに、--anchor-offsetを使用してドロップダウンを元の位置から移動する距離を制御し、--anchor-paddingを使用してドロップダウンとビューポートの間にあるべき最小スペースを制御できます。

anchorプロップは、JavaScriptを使用してgapoffset、およびpaddingの値を制御できるオブジェクトAPIもサポートしています。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'

function Example() {
  return (
    <Menu>
      <MenuButton>My account</MenuButton>
<MenuItems anchor={{ to: 'bottom start', gap: '4px' }}>
<MenuItem> <a className="block data-[focus]:bg-blue-100" href="/settings"> Settings </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/support"> Support </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/license"> License </a> </MenuItem> </MenuItems> </Menu> ) }

これらのオプションの詳細については、MenuItems APIを参照してください。

トランジションの追加

ドロップダウンの開閉をアニメーション化するには、MenuItemsコンポーネントにtransitionプロップを追加し、CSSを使用してトランジションのさまざまな段階をスタイル設定します。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'

function Example() {
  return (
    <Menu>
      <MenuButton>My account</MenuButton>
      <MenuItems
        anchor="bottom"
transition
className="origin-top transition duration-200 ease-out data-[closed]:scale-95 data-[closed]:opacity-0"
>
<MenuItem> <a className="block data-[focus]:bg-blue-100" href="/settings"> Settings </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/support"> Support </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/license"> License </a> </MenuItem> </MenuItems> </Menu> ) }

内部的には、transitionプロップはTransitionコンポーネントとまったく同じ方法で実装されています。詳細については、トランジションドキュメントを参照してください。

Framer Motion を使用したアニメーション

Headless UI は、Framer MotionReact SpringなどのReactエコシステムの他のアニメーションライブラリともよく連携します。これらのライブラリにいくつかの状態を公開するだけです。

たとえば、Framer Motion でメニューをアニメーション化するには、MenuItemsコンポーネントにstaticプロップを追加し、openレンダープロップに基づいて条件付きでレンダリングします。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
import { AnimatePresence, motion } from 'framer-motion'

function Example() {
  return (
    <Menu>
{({ open }) => (
<> <MenuButton>My account</MenuButton> <AnimatePresence>
{open && (
<MenuItems
static
as={motion.div} initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.95 }} anchor="bottom" className="origin-top" >
<MenuItem> <a className="block data-[focus]:bg-blue-100" href="/settings"> Settings </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/support"> Support </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/license"> License </a> </MenuItem> </MenuItems>
)}
</AnimatePresence> </>
)}
</Menu> ) }

メニューの手動クローズ

デフォルトでは、MenuItemをクリックするとMenuが閉じます。ただし、一部のサードパーティのLinkコンポーネントはevent.preventDefault()を使用しており、メニューが閉じることができません。

このような状況では、MenuコンポーネントとMenuItemコンポーネントの両方で使用可能なcloseレンダープロップを使用して、メニューを強制的に閉じることができます。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
import { MyCustomLink } from './MyCustomLink'

function Example() {
  return (
    <Menu>
      <MenuButton>Terms</MenuButton>
      <MenuItems anchor="bottom">
        <MenuItem>
{({ close }) => (
<MyCustomLink href="/" onClick={close}>
Read and accept </MyCustomLink>
)}
</MenuItem> </MenuItems> </Menu> ) }

デフォルトでは、Menuとそのサブコンポーネントはそれぞれ、そのコンポーネントに適したデフォルトの要素をレンダリングします。

たとえば、MenuButtonはデフォルトでbuttonをレンダリングし、MenuItemsdivをレンダリングします。対照的に、MenuMenuItem要素をレンダリングしません。代わりに、デフォルトでは直接子要素をレンダリングします。

asプロップを使用して、コンポーネントを異なる要素または独自のカスタムコンポーネントとしてレンダリングします。カスタムコンポーネントがforward refsを使用していることを確認して、Headless UIが正しく接続できるようにします。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
import { forwardRef } from 'react'

let MyCustomButton = forwardRef(function (props, ref) {
return <button className="..." ref={ref} {...props} />
})
function Example() { return ( <Menu>
<MenuButton as={MyCustomButton}>My account</MenuButton>
<MenuItems anchor="bottom" as="section">
<MenuItem as="a" className="block data-[focus]:bg-blue-100" href="/settings">
Settings </MenuItem>
<MenuItem as="a" className="block data-[focus]:bg-blue-100" href="/support">
Support </MenuItem>
<MenuItem as="a" className="block data-[focus]:bg-blue-100" href="/license">
License </MenuItem> </MenuItems> </Menu> ) }

ラッパー要素なしで子要素を直接レンダリングする要素を指定するには、as={Fragment}を使用します。

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
import { Fragment } from 'react'

function Example() {
  return (
    <Menu>
<MenuButton as={Fragment}>
<button>My account</button>
</MenuButton>
<MenuItems anchor="bottom"> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/settings"> Settings </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/support"> Support </a> </MenuItem> <MenuItem> <a className="block data-[focus]:bg-blue-100" href="/license"> License </a> </MenuItem> </MenuItems> </Menu> ) }

これは、MenuItem内で<a>タグのようなインタラクティブな要素を使用している場合に重要です。MenuItemas="div"がある場合、Headless UIによって提供されるプロップはaではなくdivに転送されるため、キーボードから<a>タグによって提供されるURLに移動できなくなります。

Next.js v13より前では、Linkコンポーネントは不明なプロップを基になるa要素に転送しなかったため、MenuItem内で使用した場合、クリック時にメニューが閉じませんでした。

Next.js v12以前を使用している場合は、Linkをラップし、不明なプロップを子a要素に転送する独自のコンポーネントを作成することで、この問題を回避できます。

import { forwardRef } from 'react'
import Link from 'next/link'
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/react'

const MyLink = forwardRef((props, ref) => {
let { href, children, ...rest } = props
return (
<Link href={href}>
<a ref={ref} {...rest}>
{children}
</a>
</Link>
)
})
function Example() { return ( <Menu> <MenuButton>My account</MenuButton> <MenuItems anchor="bottom"> <MenuItem>
<MyLink href="/settings">Settings</MyLink>
</MenuItem> </MenuItems> </Menu> ) }

これにより、Headless UIがa要素に追加する必要があるすべてのイベントリスナーが正しく適用されます。

この動作はNext.js v13で変更されたため、この回避策は不要になりました。

コマンド説明

EnterまたはSpaceMenuButtonがフォーカスされている場合)

メニューを開き、最初の有効な項目にフォーカスします。

下方向キーまたは上方向キーMenuButtonがフォーカスされている場合)

メニューを開き、最初または最後の有効な項目にフォーカスします。

Esc(メニューが開いている場合)

開いているメニューをすべて閉じます。

下方向キーまたは上方向キー(メニューが開いている場合)

前または次の有効な項目にフォーカスします。

HomeまたはPageUp(メニューが開いている場合)

最初の有効な項目にフォーカスします。

EndまたはPageDown(メニューが開いている場合)

最後の有効な項目にフォーカスします。

EnterまたはSpace(メニューが開いている場合)

現在のメニュー項目をアクティブ化/クリックします。

A~Zまたはa~z(メニューが開いている場合)

キーボード入力に一致する最初の項目にフォーカスします。

プロパティデフォルト値説明
asFragment
文字列 | コンポーネント

メニューがレンダリングされる要素またはコンポーネント。メニューとしてレンダリングするべきです。

データ属性レンダープロップ説明
data-openopen

ブール値

メニューが開いているかどうか。メニュー開いているかどうか。

close

() => void

メニューを閉じ、MenuButtonにフォーカスを戻します。

プロパティデフォルト値説明
asbutton
文字列 | コンポーネント

メニューがレンダリングされる要素またはコンポーネント。メニューボタンとしてレンダリングするべきです。

disabledfalse
ブール値

メニューが開いているかどうか。メニューボタン無効化されているかどうか.

データ属性レンダープロップ説明
data-openopen

ブール値

メニューが開いているかどうか。メニュー開いているかどうか。

data-focusfocus

ブール値

メニューが開いているかどうか。メニューボタンフォーカスされているかどうか。

data-hoverhover

ブール値

メニューが開いているかどうか。メニューボタンホバーされているかどうか。

data-activeactive

ブール値

メニューが開いているかどうか。メニューボタンアクティブまたは押されている状態かどうか。

data-autofocusautofocus

ブール値

autoFocusプロップがtrueに設定されていたかどうか。

プロパティデフォルト値説明
asdiv
文字列 | コンポーネント

メニューがレンダリングされる要素またはコンポーネント。メニュー項目としてレンダリングするべきです。

transitionfalse
ブール値

data-closed data-enterdata-leaveなどのトランジション属性を要素にレンダリングするかどうか。

anchor
オブジェクト

ドロップダウンがボタンにどのようにアンカーされるかを設定します。

anchor.tobottom
文字列

トリガーを基準としたメニュー項目位置。

toprightbottomleftの値を使用して適切なエッジに沿って中央に配置するか、startまたはendと組み合わせてtop startbottom endなどの特定のコーナーに配置します。 メニュー項目に沿って中央に配置するか、startまたはendと組み合わせて、メニュー項目を特定のコーナー(例:top startまたはbottom end)に配置します。

anchor.gap0
数値 | 文字列

ドロップダウンとメニューボタンの間隔。メニュー項目.

--anchor-gap CSS変数を使用して制御することもできます。

anchor.offset0
数値 | 文字列

ドロップダウンを元の位置からメニュー項目移動する距離。

--anchor-offset CSS変数を使用して制御することもできます。

anchor.padding0
数値 | 文字列

ドロップダウンとビューポートの間の最小間隔。メニュー項目とビューポートの間の

最小間隔。

--anchor-padding CSS変数を使用して制御することもできます。false
ブール値

内部で管理されている開閉状態を無視するかどうか。

unmounttrue
ブール値

開閉状態に基づいて要素をアンマウントするか非表示にするかどうか。

portalfalse
ブール値

要素をポータルにレンダリングするかどうか。

anchorプロップが設定されている場合、自動的にtrueに設定されます。

modaltrue
ブール値

スクロールロック、フォーカストラップ、その他の要素を不活性にするなど、アクセシビリティ機能を有効にするかどうか。 不活性.

データ属性レンダープロップ説明
data-openopen

ブール値

メニューが開いているかどうか。メニュー開いているかどうか。

プロパティデフォルト値説明
asFragment
文字列 | コンポーネント

メニューがレンダリングされる要素またはコンポーネント。メニュー項目としてレンダリングするべきです。

disabledfalse
ブール値

メニューが開いているかどうか。メニュー項目無効化されているかどうかキーボードナビゲーションとARIAの目的のため.

データ属性レンダープロップ説明
data-disableddisabled

ブール値

メニューが開いているかどうか。メニュー項目無効化されているかどうか。

data-focusfocus

ブール値

メニューが開いているかどうか。メニュー項目フォーカスされているかどうか。

close

() => void

メニューを閉じ、MenuButtonにフォーカスを戻します。

MenuItemコンポーネントのリストを、適切なアクセシビリティセマンティクスを持つセクションに分割します。

プロパティデフォルト値説明
asdiv
文字列 | コンポーネント

メニューがレンダリングされる要素またはコンポーネント。メニューセクションとしてレンダリングするべきです。

MenuSectionにアクセシブルなラベルを追加します。

プロパティデフォルト値説明
asヘッダー
文字列 | コンポーネント

メニューがレンダリングされる要素またはコンポーネント。メニュー見出しとしてレンダリングするべきです。

2つのMenuSectionコンポーネントを、適切なアクセシビリティセマンティクスで区切ります。

プロパティデフォルト値説明
asdiv
文字列 | コンポーネント

メニューがレンダリングされる要素またはコンポーネント。メニューセパレータとしてレンダリングするべきです。

Headless UIを使用した、事前にデザインされたTailwind CSSドロップダウンコンポーネントの例に興味がある場合は、私たちによって構築された美しくデザインされ、専門的に作成されたコンポーネントのコレクションであるTailwind UIをご覧ください。

これは、このようなオープンソースプロジェクトへの取り組みを支援する優れた方法であり、プロジェクトの改善と維持を可能にします。