Tabs

Navigation

여러 콘텐츠 뷰 사이를 전환하는 탭.

Usage

같은 맥락의 콘텐츠를 여러 패널로 나눠 전환. 페이지 내 섹션 전환

import

import

import { Tabs } from '@mildang/design-system/Tabs';

Tabs 하나만 가져오면 Tabs.List · Tabs.Trigger · Tabs.Content · Tabs.Icon · Tabs.Text · Tabs.ScrollSpy 를 그 아래에서 쓸 수 있다.

예제를 복사해 쓸 때 필요한 준비

@mildang/icons 를 따로 설치한다. DS 패키지에 아이콘 컴포넌트가 포함되지 않는다.

@mildang/styled-system 은 이 저장소에서 Panda 가 생성하는 산출물이다. 저장소 안에서는 turbo run ship 이후 쓸 수 있고, 패키지 소비자는 자기 Panda 산출물이나 다른 레이아웃 수단으로 바꿔야 한다.

Anatomy

tsx

import { Tabs } from '@mildang/design-system/Tabs';

export default function Example() {
  return (
    <Tabs.List>
      <Tabs.Trigger>
        <Tabs.Icon />
        <Tabs.Text />
      </Tabs.Trigger>
    </Tabs.List>
    <Tabs.Content />
    <Tabs.ScrollSpy />
  );
}

부품

필수 여부

반복

위치

Tabs.List

필수

Tabs.Trigger

필수

여러 개 가능

Tabs.List 안에 탭마다 둔다.

Tabs.Icon

선택

Tabs.Trigger 안에 둔다.

Tabs.Text

선택

Tabs.Trigger 안에 둔다.

Tabs.Content

필수

여러 개 가능

Tabs.List의 Trigger value와 같은 value로 연결한다.

Tabs.ScrollSpy

선택

Tabs와 연결된 스크롤 영역에 둔다.

  • Tabs.List 안에 Trigger를 반복하고 각 Trigger의 value를 Content의 value와 일치시킨다.
  • Trigger와 Content는 같은 Tabs 상태 context 안에 배치한다.
  • 탭에 아이콘이나 보조 Chip을 넣을 때는 Trigger 안에서 Icon·Text와 함께 조합한다.

API Reference

Tabs Props

Prop

Type

Default

showFade

boolean

true

showLine

boolean

true

type

"scroll" | "stretch"

scroll

Tabs.List

탭 트리거들을 감싸고 배치 방식을 결정한다.

Prop

Type

Default

scrollSpy

boolean

false

scrollSpyOptions

TabsScrollSpyOptions

지정 안 함

showFade

boolean

true

showLine

boolean

true

type

"scroll" | "stretch"

scroll

Tabs.Trigger

하나의 콘텐츠 패널을 선택하는 탭 버튼이다.

공개 Props 없음

Tabs.Icon

탭 레이블 앞이나 주변에 보조 아이콘을 표시한다.

Prop

Type

Default

children
필수

ReactNode

지정 안 함

Tabs.Text

탭의 읽을 수 있는 레이블을 표시한다.

Prop

Type

Default

as

any

지정 안 함

bold

boolean

지정 안 함

Tabs.Content

선택된 탭에 대응하는 콘텐츠 패널이다.

Prop

Type

Default

value
필수

string

지정 안 함

Tabs.ScrollSpy

스크롤 위치에 따라 현재 탭을 동기화하는 보조 기능이다.

Prop

Type

Default

children
필수

ReactNode

지정 안 함

enabled

boolean

false

offsetTop

number

0

onChangeActiveId

(currentId: string) => void

지정 안 함

제어 및 조합

value와 onValueChange를 함께 사용하면 활성 탭을 외부 상태로 제어할 수 있습니다. 탭 레이블에는 아이콘과 Chip을 조합할 수 있습니다.

제어 탭

외부 상태로 활성 탭을 관리합니다.

