Skip to content

[Bug] price.liquid renders both sale and sold-out badge text into HTML regardless of product availability #3924

Description

@avzdeals-ship-it

Describe the current behavior
In snippets/price.liquid, both the sale badge and the sold-out badge are rendered into the HTML whenever show_badges is true, regardless of the product's actual availability or sale status:
liquid{%- if show_badges -%}

{{ 'products.product.on_sale' | t }}

{{ 'products.product.sold_out' | t }} {%- endif -%} The theme then relies on CSS classes on the parent div (such as price--on-sale and price--sold-out) to visually show or hide the correct badge. This means both translated badge strings are always present in the raw HTML source, regardless of product state. Example observed on a French localised product page for an in-stock discounted product: Promotion Épuisé This translates to: Sale Sold out The product was not sold out. The Add to Cart button was active and the product was fully purchasable. The sold-out text was present only because both badge spans are always rendered into the HTML. This is not a visible storefront issue — CSS hides the wrong badge correctly for human visitors. However, non-visual parsers read both strings, including SEO crawlers, AI shopping assistants, accessibility screen readers, merchant feed validators, and third-party tools that parse page text without applying CSS. Describe the expected behavior Only the contextually correct badge should be rendered into the HTML:

If the product/variant is unavailable → render the sold-out badge only
If the product/variant is available and on sale → render the sale badge only
Otherwise → render no badge

The sold-out badge text should not exist in the HTML source for an in-stock sale product.
Version information (Dawn, browsers and operating systems)

Theme: Refresh
Refresh Version: 15.4.1
Browser: Chrome
Operating System: Windows 10 / Windows 11
Localised market tested: French / France
Example locale: /fr-fr/
Example issue text observed in source: Promotion Épuisé

Note: this is filed against Dawn as Refresh uses the same price.liquid badge rendering pattern. The issue is reproducible in Dawn using the same snippet logic.
Possible solution
Replace the unconditional badge block in snippets/price.liquid with conditional rendering that mirrors the logic already correctly applied to the parent div's CSS classes:
liquid{%- if show_badges -%}
{%- if available == false -%}

{{ 'products.product.sold_out' | t }}

{%- elsif compare_at_price > price and product.quantity_price_breaks_configured? != true -%}

{{ 'products.product.on_sale' | t }}

{%- endif -%}
{%- endif -%}
All variables used (available, compare_at_price, price, product) are already assigned earlier in the same snippet. No other files need to be changed.
Additional context
After applying this fix to snippets/price.liquid, the French product page source no longer contained the sold-out badge text for the in-stock discounted product. The sale badge continued to render correctly.
Before fix — raw HTML text extraction:
Promotion Épuisé
After fix — raw HTML text extraction:
Promotion
The product remained purchasable throughout. The fix only affects what is rendered into the HTML source — it does not change any visible storefront behaviour for human visitors.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions