Toggle Group
A set of two-state buttons that can be toggled on or off.
outline
ghost
import {
  AlignCenterIcon,
  AlignJustifyIcon,
  AlignLeftIcon,
  AlignRightIcon,
  BoldIcon,
  ItalicIcon,
  UnderlineIcon,
} from 'lucide-react'
import { Stack } from 'styled-system/jsx'
import { ToggleGroup } from '~/components/ui/toggle-group'
export const Demo = (props: ToggleGroup.RootProps) => {
  return (
    <Stack
      direction={props.orientation === 'horizontal' ? 'row' : 'column'}
      gap="3"
      borderRadius="l3"
      borderWidth={props.variant === 'ghost' ? '1px' : '0'}
      p={props.variant === 'ghost' ? '1' : '0'}
    >
      <ToggleGroup.Root multiple {...props}>
        <ToggleGroup.Item value="bold" aria-label="Toggle Bold">
          <BoldIcon />
        </ToggleGroup.Item>
        <ToggleGroup.Item value="italic" aria-label="Toggle Italic">
          <ItalicIcon />
        </ToggleGroup.Item>
        <ToggleGroup.Item value="underline" aria-label="Toggle Underline">
          <UnderlineIcon />
        </ToggleGroup.Item>
      </ToggleGroup.Root>
      <ToggleGroup.Root defaultValue={['left']} {...props}>
        <ToggleGroup.Item value="left" aria-label="Align Left">
          <AlignLeftIcon />
        </ToggleGroup.Item>
        <ToggleGroup.Item value="center" aria-label="Align Center">
          <AlignCenterIcon />
        </ToggleGroup.Item>
        <ToggleGroup.Item value="right" aria-label="Align Right">
          <AlignRightIcon />
        </ToggleGroup.Item>
        <ToggleGroup.Item value="justify" aria-label="Align Justify">
          <AlignJustifyIcon />
        </ToggleGroup.Item>
      </ToggleGroup.Root>
    </Stack>
  )
}
import {
  AlignCenterIcon,
  AlignJustifyIcon,
  AlignLeftIcon,
  AlignRightIcon,
  BoldIcon,
  ItalicIcon,
  UnderlineIcon,
} from 'lucide-solid'
import { Stack } from 'styled-system/jsx'
import { ToggleGroup } from '~/components/ui/toggle-group'
export const Demo = (props: ToggleGroup.RootProps) => {
  return (
    <Stack
      direction={props.orientation === 'horizontal' ? 'row' : 'column'}
      gap="3"
      borderRadius="l3"
      borderWidth={props.variant === 'ghost' ? '1px' : '0'}
      p={props.variant === 'ghost' ? '1' : '0'}
    >
      <ToggleGroup.Root multiple {...props}>
        <ToggleGroup.Item value="bold" aria-label="Toggle Bold">
          <BoldIcon />
        </ToggleGroup.Item>
        <ToggleGroup.Item value="italic" aria-label="Toggle Italic">
          <ItalicIcon />
        </ToggleGroup.Item>
        <ToggleGroup.Item value="underline" aria-label="Toggle Underline">
          <UnderlineIcon />
        </ToggleGroup.Item>
      </ToggleGroup.Root>
      <ToggleGroup.Root defaultValue={['left']} {...props}>
        <ToggleGroup.Item value="left" aria-label="Align Left">
          <AlignLeftIcon />
        </ToggleGroup.Item>
        <ToggleGroup.Item value="center" aria-label="Align Center">
          <AlignCenterIcon />
        </ToggleGroup.Item>
        <ToggleGroup.Item value="right" aria-label="Align Right">
          <AlignRightIcon />
        </ToggleGroup.Item>
        <ToggleGroup.Item value="justify" aria-label="Align Justify">
          <AlignJustifyIcon />
        </ToggleGroup.Item>
      </ToggleGroup.Root>
    </Stack>
  )
}
Usage
import { ToggleGroup } from '~/components/ui/toggle-group'Installation
npx @park-ui/cli components add toggle-group1
Add Styled Primitive
Copy the code snippet below into ~/components/ui/styled/toggle-group.tsx
'use client'
import type { Assign } from '@ark-ui/react'
import { ToggleGroup } from '@ark-ui/react/toggle-group'
import { type ToggleGroupVariantProps, toggleGroup } from 'styled-system/recipes'
import type { ComponentProps, HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(toggleGroup)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
  HTMLDivElement,
  Assign<Assign<HTMLStyledProps<'div'>, ToggleGroup.RootProviderBaseProps>, ToggleGroupVariantProps>
>(ToggleGroup.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
  HTMLDivElement,
  Assign<Assign<HTMLStyledProps<'div'>, ToggleGroup.RootBaseProps>, ToggleGroupVariantProps>
>(ToggleGroup.Root, 'root')
export const Item = withContext<
  HTMLButtonElement,
  Assign<HTMLStyledProps<'button'>, ToggleGroup.ItemBaseProps>
>(ToggleGroup.Item, 'item')
export { ToggleGroupContext as Context } from '@ark-ui/react/toggle-group'
import { type Assign, ToggleGroup } from '@ark-ui/solid'
import type { ComponentProps } from 'solid-js'
import { type ToggleGroupVariantProps, toggleGroup } from 'styled-system/recipes'
import type { HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(toggleGroup)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
  Assign<Assign<HTMLStyledProps<'div'>, ToggleGroup.RootProviderBaseProps>, ToggleGroupVariantProps>
>(ToggleGroup.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
  Assign<Assign<HTMLStyledProps<'div'>, ToggleGroup.RootBaseProps>, ToggleGroupVariantProps>
>(ToggleGroup.Root, 'root')
export const Item = withContext<Assign<HTMLStyledProps<'button'>, ToggleGroup.ItemBaseProps>>(
  ToggleGroup.Item,
  'item',
)
export { ToggleGroupContext as Context } from '@ark-ui/solid'
No snippet found2
Add Re-Export
To improve the developer experience, re-export the styled primitives in~/components/ui/toggle-group.tsx.
export * as ToggleGroup from './styled/toggle-group'
export * as ToggleGroup from './styled/toggle-group'
3
Integrate Recipe
If you're not using @park-ui/preset, add the following recipe to yourpanda.config.ts:
import { toggleGroupAnatomy } from '@ark-ui/anatomy'
import { defineSlotRecipe } from '@pandacss/dev'
export const toggleGroup = defineSlotRecipe({
  className: 'toggleGroup',
  slots: toggleGroupAnatomy.keys(),
  base: {
    root: {
      display: 'flex',
      overflow: 'hidden',
      position: 'relative',
      _vertical: {
        flexDirection: 'column',
      },
    },
    item: {
      alignItems: 'center',
      appearance: 'none',
      cursor: 'pointer',
      color: 'fg.subtle',
      display: 'inline-flex',
      fontWeight: 'semibold',
      minWidth: '0',
      justifyContent: 'center',
      outline: 'none',
      position: 'relative',
      transitionDuration: 'normal',
      transitionProperty: 'background, border-color, color, box-shadow',
      transitionTimingFunction: 'default',
      userSelect: 'none',
      verticalAlign: 'middle',
      whiteSpace: 'nowrap',
      _on: {
        background: 'gray.a3',
        color: 'fg.default',
        _hover: {
          background: 'gray.a3',
        },
      },
      _hover: {
        background: 'gray.a2',
      },
      _disabled: {
        borderColor: 'border.disabled',
        color: 'fg.disabled',
        cursor: 'not-allowed',
        _hover: {
          background: 'transparent',
          borderColor: 'border.disabled',
          color: 'fg.disabled',
        },
      },
    },
  },
  defaultVariants: {
    size: 'md',
    variant: 'outline',
  },
  variants: {
    variant: {
      outline: {
        root: {
          borderWidth: '1px',
          borderRadius: 'l2',
          borderColor: 'border.default',
          _horizontal: {
            divideX: '1px',
          },
          _vertical: {
            divideY: '1px',
          },
        },
        item: {
          borderColor: 'border.default',
          _focusVisible: {
            color: 'fg.default',
            background: 'gray.a3',
          },
        },
      },
      ghost: {
        root: {
          gap: '1',
        },
        item: {
          borderRadius: 'l2',
          _focusVisible: {
            outlineOffset: '2px',
            outline: '2px solid',
            outlineColor: 'border.outline',
          },
        },
      },
    },
    size: {
      sm: {
        item: {
          h: '9',
          minW: '9',
          textStyle: 'sm',
          gap: '2',
          '& svg': {
            width: '4.5',
            height: '4.5',
          },
        },
      },
      md: {
        item: {
          h: '10',
          minW: '10',
          textStyle: 'sm',
          gap: '2',
          '& svg': {
            width: '5',
            height: '5',
          },
        },
      },
      lg: {
        item: {
          h: '11',
          minW: '11',
          textStyle: 'md',
          gap: '2',
          '& svg': {
            width: '5',
            height: '5',
          },
        },
      },
    },
  },
})