import { useState } from 'react'; import { Tabs } from '@mildang/design-system/Tabs';
export default function TabsControlledExample() { const [value, setValue] = useState('account'); return <Tabs value={value} onValueChange={setValue}><Tabs.List><Tabs.Trigger value="account"><Tabs.Text>Account</Tabs.Text></Tabs.Trigger><Tabs.Trigger value="password"><Tabs.Text>Password</Tabs.Text></Tabs.Trigger></Tabs.List><Tabs.Content value="account">Account content</Tabs.Content><Tabs.Content value="password">Password content</Tabs.Content></Tabs>; }

아이콘과 Chip

탭에 아이콘과 보조 Chip을 조합합니다.

import { Tabs } from '@mildang/design-system/Tabs';
import { type ComponentProps } from 'react';
import StarFill from '@mildang/icons/react/star-fill';
import { Chip } from '@mildang/design-system/Chip';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  width: '400px',
});

const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof Tabs>;

const TabsTabWithIconAndChipExampleRender = (args: ComponentProps<typeof Tabs>) => (
    <Tabs {...args} defaultValue="tab1" className={containerStyle}>
      <Tabs.List>
        <Tabs.Trigger value="tab1">
          <Tabs.Icon>
            <StarFill />
          </Tabs.Icon>
          <Tabs.Text>TAB</Tabs.Text>
          <Tabs.Text color="positive.text.base">20</Tabs.Text>
          <Chip label="검수필요" size="sm" color="blue" type="fill" />
          <Tabs.Icon>
            <StarFill />
          </Tabs.Icon>
        </Tabs.Trigger>
        <Tabs.Trigger value="tab2">
          <Tabs.Icon>
            <StarFill />
          </Tabs.Icon>
          <Tabs.Text>TAB</Tabs.Text>
          <Tabs.Text color="positive.text.base">20</Tabs.Text>
          <Chip label="검수필요" size="sm" color="red" type="fill" />
          <Tabs.Icon color="critical.text.base">
            <StarFill />
          </Tabs.Icon>
        </Tabs.Trigger>
        <Tabs.Trigger value="tab3">
          <Tabs.Icon>
            <StarFill />
          </Tabs.Icon>
          <Tabs.Text>TAB</Tabs.Text>
          <Tabs.Text color="positive.text.base">20</Tabs.Text>
          <Chip label="검수필요" size="sm" color="red" type="fill" />
          <Tabs.Icon color="critical.text.base">
            <StarFill />
          </Tabs.Icon>
        </Tabs.Trigger>
      </Tabs.List>
      <Tabs.Content value="tab1">첫 번째 탭 내용입니다.</Tabs.Content>
      <Tabs.Content value="tab2">두 번째 탭 내용입니다.</Tabs.Content>
      <Tabs.Content value="tab3">세 번째 탭 내용입니다.</Tabs.Content>
    </Tabs>
  );

export default function TabsTabWithIconAndChipExample(props: Partial<ComponentProps<typeof Tabs>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof Tabs>;
  return TabsTabWithIconAndChipExampleRender(mergedProps);
}

사용 가이드

권장

  • scroll 타입은 항목 수나 레이블 폭이 유동적일 때 사용하고 오버플로우 안내를 유지한다.
  • stretch 타입은 2~3개의 짧은 탭을 같은 너비로 나눌 때 사용한다.

지양

  • 서로 다른 정보 구조나 페이지 이동을 한 Tabs 안에 섞지 않는다.

예제

기본 사용

scroll·stretch 두 type 을 나란히 비교한다. scroll 은 탭이 넘치면 가로 스크롤, stretch 는 폭을 균등하게 나눈다.

import type { ComponentProps } from 'react';
import { Tabs } from '@mildang/design-system/Tabs';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  width: '400px',
});

const STORY_DEFAULT_ARGS = { ...({}), ...({
    showLine: true,
  }) } as ComponentProps<typeof Tabs>;

const TabsDemoExampleRender = (args: ComponentProps<typeof Tabs>) => (
    <Tabs {...args} defaultValue="account" className={containerStyle}>
      <div>
        <Tabs.List>
          <Tabs.Trigger value="account">
            <Tabs.Text>Account</Tabs.Text>
          </Tabs.Trigger>
          <Tabs.Trigger value="password">
            <Tabs.Text>Password</Tabs.Text>
          </Tabs.Trigger>
        </Tabs.List>
      </div>
      <Tabs.Content value="account">Make changes to your account here.</Tabs.Content>
      <Tabs.Content value="password">Change your password here.</Tabs.Content>
    </Tabs>
  );

