

<Preview name="BasicFeedbackButtonExample" />

## Overview [#overview]

The **Feedback Button** component provides visual feedback through icon transitions when clicked. It supports both automatic mode with auto-reset and controlled mode for persistent state, making it useful for copy buttons, like buttons, bookmark toggles, and other interactive elements that need visual confirmation.

## Usage [#usage]

```tsx
import { FeedbackButton } from "@tilt-legal/cubitt-components/feedback-button";
import { Check, Copy } from "@tilt-legal/cubitt-icons/ui/outline";
```

```tsx
<FeedbackButton
  defaultIcon={<Copy />}
  feedbackIcon={<Check />}
  onFeedback={() => navigator.clipboard.writeText("Copied!")}
  variant="secondary"
>
  Copy to Clipboard
</FeedbackButton>
```

<Accordions type="single">
  <Accordion title="Automatic vs Controlled Mode">
    The FeedbackButton supports two modes:

    **Automatic (default):** Uses internal state and auto-resets after `resetDelay`. Perfect for copy actions, likes, and temporary feedback.

    ```tsx
    <FeedbackButton
      defaultIcon={<Copy />}
      feedbackIcon={<Check />}
      onFeedback={() => navigator.clipboard.writeText("Copied!")}
      resetDelay={1500}
    >
      Copy
    </FeedbackButton>
    ```

    **Controlled:** Uses external state management with no auto-reset. Ideal for toggles and persistent state changes.

    ```tsx
    const [isStarred, setIsStarred] = React.useState(false);

    <FeedbackButton
      controlled
      defaultIcon={<Star />}
      feedbackIcon={<StarFill />}
      onFeedback={() => setIsStarred(!isStarred)}
      showFeedback={isStarred}
    />;
    ```
  </Accordion>
</Accordions>

## Examples [#examples]

### Icon Only [#icon-only]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="IconOnlyFeedbackButtonExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { FeedbackButton } from "@tilt-legal/cubitt-components/feedback-button";
    import { Heart } from "@tilt-legal/cubitt-icons/ui/outline";
    import { Heart as HeartFill } from "@tilt-legal/cubitt-icons/ui/fill";

    <FeedbackButton
      defaultIcon={<Heart />}
      feedbackIcon={<HeartFill />}
      mode="icon"
      onFeedback={() => console.log("Liked!")}
      resetDelay={2000}
      variant="outline"
    />;
    ```
  </Tab>
</Tabs>

### Icon on Right [#icon-on-right]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="IconRightFeedbackButtonExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { FeedbackButton } from "@tilt-legal/cubitt-components/feedback-button";
    import { Check, Copy } from "@tilt-legal/cubitt-icons/ui/outline";

    <FeedbackButton
      defaultIcon={<Copy />}
      feedbackIcon={<Check />}
      iconSide="right"
      onFeedback={() => navigator.clipboard.writeText("Text copied!")}
      resetDelay={1500}
      variant="secondary"
    >
      Copy
    </FeedbackButton>;
    ```
  </Tab>
</Tabs>

### Controlled Toggle [#controlled-toggle]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="ControlledFeedbackButtonExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { FeedbackButton } from "@tilt-legal/cubitt-components/feedback-button";
    import { Star } from "@tilt-legal/cubitt-icons/ui/outline";
    import { Star as StarFill } from "@tilt-legal/cubitt-icons/ui/fill";
    import React from "react";

    function Example() {
      const [isStarred, setIsStarred] = React.useState(false);

      return (
        <FeedbackButton
          controlled
          defaultIcon={<Star />}
          feedbackIcon={<StarFill />}
          mode="icon"
          onFeedback={() => setIsStarred(!isStarred)}
          showFeedback={isStarred}
          variant="ghost"
        />
      );
    }
    ```
  </Tab>
</Tabs>

### Custom Reset Delay [#custom-reset-delay]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="CustomDelayFeedbackButtonExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { FeedbackButton } from "@tilt-legal/cubitt-components/feedback-button";
    import { ThumbsDown, ThumbsUp } from "@tilt-legal/cubitt-icons/ui/outline";

    <FeedbackButton
      defaultIcon={<ThumbsUp />}
      feedbackIcon={<ThumbsDown />}
      mode="icon"
      onFeedback={() => console.log("Upvoted!")}
      resetDelay={3000}
      variant="outline"
    />;
    ```
  </Tab>
