# Response Button

> An availability or RSVP answer, like Going, Can't go, Maybe, or Online. Answers sit in a ResponseGroup named for the question, and work like native radios.

- Name: `response-button`
- Status: beta
- Categories: actions
- File: the install writes `response-button.tsx` to your `ui` alias, `@/components/ui` by default.
- npm packages: `class-variance-authority`. The install adds them.
- Gallery: https://arbokui.com/components/response-button
- Registry item: https://arbokui.com/r/response-button.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@arbokui=https://arbokui.com/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@arbokui": "https://arbokui.com/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@arbokui` in a public list that Arbokui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @arbokui/theme
   ```

3. Add the component:

   ```bash
   npx shadcn@latest add @arbokui/response-button
   ```

## Use it for

- Answering an event or an invite: two to four answers in a ResponseGroup named for the question, like 'Tuesday workshop'.
- Give each answer the tone of its meaning: success for Going, danger for Can't go, warning for Maybe, neutral for Online.
- md (32px) on event pages. sm (28px) inline in rows and status strips.
- Add an icon before the words, like a check for Going, so the answer reads without its color.

## Avoid it for

- Answers that can be picked together. Use checkboxes.
- A long list of choices. Use RadioGroup or Select.
- A ResponseButton on its own, outside a ResponseGroup. It then has no arrow keys and no single Tab stop.

## Props

- `children`: `ReactNode`. The answer, like 'Going', with an optional icon before it.
- `tone`: `"neutral" | "brand" | "danger" | "success" | "warning"`. Default `"neutral"`. Color role of the answer. The chosen answer fills with it, and an open one hints it on hover.
- `size`: `"sm" | "md" | "lg"`. Default `"md"`. 28, 32, or 36px tall at the default density, scaled by density.
- `selected`: `boolean`. Default `false`. This answer is the chosen one.
- `onClick`: `() => void`. Runs on a press, and when the arrow keys move onto it. Choose the answer here.
- `disabled`: `boolean`. Default `false`. Blocks the answer. The arrow keys skip it.
- `ref`: `Ref<HTMLButtonElement>`. Reaches the button. id, aria-\* and data-\* land on it too.
- `className`: `string`. Extra classes on the button. Later classes win.
- `ResponseGroup`: `ComponentProps<"div"> & ({ "aria-label": string } | { "aria-labelledby": string })`. Component. The radio group for one question's answers, in a row with a 6px gap. Its name is required.

## Accessibility

- Each answer is a radio, named by its words, with aria-checked for the chosen one.
- It finds its group on the page, the closest role='radiogroup'. ResponseGroup is that group, named for the question.
- The group is one Tab stop: the chosen answer, or the first one when none is chosen.
- The arrow keys move and choose, and wrap. Home and End jump to the ends. Disabled answers are skipped. In right-to-left text, left and right swap.
- The chosen answer differs by fill as well as color, so it reads without color.

## Examples

### Answering an event

```tsx
import { Check, CircleHelp, Video, X } from "lucide-react";
import { useState } from "react";

function Rsvp() {
  const [answer, setAnswer] = useState<string>();
  const answers = [
    { value: "going", label: "Going", tone: "success", icon: <Check /> },
    { value: "maybe", label: "Maybe", tone: "warning", icon: <CircleHelp /> },
    { value: "no", label: "Can't go", tone: "danger", icon: <X /> },
    { value: "online", label: "Online", tone: "neutral", icon: <Video /> },
  ] as const;
  return (
    <ResponseGroup aria-label="Tuesday workshop">
      {answers.map(({ value, label, tone, icon }) => (
        <ResponseButton key={value} tone={tone} selected={answer === value} onClick={() => setAnswer(value)}>
          {icon}
          {label}
        </ResponseButton>
      ))}
    </ResponseGroup>
  );
}
```