export default function TabsDemoExample(props: Partial<ComponentProps<typeof Tabs>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof Tabs>;
  return TabsDemoExampleRender(mergedProps);
}

ScrollWithGradientOverlay

import type { ComponentProps } from 'react';
import { Tabs } from '@mildang/design-system/Tabs';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  width: '400px',
});

const STORY_DEFAULT_ARGS = { ...({}), ...({
    showLine: true,
    type: 'scroll',
  }) } as ComponentProps<typeof Tabs>;

const TabsScrollWithGradientOverlayExampleRender = (args: ComponentProps<typeof Tabs>) => (
    <Tabs {...args} defaultValue="tab-0" className={containerStyle}>
      <Tabs.List type="scroll">
        {Array.from({ length: 10 }, (_, i) => (
          <Tabs.Trigger key={i} value={`tab-${i}`}>
            <Tabs.Text>Tab {i + 1}</Tabs.Text>
          </Tabs.Trigger>
        ))}
      </Tabs.List>
      <Tabs.Content value="tab-0">
        탭이 많아 스크롤될 때 오른쪽에 gradient overlay가 자동으로 나타납니다.
      </Tabs.Content>
    </Tabs>
  );

export default function TabsScrollWithGradientOverlayExample(props: Partial<ComponentProps<typeof Tabs>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof Tabs>;
  return TabsScrollWithGradientOverlayExampleRender(mergedProps);
}

StretchDemo

import type { ComponentProps } from 'react';
import { Tabs } from '@mildang/design-system/Tabs';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  width: '400px',
});

const STORY_DEFAULT_ARGS = { ...({}), ...({
    showLine: true,
    type: 'stretch',
  }) } as ComponentProps<typeof Tabs>;

const TabsStretchDemoExampleRender = (args: ComponentProps<typeof Tabs>) => (
    <Tabs {...args} defaultValue="account" className={containerStyle}>
      <Tabs.List type="stretch">
        <Tabs.Trigger value="account">
          <Tabs.Text>Account</Tabs.Text>
        </Tabs.Trigger>
        <Tabs.Trigger value="password">
          <Tabs.Text>Password</Tabs.Text>
        </Tabs.Trigger>
        <Tabs.Trigger value="settings">
          <Tabs.Text>Settings</Tabs.Text>
        </Tabs.Trigger>
      </Tabs.List>
      <Tabs.Content value="account">Account 탭 내용입니다.</Tabs.Content>
      <Tabs.Content value="password">Password 탭 내용입니다.</Tabs.Content>
      <Tabs.Content value="settings">Settings 탭 내용입니다.</Tabs.Content>
    </Tabs>
  );

export default function TabsStretchDemoExample(props: Partial<ComponentProps<typeof Tabs>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof Tabs>;
  return TabsStretchDemoExampleRender(mergedProps);
}

StretchWithoutLine

import type { ComponentProps } from 'react';
import { Tabs } from '@mildang/design-system/Tabs';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  width: '400px',
});

const STORY_DEFAULT_ARGS = { ...({}), ...({
    showLine: false,
    type: 'stretch',
  }) } as ComponentProps<typeof Tabs>;

const TabsStretchWithoutLineExampleRender = (args: ComponentProps<typeof Tabs>) => (
    <Tabs {...args} defaultValue="account" className={containerStyle}>
      <Tabs.List type="stretch">
        <Tabs.Trigger value="account">
          <Tabs.Text>Account</Tabs.Text>
        </Tabs.Trigger>
        <Tabs.Trigger value="password">
          <Tabs.Text>Password</Tabs.Text>
        </Tabs.Trigger>
      </Tabs.List>
      <Tabs.Content value="account">Account 탭 내용입니다.</Tabs.Content>
      <Tabs.Content value="password">Password 탭 내용입니다.</Tabs.Content>
    </Tabs>
  );

