Feedback buttons
Feedback buttons let people rate an AI answer, summary or flashcard with thumbs up or down, and copy it. Use them under generated content.
Beta until design confirms the code-only parts (md size, ghost appearance, filled thumb when pressed, the thank-you text, Tell us more and the copied colour) and the buttons are tested with VoiceOver, NVDA and TalkBack. The live app has Copy only; thumbs come from Figma.
Preview
Change the controls to try every supported combination. Copy writes the sample answer to your clipboard.
const [rating, setRating] = useState<FeedbackValue>(null)
<div className="answer-actions">
<CopyButton text={answer} />
<FeedbackButtons
value={rating}
onValueChange={setRating}
onTellUsMore={(r) => openFeedbackForm(r)}
/>
</div>Installation
Availability
@nuton/ui and @nuton/tokens are private workspace packages and aren't published yet (D4, D12). Inside the Nuton monorepo, add them as workspace dependencies.
pnpm add @nuton/ui @nuton/tokensnpm install @nuton/ui @nuton/tokensyarn add @nuton/ui @nuton/tokensimport '@nuton/tokens/tokens.css'
import '@nuton/ui/styles.css'Usage
import { CopyButton, FeedbackButtons, type FeedbackValue } from '@nuton/ui'
const [rating, setRating] = useState<FeedbackValue>(null)
<CopyButton text={answer} />
<FeedbackButtons
value={rating}
onValueChange={(r) => { setRating(r); saveRating(answerId, r) }}
onTellUsMore={(r) => openFeedbackForm(answerId, r)}
/>FeedbackButtonsis a named group of two toggle buttons (aria-pressed). Pressing the pressed one clears the rating.- After a rating it says “Thanks for your feedback” in a polite status message. With
onTellUsMoreit also offers “Tell us more”; open a short form and move focus into it. CopyButtonshows the check and “Copied” for 2 seconds only after the clipboard write succeeds, and announces it. Its name stays “Copy”.- Inside a region that already announces changes, such as a chat log, set
announce={false}so nothing is read twice. - Rename the strings for the content: “Rate this summary”, “Copy card”.
Variants and sizes
Appearances
Figma's Type: Lighter (default) and Stroke. Ghost (no fill) is code-only, for dense toolbars.
<FeedbackButtons />
<FeedbackButtons appearance="stroke" />
<FeedbackButtons appearance="ghost" />Sizes
<CopyButton text="…" />
<FeedbackButtons />
<CopyButton text="…" size="md" />
<FeedbackButtons size="md" />| Part | sm (Figma, live) | md (code-only) |
|---|---|---|
| Button | 28 × 28, radius 8 | 36 × 36, radius 10 |
| Icon | 16 (Figma draws 20; live and Chat use 16) | 20 |
| Gap | 4 between buttons, 8 before the thank-you | |
| Thank-you | Paragraph/X Small, Text/Sub-600 | |
| Copy with text | Icon + Label/X Small, padding 8 | |
Copy with a text label
<CopyButton text={summary} showLabel appearance="stroke" />States and interactions
State tokens
Set on .nu-feedback-action.
| Token | Value | Used for |
|---|---|---|
--nu-feedback-action-size · --nu-feedback-action-icon | 28 · 16 (md 36 · 20) | Geometry |
--nu-feedback-action-bg · --nu-feedback-action-border | Bg/Weak-50 · none (Stroke: Bg/White · Stroke/Sub-300) | Default |
--nu-feedback-action-bg-hover · --nu-feedback-action-border-hover | Bg/Soft-200 (Stroke: Bg/Weak-50 · Bg/Weak-50) | Hover |
| (pressed) | Bg/Strong-950 · Icon/White; hover Bg/Surface-800 | Figma Active |
Composition examples
AI Tutor answer with a follow-up form
“Tell us more” opens a short form and moves focus into it. Sending it confirms in a status message.
Summaries and flashcards
Rename the strings for the content being rated and copied.
In Chat
ChatMessageActions in Chat shows the same Copy and rating buttons under AI Tutor answers.
API reference
FeedbackButtons props
| Prop | Type | Default | Description |
|---|---|---|---|
value · defaultValue | 'good' | 'bad' | null | null | Controlled or uncontrolled rating. |
onValueChange | (value: FeedbackValue) => void | — | Called with the new rating; null when the pressed button is pressed again. |
onTellUsMore | (value: 'good' | 'bad') => void | — | Shows "Tell us more" after a rating and calls this with it. |
confirmation | 'visible' | 'hidden' | 'none' | 'visible' | The thank-you after a rating: shown, for screen readers only, or off. It appears only after a rating made here. |
announce | boolean | true | Puts the thank-you in a polite status region. Turn off inside a live region. |
size | 'sm' | 'md' | 'sm' | 28px (Figma) or 36px buttons. |
appearance | 'lighter' | 'stroke' | 'ghost' | 'lighter' | Figma Type Lighter or Stroke, or no fill. |
tooltips | boolean | true | Shows each button name as a tooltip. |
disabled | boolean | false | Disables both buttons. |
labels | Partial<FeedbackButtonsLabels> | FEEDBACK_BUTTONS_LABELS | group (“Rate this response”), good (“Good response”), bad (“Bad response”), thanks, tellUsMore. |
className · …div props | HTMLAttributes<HTMLDivElement> | — | Go to the group element; the ref points at it. |
CopyButton props
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | (() => string | Promise<string>) | — | What to copy, read when pressed. |
onCopy | (text: string | undefined) => void | Promise<unknown> | — | Called after the write. Return a promise to show success only when it resolves. Without text, it does the copying. |
onCopyError | (error: unknown) => void | — | Called when nothing was copied (no clipboard access, permission denied, or onCopy rejected). |
copiedDuration | number | 2000 | How long the success state shows, in ms. |
showLabel | boolean | false | Shows "Copy" / "Copied" as text; no tooltip then. |
tooltip | boolean | true | Shows "Copy", then "Copied", as a tooltip. |
announce | boolean | true | Announces "Copied" in a polite status region. |
size · appearance | 'sm' | 'md' · 'lighter' | 'stroke' | 'ghost' | 'sm' · 'lighter' | As for FeedbackButtons. |
labels | Partial<CopyButtonLabels> | COPY_BUTTON_LABELS | copy (“Copy”) and copied (“Copied”). |
…button props | ButtonHTMLAttributes | — | disabled, onClick (call preventDefault to stop the copy) and other attributes go to the button; the ref points at it. |
Accessibility
Keyboard
| Key | Behaviour |
|---|---|
| Tab | Moves to Copy, Good response, Bad response, then Tell us more |
| Enter / Space | Copies, or presses and releases a rating |
| Esc | Closes a tooltip |
Screen readers
The ratings are toggle buttons in a group: “Rate this response, group. Good response, toggle button, not pressed”. After a rating, “Thanks for your feedback” is announced politely. Copy keeps its name and announces “Copied”. Tooltips repeat the names, so they aren't linked as descriptions.
Focus
The primary focus halo on each button. Focus doesn't move after a rating; “Tell us more” comes next in the tab order.
Contrast and colour
Icons Sub-600 on Weak-50 (6.24:1, 5.61:1 on the Soft-200 hover); pressed is white on Strong-950 and also swaps to a filled thumb, so the state isn't shown by colour alone. Thank-you text Sub-600 (6.69:1). In forced-colours mode the pressed button uses Highlight.
Touch and pointer
Buttons are 28 or 36px square with 4px between them (WCAG 2.5.8). Tooltips don't show on touch, so the names never rely on them. Transitions stop with reduced motion.
You must: save the rating, keep the label strings specific to the content, and turn announce off inside an existing live region.
Figma mapping
| Figma property | Figma values | Code |
|---|---|---|
| Type | Lighter · Stroke | appearance lighter · stroke (ghost is code-only) |
| State | Default · Hover · Active | Interactive states; Active = pressed rating |
| Icon | file-copy-line · thumb-up-line · thumb-down-line | CopyButton, FeedbackButtons |
Guidelines and related components
Use feedback buttons for
- Rating AI answers, summaries, flashcards and podcasts
- Copying generated text
Don't use them for
- Star ratings or surveys (use a form)
- Liking or saving content (use a toggle Button)
- Quiz answers (use Quiz)