Docs
検索

Tabs

タブ要素を作成できるコンポーネントです。
スタイリングはほぼなく、動きに関わる部分だけを提供しています。表示スタイルは適宜カスタマイズしてご利用ください。

Overview

↓
Preview

Tab 01: Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut.

<Tabs.Root>
<Tabs.Item>
<Tabs.Tab>Tab 1</Tabs.Tab>
<Tabs.Panel>
<DummyText pre="Tab 01: "/>
</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab>Tab 2</Tabs.Tab>
<Tabs.Panel>
<DummyText length="l" offset={1} pre="Tab 02: " />
</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab>Tab 3</Tabs.Tab>
<Tabs.Panel>
<DummyText length="l" offset={2} pre="Tab 03: " />
</Tabs.Panel>
</Tabs.Item>
</Tabs.Root>

<Tabs.Item>の子要素は<Tabs.Tab>と<Tabs.Panel>しか認識しません。また、この2つは必ず両方使用してください。

Styles

Tabs のベーススタイルは、以下のCSSで定義されています。

_style.css
@layer lism-block {
  /* l--grid との併用が前提 */
  .b--tabs {
    grid: 'list' 'panel' / 100%;
    gap: var(--s20);
  }
  .b--tabs_list {
    grid-area: list;
    display: flex;
    gap: 0.25em;
    overflow-x: auto;
  }
  .b--tabs_tab {
    --hl: var(--hl--s);
    font-size: var(--fz--s);
    padding: 0.375em 0.5em;
  }
  .b--tabs_tab[aria-selected='true'] {
    --_notSelected: ;
  }
  .b--tabs_tab[aria-selected='false'] {
    --_isSelected: ;
  }
  .b--tabs_panel {
    grid-area: panel;
    width: 100%;
  }

  /* 既定バリアントの装飾。variant指定時は適用されないため、打ち消し不要で独自スタイルを組める */
  .b--tabs--default .b--tabs_tab {
    border-radius: var(--bdrs--10);
    color: var(--_notSelected, var(--text-2));
    background-color: var(--_notSelected, var(--base-2));
    box-shadow: var(--_isSelected, inset 0 0 0 1px currentColor);
  }

  /* line バリアント: 選択中のタブを下線で表示 */
  .b--tabs--line > .b--tabs_list {
    /* Memo: ov-x='auto' の時に margin がネガティブだと崩れるので、box-shadowで表現する。 */
    box-shadow: inset 0 -2px 0 var(--divider);
  }
  .b--tabs--line .b--tabs_tab {
    box-shadow: var(--_isSelected, inset 0 -2px 0 0 currentColor);
  }
}

ソースコード全体は GitHub で公開しています。

How to use

@lism-css/ui パッケージでTabsとして提供しています。

Import

import { Tabs } from '@lism-css/ui/react';

Props

プロパティ 説明
<Tabs.Root>
variant
b--tabs--{variant}クラスが出力されます。初期値はdefaultで、lineも標準で提供しています。独自のvariantを指定すると既定バリアント(b--tabs--default)の装飾は適用されなくなるため、初期スタイルを打ち消すことなく独自のスタイルを構築できます。
<Tabs.Root>
tabId
タブを特定するためのIDを文字列で指定できます。タブボタンのid({tabId}-{index}-tab)、パネルのid({tabId}-{index})、aria-controlsの値の生成に使用されます。未指定の場合は自動生成されたIDが使われます。
<Tabs.Root>
defaultIndex
最初に開いておくタブ番号を指定できます。タブの数を超える番号など、範囲外の値を指定した場合は1にフォールバックします。
<Tabs.Root>
listProps
タブボタンを囲むリスト要素(b--tabs_list)へ渡すpropsを指定できます。
<Tabs.Tab> <Tabs.Panel>
index
1から始まるタブ番号です。対応する<Tabs.Tab>と<Tabs.Panel>で同じ番号を指定します。通常の<Tabs.Item>構成では自動で設定されるため指定不要です。<Tabs.Item>を使わない構成では必須です。
<Tabs.Tab> <Tabs.Panel>
tabId
id({tabId}-{index}-tab / {tabId}-{index})の生成に使うIDです。通常の<Tabs.Item>構成では<Tabs.Root>の値が自動で設定されるため指定不要です。<Tabs.Item>を使わない構成では、Astro版のみ指定が必要です。
<Tabs.Tab> <Tabs.Panel>
isActive
最初に開いておくタブかどうかを指定します。通常の<Tabs.Item>構成ではdefaultIndexをもとに自動で設定されるため指定不要です。<Tabs.Item>を使わない構成のAstro版で、最初に開いておく組の<Tabs.Tab>と<Tabs.Panel>へ指定します。

