VariantTile
StableA square tile for one AI-made option that a person can pick.
Also known as variation, option tile, image tile
Install with npm install @tattva/ui (setup), or copy the code from the kit in Downloads.
Choose one
const [selected, setSelected] = useState("a");
<div role="radiogroup" aria-label="Generated options" className="grid w-full max-w-md grid-cols-3 gap-3">
{variants.map((v) => (
<VariantTile key={v.id} variant={v} selected={selected === v.id} onSelect={setSelected} />
))}
</div>Here the demo keeps track of the choice. Without VariantGrid, arrow keys do not move between tiles, so the Tab key stops on each one.
When to use it
One AI-made option shown as a square, so options can be compared side by side. The chosen one gets a check mark and a dark border, not only a color change.
Made of: Content · AI badge · Check mark
Use it for
- Picking one of several generated images, layouts or drafts.
- Inside VariantGrid, which adds arrow key movement between tiles.
Not for
- A whole set of options to choose from: use VariantGrid.
- Picking several images at once: use MediaGrid.
- A few text options with no picture: use SegmentedControl.
Do and avoid
Do
- Use VariantGrid, or wrap tiles in a group that screen readers know is a set of choices, with a name.
- Give each option a clear name, like Option B, sand block.
- Let the content fill the whole square, like a cropped image.
Avoid
- Do not allow picking more than one. Only one tile can be chosen.
- Do not remove the AI badge from AI-made content.
- Do not use it for navigation. It is for choosing between AI-made options.
On a phone
- Each tile is a square that fills the width its grid gives it, and has a tap area of at least 44px on touch screens.
- Selection is a tap, and the check mark shows without hover.
- Choose the number of columns in the grid, since the tile size comes from it.
Properties
| Prop | Type | Default | Description |
|---|---|---|---|
| variantrequired | Variant | none | Object with id (string), node (ReactNode shown inside the tile) and label (string used as the accessible name). |
| selectedrequired | boolean | none | Whether this tile is the chosen option. |
| onSelectrequired | (id: string) => void | none | Called with variant.id when the tile is pressed. |
| tabIndex | number | none | Set to -1 on tiles that are not the tab stop of a roving group. VariantGrid does this for you. Leave it unset for a standalone tile, which then stays a tab stop. |
"Show code under the preview gives the code for the example on the stage."
In Figma
In Figma, one VariantTile switches all of this from its properties panel, across 8 variants. Each property sets the React prop beside it, so design and code use the same names.
| Figma property | React | Values |
|---|---|---|
| Selected | selected | On or off |
| State | Hover, Pressed and Focus come from the browser. | Default, Hover, Pressed, Focus |
Tokens
Change one of these and every part that uses it follows.
--fg--bg--border--border-strong--shadow-md--radius-card
Built in
- Each tile is a button that screen readers treat as a choice. They read its label and whether it is selected.
- A chosen tile shows a check mark and a thicker border, not just color.
- The rise-in motion stops when people turn off motion in their system settings.
What you need to do
- Use VariantGrid, or build the group yourself: set tabIndex to -1 on tiles that are not chosen and move between tiles with the arrow keys.
- Give each tile a label that says how it differs, such as "Option A, warm gradient".
- Say that the option is AI-made in the label or the nearby text if that matters. The label replaces the tile's content, so the badge is not read out.
Built toward WCAG 2.2 AA and checked automatically; no independent audit yet. Read the accessibility guide