export default function TabsStretchWithoutLineExample(props: Partial<ComponentProps<typeof Tabs>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof Tabs>;
  return TabsStretchWithoutLineExampleRender(mergedProps);
}

WithoutLine

import type { ComponentProps } from 'react';
import { Tabs } from '@mildang/design-system/Tabs';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  width: '400px',
});

const STORY_DEFAULT_ARGS = { ...({}), ...({
    showLine: false,
  }) } as ComponentProps<typeof Tabs>;

const TabsWithoutLineExampleRender = (args: ComponentProps<typeof Tabs>) => (
    <Tabs {...args} defaultValue="account" className={containerStyle}>
      <Tabs.List>
        <Tabs.Trigger value="account">
          <Tabs.Text>Account</Tabs.Text>
        </Tabs.Trigger>
        <Tabs.Trigger value="password">
          <Tabs.Text>Password</Tabs.Text>
        </Tabs.Trigger>
      </Tabs.List>
      <Tabs.Content value="account">Make changes to your account here.</Tabs.Content>
      <Tabs.Content value="password">Change your password here.</Tabs.Content>
    </Tabs>
  );

export default function TabsWithoutLineExample(props: Partial<ComponentProps<typeof Tabs>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof Tabs>;
  return TabsWithoutLineExampleRender(mergedProps);
}

DisabledTab

import { Tabs } from '@mildang/design-system/Tabs';
import { type ComponentProps } from 'react';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  width: '400px',
});

const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof Tabs>;

const TabsDisabledTabExampleRender = (args: ComponentProps<typeof Tabs>) => (
    <Tabs {...args} defaultValue="account" className={containerStyle}>
      <Tabs.List>
        <Tabs.Trigger value="account">
          <Tabs.Text>Account</Tabs.Text>
        </Tabs.Trigger>
        <Tabs.Trigger value="password" disabled>
          <Tabs.Text>Password</Tabs.Text>
        </Tabs.Trigger>
      </Tabs.List>
      <Tabs.Content value="account">Make changes to your account here.</Tabs.Content>
      <Tabs.Content value="password">Change your password here.</Tabs.Content>
    </Tabs>
  );

export default function TabsDisabledTabExample(props: Partial<ComponentProps<typeof Tabs>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof Tabs>;
  return TabsDisabledTabExampleRender(mergedProps);
}

TabWithTabIcon

import { Tabs } from '@mildang/design-system/Tabs';
import { type ComponentProps } from 'react';
import StarFill from '@mildang/icons/react/star-fill';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  width: '400px',
});

const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof Tabs>;

const TabsTabWithTabIconExampleRender = (args: ComponentProps<typeof Tabs>) => (
    <Tabs {...args} defaultValue="tab1" className={containerStyle}>
      <Tabs.List>
        <Tabs.Trigger value="tab1">
          <Tabs.Icon>
            <StarFill />
          </Tabs.Icon>
          <Tabs.Text>TAB</Tabs.Text>
          <Tabs.Text color="positive.text.base">2012231</Tabs.Text>
          <Tabs.Icon>
            <StarFill />
          </Tabs.Icon>
        </Tabs.Trigger>
        <Tabs.Trigger value="tab2">
          <Tabs.Icon>
            <StarFill />
          </Tabs.Icon>
          <Tabs.Text>TAB</Tabs.Text>
          <Tabs.Text color="positive.text.base">20</Tabs.Text>
          <Tabs.Icon color="critical.text.base">
            <StarFill />
          </Tabs.Icon>
        </Tabs.Trigger>
      </Tabs.List>
      <Tabs.Content value="tab1">첫 번째 탭 내용입니다.</Tabs.Content>
      <Tabs.Content value="tab2">두 번째 탭 내용입니다.</Tabs.Content>
    </Tabs>
  );

export default function TabsTabWithTabIconExample(props: Partial<ComponentProps<typeof Tabs>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof Tabs>;
  return TabsTabWithTabIconExampleRender(mergedProps);
}

WithScrollSpy

