タブ

堅牢なフォーカス管理とキーボードナビゲーションのサポートを備えた、アクセシブルで完全にカスタマイズ可能なタブインターフェースを簡単に作成できます。

インストール

はじめに、npm経由でHeadless UIをインストールします

npm install @headlessui/react

基本的な例

タブは、TabGroupTabListTabTabPanels、および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とそのサブコンポーネントはそれぞれ、そのコンポーネントにとって適切なデフォルト要素をレンダリングします。

たとえば、TabGroupdivをレンダリングし、TabListdivをレンダリングし、Tabbuttonをレンダリングします。

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コンポーネントにフォーカスがある場合に適用されます。

コマンド説明

左矢印右矢印

前/次の無効になっていないタブを選択します。

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

前/次の無効になっていないタブを選択します。

Home または PageUp

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

End または PageDown

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

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

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

メインの TabGroup コンポーネントです。

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

タブグループをレンダリングするタブグループ要素またはコンポーネント。

defaultIndex0
数値

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

selectedIndex
数値

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

onChange
(index: number) => void

選択されたタブが変更されるたびに呼び出される関数。

verticalfalse
真偽値

true の場合、TabList の方向は vertical(垂直)になり、そうでない場合は horizontal(水平)になります。

manualfalse
真偽値

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

データ属性レンダープロップ説明
selectedIndex

数値

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

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

タブグループをレンダリングするタブリスト要素またはコンポーネント。

データ属性レンダープロップ説明
selectedIndex

数値

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

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

タブグループをレンダリングするタブ要素またはコンポーネント。

disabledfalse
真偽値

タブがタブ無効になっているかどうか.

autoFocusfalse
真偽値

タブがタブ最初にレンダリングされたときにフォーカスを受け取るかどうか。

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

真偽値

タブがタブ選択されている。

data-focusfocus

真偽値

タブがタブフォーカスされている。

data-hoverhover

真偽値

タブがタブホバーされている。

data-activeactive

真偽値

タブがタブアクティブまたは押下状態である。

data-autofocusautofocus

真偽値

autoFocus プロパティが true に設定されているかどうか。

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

タブグループをレンダリングするタブパネル要素またはコンポーネント。

データ属性レンダープロップ説明
selectedIndex

数値

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

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

タブグループをレンダリングするタブパネル要素またはコンポーネント。

staticfalse
真偽値

要素が内部的に管理されている開閉状態を無視するかどうか。

unmounttrue
真偽値

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

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

真偽値

タブがタブパネル選択されている。

あらかじめデザインされたTailwind CSS タブコンポーネントの例にご興味がある場合は、美しくデザインされ、専門的に作成されたコンポーネントのコレクションである **Tailwind UI** をご覧ください。

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