figma guide
Designing price tags and pricing UI in Figma: sale, compare-at, and handoff
Design sale prices, compare-at strikethroughs, currency formatting, and tiered pricing in Figma with variants, tokens, and Dev Mode specs for ecommerce and SaaS.
- Published
- Updated
- Jun 17, 2026
- Read time
- 7 min
- Level
- Beginner
Quick answer
Price UI is not one text layer—it is a small system of current price, optional compare-at (strikethrough), currency symbol, sale badge, and unit suffix (per month, each) with explicit formatting rules. Build a PriceDisplay component with variants for emphasis=default | sale | muted, size=sm | md | lg, and layout=inline | stacked. Bind compare-at visibility to a boolean and document rounding, locale, and tax disclaimers in Dev Mode. Pair with cards, rating UI, and badges. Start from the Figma guides hub.
Who this is for
- Product designers shipping PDPs, plan pickers, carts, and promotional modules.
- Design system teams standardizing currency display across markets and breakpoints.
- Engineers implementing locale-aware formatting, sale logic, and accessible price announcements.
Price display types
| Type | Compare-at? | Typical placement | Component |
|---|---|---|---|
| Standard | No | Product cards, line items | PriceDisplay |
| Sale | Yes (strikethrough) | PDP hero, promo rows | PriceDisplay + SaleBadge |
| Tiered / plan | Optional anchor price | SaaS pricing tables | PlanPrice |
| Range | No | Category filters, bundles | PriceRange |
| Unit price | No | Grocery, B2B quotes | PriceDisplay + suffix |
| Installment | No | BNPL, financing | PriceDisplay + subline |
Verdict: separate display (read-only formatted string) from input (rare—quotes, admin). Most checkout flows only need display components with documented formatting props.
PriceDisplay anatomy
| Part | Purpose | Spec tip |
|---|---|---|
| Currency symbol | $, €, £ | Position: prefix vs suffix per locale |
| Amount | Numeric value | Tabular figures (font-variant-numeric) |
| Decimal separator | . or , | Locale-driven—not hard-coded in design |
| Compare-at | Was price | Strikethrough + muted color token |
| Suffix | /mo, each, per seat | Smaller type, text/secondary |
| Sale badge | % off, Sale | Badge optional |
PriceDisplay
├── Variant: emphasis=default | sale | muted
├── Variant: size=sm | md | lg
├── Variant: layout=inline | stacked
├── Property: showCompareAt (boolean)
├── Property: showCurrency (boolean)
└── Layers:
├── CompareAt (optional)
├── Current
└── Suffix (optional)
Use typography tokens with tabular lining for aligned columns in tables and cards.
Sale vs regular states
| State | Current price | Compare-at | Badge | Color |
|---|---|---|---|---|
| Regular | Single amount | Hidden | None | text/primary |
| On sale | Lower amount | Visible, struck | Optional % off | Current: text/sale or text/primary |
| Clearance | Lowest tier | Was price | Clearance badge | Stronger sale color |
| Member price | Discounted | Public price struck | Member badge | Distinct from generic sale |
| Free | $0 or Free | — | — | Do not fake $0.00 if copy says Free |
| Unavailable | Hidden or — | — | — | Pair with empty states |
Document which price wins when multiple discounts stack—designers often show member + coupon without defining precedence; engineers need a single source of truth.
Size tokens
| Token | Current size | Compare-at | Use case |
|---|---|---|---|
sm | 14px | 12px | Cart line items, dense lists |
md | 16–18px | 14px | Product cards, search results |
lg | 24–32px | 18–20px | PDP hero, plan highlight |
xl | 36–48px | 20–24px | Landing hero, annual plan CTA |
Keep currency symbol and amount on one baseline in inline layout; stacked layout puts compare-at above current for narrow mobile cards.
Tiered and SaaS pricing patterns
| Pattern | Structure | Figma approach |
|---|---|---|
| 3-column plans | Free / Pro / Enterprise | Auto layout grid; PlanCard component |
| Toggle monthly/annual | Segmented control | Swap price variant or use component property |
| Per-seat | $12 / user / mo | Suffix layer + footnote |
| Usage-based | From $0.002 / request | PriceRange with from prefix |
| Contact sales | No numeric price | PriceDisplay variant type=contact |
| Featured plan | Border, scale, badge | emphasis=featured on one column |
Prototype annual toggle by swapping component properties on three PlanPrice instances—note that production recalculates with billing APIs, not static text overrides.
Currency and locale rules
| Rule | Design spec | Dev handoff |
|---|---|---|
| Symbol position | Show EN vs EU examples | Intl.NumberFormat locale |
| Thousands separator | 1,299.00 vs 1.299,00 | Never bake commas into component text |
| Decimal places | 0 for JPY, 2 for USD | minimumFractionDigits |
| Negative prices | Avoid in UI | Refunds use separate pattern |
| Tax | excl. VAT subline | Link to legal copy, not invented rates |
| Rounding | $9.99 not $9.987 | Document round mode |
Show one reference locale in Figma (e.g. en-US) and add a Dev Mode note: “Format via i18n—do not copy literal strings.”
Accessibility requirements
| Requirement | Implementation |
|---|---|
| Screen reader | Full phrase: “Sale price 29 dollars, was 49 dollars” |
| Color alone | Do not rely on red/green only for sale—use strikethrough or badge |
| Contrast | Sale color on background ≥ 4.5:1 for body size |
| Focus | Price is usually not interactive; links (See plans) get focus ring |
| Motion | Flashing sale banners fail WCAG—use static badges |
Run accessibility plugins on sale red against white backgrounds—many brand sale colors fail contrast at sm size.
Handoff to engineering
| Deliverable | Type | Notes |
|---|---|---|
amount | number (minor units or decimal) | Prefer cents in API, format in UI |
currency | ISO 4217 code | USD, EUR |
compareAt | number optional | Omit when null |
locale | BCP 47 string | en-US |
layout | inline | stacked | Responsive switch at breakpoint |
suffix | string optional | /mo, per seat |
showTaxNote | boolean | Renders excl. tax subline |
emphasis | default | sale | muted | Maps to color tokens |
Publish Patterns / Commerce / Price in your team library. Link semantic color tokens for sale and muted compare-at. Use the Dev Mode checklist for formatting props.
Real-world examples
Product card (search results)
md inline price: $34.99 with sm compare-at $49.99 struck beside or above on mobile stacked layout. Rating row below—price left, stars right in auto layout.
PDP hero
lg stacked: compare-at $129.00 muted, current $89.00 bold, 20% off badge. Installment subline: “or 4 payments of $22.25” in text/secondary 14px.
SaaS plan picker
Three PlanCard columns; middle featured with lg $29 /mo and annual toggle via segmented control swapping to $24 /mo billed yearly. Enterprise column shows Contact sales without numeric amount.
Common mistakes
| Mistake | Consequence | Fix |
|---|---|---|
Hard-coded $ in all locales | Wrong symbol position | Use locale note + sample variants |
| Compare-at without sale current | Misleading discount | Hide compare-at when equal |
| Strikethrough on current price | Inverted meaning | Only strike compare-at |
$9.9 inconsistent decimals | Looks broken | Always two decimals or document zero-decimal currencies |
| Sale badge without real discount | Trust loss | Badge tied to compareAt > amount |
| Tiny compare-at unreadable | Accessibility fail | Minimum 12px sm, 3:1 contrast |
| Price in image only | Not translatable | Live text component |
| Mixed alignment in tables | Ragged scan | Tabular figures + right align |
| Tax shown as part of price | Legal issues | Separate subline with disclaimer |
FAQ
Inline or stacked compare-at on mobile?
Stacked when width < 320px or when current price is lg; inline for md cards in grids.
Show cents for whole dollars?
Locale default—USD often shows .00 in formal contexts, hides in casual cards. Pick one rule per product surface.
Red sale color required?
No—strikethrough + badge is enough if sale color fails contrast. Use dark mode tokens for night theme.
Price next to sliders?
Budget filters show range ($50 – $200); PDP shows single PriceDisplay—do not reuse the same component without a type property.
Bottom line
Treat pricing as a typed component system with explicit sale states, locale formatting notes, and compare-at rules—not a one-off text layer per screen. Document rounding, tax disclaimers, and annual toggle behavior in Dev Mode so engineers and legal stay aligned. Continue with shopping cart UI, cards, forms, and the tutorials hub.
§ Keep reading