import { Tabs } from '@mildang/design-system/Tabs';
import { type ComponentProps } from 'react';
import { useState } from 'react';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  width: '400px',
});

const scrollSpyContainerStyle = css({
  width: '400px',
});

const scrollSpySectionStyle = css({
  minHeight: '320px',
  py: '24',
  borderBottom: '1px solid',
  borderColor: 'neutral.border.low',
});

const TabsWithScrollSpyDemo = ({ args }: { args: ComponentProps<typeof Tabs> }) => {
  const [value, setValue] = useState('intro');

  const handleValueChange = (nextValue: string) => {
    setValue(nextValue);
    const section = document.getElementById(nextValue);
    section?.scrollIntoView({ behavior: 'smooth', block: 'start' });
  };

  const handleChangeActiveId = (currentId: string) => {
    setValue(currentId);
  };

  return (
    <div className={scrollSpyContainerStyle}>
      <Tabs {...args} value={value} onValueChange={handleValueChange} className={containerStyle}>
        <Tabs.List
          scrollSpy
          scrollSpyOptions={{
            onChangeActiveId: handleChangeActiveId,
            offsetTop: 72,
          }}
        >
          <Tabs.Trigger asChild value="intro">
            <a href="#intro">
              <Tabs.Text>Intro</Tabs.Text>
            </a>
          </Tabs.Trigger>
          <Tabs.Trigger asChild value="lessons">
            <a href="#lessons">
              <Tabs.Text>Lessons</Tabs.Text>
            </a>
          </Tabs.Trigger>
          <Tabs.Trigger asChild value="reviews">
            <a href="#reviews">
              <Tabs.Text>Reviews</Tabs.Text>
            </a>
          </Tabs.Trigger>
        </Tabs.List>
      </Tabs>

      <section id="intro" className={scrollSpySectionStyle}>
        Intro section
      </section>
      <section id="lessons" className={scrollSpySectionStyle}>
        Lessons section
      </section>
      <section id="reviews" className={scrollSpySectionStyle}>
        Reviews section
      </section>
    </div>
  );
};

const STORY_DEFAULT_ARGS = { ...({}), ...({
    showLine: true,
  }) } as ComponentProps<typeof Tabs>;

const TabsWithScrollSpyExampleRender = (args: ComponentProps<typeof Tabs>) => <TabsWithScrollSpyDemo args={args} />;

export default function TabsWithScrollSpyExample(props: Partial<ComponentProps<typeof Tabs>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof Tabs>;
  return TabsWithScrollSpyExampleRender(mergedProps);
}

DynamicTabInsertion

import { Tabs } from '@mildang/design-system/Tabs';
import { type ComponentProps } from 'react';
import { useState } from 'react';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  width: '400px',
});

// 탭이 동적으로 추가/제거될 때 밑줄(thumb)이 active 탭을 계속 따라가야 한다.
// 초기 [메시지 작성, 액션] 중 '액션'이 active. 버튼으로 '추천 답변'을 액션 왼쪽(중간)에 삽입하면
// active 탭('액션')은 그대로지만 오른쪽으로 밀린다. 밑줄은 액션 아래에 유지되어야 한다.
const toggleButtonStyle = css({
  alignSelf: 'flex-start',
  px: '12',
  py: '8',
  borderRadius: '8',
  border: '1px solid',
  borderColor: 'neutral.border.low',
  cursor: 'pointer',
});

const dynamicContainerStyle = css({
  width: '400px',
  display: 'flex',
  flexDirection: 'column',
  gap: '16',
});

const DynamicTabInsertionDemo = ({ args }: { args: ComponentProps<typeof Tabs> }) => {
  const [hasRecommendation, setHasRecommendation] = useState(false);
  const [value, setValue] = useState('action');

  return (
    <div className={dynamicContainerStyle}>
      <button
        type="button"
        className={toggleButtonStyle}
        onClick={() => setHasRecommendation((prev) => !prev)}
      >
        {hasRecommendation ? '추천 답변 탭 제거' : '추천 답변 탭 추가 (중간 삽입)'}
      </button>
      <Tabs {...args} value={value} onValueChange={setValue} className={containerStyle}>
        <Tabs.List type="stretch">
          <Tabs.Trigger value="compose">
            <Tabs.Text>메시지 작성</Tabs.Text>
          </Tabs.Trigger>
          {hasRecommendation && (
            <Tabs.Trigger value="recommendation">
              <Tabs.Text>추천 답변</Tabs.Text>
            </Tabs.Trigger>
          )}
          <Tabs.Trigger value="action">
            <Tabs.Text>액션</Tabs.Text>
          </Tabs.Trigger>
        </Tabs.List>
      </Tabs>
    </div>
  );
};

