Skip to content

fix(core): improve accessibility for price displays#2829

Merged
jorgemoya merged 4 commits intocanaryfrom
fix-accessibility-price
Jan 22, 2026
Merged

fix(core): improve accessibility for price displays#2829
jorgemoya merged 4 commits intocanaryfrom
fix-accessibility-price

Conversation

@jorgemoya
Copy link
Contributor

@jorgemoya jorgemoya commented Jan 20, 2026

What/Why?

Improve accessibility for price displays by adding screen reader announcements for original prices, sale prices, and price ranges. Visual price elements are hidden from assistive technologies using aria-hidden="true" to prevent duplicate announcements, while visually hidden text provides context about pricing information.

Testing

Screenshot 2026-01-21 at 1 17 25 PM

Migration steps

Step 1: Update Cart Price Display

Update core/vibes/soul/sections/cart/client.tsx to add accessibility labels for sale prices:

        {lineItem.salePrice && lineItem.salePrice !== lineItem.price ? (
          <span className="font-medium @xl:ml-auto">
-           <span className="line-through">{lineItem.price}</span> {lineItem.salePrice}
+           <span className="sr-only">{t('originalPrice', { price: lineItem.price })}</span>
+           <span aria-hidden="true" className="line-through">
+             {lineItem.price}
+           </span>{' '}
+           <span className="sr-only">{t('currentPrice', { price: lineItem.salePrice })}</span>
+           <span aria-hidden="true">{lineItem.salePrice}</span>
          </span>
        ) : (
          <span className="font-medium @xl:ml-auto">{lineItem.price}</span>
        )}

Step 2: Update PriceLabel Component

Update core/vibes/soul/primitives/price-label/index.tsx to add accessibility improvements for sale prices and price ranges:

  import { clsx } from 'clsx';
+ import { useTranslations } from 'next-intl';

  export function PriceLabel({ className, colorScheme = 'light', price }: Props) {
+   const t = useTranslations('Components.Price');

    if (typeof price === 'string') {
      return (
        ...
      );
    }

    switch (price.type) {
      case 'range':
        return (
          <span ...>
-           {price.minValue}
-           &nbsp;&ndash;&nbsp;
-           {price.maxValue}
+           <span className="sr-only">
+             {t('range', { minValue: price.minValue, maxValue: price.maxValue })}
+           </span>
+           <span aria-hidden="true">
+             {price.minValue} - {price.maxValue}
+           </span>
          </span>
        );

      case 'sale':
        return (
          <span className={clsx('block font-semibold', className)}>
+           <span className="sr-only">{t('originalPrice', { price: price.previousValue })}</span>
            <span
+             aria-hidden="true"
              className={clsx(
                'font-normal line-through opacity-50',
                ...
              )}
            >
              {price.previousValue}
            </span>{' '}
+           <span className="sr-only">{t('currentPrice', { price: price.currentValue })}</span>
            <span
+             aria-hidden="true"
              className={clsx(
                ...
              )}
            >
              {price.currentValue}
            </span>
          </span>
        );
    }
  }

Step 3: Add Translation Keys

Update core/messages/en.json to include new translation keys for price accessibility:

  "Cart": {
    "title": "Cart",
    "heading": "Your cart",
    "proceedToCheckout": "Proceed to checkout",
    "increment": "Increase quantity",
    "decrement": "Decrease quantity",
    "removeItem": "Remove item",
    "cartCombined": "We noticed you had items saved in a previous cart, so we've added them to your current cart for you.",
    "cartRestored": "You started a cart on another device, and we've restored it here so you can pick up where you left off.",
    "cartUpdateInProgress": "You have a cart update in progress. Are you sure you want to leave this page? Your changes may be lost.",
+   "originalPrice": "Original price was {price}.",
+   "currentPrice": "Current price is {price}.",
    },
+   "Price": {
+     "originalPrice": "Original price was {price}.",
+     "currentPrice": "Current price is {price}.",
+     "range": "Price from {minValue} to {maxValue}."
+   }
  },
  "GiftCertificates": {

@jorgemoya jorgemoya requested a review from a team as a code owner January 20, 2026 21:54
@changeset-bot
Copy link

changeset-bot bot commented Jan 20, 2026

🦋 Changeset detected

Latest commit: d84493b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@bigcommerce/catalyst-core Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Jan 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
catalyst Ready Ready Preview, Comment Jan 21, 2026 8:57pm

Request Review

@jorgemoya
Copy link
Contributor Author

@chanceaclark updated per the recommended suggestions in offline discussion.

Copy link
Contributor

@chanceaclark chanceaclark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🍹 @jorgemoya do we also need to apply this to other places on the storefront, e.g. the product page?

@jorgemoya
Copy link
Contributor Author

🍹 @jorgemoya do we also need to apply this to other places on the storefront, e.g. the product page?

Good callout, updated!

@jorgemoya jorgemoya changed the title fix(core): remove decorative price element from accessibility tree fix(core): improve accessibility for price displays Jan 21, 2026
@jorgemoya jorgemoya added this pull request to the merge queue Jan 22, 2026
Merged via the queue into canary with commit 8096cc5 Jan 22, 2026
8 checks passed
@jorgemoya jorgemoya deleted the fix-accessibility-price branch January 22, 2026 02:22
jamesqquick pushed a commit that referenced this pull request Feb 11, 2026
* fix(core): remove decorative price element from accessibility tree

* fix: update to include accessibility best practices

* fix: update changeset

* fix: add price label component
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants