Skip to main content

CSS-only Tailwind CSS tabs with border, lifted, boxed, and pill style variants. Active state and sizes, no JavaScript required. WCAG AA accessible, works with Django, HTMX, Laravel, React, and any stack.

Komponen tab mengatur konten menjadi beberapa bagian terpisah sehingga pengguna dapat beralih di antara keduanya tanpa meninggalkan halaman. Dibangun dengan HTML semantik dan pola masukan radio, tab menyediakan navigasi intuitif untuk mengelompokkan informasi terkait. Sistem tab Frutjam mendukung berbagai ukuran, gaya, dan arah tata letak—hanya CSS atau dikontrol JavaScript—sehingga ideal untuk dasbor, dokumentasi, dan antarmuka tab.

Hanya CSS, tidak diperlukan JavaScript. WCAG AA dapat diakses dan tidak memiliki kerangka kerja — bekerja dengan Django, HTMX, Laravel, React, dan tumpukan apa pun.

Kelas Jenis Keterangan
tabsBasisWadah untuk sekelompok input tab dan panel konten
tabPengubahMasukan radio tab individual
tab-contentPengubahPanel konten terkait dengan tab
tab-activePengubahMemaksa status aktif/dipilih pada tab (untuk tab yang dikontrol JS)
tab-disabledPengubahMenonaktifkan tampilan tab
tabs-underlineGayaGaris bawahi gaya indikator
tabs-liftedGayaGaya tab terangkat seperti kartu
tabs-boxGayaTab bergaya pil kotak
tabs-verticalPengubahTata letak tab vertikal dengan tab di kolom sebelah kiri
tabs-endPengubahSejajarkan baris tab ke akhir (tepat di LTR)
tabs-bottomPengubahTempatkan tab di bawah panel konten
tabs-xsUkuranBantalan tab ekstra kecil
tabs-smUkuranBantalan tab kecil
tabs-mdUkuranBantalan tab sedang (default)
tabs-lgUkuranBantalan tab besar
tabs-xlUkuranBantalan tab ekstra besar

Penggunaan Dasar

Tab default menggunakan gaya garis bawah aktif sederhana dengan input radio. Setiap masukan tab dipasangkan dengan panel tab-content tepat setelahnya.

Overview content: a high-level summary of features, metrics, or status. This panel is visible by default.

Details content: in-depth information such as configuration options, data breakdown, or extended documentation.

Activity content: a timeline, event log, or recent changes relevant to this section.

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<div class="tabs">
  <input type="radio" name="tabs-basic" class="tab" aria-label="Overview" checked>
  <div class="tab-content pt-4">
    <p>Overview content: a high-level summary of features, metrics, or status. This panel is visible by default.</p>
  </div>

  <input type="radio" name="tabs-basic" class="tab" aria-label="Details">
  <div class="tab-content pt-4">
    <p>Details content: in-depth information such as configuration options, data breakdown, or extended documentation.</p>
  </div>

  <input type="radio" name="tabs-basic" class="tab" aria-label="Activity">
  <div class="tab-content pt-4">
    <p>Activity content: a timeline, event log, or recent changes relevant to this section.</p>
  </div>
</div>

Gaya Tab

Gunakan tabs-underline, tabs-box, atau tabs-lifted untuk mengubah gaya visual tab strip.

Underline style — a colored bottom border marks the active tab against a full-width separator line.
Details panel.
Activity panel.
Box style — the active tab gets a raised background inside a tinted pill container.
Details panel.
Activity panel.
Lifted style — the active tab appears as a card tab with borders that merge into the content panel below.
Details panel.
Activity panel.
html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div class="tabs tabs-underline">
  <input type="radio" name="style-underline" class="tab" aria-label="Overview" checked>
  <div class="tab-content pt-4">Underline style — a colored bottom border marks the active tab against a full-width separator line.</div>
  <input type="radio" name="style-underline" class="tab" aria-label="Details">
  <div class="tab-content pt-4">Details panel.</div>
  <input type="radio" name="style-underline" class="tab" aria-label="Activity">
  <div class="tab-content pt-4">Activity panel.</div>
</div>

<div class="tabs tabs-box">
  <input type="radio" name="style-box" class="tab" aria-label="Overview" checked>
  <div class="tab-content pt-4">Box style — the active tab gets a raised background inside a tinted pill container.</div>
  <input type="radio" name="style-box" class="tab" aria-label="Details">
  <div class="tab-content pt-4">Details panel.</div>
  <input type="radio" name="style-box" class="tab" aria-label="Activity">
  <div class="tab-content pt-4">Activity panel.</div>
</div>

<div class="tabs tabs-lifted">
  <input type="radio" name="style-lifted" class="tab" aria-label="Overview" checked>
  <div class="tab-content p-4">Lifted style — the active tab appears as a card tab with borders that merge into the content panel below.</div>
  <input type="radio" name="style-lifted" class="tab" aria-label="Details">
  <div class="tab-content p-4">Details panel.</div>
  <input type="radio" name="style-lifted" class="tab" aria-label="Activity">
  <div class="tab-content p-4">Activity panel.</div>
</div>

Ukuran Tab