キーボード操作

タブボタンにフォーカスがある状態で、以下のキー操作が可能です。

キー 動作
← → 前後のタブへフォーカスを移動し、同時にそのタブを選択します。端まで移動すると反対側の端へループします。
Home 先頭のタブへ移動して選択します。
End 末尾のタブへ移動して選択します。

タブリストを縦並びにする場合は、listPropsからaria-orientation="vertical"を指定してください。矢印キーが↑ ↓に切り替わります。

<Tabs.Root listProps={{ 'aria-orientation': 'vertical' }}>

横並び(既定)で↑ ↓を使わないのは、ブラウザ本来のスクロール操作を妨げないためです。

なお、aria-orientationはHTML属性のため、画面幅に応じて横並びと縦並びが切り替わるレイアウトでは値を出し分けられません。その場合は既定(横並び・← →)のまま使ってください。

Examples

variant=“line”

variant="line"を指定すると、選択中のタブを下線で表示するラインスタイルになります。

↓
variant='line'

Tab 01: Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut.

<Tabs.Root variant="line">
<Tabs.Item>
<Tabs.Tab>Tab 1</Tabs.Tab>
<Tabs.Panel>
<DummyText pre="Tab 01: " />
</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab>Tab 2</Tabs.Tab>
<Tabs.Panel>
<DummyText length="l" offset={1} pre="Tab 02: " />
</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab>Tab 3</Tabs.Tab>
<Tabs.Panel>
<DummyText length="l" offset={2} pre="Tab 03: " />
</Tabs.Panel>
</Tabs.Item>
</Tabs.Root>

最初に開いておくタブ番号を指定する

<Tabs.Root>の defaultIndex で、最初に開いておくタブ番号を指定できます。
(インデックス番号は1から始まります。)

↓
例

Tab 02: Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.

<Tabs.Root defaultIndex={2}>
<Tabs.Item>
<Tabs.Tab>Tab 1</Tabs.Tab>
<Tabs.Panel>
<DummyText pre="Tab 01: "/>
</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab>Tab 2</Tabs.Tab>
<Tabs.Panel>
<DummyText length="l" offset={1} pre="Tab 02: " />
</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab>Tab 3</Tabs.Tab>
<Tabs.Panel>
<DummyText length="l" offset={2} pre="Tab 03: " />
</Tabs.Panel>
</Tabs.Item>
</Tabs.Root>

URLパラメータで初期タブを指定する

URLに?lism-tab={tabId}-{index}を付けてページを開くと、指定したタブが最初に選択された状態で表示されます。

この機能を使う場合は、<Tabs.Root>のtabIdを明示的に指定してください。
(未指定の場合はIDが自動生成されるため、URLから指定できません。)

<Tabs.Root tabId="sample-tabs">
<Tabs.Item>
<Tabs.Tab>Tab 1</Tabs.Tab>
<Tabs.Panel>...</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab>Tab 2</Tabs.Tab>
<Tabs.Panel>...</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab>Tab 3</Tabs.Tab>
<Tabs.Panel>...</Tabs.Panel>
</Tabs.Item>
</Tabs.Root>

上記のタブを設置したページに?lism-tab=sample-tabs-2を付けてアクセスすると、2番目のタブが選択された状態で表示されます。

Tabs.Itemを使わずに構成する

<Tabs.Item>を使わず、<Tabs.Root>の中へ<Tabs.List>(role="tablist"のリスト)・<Tabs.Tab>・<Tabs.Panel>を直接配置することもできます。
タブボタンを<li>などで包みたい場合や、リストとパネルの間に別の要素を挟みたい場合など、出力される構造を自分で組みたいときに使います。

