Skip to content

Comparison

A UX principle for coding agents. Also covers compare options, side by side, comparison table, results comparison, versus, choosing between options, and 1 more.

Show all 7 aliases

compare options, side by side, comparison table, results comparison, versus, choosing between options, comparison shopping

Some products exist to help the user choose between options: flights, plans, apartments, laptops, insurance quotes. For those products, comparison is the job — not a feature bolted onto search, but the core loop the whole interface serves. Skyscanner is the reference case: clear results, flexible filters, honest numbers, and nothing competing with the decision. It wins not by doing more than Google Flights but by overdelivering on the same single job with more personality. Use this doc when the screen's purpose is "help me pick one of these."

The lesson generalizes: know your one job, and overdeliver on it (see tradeoffs.md — Be Opinionated, Not Well-Rounded; ui/distill.md — Start From the One Job). A comparison surface that also tries to inspire, upsell, and editorialize does none of it well. Everything on the screen either helps the user decide or gets between them and the decision.

One Row, One Option, Same Fields

Comparison only works when options are structurally identical. The user's eye moves vertically down one attribute at a time — price, then duration, then stops — not horizontally through prose.

  • Every result renders the same fields in the same order and position. If one flight card shows the airline logo top-left and another shows it mid-card, the user re-parses every row instead of scanning one column.
  • Right-align or column-align the numbers and set them in tabular-nums so magnitudes compare at a glance. A price that jumps position between rows can't be compared, only re-read.
  • Show the deciding attributes on the card; defer the rest. Price, duration, and stops decide most flight choices; baggage rules and aircraft type belong behind an expand, not crowding every row (see ui/distill.md — Reduce Every Set to Its Minimum).
  • Keep one option per row. Bundling "similar" results into a stacked card saves pixels and costs comparability — the one thing the screen exists for.

Rank Honestly, Label the Sort

The default order is the strongest recommendation the product makes. Users assume the top result is the best one for them, so the ranking criteria must be visible and true.

  • Name the sort. "Best," "Cheapest," "Fastest" as labeled, switchable tabs beats an unlabeled feed. If "Best" blends price and duration, a one-line explanation of the blend earns trust; a mystery ranking spends it.
  • Never let paid placement pose as relevance. A promoted result wears a visible label, full stop — in a slot every row reserves, so the badge never shifts the comparison fields out of alignment. One discovered ad disguised as the "best" option poisons every future ranking (see anti-patterns.md).
  • Summarize each sort's winner up front. Skyscanner's Best/Cheapest/Fastest tabs each show their price and duration before the user commits to a tab — the tabs themselves become a mini-comparison.
  • Keep the order stable while the user reads. Late-arriving results append or announce ("3 new results"); silently reshuffling rows under the user's eyes breaks the spatial memory comparison depends on.

Filters Are the Steering Wheel

In a comparison product, filtering isn't refinement — it's how the user expresses what they actually want and watches the tradeoff move. The mechanics of filter UI live in search.md; comparison adds its own requirements.

  • Filter on the attributes people decide with, not the ones the database happens to index: stops, departure window, total duration, "exclude red-eyes" — the vocabulary of the decision, not of the schema.
  • Show the cost of each constraint before it's applied. A count ("nonstop · 12") or a delta ("from $340") next to each filter option turns filtering into informed steering instead of trial and error.
  • Apply instantly, reverse instantly. Sliders and checkboxes update results live; a prominent reset undoes an over-constrained state. Dead ends teach users to stop touching the filters.
  • Keep active filters visible as removable chips above the results, mirrored in the URL so a filtered comparison can be shared or revisited (see search.md — Filters & Facets).

No Bullshit Numbers

Comparison products live or die on trust in the numbers. The user is about to make a decision using your figures; any gap between the number shown and the number charged converts a customer into an ex-customer.

  • The price on the card is the price at checkout. Fold mandatory fees and taxes in from the first screen. Drip pricing wins one conversion and loses the comparison habit that brings users back (see anti-patterns.md; cognitive-laws.md — Reference Dependence).
  • No manufactured urgency. "2 seats left" is fine when it's true and ruinous when it's ever caught false. If you can't guarantee the data, don't show the pressure.
  • Keep numbers consistent across surfaces. The price in the list, on the detail page, and at handoff must match. Every discrepancy forces the user to re-verify everything else you've told them.
  • State data freshness when it matters. Prices that may have changed since caching say so ("prices from 2 hours ago") instead of letting the checkout page break the news.

Personality in the Seams, Never in the Data

"Google Flights with personality" is the ceiling for this pattern: the results are just as fast and scannable, and the product still feels like someone made it. The trick is placement — personality lives in the moments between comparisons, never inside them.

  • Data rows stay quiet. No illustrations, mascots, or voice inside the result list; there they compete with the decision. Rows are set in the calm, dense register of a tool.
  • Loading, empty, and error states carry the brand. The search-in-progress animation, the "no flights this day" illustration, the confirmation moment — these are free real estate for warmth because nothing is being compared there (see microcopy.md).
  • Voice shows up in labels and summaries, one register warmer than the data: "Get me anywhere" as a destination option is personality that adds capability, not decoration.
  • Motion stays subordinate to scanning. Filter transitions and result updates can be smooth, but never animate values the user is actively comparing (see interactions.md).

Checklist

  • Every result shows the same fields in the same order; numbers column-aligned in tabular-nums
  • Deciding attributes on the card; secondary detail behind an expand
  • Sort labeled and switchable; default ranking criteria visible and honest
  • Each sort tab shows its winner's headline numbers before the user commits to it
  • Promoted results visibly labeled, never disguised as relevance
  • Filters use decision vocabulary, show counts or price deltas, apply and reverse instantly
  • Active filters shown as removable chips and reflected in the URL
  • Card price equals checkout price; fees folded in from the first screen
  • Numbers match across list, detail, and handoff; staleness disclosed when data is cached
  • No urgency or scarcity claims that aren't verifiably true
  • Result order stable while the user reads; new results announced, not reshuffled
  • Personality confined to loading, empty, error, and confirmation states; data rows stay quiet
  • Values the user is actively comparing never animate

Use this guidance in your coding agent

Install the Better Design MCP once. Your agent then loads this page with one call.

get-ux-principle({ topic: "comparison" })
claude mcp add --scope user better-design --transport http https://better-design.com/api/mcp --header "Authorization: Bearer <YOUR_API_KEY>"
Browse related design systems