Skalakan padding tab dari tabs-xs ke tabs-xl agar sesuai dengan kepadatan tata letak Anda.

Extra small tabs
Content
Small tabs
Content
Medium tabs (default)
Content
Large tabs
Content
Extra large tabs
Content
html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<div class="tabs tabs-underline tabs-xs">
  <input type="radio" name="size-xs" class="tab" aria-label="Extra Small" checked>
  <div class="tab-content pt-3">Extra small tabs</div>
  <input type="radio" name="size-xs" class="tab" aria-label="Tab 2">
  <div class="tab-content pt-3">Content</div>
</div>

<div class="tabs tabs-underline tabs-sm">
  <input type="radio" name="size-sm" class="tab" aria-label="Small" checked>
  <div class="tab-content pt-3">Small tabs</div>
  <input type="radio" name="size-sm" class="tab" aria-label="Tab 2">
  <div class="tab-content pt-3">Content</div>
</div>

<div class="tabs tabs-underline tabs-md">
  <input type="radio" name="size-md" class="tab" aria-label="Medium" checked>
  <div class="tab-content pt-3">Medium tabs (default)</div>
  <input type="radio" name="size-md" class="tab" aria-label="Tab 2">
  <div class="tab-content pt-3">Content</div>
</div>

<div class="tabs tabs-underline tabs-lg">
  <input type="radio" name="size-lg" class="tab" aria-label="Large" checked>
  <div class="tab-content pt-3">Large tabs</div>
  <input type="radio" name="size-lg" class="tab" aria-label="Tab 2">
  <div class="tab-content pt-3">Content</div>
</div>

<div class="tabs tabs-underline tabs-xl">
  <input type="radio" name="size-xl" class="tab" aria-label="Extra Large" checked>
  <div class="tab-content pt-3">Extra large tabs</div>
  <input type="radio" name="size-xl" class="tab" aria-label="Tab 2">
  <div class="tab-content pt-3">Content</div>
</div>

Tab dengan Tombol

Gabungkan tab dengan btn untuk merender tab sebagai tombol bergaya. Kelas btn mengambil alih warna dan radius batas sementara logika pemilihan tab dipertahankan.

Home — your primary landing panel with quick access to key actions and a summary view.

Profile — manage personal information, preferences, and account details.

Settings — configure notifications, privacy options, and connected integrations.

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<div class="tabs gap-2">
  <input type="radio" name="btn-tabs" class="tab btn btn-sm" aria-label="Home" checked>
  <div class="tab-content pt-4">
    <p>Home — your primary landing panel with quick access to key actions and a summary view.</p>
  </div>

  <input type="radio" name="btn-tabs" class="tab btn btn-sm" aria-label="Profile">
  <div class="tab-content pt-4">
    <p>Profile — manage personal information, preferences, and account details.</p>
  </div>

  <input type="radio" name="btn-tabs" class="tab btn btn-sm" aria-label="Settings">
  <div class="tab-content pt-4">
    <p>Settings — configure notifications, privacy options, and connected integrations.</p>
  </div>
</div>

Tab yang Dinonaktifkan

Tambahkan atribut tab-disabled dan disabled untuk mencegah interaksi dengan tab.

This tab is active and accessible.
This content is not reachable.
Another available tab panel.
html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<div class="tabs tabs-underline">
  <input type="radio" name="disabled-tabs" class="tab" aria-label="Available" checked>
  <div class="tab-content pt-4">This tab is active and accessible.</div>

  <input type="radio" name="disabled-tabs" class="tab tab-disabled" aria-label="Unavailable" disabled>
  <div class="tab-content pt-4">This content is not reachable.</div>

  <input type="radio" name="disabled-tabs" class="tab" aria-label="Another">
  <div class="tab-content pt-4">Another available tab panel.</div>
</div>

Tab Vertikal

tabs-vertical beralih ke tata letak kotak dengan tab di kolom sebelah kiri dan konten memenuhi kolom sebelah kanan. Garis pemisah tinggi penuh secara otomatis ditambahkan antar kolom.

General Settings

Manage your display name, language preference, and time zone.

Security

Update your password, enable two-factor authentication, and review active sessions.

Notifications

Choose which events trigger email, push, or in-app notifications.

Billing

View invoices, update your payment method, and manage your subscription plan.

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<div class="tabs tabs-vertical">
  <input type="radio" name="vertical" class="tab" aria-label="General" checked>
  <div class="tab-content px-6 py-2">
    <h3 class="font-semibold mb-1">General Settings</h3>
    <p>Manage your display name, language preference, and time zone.</p>
  </div>

  <input type="radio" name="vertical" class="tab" aria-label="Security">
  <div class="tab-content px-6 py-2">
    <h3 class="font-semibold mb-1">Security</h3>
    <p>Update your password, enable two-factor authentication, and review active sessions.</p>
  </div>

  <input type="radio" name="vertical" class="tab" aria-label="Notifications">
  <div class="tab-content px-6 py-2">
    <h3 class="font-semibold mb-1">Notifications</h3>
    <p>Choose which events trigger email, push, or in-app notifications.</p>
  </div>

  <input type="radio" name="vertical" class="tab" aria-label="Billing">
  <div class="tab-content px-6 py-2">
    <h3 class="font-semibold mb-1">Billing</h3>
    <p>View invoices, update your payment method, and manage your subscription plan.</p>
  </div>
