タブ

堅牢なフォーカス管理とキーボードナビゲーションサポートにより、アクセシビリティが高く、完全にカスタマイズ可能なタブインターフェースを簡単に作成できます。

インストール

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

npm install @headlessui/react

基本例

タブは、Tab.GroupTab.ListTabTab.Panels、およびTab.Panelコンポーネントを使用して構築されています。デフォルトでは最初のタブが選択されており、任意のタブをクリックするか、キーボードで選択すると、対応するパネルがアクティブになります。

import { Tab } from '@headlessui/react' function MyTabs() { return ( <Tab.Group> <Tab.List> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> <Tab.Panel>Content 2</Tab.Panel> <Tab.Panel>Content 3</Tab.Panel> </Tab.Panels> </Tab.Group> ) }

様々な状態のスタイル設定

Headless UI は、どのタブオプションが現在チェックされているか、ポップオーバーが開いているか閉じているか、メニューのどのアイテムが現在キーボードでアクティブになっているかなど、各コンポーネントに関する多くの状態を追跡します。

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

レンダープロップの使用

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

たとえば、Tabコンポーネントはselected状態を公開しており、タブが現在選択されているかどうかを示します。

import { Fragment } from 'react' import { Tab } from '@headlessui/react' function MyTabs() { return ( <Tab.Group> <Tab.List> <Tab as={Fragment}>
{({ selected }) => (
/* Use the `selected` state to conditionally style the selected tab. */ <button className={
selected ? 'bg-blue-500 text-white' : 'bg-white text-black'
}
>
Tab 1 </button> )} </Tab> {/* ... */} </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> {/* ... */} </Tab.Panels> </Tab.Group> ) }

各コンポーネントの完全なレンダープロップAPIについては、コンポーネントAPIドキュメントを参照してください。

データ属性の使用

各コンポーネントは、条件付きで異なるスタイルを適用するために使用できるdata-headlessui-state属性を介して、現在の状態に関する情報を公開します。

レンダープロップAPIのいずれかの状態がtrueの場合、それらはスペース区切りの文字列としてこの属性にリストされ、[attr~=value]形式のCSS属性セレクターでターゲットにすることができます。

たとえば、2番目のタブがselectedの場合、いくつかの子Tabコンポーネントを含むTab.Groupコンポーネントは次のようにレンダリングされます。

<!-- Rendered `Tab.Group` --> <div> <button data-headlessui-state="">Tab 1</button> <button data-headlessui-state="selected">Tab 2</button> <button data-headlessui-state="">Tab 3</button> </div> <div> <div data-headlessui-state="">Content 1</div> <div data-headlessui-state="selected">Content 2</div> <div data-headlessui-state="">Content 3</div> </div>

Tailwind CSSを使用している場合は、@headlessui/tailwindcssプラグインを使用して、ui-open:*などの修飾子でこの属性をターゲットにすることができます。

import { Tab } from '@headlessui/react' function MyTabs() { return ( <Tab.Group> <Tab.List>
<Tab className="ui-selected:bg-blue-500 ui-selected:text-white ui-not-selected:bg-white ui-not-selected:text-black">
Tab 1 </Tab> {/* ... */} </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> {/* ... */} </Tab.Panels> </Tab.Group> ) }

タブの無効化

タブを無効にするには、Tabコンポーネントでdisabledプロップを使用します。無効なタブはマウスで選択できず、キーボードを使用してタブリストをナビゲーションする際にもスキップされます。

import { Tab } from '@headlessui/react' function MyTabs() { return ( <Tab.Group> <Tab.List> <Tab>Tab 1</Tab>
<Tab disabled>Tab 2</Tab>
<Tab>Tab 3</Tab> </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> <Tab.Panel>Content 2</Tab.Panel> <Tab.Panel>Content 3</Tab.Panel> </Tab.Panels> </Tab.Group> ) }

タブの手動によるアクティブ化

デフォルトでは、ユーザーが矢印キーを使用してタブを移動すると、タブが自動的に選択されます。

ユーザーがEnterキーまたはSpaceキーを押すまで現在のタブを変更しない場合は、Tab.Groupコンポーネントでmanualプロップを使用します。これは、タブの選択がコストの高い操作を実行する場合に役立ち、不要に実行したくない場合に便利です。

import { Tab } from '@headlessui/react' function MyTabs() { return (
<Tab.Group manual>
<Tab.List> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> <Tab.Panel>Content 2</Tab.Panel> <Tab.Panel>Content 3</Tab.Panel> </Tab.Panels> </Tab.Group> ) }

manualプロップはマウス操作には影響しません。タブはクリックされるとすぐに選択されます。

垂直タブ

Tab.Listを垂直に表示するようにスタイル設定した場合は、verticalプロップを使用して、左右の矢印キーではなく上下の矢印キーでナビゲーションできるようにし、支援技術用のaria-orientation属性を更新します。

import { Tab } from '@headlessui/react' function MyTabs() { return (
<Tab.Group vertical>
<Tab.List> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> <Tab.Panel>Content 2</Tab.Panel> <Tab.Panel>Content 3</Tab.Panel> </Tab.Panels> </Tab.Group> ) }

デフォルトタブの指定

デフォルトで選択されているタブを変更するには、Tab.GroupコンポーネントでdefaultIndex={number}プロップを使用します。

