Actions

Toolbar

Horizontal or vertical container that groups toggle actions and separators into a single keyboard-navigable surface, built on react-aria-components and styled with the quebi design system.

actiontoolbargrouptoggleinteractive

Default

A horizontal toolbar grouping text-formatting toggles with a separator.

With labels

Items can carry text alongside (or instead of) icons.

Vertical

Set orientation to stack items into a vertical rail.

Circle

isCircle rounds the toolbar and its items into pill/circle shapes.

Controlled selection

ToolbarItem is a toggle — wire its selected state like any controlled toggle.

Disabled group

A ToolbarGroup can disable all of its items at once.

Source

Copy this into your project. Resolve its dependencies from the registryDependencies in the component's API entry.

"use client"

import { createContext, use } from "react"
import {
  composeRenderProps,
  Group,
  type GroupProps,
  type SeparatorProps,
  Toolbar as ToolbarPrimitive,
  type ToolbarProps as ToolbarPrimitiveProps,
} from "react-aria-components"
import { Separator } from "@/components/separator"
import { Toggle, type ToggleProps } from "@/components/toggle"
import { cn } from "@/lib/utils"

/**
 * Toolbar — quebi design system
 *
 * A horizontal or vertical container that groups related actions (toggle
 * buttons, separators) into a single keyboard-navigable surface. Built on
 * react-aria-components for roving-focus and arrow-key navigation.
 *
 * Styled as a quebi surface: a subtle background panel with the signature
 * hairline cyan border and a quebi radius. Items are quebi Toggles, so the
 * active state lights up with brand teal.
 */

interface ToolbarProps extends ToolbarPrimitiveProps {
  isCircle?: boolean
}

const ToolbarContext = createContext<{
  orientation?: ToolbarProps["orientation"]
  isCircle?: boolean
}>({
  orientation: "horizontal",
  isCircle: false,
})

const Toolbar = ({ orientation = "horizontal", isCircle, className, ...props }: ToolbarProps) => {
  return (
    <ToolbarContext value={{ orientation, isCircle }}>
      <ToolbarPrimitive
        orientation={orientation}
        {...props}
        className={composeRenderProps(className, (className, { orientation }) =>
          cn(
            "group inline-flex gap-1.5 border border-cyan-500/10 bg-quebi-bg p-1.5",
            "[-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
            isCircle ? "rounded-full" : "rounded-quebi-md",
            orientation === "horizontal"
              ? "flex-row items-center"
              : "flex-col items-start",
            className,
          ),
        )}
      />
    </ToolbarContext>
  )
}

const ToolbarGroupContext = createContext<{ isDisabled?: boolean; isCircle?: boolean }>({})

interface ToolbarGroupProps extends GroupProps {}

const ToolbarGroup = ({ isDisabled, className, ...props }: ToolbarGroupProps) => {
  return (
    <ToolbarGroupContext value={{ isDisabled }}>
      <Group
        className={cn(
          "flex gap-1.5",
          "group-orientation-vertical:flex-col group-orientation-vertical:items-start",
          "group-orientation-horizontal:items-center",
          className,
        )}
        {...props}
      >
        {props.children}
      </Group>
    </ToolbarGroupContext>
  )
}

interface ToolbarItemProps extends ToggleProps {}

const ToolbarItem = ({
  isDisabled,
  isCircle,
  size = "sm",
  intent = "outline",
  ref,
  className,
  ...props
}: ToolbarItemProps) => {
  const context = use(ToolbarGroupContext)
  const { isCircle: contextCircle } = use(ToolbarContext)
  const effectiveIsDisabled = isDisabled || context.isDisabled
  const effectiveIsCircle = isCircle || contextCircle
  return (
    <Toggle
      intent={intent}
      size={size}
      ref={ref}
      data-slot="toolbar-item"
      isCircle={effectiveIsCircle}
      className={className}
      isDisabled={effectiveIsDisabled}
      {...props}
    />
  )
}

type ToolbarSeparatorProps = SeparatorProps

const ToolbarSeparator = ({ className, ...props }: ToolbarSeparatorProps) => {
  const { orientation } = use(ToolbarContext)
  const reverseOrientation = orientation === "vertical" ? "horizontal" : "vertical"
  return (
    <Separator
      orientation={reverseOrientation}
      className={cn(
        reverseOrientation === "vertical" ? "mx-0.5 h-6" : "my-0.5 w-8",
        className,
      )}
      {...props}
    />
  )
}

export type { ToolbarItemProps, ToolbarGroupProps, ToolbarProps, ToolbarSeparatorProps }
export { Toolbar, ToolbarGroup, ToolbarItem, ToolbarSeparator }