</Tabs>

### Bookmark Toggle with Text [#bookmark-toggle-with-text]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="BookmarkToggleFeedbackButtonExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { FeedbackButton } from "@tilt-legal/cubitt-components/feedback-button";
    import { Bookmark, BookmarkCheck } from "@tilt-legal/cubitt-icons/ui/outline";
    import React from "react";

    function Example() {
      const [isBookmarked, setIsBookmarked] = React.useState(false);

      return (
        <FeedbackButton
          controlled
          defaultIcon={<Bookmark />}
          feedbackIcon={<BookmarkCheck />}
          onFeedback={() => setIsBookmarked(!isBookmarked)}
          showFeedback={isBookmarked}
          variant="secondary"
        >
          {isBookmarked ? "Bookmarked" : "Bookmark"}
        </FeedbackButton>
      );
    }
    ```
  </Tab>
</Tabs>

### Variants [#variants]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="VariantsFeedbackButtonExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { FeedbackButton } from "@tilt-legal/cubitt-components/feedback-button";
    import { Heart } from "@tilt-legal/cubitt-icons/ui/outline";
    import { Heart as HeartFill } from "@tilt-legal/cubitt-icons/ui/fill";

    <div className="flex gap-2">
      <FeedbackButton
        defaultIcon={<Heart />}
        feedbackIcon={<HeartFill />}
        mode="icon"
        onFeedback={() => {}}
        variant="default"
      />
      <FeedbackButton
        defaultIcon={<Heart />}
        feedbackIcon={<HeartFill />}
        mode="icon"
        onFeedback={() => {}}
        variant="secondary"
      />
      <FeedbackButton
        defaultIcon={<Heart />}
        feedbackIcon={<HeartFill />}
        mode="icon"
        onFeedback={() => {}}
        variant="outline"
      />
      <FeedbackButton
        defaultIcon={<Heart />}
        feedbackIcon={<HeartFill />}
        mode="icon"
        onFeedback={() => {}}
        variant="ghost"
      />
    </div>;
    ```
  </Tab>
</Tabs>

## API Reference [#api-reference]

### FeedbackButton [#feedbackbutton]

Extends all Button component props except `onClick`, which is wrapped so feedback state can be triggered before the click handler runs.

| Prop             | Type                                               | Default   | Description                                                                                      |
| ---------------- | -------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------ |
| `defaultIcon`    | `ReactNode`                                        | -         | Icon to show in default state. Required.                                                         |
| `feedbackIcon`   | `ReactNode`                                        | -         | Icon to show in feedback state. Required.                                                        |
| `onFeedback`     | `() => void`                                       | -         | Callback when the button is clicked and feedback starts.                                         |
| `resetDelay`     | `number`                                           | `2000`    | How long to show feedback state in milliseconds in automatic mode.                               |
| `showFeedback`   | `boolean`                                          | -         | External control of feedback state in controlled mode.                                           |
| `controlled`     | `boolean`                                          | `false`   | Whether to use external state control instead of automatic reset.                                |
| `iconSide`       | `"left" \| "right"`                                | `"left"`  | Position of the icon relative to text content.                                                   |
| `iconAnimation`  | `"scale" \| "fade"`                                | `"scale"` | Icon transition style.                                                                           |
| `onClick`        | `(e: React.MouseEvent<HTMLButtonElement>) => void` | -         | Standard click handler called after `onFeedback`.                                                |
| `children`       | `ReactNode`                                        | -         | Button text content. Optional for icon-only buttons.                                             |
| `...buttonProps` | `Omit<ComponentProps<typeof Button>, "onClick">`   | -         | All standard Button props except `onClick`, including `variant`, `size`, `mode`, and `disabled`. |