import { Tab } from '@headlessui/react' function MyTabs() { return (
<Tab.Group defaultIndex={1}>
<Tab.List> <Tab>Tab 1</Tab>
{/* Selects this tab by default */}
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab> </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel>
{/* Displays this panel by default */}
<Tab.Panel>Content 2</Tab.Panel>
<Tab.Panel>Content 3</Tab.Panel> </Tab.Panels> </Tab.Group> ) }

範囲外のインデックスを指定した場合、最初のレンダリング時に最後の無効でないタブが選択されます。(たとえば、上記の例では、<Tab.Group defaultIndex={5}は3番目のパネルを選択してレンダリングします。)

変更の監視

選択されたタブが変更されるたびに関数を実行するには、Tab.GroupコンポーネントでonChangeプロップを使用します。

import { Tab } from '@headlessui/react' function MyTabs() { return ( <Tab.Group
onChange={(index) => {
console.log('Changed selected tab to:', index)
}}
>
<Tab.List> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> <Tab.Panel>Content 2</Tab.Panel> <Tab.Panel>Content 3</Tab.Panel> </Tab.Panels> </Tab.Group> ) }

アクティブなタブの制御

タブコンポーネントは、制御されたコンポーネントとしても使用できます。これを行うには、selectedIndexを提供し、状態を自分で管理します。

import { useState } from 'react' import { Tab } from '@headlessui/react' function MyTabs() {
const [selectedIndex, setSelectedIndex] = useState(0)
return (
<Tab.Group selectedIndex={selectedIndex} onChange={setSelectedIndex}>
<Tab.List> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> <Tab.Panel>Content 2</Tab.Panel> <Tab.Panel>Content 3</Tab.Panel> </Tab.Panels> </Tab.Group> ) }

アクセシビリティに関する注意

マウス操作

Tabをクリックすると、そのタブが選択され、対応するTab.Panelが表示されます。

キーボード操作

Tabコンポーネントにフォーカスがある場合、すべての操作が適用されます。

コマンド説明

左矢印および右矢印

前後の無効でないタブを選択します。

上矢印および下矢印verticalが設定されている場合)

前後の無効でないタブを選択します。

HomeまたはPageUp

最初の無効でないタブを選択します。

EndまたはPageDown

最後の無効でないタブを選択します。

EnterまたはSpacemanualが設定されている場合)

選択されたタブをアクティブにします。

その他

関連するすべてのARIA属性は自動的に管理されます。

タブに実装されているすべてのアクセシビリティ機能の完全なリファレンスについては、タブに関するARIA仕様を参照してください。

コンポーネントAPI

Tab.Group

メインのTab.Groupコンポーネント。

プロップデフォルト説明
asFragment
文字列 | コンポーネント

タブがレンダリングされる要素またはコンポーネント。

defaultIndex0
数値

デフォルトで選択されているインデックス

selectedIndex
数値

タブコンポーネントを制御されたコンポーネントとして使用する場合は、選択されたインデックス。

onChange
(index: number) => void

アクティブなタブが変更されるたびに呼び出される関数。

verticalfalse
ブール値

trueの場合、Tab.Listの方向はverticalになり、それ以外の場合はhorizontalになります。

manualfalse
ブール値

trueの場合、ユーザーはキーボードを使用してパネルを表示するには、最初に矢印キーを使用してパネルに移動してから、EnterキーまたはSpaceキーを押す必要があります。デフォルトでは、矢印キーを使用して移動すると、パネルが自動的に表示されます。このプロップはマウスの動作には影響しません。

レンダープロップ説明
selectedIndex

数値

現在選択されているインデックス。

プロップデフォルト説明
asdiv
文字列 | コンポーネント

Tab.Listがレンダリングされる要素またはコンポーネント。

レンダープロップ説明
selectedIndex

数値

現在選択されているインデックス。

プロップデフォルト説明
asbutton
文字列 | コンポーネント

Tabがレンダリングされる要素またはコンポーネント。

disabledfalse
ブール値

Tabが無効になっているかどうか。

レンダープロップ説明
selected

ブール値

Tabが現在選択されているかどうか。

プロップデフォルト説明
asdiv
文字列 | コンポーネント

Tab.Panels がレンダリングされる要素またはコンポーネント。

レンダープロップ説明
selectedIndex

数値

現在選択されているインデックス。

プロップデフォルト説明
asdiv
文字列 | コンポーネント

Tab.Panel がレンダリングされる要素またはコンポーネント。

静的false
ブール値

選択されたインデックスを無視するかどうか。

注:staticunmount は同時に使用できません。試行するとTypeScriptエラーが発生します。

アンマウントtrue
ブール値

選択されたインデックスに基づいて、要素をアンマウントするか非表示にするかどうか。

注:staticunmount は同時に使用できません。試行するとTypeScriptエラーが発生します。

レンダープロップ説明
selected

ブール値

Tab.Panel が現在選択されているかどうか。

Headless UIとTailwind CSSを使った、事前にデザインされたコンポーネント例にご興味があれば、Tailwind UIをご覧ください。これは、私たちによって美しくデザインされ、専門的に作成されたコンポーネントのコレクションです。

これは、このようなオープンソースプロジェクトへの私たちの取り組みをサポートする素晴らしい方法であり、それらを改善し、適切に維持することを可能にします。