</div>

Tab di Bawah

tabs-bottom membalik tata letak sehingga konten muncul di atas setrip tab, berguna untuk bilah navigasi gaya seluler atau bilah alat bawah.

Feed — latest posts and updates from people you follow.

Explore — discover trending topics, creators, and recommended content.

Saved — articles and posts you bookmarked for later.

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<div class="tabs tabs-bottom tabs-underline">
  <input type="radio" name="bottom-tabs" class="tab" aria-label="Feed" checked>
  <div class="tab-content pb-4">
    <p>Feed — latest posts and updates from people you follow.</p>
  </div>

  <input type="radio" name="bottom-tabs" class="tab" aria-label="Explore">
  <div class="tab-content pb-4">
    <p>Explore — discover trending topics, creators, and recommended content.</p>
  </div>

  <input type="radio" name="bottom-tabs" class="tab" aria-label="Saved">
  <div class="tab-content pb-4">
    <p>Saved — articles and posts you bookmarked for later.</p>
  </div>
</div>

Tab Disejajarkan ke Akhir

tabs-end mendorong tab strip ke kanan (akhir) penampung, berguna untuk navigasi sekunder atau menu tindakan.

Weekly view — aggregated metrics for the past 7 days.
Monthly view — trend analysis over the past 30 days.
Yearly view — annual summary and year-over-year comparison.
html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<div class="tabs tabs-underline tabs-end">
  <input type="radio" name="end-tabs" class="tab" aria-label="Week" checked>
  <div class="tab-content pt-4">Weekly view — aggregated metrics for the past 7 days.</div>

  <input type="radio" name="end-tabs" class="tab" aria-label="Month">
  <div class="tab-content pt-4">Monthly view — trend analysis over the past 30 days.</div>

  <input type="radio" name="end-tabs" class="tab" aria-label="Year">
  <div class="tab-content pt-4">Yearly view — annual summary and year-over-year comparison.</div>
</div>

Tab yang dikontrol JavaScript

Untuk pola non-radio (misalnya tautan jangkar, konten dinamis), tambahkan tab-active ke tab yang dipilih di JavaScript. tab-content yang dipasangkan ditampilkan melalui pemilih .tab-active + .tab-content.

Dashboard panel — visible because this tab has tab-active.
Reports panel.
Integrations panel.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<div class="tabs tabs-box" id="js-tabs">
  <button class="tab tab-active" data-tab="0">Dashboard</button>
  <div class="tab-content pt-4">Dashboard panel — visible because this tab has <code>tab-active</code>.</div>

  <button class="tab" data-tab="1">Reports</button>
  <div class="tab-content pt-4">Reports panel.</div>

  <button class="tab" data-tab="2">Integrations</button>
  <div class="tab-content pt-4">Integrations panel.</div>
</div>

<script>
  document.querySelectorAll('#js-tabs .tab').forEach(tab => {
    tab.addEventListener('click', () => {
      document.querySelectorAll('#js-tabs .tab').forEach(t => t.classList.remove('tab-active'));
      tab.classList.add('tab-active');
    });
  });
</script>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import { useTabs } from 'frutjam/react'

export default function TabsDemo() {
  const tabs = useTabs(0)
  return (
    <div className="tabs tabs-box">
      <button {...tabs.tabProps(0)}>Dashboard</button>
      <div {...tabs.panelProps(0)} className="tab-content pt-4">Dashboard panel.</div>

      <button {...tabs.tabProps(1)}>Reports</button>
      <div {...tabs.panelProps(1)} className="tab-content pt-4">Reports panel.</div>

      <button {...tabs.tabProps(2)}>Integrations</button>
      <div {...tabs.panelProps(2)} className="tab-content pt-4">Integrations panel.</div>
    </div>
  )
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script setup>
import { ref } from 'vue'
const active = ref(0)
</script>

<template>
  <div class="tabs tabs-box">
    <button
      v-for="(label, i) in ['Dashboard', 'Reports', 'Integrations']"
      :key="i"
      class="tab"
      :class="{ 'tab-active': active === i }"
      :aria-selected="active === i"
      role="tab"
      @click="active = i"
    ></button>

    <div class="tab-content pt-4" :hidden="active !== 0">Dashboard panel.</div>
    <div class="tab-content pt-4" :hidden="active !== 1">Reports panel.</div>
    <div class="tab-content pt-4" :hidden="active !== 2">Integrations panel.</div>
  </div>
</template>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<script>
  let active = $state(0)
  const labels = ['Dashboard', 'Reports', 'Integrations']
</script>

<div class="tabs tabs-box">
  {#each labels as label, i}
    <button
      class="tab"
      class:tab-active={active === i}
      aria-selected={active === i}
      role="tab"
      onclick={() => active = i}
    >{label}</button>
    <div class="tab-content pt-4" hidden={active !== i}>{label} panel.</div>
  {/each}
</div>