const STORY_DEFAULT_ARGS = { ...({}), ...({
    showLine: true,
  }) } as ComponentProps<typeof Tabs>;

const TabsDynamicTabInsertionExampleRender = (args: ComponentProps<typeof Tabs>) => <DynamicTabInsertionDemo args={args} />;

export default function TabsDynamicTabInsertionExample(props: Partial<ComponentProps<typeof Tabs>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof Tabs>;
  return TabsDynamicTabInsertionExampleRender(mergedProps);
}

ScrollFadeToggle

import { Tabs } from '@mildang/design-system/Tabs';
import { type ComponentProps } from 'react';
import { useState } from 'react';
import { css } from '@mildang/styled-system/css';

const containerStyle = css({
  width: '400px',
});

// 탭이 동적으로 추가/제거될 때 밑줄(thumb)이 active 탭을 계속 따라가야 한다.
// 초기 [메시지 작성, 액션] 중 '액션'이 active. 버튼으로 '추천 답변'을 액션 왼쪽(중간)에 삽입하면
// active 탭('액션')은 그대로지만 오른쪽으로 밀린다. 밑줄은 액션 아래에 유지되어야 한다.
const toggleButtonStyle = css({
  alignSelf: 'flex-start',
  px: '12',
  py: '8',
  borderRadius: '8',
  border: '1px solid',
  borderColor: 'neutral.border.low',
  cursor: 'pointer',
});

const dynamicContainerStyle = css({
  width: '400px',
  display: 'flex',
  flexDirection: 'column',
  gap: '16',
});

// [회귀] scroll 타입에서 탭이 많아 오버플로우되면 gradient가 나타나고,
// 탭이 줄어 더 이상 넘치지 않으면 gradient가 사라져야 한다.
// thumb(밑줄)은 스크롤 컨테이너 내부의 absolute 요소라, 과거엔 이전 너비가 container.scrollWidth에
// 남아 탭을 줄여도 오버플로우로 오판돼 gradient가 계속 노출되던 버그가 있었다. 그 회귀 방지용.
const ScrollFadeToggleDemo = ({ args }: { args: ComponentProps<typeof Tabs> }) => {
  const [many, setMany] = useState(true);
  const count = many ? 10 : 2;

  return (
    <div className={dynamicContainerStyle}>
      <button type="button" className={toggleButtonStyle} onClick={() => setMany((prev) => !prev)}>
        {many ? '탭 줄이기 (2개 · fade 사라져야 함)' : '탭 늘리기 (10개 · fade 나타나야 함)'}
      </button>
      <Tabs {...args} defaultValue="tab-0" className={containerStyle}>
        <Tabs.List type="scroll">
          {Array.from({ length: count }, (_, i) => (
            <Tabs.Trigger key={i} value={`tab-${i}`}>
              <Tabs.Text>Tab {i + 1}</Tabs.Text>
            </Tabs.Trigger>
          ))}
        </Tabs.List>
      </Tabs>
    </div>
  );
};

const STORY_DEFAULT_ARGS = { ...({}), ...({
    showLine: true,
    type: 'scroll',
  }) } as ComponentProps<typeof Tabs>;

const TabsScrollFadeToggleExampleRender = (args: ComponentProps<typeof Tabs>) => <ScrollFadeToggleDemo args={args} />;

export default function TabsScrollFadeToggleExample(props: Partial<ComponentProps<typeof Tabs>>) {
  const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof Tabs>;
  return TabsScrollFadeToggleExampleRender(mergedProps);
}