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

TypeCompare-at?Typical placementComponent
StandardNoProduct cards, line itemsPriceDisplay
SaleYes (strikethrough)PDP hero, promo rowsPriceDisplay + SaleBadge
Tiered / planOptional anchor priceSaaS pricing tablesPlanPrice
RangeNoCategory filters, bundlesPriceRange
Unit priceNoGrocery, B2B quotesPriceDisplay + suffix
InstallmentNoBNPL, financingPriceDisplay + 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

PartPurposeSpec tip
Currency symbol$, , £Position: prefix vs suffix per locale
AmountNumeric valueTabular figures (font-variant-numeric)
Decimal separator. or ,Locale-driven—not hard-coded in design
Compare-atWas priceStrikethrough + muted color token
Suffix/mo, each, per seatSmaller type, text/secondary
Sale badge% off, SaleBadge 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

StateCurrent priceCompare-atBadgeColor
RegularSingle amountHiddenNonetext/primary
On saleLower amountVisible, struckOptional % offCurrent: text/sale or text/primary
ClearanceLowest tierWas priceClearance badgeStronger sale color
Member priceDiscountedPublic price struckMember badgeDistinct from generic sale
Free$0 or FreeDo not fake $0.00 if copy says Free
UnavailableHidden 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

TokenCurrent sizeCompare-atUse case
sm14px12pxCart line items, dense lists
md16–18px14pxProduct cards, search results
lg24–32px18–20pxPDP hero, plan highlight
xl36–48px20–24pxLanding 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

PatternStructureFigma approach
3-column plansFree / Pro / EnterpriseAuto layout grid; PlanCard component
Toggle monthly/annualSegmented controlSwap price variant or use component property
Per-seat$12 / user / moSuffix layer + footnote
Usage-basedFrom $0.002 / requestPriceRange with from prefix
Contact salesNo numeric pricePriceDisplay variant type=contact
Featured planBorder, scale, badgeemphasis=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

RuleDesign specDev handoff
Symbol positionShow EN vs EU examplesIntl.NumberFormat locale
Thousands separator1,299.00 vs 1.299,00Never bake commas into component text
Decimal places0 for JPY, 2 for USDminimumFractionDigits
Negative pricesAvoid in UIRefunds use separate pattern
Taxexcl. VAT sublineLink to legal copy, not invented rates
Rounding$9.99 not $9.987Document 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

RequirementImplementation
Screen readerFull phrase: “Sale price 29 dollars, was 49 dollars”
Color aloneDo not rely on red/green only for sale—use strikethrough or badge
ContrastSale color on background ≥ 4.5:1 for body size
FocusPrice is usually not interactive; links (See plans) get focus ring
MotionFlashing 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

DeliverableTypeNotes
amountnumber (minor units or decimal)Prefer cents in API, format in UI
currencyISO 4217 codeUSD, EUR
compareAtnumber optionalOmit when null
localeBCP 47 stringen-US
layoutinline | stackedResponsive switch at breakpoint
suffixstring optional/mo, per seat
showTaxNotebooleanRenders excl. tax subline
emphasisdefault | sale | mutedMaps 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

MistakeConsequenceFix
Hard-coded $ in all localesWrong symbol positionUse locale note + sample variants
Compare-at without sale currentMisleading discountHide compare-at when equal
Strikethrough on current priceInverted meaningOnly strike compare-at
$9.9 inconsistent decimalsLooks brokenAlways two decimals or document zero-decimal currencies
Sale badge without real discountTrust lossBadge tied to compareAt > amount
Tiny compare-at unreadableAccessibility failMinimum 12px sm, 3:1 contrast
Price in image onlyNot translatableLive text component
Mixed alignment in tablesRagged scanTabular figures + right align
Tax shown as part of priceLegal issuesSeparate 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.

Share on X

§ Keep reading

Related guides.