Tabs
Flexible navigation tool with various modes and features.
Usage
Know React? Check out Solid!
Know Solid? Check out Svelte!
Know Svelte? Check out Vue!
Know Vue? Check out React!
line
outline
enclosed
import { Tabs } from '~/components/ui'
export const Demo = (props: Tabs.RootProps) => {
const options = [
{ id: 'react', label: 'React' },
{ id: 'solid', label: 'Solid' },
{ id: 'svelte', label: 'Svelte' },
{ id: 'vue', label: 'Vue' },
]
return (
<Tabs.Root defaultValue="react" {...props}>
<Tabs.List>
{options.map((option) => (
<Tabs.Trigger key={option.id} value={option.id} disabled={option.id === 'svelte'}>
{option.label}
</Tabs.Trigger>
))}
<Tabs.Indicator />
</Tabs.List>
<Tabs.Content value="react">Know React? Check out Solid!</Tabs.Content>
<Tabs.Content value="solid">Know Solid? Check out Svelte!</Tabs.Content>
<Tabs.Content value="svelte">Know Svelte? Check out Vue!</Tabs.Content>
<Tabs.Content value="vue">Know Vue? Check out React!</Tabs.Content>
</Tabs.Root>
)
}
Installation
npx @park-ui/cli components add tabs
1
Styled Primitive
Copy the code snippet below into ~/components/ui/primitives/tabs.tsx
'use client'
import type { Assign } from '@ark-ui/react'
import { Tabs } from '@ark-ui/react/tabs'
import { type TabsVariantProps, tabs } from 'styled-system/recipes'
import type { ComponentProps, HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from '~/lib/create-style-context'
const { withProvider, withContext } = createStyleContext(tabs)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
HTMLDivElement,
Assign<Assign<HTMLStyledProps<'div'>, Tabs.RootProviderBaseProps>, TabsVariantProps>
>(Tabs.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
HTMLDivElement,
Assign<Assign<HTMLStyledProps<'div'>, Tabs.RootBaseProps>, TabsVariantProps>
>(Tabs.Root, 'root')
export const Content = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<'div'>, Tabs.ContentBaseProps>
>(Tabs.Content, 'content')
export const Indicator = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<'div'>, Tabs.IndicatorBaseProps>
>(Tabs.Indicator, 'indicator')
export const List = withContext<HTMLDivElement, Assign<HTMLStyledProps<'div'>, Tabs.ListBaseProps>>(
Tabs.List,
'list',
)
export const Trigger = withContext<
HTMLButtonElement,
Assign<HTMLStyledProps<'button'>, Tabs.TriggerBaseProps>
>(Tabs.Trigger, 'trigger')
export { TabsContext as Context } from '@ark-ui/react/tabs'
import { type Assign, Tabs } from '@ark-ui/solid'
import type { ComponentProps } from 'solid-js'
import { type TabsVariantProps, tabs } from 'styled-system/recipes'
import type { HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from '~/lib/create-style-context'
const { withProvider, withContext } = createStyleContext(tabs)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
Assign<Assign<HTMLStyledProps<'div'>, Tabs.RootProviderBaseProps>, TabsVariantProps>
>(Tabs.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
Assign<Assign<HTMLStyledProps<'div'>, Tabs.RootBaseProps>, TabsVariantProps>
>(Tabs.Root, 'root')
export const Content = withContext<Assign<HTMLStyledProps<'div'>, Tabs.ContentBaseProps>>(
Tabs.Content,
'content',
)
export const Indicator = withContext<Assign<HTMLStyledProps<'div'>, Tabs.IndicatorBaseProps>>(
Tabs.Indicator,
'indicator',
)
export const List = withContext<Assign<HTMLStyledProps<'div'>, Tabs.ListBaseProps>>(
Tabs.List,
'list',
)
export const Trigger = withContext<Assign<HTMLStyledProps<'button'>, Tabs.TriggerBaseProps>>(
Tabs.Trigger,
'trigger',
)
export { TabsContext as Context } from '@ark-ui/solid'
No snippet found
Extend ~/components/ui/primitives/index.ts
with the following line:
export * as Tabs from './tabs'
2
Integrate Recipe
If you're not using @park-ui/preset
, add the following recipe to yourpanda.config.ts
:
import { tabsAnatomy } from '@ark-ui/anatomy'
import { defineSlotRecipe } from '@pandacss/dev'
export const tabs = defineSlotRecipe({
className: 'tabs',
slots: tabsAnatomy.keys(),
base: {
root: {
colorPalette: 'accent',
display: 'flex',
width: 'full',
_horizontal: {
flexDirection: 'column',
},
_vertical: {
flexDirection: 'row',
},
},
list: {
display: 'flex',
flexShrink: '0',
_horizontal: {
flexDirection: 'row',
},
_vertical: {
flexDirection: 'column',
},
overflow: 'auto',
position: 'relative',
scrollbarWidth: 'none',
'&::-webkit-scrollbar': {
display: 'none',
},
},
trigger: {
alignItems: 'center',
color: 'fg.muted',
cursor: 'pointer',
display: 'inline-flex',
flexShrink: '0',
fontWeight: 'semibold',
gap: '2',
justifyContent: 'center',
transitionDuration: 'normal',
transitionProperty: 'color, background, border-color',
transitionTimingFunction: 'default',
whiteSpace: 'nowrap',
zIndex: '1',
_disabled: {
color: 'fg.disabled',
cursor: 'not-allowed',
_hover: {
color: 'fg.disabled',
},
},
_hover: {
color: 'fg.muted',
},
_selected: {
color: 'fg.default',
_hover: {
color: 'fg.default',
},
},
_vertical: {
justifyContent: 'flex-start',
},
},
},
defaultVariants: {
size: 'md',
variant: 'line',
},
variants: {
variant: {
enclosed: {
list: {
borderRadius: 'l3',
borderWidth: '1px',
px: '1',
backgroundColor: {
base: 'gray.a2',
_dark: 'bg.canvas',
},
_horizontal: {
alignItems: 'center',
},
_vertical: {
height: 'fit-content!',
py: '1',
},
},
indicator: {
backgroundColor: {
base: 'bg.default',
_dark: 'bg.subtle',
},
boxShadow: 'xs',
borderRadius: 'l2',
'--transition-duration': '200ms!',
height: 'var(--height)',
width: 'var(--width)',
},
},
line: {
list: {
_horizontal: {
boxShadow: '0 -1px 0 0 inset var(--colors-border-default)',
gap: '4',
},
_vertical: {
boxShadow: '1px 0 0 0 inset var(--colors-border-default)',
gap: '1',
},
},
indicator: {
background: 'colorPalette.default',
_horizontal: {
bottom: '0',
height: '2px',
width: 'var(--width)',
},
_vertical: {
height: 'var(--height)',
left: '0',
width: '2px',
},
},
content: {
pt: '4',
},
trigger: {
_horizontal: {
pb: '2.5',
},
},
},
outline: {
list: {
_horizontal: {
mb: '-1px',
},
_vertical: {
mr: '-1px',
},
},
trigger: {
borderColor: 'transparent',
borderWidth: '1px',
_horizontal: {
borderTopRadius: 'l2',
},
_vertical: {
borderTopLeftRadius: 'l2',
borderBottomLeftRadius: 'l2',
},
_selected: {
background: 'bg.default',
borderColor: 'border.subtle',
_horizontal: {
borderBottomColor: 'transparent',
},
_vertical: {
borderRightColor: 'transparent',
},
},
},
content: {
borderWidth: '1px',
borderColor: 'border.subtle',
background: 'bg.default',
width: 'full',
},
},
},
size: {
sm: {
trigger: {
'& svg': {
width: '4',
height: '4',
},
},
},
md: {
trigger: {
'& svg': {
width: '5',
height: '5',
},
},
},
lg: {
trigger: {
'& svg': {
width: '5',
height: '5',
},
},
},
},
},
compoundVariants: [
{
size: 'sm',
variant: 'enclosed',
css: {
list: {
height: '10',
},
trigger: {
h: '8',
minW: '8',
textStyle: 'sm',
px: '3',
},
content: {
p: '3.5',
},
},
},
{
size: 'md',
variant: 'enclosed',
css: {
list: {
height: '11',
},
trigger: {
h: '9',
minW: '9',
textStyle: 'sm',
px: '3.5',
},
content: {
p: '4',
},
},
},
{
size: 'lg',
variant: 'enclosed',
css: {
list: {
height: '12',
},
trigger: {
h: '10',
minW: '10',
textStyle: 'sm',
px: '4',
},
content: {
p: '4.5',
},
},
},
{
size: 'sm',
variant: 'outline',
css: {
trigger: {
h: '9',
minW: '9',
textStyle: 'sm',
px: '3.5',
},
content: {
p: '3.5',
},
},
},
{
size: 'md',
variant: 'outline',
css: {
trigger: {
h: '10',
minW: '10',
textStyle: 'sm',
px: '4',
},
content: {
p: '4',
},
},
},
{
size: 'lg',
variant: 'outline',
css: {
trigger: {
h: '11',
minW: '11',
textStyle: 'md',
px: '4.5',
},
content: {
p: '4.5',
},
},
},
{
size: 'sm',
variant: 'line',
css: {
trigger: {
fontSize: 'sm',
h: '9',
minW: '9',
px: '2.5',
},
content: {
pt: '3',
},
},
},
{
size: 'md',
variant: 'line',
css: {
trigger: {
fontSize: 'md',
h: '10',
minW: '10',
px: '3',
},
content: {
pt: '4',
},
},
},
{
size: 'lg',
variant: 'line',
css: {
trigger: {
px: '3.5',
h: '11',
minW: '11',
fontSize: 'md',
},
content: {
pt: '5',
},
},
},
],
})