この構成では、対応する<Tabs.Tab>と<Tabs.Panel>をindex(1から始まる番号)で紐付けます。クリックやキーボード操作によるタブの切り替え、フォーカス移動、ARIA属性の更新はコンポーネント側で行われるため、状態管理やイベント処理を自分で書く必要はありません。

↓
例

Tab 02: Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.

<Tabs.Root tabId="manual-tabs" defaultIndex={2}>
<Tabs.List>
<Tabs.Tab index={1}>Tab 1</Tabs.Tab>
<Tabs.Tab index={2}>Tab 2</Tabs.Tab>
<Tabs.Tab index={3}>Tab 3</Tabs.Tab>
</Tabs.List>
<Tabs.Panel index={1}>
<DummyText pre="Tab 01: " />
</Tabs.Panel>
<Tabs.Panel index={2}>
<DummyText length="l" offset={1} pre="Tab 02: " />
</Tabs.Panel>
<Tabs.Panel index={3}>
<DummyText length="l" offset={2} pre="Tab 03: " />
</Tabs.Panel>
</Tabs.Root>

必要なpropsは、React版とAstro版で異なります。

React Astro
index 各<Tabs.Tab>・<Tabs.Panel>に必須です。 各<Tabs.Tab>・<Tabs.Panel>に必須です。
tabId <Tabs.Root>の値(未指定なら自動生成ID)が自動で共有されるため、指定不要です。個別に指定しても<Tabs.Root>側が優先されます。 各<Tabs.Tab>・<Tabs.Panel>へ同じ値を指定します。
初期選択 <Tabs.Root>のdefaultIndex(初期値1)で指定します。 最初に開いておく組の<Tabs.Tab>と<Tabs.Panel>の両方へisActiveを指定します。
  • React版では、defaultIndexが未指定のときに限り、isActiveを付けた<Tabs.Tab>が初期選択になります。Astro版と同じ書き方をそのまま使えるようにするための互換機能で、ReactではdefaultIndexの使用を推奨します。<Tabs.Panel>のisActiveはReact版では不要です。
  • React版で<Tabs.Tab>へ渡したonClick・onKeyDownは、内部の処理より先に呼ばれます。その中でe.preventDefault()を実行すると、タブの切り替えは行われません(<Tabs.Item>構成でも同じです)。
  • Astro版では親から子へ値を共有できないため、この構成では<Tabs.Root>のtabId・defaultIndex・listPropsは反映されません。リスト要素へ渡したいpropsは、<Tabs.List>へ直接指定してください。
  • URLパラメータによる初期タブの指定はこの構成でも使えます。React版で使う場合は、<Tabs.Root>にtabIdを明示的に指定してください。
  • <Tabs.Tab>は必ず<Tabs.List>の中に配置してください。キーボード操作は、同じrole="tablist"要素内のタブをDOM順にたどります。
  • この構成では、範囲外の番号を1へフォールバックする処理は行われません。存在しない番号を初期選択に指定すると、どのタブも選択されていない状態で表示されます(タブをクリックすれば選択できます)。

縦並びタブ

<Tabs.Root>(b--tabs)にgrid-template:'list panel' auto / auto 1fr; を指定すると、タブリストとコンテンツが横並びになります。

それと同時に、タブリスト(b--tabs_list)のボタンを縦並び(flex-direction:column;)に変更すると、以下のようなレイアウトが実現できます。

↓
@smサイズより大きい場合、コンテンツを横並びにする例

Tab 01: Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut.

リサイズ
<Tabs.Root gt={[null, '"list panel" auto / auto 1fr']} listProps={{ fxd: [null, 'column'] }}>
<Tabs.Item>
<Tabs.Tab>Tab 1</Tabs.Tab>
<Tabs.Panel>
<DummyText pre="Tab 01: "/>
</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab>Tab 2</Tabs.Tab>
<Tabs.Panel>
<DummyText length="l" offset={1} pre="Tab 02: " />
</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab>Tab 3</Tabs.Tab>
<Tabs.Panel>
<DummyText length="l" offset={2} pre="Tab 03: " />
</Tabs.Panel>
</Tabs.Item>
</Tabs.Root>

