Suggestion

Prompt-starter chips on a horizontal scroll row — staggered entrance, a neutral pill that tints on hover, and the suggestion string handed straight to onClick.

<Suggestions>  {starters.map((starter) => (    <Suggestion key={starter} suggestion={starter} onClick={setDraft} />  ))}</Suggestions>

Usage

import { Suggestion, Suggestions } from "@reva/ui";

Suggestions is the prompt-starter row — chips never wrap, the row scrolls horizontally on the house ScrollArea (the strongest ScrollArea case: native horizontal scrollbars are platform-inconsistent and undiscoverable on mobile). ScrollArea's type default (hover) applies, so the scrollbar appears on pointer-over; pass type="always" when discoverability matters — recommended for touch-first layouts, where there is no hover to discover the overflow with.

Chips stagger in ~50ms apart (the row orchestrates; chips subscribe via variants); on hover the pill tints up a step — a colour shift and a pointer cursor, no transform. Reduced motion branches values only: the row's initial collapses so chips mount directly at their visible values — the markup never forks. A chip rendered outside Suggestions gets no variant labels propagated to it, so it renders static and fully visible — graceful standalone degradation by construction.

onClick is called with the suggestion string, not the event — wire it straight to "set the composer draft" on the Prompt Input. children override the visible label while suggestion stays the click payload.

Each chip is the house Button (outline / sm) forced to a true pill with rounded-full — per the squircle rules, pills are always true rounds, never a large fixed radius.

Composition

Use the following composition to build a Suggestions row:

Suggestions          (horizontal ScrollArea row — the stagger orchestrator)
└── Suggestion (× n) (one chip per prompt starter — Button outline/sm pill)

Examples

Selected readout

The string payload means a state setter wires in directly — here it feeds a readout instead of a composer. initial={false} skips the entrance stagger for rows that shouldn't animate.

Nothing selected yet.

const [selected, setSelected] = useState<string | null>(null);<Suggestions initial={false}>  {starters.map((starter) => (    <Suggestion key={starter} suggestion={starter} onClick={setSelected} />  ))}</Suggestions>

Always-visible scrollbar

On touch there is no hover to summon the scrollbar, so a row that must read as scrollable should keep the bar visible.

<Suggestions type="always">  {starters.map((starter) => (    <Suggestion key={starter} suggestion={starter} />  ))}</Suggestions>

Accessibility

  • Chips are real buttons (type="button") via the house Button — ordinary tab stops, and tabbing to an off-screen chip scrolls it into view natively.
  • The row carries a small padding bleed so the 3px focus ring isn't clipped by the scroll viewport, while chips still align flush with surrounding content.
  • Reduced motion changes values only — the stagger collapses (chips mount visible); the markup is identical either way.

Props

ComponentPropTypeDefaultDescription
Suggestionstype"auto" | "always" | "scroll" | "hover""hover"ScrollArea scrollbar visibility — use "always" for touch-first layouts.
Suggestionsinitial / animateMotion variant labels"hidden" / "visible"Override the row's variant labels — initial={false} skips the entrance.
SuggestionstransitionTransition~50ms staggerOverride the stagger orchestration (the visible variant's transition).
SuggestionsuggestionstringThe prompt-starter string — the chip label and the click payload. Required.
SuggestiononClick(suggestion: string) => voidCalled with the suggestion string, not the event.
SuggestionchildrenReactNodethe suggestion stringOverride the visible label; suggestion stays the payload.
Suggestionvariant / sizeButton variants"outline" / "sm"Pass through to the underlying Button.
Suggestionvariants / whileHoverMotion propschip enter / noneOverride the entrance variants, or add a Motion hover gesture (none by default).

On this page