Components/ChoiceCard

ChoiceCard

A question card answered with rich rows, optionally split into steps.

VerifiedSince 0.6.0

Installation

EXAMPLE.BASH
pnpm add @talex-touch/tuffex
EXAMPLE.TYPESCRIPT
import { TxChoiceCard } from '@talex-touch/tuffex/choice-card'
import '@talex-touch/tuffex/choice-card/style.css'
// It renders TxIcon and TxSkeleton, whose sheets are separate
import '@talex-touch/tuffex/icon/style.css'
import '@talex-touch/tuffex/skeleton/style.css'
import '@talex-touch/tuffex/base.css' // tokens + resets, once per app

Usage

Single Question

selected marks the picked row; a disabled row can't be picked.

Loading demo...

Steps

The host pages by updating v-model:step in select; columns="2" lays the rows out two per row.

Loading demo...

Loading

loading draws skeleton rows in the rows' own boxes, so nothing shifts when the options arrive.

Loading demo...

Best Practices

  • Ask one question per step, with three to six answers.
  • Page by updating v-model:step in select, and pass selected for the page on screen so going back shows the pick.
  • Bind loading through useDeferredLoading, and set loadingRows to the number of answers coming.
  • Keep a row that needs something first disabled rather than hidden, and say what it needs in its description.
  • Pass prevLabel, nextLabel, and stepLabel in the page's language; the card ships English defaults only.

API Reference

Props

PropTypeDefaultDescription
stepsChoiceStep[]-The pages. Required; when empty and not loading, nothing renders.
stepnumberundefinedCurrent page, 0-based, for v-model:step; clamped to the pages there are.
selectedstringundefinedId of the answer to mark on the page on screen.
loadingbooleanfalseReplaces the rows with skeleton rows and sets aria-busy; the title and pager stay.
loadingRowsnumber3How many skeleton rows loading draws.
columns1 | 212 lays rows out two per row; below a 480px card it falls back to one.
appearbooleantrueRows rise in one after another on first render and when loading ends.
prevLabelstring'Previous'Accessible name of the back arrow.
nextLabelstring'Next'Accessible name of the forward arrow.
stepLabel(current: number, total: number) => string`${current} / ${total}`Text of the pager counter; both numbers are 1-based.

Events

EventParamsDescription
select(payload: ChoiceSelectPayload)Fires when an enabled row is clicked or activated with Enter or Space; the page stays.
update:step(index: number)Fires when an arrow moves to page index, 0-based.

Slots

SlotPropsDescription
header{ step: ChoiceStep, stepIndex: number, total: number }Replaces the default <h3> title; it names the card, so keep the question in it.

Types

ChoiceStep, one entry of steps:

FieldTypeDescription
idstringPage identity; a new id on the page on screen replays the page entrance. Required.
titlestringThe question: the card's heading and the name of its answer list. Required.
optionsChoiceOption[]The answers. Required.

ChoiceOption, one answer:

FieldTypeDefaultDescription
idstring-Unique within the page; what selected matches. Required.
labelstring-The row's title and accessible name. Required.
descriptionstring-One line under the label, announced as the row's description.
iconTxIconSource | string-A TxIcon source, or an icon class such as 'i-carbon-edit'.
disabledbooleanfalseDimmed; the arrow keys skip it and it emits nothing.
EXAMPLE.TS
import type {
  ChoiceCardColumns, // 1 | 2
  ChoiceCardEmits,
  ChoiceCardProps,
  ChoiceOption,
  ChoiceSelectPayload, // { step, stepIndex, option }
  ChoiceStep,
  ChoiceStepLabelFormatter, // (current, total) => string
  TxChoiceCardInstance,
} from '@talex-touch/tuffex/choice-card'

CSS Variables

VariableDescription
--tx-choice-card-padInset between the card edge and the rows, default 8px; the card radius grows with it.
--tx-choice-card-option-radiusRow corner radius, default 10px; the card radius adds the inset, keeping corners concentric.
--tx-choice-card-option-pad-xHorizontal inset of the rows and the title, default 12px.
--tx-choice-card-label-lineLabel line height and icon box height, default 20px.
--tx-choice-card-desc-lineDescription line height, default 18px.

Set them on the card or any ancestor; the component only writes --tx-choice-card-index (the row's stagger position) on each row.

Overview

  • Semantics: a <section> named by the question, with rows as native <button>s in a <ul role="list">, named by the label and described by the description. The selected row has a check and aria-current="true", so colour is never the only mark.
  • select never changes the page. Without step, the card tracks its own page and emits update:step; the pager renders only with two or more pages, and its counter is a role="status" region.
  • Keyboard: the list is one Tab stop (the selected row, else the first enabled one); Up and Down move and wrap, Left and Right move in reading order in two columns, Home and End jump to the ends, and disabled rows are skipped.
  • When the page changes with focus in the list, focus moves to the new page's Tab stop; when the arrow in use becomes disabled, focus moves to the other arrow.
  • Two columns fall back to one below a 480px card (a container query), and the arrow keys follow the rendered column count.
  • Entrance and page-change motion are CSS animations on fresh nodes, so rows are clickable from their first frame; reduced motion drops them.

Technologies

  • Skeleton rows reuse the loaded row's own containers, with TxSkeleton bars inside.
  • Source: packages/tuffex/packages/components/src/choice-card/.
查看源码
packages/tuffex/packages/components/src/choice-card/index.ts

Use cases

  • An assistant's opening guide: "Where should we start?", one question per step.
  • A "Ready for you" list of suggestions that arrives after the card.
  • Reviewing an answered question: selected marks what was picked.
ComponentFor
SuggestionChipsOne-line follow-up prompts
RecommendationCardOne recommendation with alternatives
ApprovalCardMulti-question answers, sent together