タブ
堅牢なフォーカス管理とキーボードナビゲーションのサポートを備えた、アクセシブルで完全にカスタマイズ可能なタブインターフェースを簡単に作成できます。
インストール
はじめに、npm経由でHeadless UIをインストールします
npm install @headlessui/react
基本的な例
タブは、TabGroup
、TabList
、Tab
、TabPanels
、およびTabPanel
コンポーネントを使用して構築されます。デフォルトでは、最初のタブが選択されており、任意のタブをクリックするか、キーボードで選択すると、対応するパネルがアクティブになります。
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
function Example() {
return (
<TabGroup>
<TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab>
</TabList>
<TabPanels>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
<TabPanel>Content 3</TabPanel>
</TabPanels>
</TabGroup>
)
}
スタイリング
Headless UIは、現在どのタブが選択されているか、ポップオーバーが開いているか閉じているか、メニューのどの項目が現在キーボードでフォーカスされているかなど、各コンポーネントに関する多くの状態を追跡します。
ただし、コンポーネントはヘッドレスであり、初期状態では完全にスタイルが設定されていないため、各状態に必要なスタイルを自分で提供するまで、UIでこの情報を確認することはできません。
データ属性の使用
Headless UIコンポーネントのさまざまな状態をスタイル設定する最も簡単な方法は、各コンポーネントが公開するdata-*
属性を使用することです。
たとえば、Tab
コンポーネントは、タブが現在選択されているかどうかを示すdata-selected
属性と、タブが現在マウスでホバーされているかどうかを示すdata-hover
属性を公開します。
<!-- Rendered `TabGroup` -->
<div>
<div>
<button>Tab 1</button>
<button data-selected>Tab 2</button>
<button data-hover>Tab 3</button>
</div>
<div>
<div>Content 1</div>
<div data-selected>Content 2</div>
<div>Content 3</div>
</div>
</div>
CSS属性セレクターを使用して、これらのデータ属性の有無に基づいて条件付きでスタイルを適用します。Tailwind CSSを使用している場合は、データ属性修飾子を使用すると、これが簡単になります
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
function Example() {
return (
<TabGroup>
<TabList>
<Tab className="data-[selected]:bg-blue-500 data-[selected]:text-white data-[hover]:underline">Tab 1</Tab> <Tab className="data-[selected]:bg-blue-500 data-[selected]:text-white data-[hover]:underline">Tab 2</Tab> <Tab className="data-[selected]:bg-blue-500 data-[selected]:text-white data-[hover]:underline">Tab 3</Tab> </TabList>
<TabPanels>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
<TabPanel>Content 3</TabPanel>
</TabPanels>
</TabGroup>
)
}
使用可能なすべてのデータ属性のリストについては、コンポーネントAPIを参照してください。
レンダープロップスの使用
各コンポーネントは、現在の状態に関する情報をレンダープロップスを介して公開しており、これを使用して条件付きで異なるスタイルを適用したり、異なるコンテンツをレンダリングしたりできます。
たとえば、Tab
コンポーネントは、タブが現在選択されているかどうかを示すselected
状態と、タブが現在マウスでホバーされているかどうかを示すhover
状態を公開します。
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
import clsx from 'clsx'
import { Fragment } from 'react'
function Example() {
return (
<TabGroup>
<TabList>
<Tab as={Fragment}>
{({ hover, selected }) => ( <button className={clsx(hover && 'underline', selected && 'bg-blue-500 text-white')}>Tab 1</button> )} </Tab>
<Tab as={Fragment}>
{({ hover, selected }) => ( <button className={clsx(hover && 'underline', selected && 'bg-blue-500 text-white')}>Tab 2</button> )} </Tab>
<Tab as={Fragment}>
{({ hover, selected }) => ( <button className={clsx(hover && 'underline', selected && 'bg-blue-500 text-white')}>Tab 3</button> )} </Tab>
</TabList>
<TabPanels>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
<TabPanel>Content 3</TabPanel>
</TabPanels>
</TabGroup>
)
}
使用可能なすべてのレンダープロップスのリストについては、コンポーネントAPIを参照してください。
例
タブの無効化
disabled
プロップを使用して、Tab
を無効にし、選択できないようにします
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
function Example() {
return (
<TabGroup>
<TabList>
<Tab>Tab 1</Tab>
<Tab disabled className="disabled:opacity-50"> Tab 2
</Tab>
<Tab>Tab 3</Tab>
</TabList>
<TabPanels>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
<TabPanel>Content 3</TabPanel>
</TabPanels>
</TabGroup>
)
}
タブを縦に表示する
TabList
を縦に表示するようにスタイル設定した場合は、vertical
プロップを使用して、左右の矢印キーではなく上下の矢印キーでナビゲートできるようにし、支援技術のaria-orientation
属性を更新します。
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
function Example() {
return (
<TabGroup vertical> <TabList className="flex flex-col">
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab>
</TabList>
<TabPanels>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
<TabPanel>Content 3</TabPanel>
</TabPanels>
</TabGroup>
)
}
タブの手動アクティブ化
デフォルトでは、ユーザーが矢印キーを使用してタブ間を移動すると、タブは自動的に選択されます。
ユーザーがEnter
キーまたはSpace
キーを押すまで現在のタブを変更しない場合は、TabGroup
コンポーネントでmanual
プロップを使用します。これは、タブの選択に負荷の高い操作が必要で、不必要に実行したくない場合に役立ちます。
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
function Example() {
return (
<TabGroup manual> <TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab>
</TabList>
<TabPanels>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
<TabPanel>Content 3</TabPanel>
</TabPanels>
</TabGroup>
)
}
manual
プロップはマウスの操作には影響しません。タブはクリックされるとすぐに選択されます。
デフォルトタブの指定
デフォルトで選択されるタブを変更するには、TabGroup
コンポーネントでdefaultIndex={number}
プロップを使用します。
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
function Example() {
return (
<TabGroup defaultIndex={1}> <TabList>
<Tab>Tab 1</Tab>
{/* Selects this tab by default */} <Tab>Tab 2</Tab>
<Tab>Tab 3</Tab>
</TabList>
<TabPanels>
<TabPanel>Content 1</TabPanel>
{/* Displays this panel by default */} <TabPanel>Content 2</TabPanel>
<TabPanel>Content 3</TabPanel>
</TabPanels>
</TabGroup>
)
}
範囲外のインデックスを指定した場合、初期レンダリング時に無効になっていない最後のタブが選択されます。(たとえば、上記の例で<TabGroup defaultIndex={5}>
とすると、3番目のパネルが選択された状態でレンダリングされます。)
変更のリスニング
選択されたタブが変更されるたびに 함수를 실행하려면、TabGroup
コンポーネントでonChange
プロップを使用します。
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
function Example() {
return (
<TabGroup
onChange={(index) => { console.log('Changed selected tab to:', index) }} >
<TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab>
</TabList>
<TabPanels>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
<TabPanel>Content 3</TabPanel>
</TabPanels>
</TabGroup>
)
}
選択されたタブの制御
デフォルトでは、タブコンポーネントは選択されたタブを内部で管理します。ただし、selectedIndex
プロップとonChange
コールバックを使用して、選択されたタブを自分で制御できます
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
import { useState } from 'react'
function Example() {
const [selectedIndex, setSelectedIndex] = useState(0)
return (
<TabGroup selectedIndex={selectedIndex} onChange={setSelectedIndex}> <TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab>
</TabList>
<TabPanels>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
<TabPanel>Content 3</TabPanel>
</TabPanels>
</TabGroup>
)
}
異なる要素としてのレンダリング
デフォルトでは、TabGroup
とそのサブコンポーネントはそれぞれ、そのコンポーネントにとって適切なデフォルト要素をレンダリングします。
たとえば、TabGroup
はdiv
をレンダリングし、TabList
はdiv
をレンダリングし、Tab
はbutton
をレンダリングします。
as
プロップを使用して、コンポーネントを異なる要素として、または独自のカスタムコンポーネントとしてレンダリングします。カスタムコンポーネントがrefsを転送することを確認して、Headless UIが正しく接続できるようにします。
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
import { forwardRef } from 'react'
let MyCustomButton = forwardRef(function (props, ref) { return <button className="..." ref={ref} {...props} />})
function Example() {
return (
<TabGroup>
<TabList as="aside"> <Tab as={MyCustomButton}>Tab 1</Tab>
<Tab as={MyCustomButton}>Tab 2</Tab>
<Tab as={MyCustomButton}>Tab 3</Tab>
</TabList>
<TabPanels as="section"> <TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
<TabPanel>Content 3</TabPanel>
</TabPanels>
</TabGroup>
)
}
要素にラッパー要素なしで子を直接レンダリングするように指示するには、Fragment
を使用します。
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
import { Fragment } from 'react'
function Example() {
return (
<TabGroup as={Fragment}> <TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab>
</TabList>
<TabPanels>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
<TabPanel>Content 3</TabPanel>
</TabPanels>
</TabGroup>
)
}
キーボード操作
すべての操作は、Tab
コンポーネントにフォーカスがある場合に適用されます。
コマンド | 説明 |
左矢印← と 右矢印→ | 前/次の無効になっていないタブを選択します。 |
上矢印↑ と 下矢印↓( | 前/次の無効になっていないタブを選択します。 |
Home または PageUp | 無効になっていない最初のタブを選択します。 |
End または PageDown | 無効になっていない最後のタブを選択します。 |
Enter または Space( | 選択したタブをアクティブにします。 |
プロパティ | デフォルト値 | 説明 |
as | div | 文字列 | コンポーネント タブグループをレンダリングするタブグループ要素またはコンポーネント。 |
defaultIndex | 0 | 数値 デフォルトで選択されているインデックス |
selectedIndex | — | 数値 タブコンポーネントを制御されたコンポーネントとして使用する場合の、選択されたインデックス。 |
onChange | — | (index: number) => void 選択されたタブが変更されるたびに呼び出される関数。 |
vertical | false | 真偽値 true の場合、 |
manual | false | 真偽値 true の場合、ユーザーはキーボードを使用してパネルを表示するには、まず矢印キーを使用してパネルに移動し、次に |
データ属性 | レンダープロップ | 説明 |
— | selectedIndex |
現在選択されているインデックス。 |
プロパティ | デフォルト値 | 説明 |
as | div | 文字列 | コンポーネント タブグループをレンダリングするタブリスト要素またはコンポーネント。 |
データ属性 | レンダープロップ | 説明 |
— | selectedIndex |
現在選択されているインデックス。 |
プロパティ | デフォルト値 | 説明 |
as | フラグメント | 文字列 | コンポーネント タブグループをレンダリングするタブ要素またはコンポーネント。 |
disabled | false | 真偽値 タブがタブ無効になっているかどうか. |
autoFocus | false | 真偽値 タブがタブ最初にレンダリングされたときにフォーカスを受け取るかどうか。 |
データ属性 | レンダープロップ | 説明 |
data-selected | selected |
タブがタブ選択されている。 |
data-focus | focus |
タブがタブフォーカスされている。 |
data-hover | hover |
タブがタブホバーされている。 |
data-active | active |
タブがタブアクティブまたは押下状態である。 |
data-autofocus | autofocus |
|
プロパティ | デフォルト値 | 説明 |
as | div | 文字列 | コンポーネント タブグループをレンダリングするタブパネル要素またはコンポーネント。 |
データ属性 | レンダープロップ | 説明 |
— | selectedIndex |
現在選択されているインデックス。 |
プロパティ | デフォルト値 | 説明 |
as | div | 文字列 | コンポーネント タブグループをレンダリングするタブパネル要素またはコンポーネント。 |
static | false | 真偽値 要素が内部的に管理されている開閉状態を無視するかどうか。 |
unmount | true | 真偽値 開閉状態に基づいて要素をアンマウントするか非表示にするか。 |
データ属性 | レンダープロップ | 説明 |
data-selected | selected |
タブがタブパネル選択されている。 |
あらかじめデザインされたTailwind CSS タブコンポーネントの例にご興味がある場合は、美しくデザインされ、専門的に作成されたコンポーネントのコレクションである **Tailwind UI** をご覧ください。
これは、このようなオープンソースプロジェクトへの私たちの取り組みを支援するための素晴らしい方法であり、私たちがそれらを改善し、適切に維持することを可能にします。