Opt-in

独自のスタイルバリエーションを作成する例です。

b--tabs_tabでは--_isSelected と --_notSelected 変数が使えるようになっていますので、活用していただくと便利です。 (set--hovの--_isHov,--_notHovと同じようにして使用できます。)

variant=“emboss”

↓
variant='emboss'

Tab 01: Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut.

<Tabs.Root variant="emboss">
<Tabs.Item>
<Tabs.Tab>Tab 1</Tabs.Tab>
<Tabs.Panel>
<DummyText pre="Tab 01: " />
</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab>Tab 2</Tabs.Tab>
<Tabs.Panel>
<DummyText length="l" offset={1} pre="Tab 02: " />
</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab>Tab 3</Tabs.Tab>
<Tabs.Panel>
<DummyText length="l" offset={2} pre="Tab 03: " />
</Tabs.Panel>
</Tabs.Item>
</Tabs.Root>
↓
必要な追加CSS
@layer lism-block {
  .b--tabs--emboss > .b--tabs_list {
    justify-self: center;
    padding: 4px;
    gap: 2px;
    background-color: var(--base-2);
    border-radius: var(--bdrs--20);
  }
  .b--tabs--emboss .b--tabs_tab {
    padding: 0.375em 0.75em;
    border-radius: calc(var(--bdrs--20) - 2px); /* 親の bdrs - (親のpadding / 2) */
    background-color: var(--_isSelected, var(--base));
    box-shadow: var(--_isSelected, var(--bxsh--10));
  }
}

variant=“folder”

↓
variant='folder'

Tab 01: Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut.

<Tabs.Root variant="folder">
<Tabs.Item>
<Tabs.Tab>Tab 1</Tabs.Tab>
<Tabs.Panel>
<DummyText pre="Tab 01: " />
</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab>Tab 2</Tabs.Tab>
<Tabs.Panel>
<DummyText length="l" offset={1} pre="Tab 02: " />
</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab>Tab 3</Tabs.Tab>
<Tabs.Panel>
<DummyText length="l" offset={2} pre="Tab 03: " />
</Tabs.Panel>
</Tabs.Item>
</Tabs.Root>

タブボタンとパネルがつながって見える、フォルダー型スタイルの作成例です。

↓
必要な追加CSS
@layer lism-block {
  .b--tabs--folder {
    --folder-bgc: var(--base-2);

    row-gap: 0;
  }
  .b--tabs--folder .b--tabs_tab {
    padding: 0.375em 1em;
    text-align: center;
    border-radius: var(--bdrs--20) var(--bdrs--20) 0 0;
    color: var(--_notSelected, var(--text-2));
    background-color: var(--_isSelected, var(--folder-bgc)) var(--_notSelected, transparent);
  }
  .b--tabs--folder > .b--tabs_panel {
    padding: var(--s25) var(--s30);
    background-color: var(--folder-bgc);
    border-radius: var(--bdrs--20);
    border-start-start-radius: 0;
  }
}

高さを固定する

タブ全体の高さを一番長いコンテンツパネルに合わせて固定する方法を紹介します。

↓
例(Tab 2の高さに固定されます)

Tab 01: Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut.

<Tabs.Root data-tabs-keep-height g="15">
<Tabs.Item>
<Tabs.Tab px="10">Tab 1</Tabs.Tab>
<Tabs.Panel>
<DummyText pre="Tab 01: " />
</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab px="10">Tab 2</Tabs.Tab>
<Tabs.Panel>
<DummyText length="xl" offset={1} pre="Tab 02: " />
</Tabs.Panel>
</Tabs.Item>
<Tabs.Item>
<Tabs.Tab px="10">Tab 3</Tabs.Tab>
<Tabs.Panel>
<DummyText length="l" offset={2} pre="Tab 03: " />
</Tabs.Panel>
</Tabs.Item>
</Tabs.Root>
↓
必要な追加CSS
@layer lism-custom {
  [data-tabs-keep-height] > .b--tabs_panel[hidden] {
    display: block;
    visibility: hidden;
    opacity: 0;
  }
}

© 2026 Lism CSS.