COMPONENTS/INPUTS

Toggle group.

A few choices, kept together.

In practice

Supply an array of strings with defaultValue or value/onValueChange. Single or multiple mode; arrow keys move focus, Space toggles. Add aria-label.

Installation

Install Coal once. Components and TypeScript declarations are included. React 19 and React DOM 19 are peer dependencies.

Terminal · local package
npm install http://localhost:3100/downloads/chlohal-coal-ui-0.5.0.tgz

This installs the local package. It is not yet published on npm. See installation for stylesheet setup and integration.

Usage

example.tsx
"use client";
import * as React from "react";
import "@chlohal/coal-ui/styles.css";
import { ToggleGroup, ToggleGroupItem } from "@chlohal/coal-ui";
import { Bold, Italic } from "lucide-react";
export default function Example() {
    return (<ToggleGroup type="multiple" defaultValue={["bold"]} aria-label="Text formatting">
          <ToggleGroupItem value="bold">Bold</ToggleGroupItem>
          <ToggleGroupItem value="italic">Italic</ToggleGroupItem>
          <ToggleGroupItem value="underline">Underline</ToggleGroupItem>
        </ToggleGroup>);
}

Source

The original Coal implementation. Use the package export to integrate it.

packages/react/src/toggle-group.tsx
"use client";
import * as React from "react";
import { cn, useRequired, useValue, moveFocus } from "./internal.js";
import { Toggle, type ToggleProps } from "./toggle.js";
const Context = React.createContext<
  { value: string[]; set: (v: string) => void; disabled?: boolean } | undefined
>(undefined);
export type ToggleGroupProps = Omit<
  React.ComponentPropsWithRef<"div">,
  "defaultValue" | "onChange"
> & {
  type?: "single" | "multiple";
  value?: string[];
  defaultValue?: string[];
  onValueChange?: (v: string[]) => void;
  disabled?: boolean;
  orientation?: "horizontal" | "vertical";
};
export function ToggleGroup({
  type = "single",
  value,
  defaultValue = [],
  onValueChange,
  disabled,
  orientation = "horizontal",
  children,
  className,
  onKeyDown,
  ...props
}: ToggleGroupProps) {
  const [v, set] = useValue(value, defaultValue, onValueChange);
  return (
    <Context.Provider
      value={{
        value: v,
        disabled,
        set: (item) =>
          set(
            v.includes(item)
              ? v.filter((x) => x !== item)
              : type === "single"
                ? [item]
                : [...v, item],
          ),
      }}
    >
      <div
        role="group"
        className={cn("coal-toggle-group", className)}
        data-orientation={orientation}
        {...props}
        onKeyDown={(e) => {
          onKeyDown?.(e);
          if (!e.defaultPrevented) moveFocus(e, "button", orientation);
        }}
      >
        {children}
      </div>
    </Context.Provider>
  );
}
export function ToggleGroupItem({
  value,
  disabled,
  ...props
}: Omit<
  ToggleProps,
  "value" | "pressed" | "defaultPressed" | "onPressedChange"
> & { value: string }) {
  const c = useRequired(Context, "ToggleGroupItem");
  return (
    <Toggle
      {...props}
      disabled={disabled || c.disabled}
      pressed={c.value.includes(value)}
      onPressedChange={() => c.set(value)}
    />
  );
}

Accessibility checklist

  • Provide an accessible name for controls, including icon-only buttons.
  • Check keyboard navigation, visible focus and disabled states in your application.
  • Do not rely on color alone to communicate status.