RecommendationCard
An agent suggestion with its confidence: rationale, alternatives drawer, and the action that confirms it — all without the card changing shape.
Usage
A Suggestion and Its Alternatives
The footer states confidence as a meter and as words. "Alternatives" opens the drawer, and picking one promotes it to the current recommendation.
Loading demo...
Best Practices
- Write
shortso it reads on its own — inside the drawer it is the only information there is. - Pair
labelwith the meter rather than relying on colour; colour drops out under high contrast and for colour-blind readers. - Use
ctaTone: 'danger'for destructive actions so the primary button's weight matches the consequence. - Lift
acceptedinto the host and set it true only after the request succeeds, or readers will believe the order was placed. - Keep alternatives to two or three; beyond that the choice belongs on a list page, not in a drawer.
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | — | Card heading, usually the question awaiting confirmation. Required. |
options | RecommendationOption[] | — | The options: { key, text?, short, confidence?, signal?, tone?, label, cta?, ctaTone? }. Required. |
modelValue | string | first option's key | v-model, key of the promoted recommendation. |
open | boolean | — | v-model:open, the alternatives drawer. Omit to let the card own it. |
accepted | boolean | — | v-model:accepted, whether it was confirmed. |
alternativesLabel | string | 'Alternatives' | Text of the drawer toggle. |
otherOptionsLabel | string | 'Other options' | Heading inside the drawer. |
acceptedLabel | string | 'Accepted' | Primary action text once confirmed. |
acceptLabel | string | 'Accept' | Fallback text for an option without its own cta. |
Events
| Event | Arguments | Description |
|---|---|---|
update:modelValue | (key: string) | The promoted option changed. |
update:open | (open: boolean) | The drawer opened or closed. |
update:accepted | (accepted: boolean) | The confirmed state changed. |
accept | (option: RecommendationOption) | The primary action was pressed, carrying the whole option. |
select | (option: RecommendationOption) | An alternative was picked in the drawer. |
Slots
| Slot | Scope | Description |
|---|---|---|
body | { option } | Replaces the rationale. Rich content goes here: code for identifiers, mark for entities. |
meter | { option } | Replaces a meter, used in the footer and in every drawer row. |
footer-extra | — | Inserted on the left of the footer, before the actions. |
How Confidence Maps
confidence is the semantic entry point; the component derives the meter's fill and colour from it:
confidence | Segments | Default colour |
|---|---|---|
high | 3 | --tx-bui-green |
medium | 2 | --tx-bui-orange |
low | 1 | --tx-bui-red |
none (default) | 0 | --tx-bui-ink-3 |
signal and tone are the escape hatches, overriding the count and the colour respectively. label is always required — colour cannot be the only carrier of state.
Overview
- Picking promotes. Choosing an alternative makes it the current recommendation, closes the drawer, and clears the confirmed state — a confirmation of the previous option must not carry over to a new one.
- The drawer lists only the other options; the current recommendation never appears twice.
- The collapsed drawer is
inert. A0frgrid only squeezes the height to zero, leaving its buttons in the tab order; the component marks itinertto take them out of the accessibility tree and the focus order. This is a deliberate improvement over upstream. acceptedonly swaps text and colour and has no undo. Real workflows usually need one, so it is a controlled prop and the host owns the retraction.- The rationale has a minimum height (48px by default) so the card does not jump as options of different lengths swap in. Line heights differ by script; tune it with
--tx-bui-recommendation-card-body-min-height. - Both
<code>and<mark>inside the rationale are styled by the component, so filling thebodyslot with rich content needs no styling of its own:<code>is a real identifier — a SKU, a filename, a command. An accent tint by default, switched with theis-success/is-warningclasses.<mark>is an entity the suggestion refers to — a supplier, a file, a person. It renders as a pill with a colour dot, not monospaced, because this is a name to read rather than a string to type. The host supplies the dot colour through--tx-entity-color(only the host knows what colour a given supplier is); without it the dot falls back to neutral grey.<mark>rather than a convention class, because the element already means "text singled out for reference" and carries that to a screen reader instead of relying on colour.
tonetakes a raw CSS colour string and does not follow the theme; preferconfidence.- The drawer eases more softly than the rest of this family (
cubic-bezier(0.16, 1, 0.3, 1)). That difference is deliberate upstream and is preserved.
Technologies
- Component source:
packages/tuffex/packages/components/src/recommendation-card/src/TxRecommendationCard.vue. - Types:
packages/tuffex/packages/components/src/recommendation-card/src/types.ts. - Verified coverage:
packages/tuffex/packages/components/src/recommendation-card/__tests__/recommendation-card.test.ts(14 cases) andrecommendation-card-inline.test.ts(9 compiled-CSS contracts:codestaying monospaced with its semantic variants on the BUI token layer,markas a non-mono pill, the engine defaultmarkhighlight being overridden, and the dot reading--tx-entity-colorwith a fallback) covers promoting the first option by default, theconfidenceto segments-and-colour mapping,signal/toneoverrides, the drawer listing only alternatives, pairedaria-expanded/aria-controls,inertwhile collapsed, promotion clearing the confirmed state,ctaTonemapping, controlledmodelValue/acceptedprecedence, thectafallback, an empty list rendering nothing, and rich content through thebodyslot. - Adapted from Beautiful UI (https://www.beautifului.dev), © 2026 Shane Levine, MIT.
查看源码
packages/tuffex/packages/components/src/recommendation-card/index.ts
- How it divides from
TxToolConfirmation: that is a binary authorisation (allow / deny plus a risk tier); this is a multi-option suggestion with evidence — an option list, a confidence level, and promotion semantics. Only the two footer buttons look alike. - The meter is its own primitive: the footer and every drawer row render
TxSignalMeter, which can be used independently — see its own doc. - Accessibility:
inerton the collapsed drawer corrects an upstream defect. - Known deviation: the primary button's
box-shadowuses the same hardcodedrgba(16,24,40,·)values in both themes upstream. That is preserved to match the reference screenshots — it reads as a bevel on a solid fill rather than as a themed hairline.