Add Percentage Discount in Shopify Dawn Theme
Recently, I encountered a situation while working with Shopify’s Dawn theme (Version 15.1.0) where I needed to display a percentage discount on the product cards in the Featured Collection. While Shopify provides built-in sale badges, they don’t show percentage-based discounts by default, which is something I really wanted to add.
In this post, I’ll walk you through how I approached the problem, what didn’t work at first, and how I eventually got it right using some Liquid logic to calculate the percentage discount. I’ll also share some useful debugging techniques and tips to ensure your code is optimized and scalable.

The Initial Code & Problem
The first approach I tried was straightforward—using Shopify’s compare_at_price and price to calculate the discount percentage. Here’s the initial code snippet I started with in the card-product.liquid snippet:
<span class="badge price__badge-sale color-{{ settings.sale_badge_color_scheme }}">
{{ 'products.product.on_sale' | t }}
</span>This simply added a sale badge, but I wanted it to show the actual percentage discount. So, I modified it like this:
{%- elsif card_product.compare_at_price > card_product.price and card_product.available -%}
{% assign discount_percentage = card_product.compare_at_price | minus: card_product.price | divided_by: card_product.compare_at_price | times: 100 | round %}
<span class="badge">{{ discount_percentage }}% OFF</span>Unfortunately, this didn’t work. Instead of the correct percentage, I ended up with “0% OFF.” After some investigation, I realized the problem was with how Liquid handles integer division—it rounds down, which was causing the percentage to round down to zero.
Debugging the Issue
To better understand what was going wrong, I added a quick debug block to print out the compare_at_price and price values:
<div class="debug">
compare_at_price: {{ card_product.compare_at_price }}
price: {{ card_product.price }}
</div>
This showed me the actual prices, and it confirmed that Liquid was indeed rounding the result to zero during the division.
The Solution: Force Floating-Point Calculation
To fix this, I needed to ensure Liquid was treating the numbers as floating-point values instead of integers. The trick here was to multiply the result by 1.0 to force floating-point precision. Here’s the final working solution:
<div class="card__badge {{ settings.badge_position }}">
{%- if card_product.available == false -%}
<span
id="Badge-{{ section_id }}-{{ card_product.id }}"
class="badge badge--bottom-left color-{{ settings.sold_out_badge_color_scheme }}"
>
{{- 'products.product.sold_out' | t -}}
</span>
{%- elsif card_product.compare_at_price > card_product.price and card_product.available -%}
{% assign compare_price = card_product.compare_at_price | default: 0 %}
{% assign current_price = card_product.price | default: 0 %}
{%- if compare_price > current_price -%}
{% assign discount_value = compare_price | minus: current_price %}
{% assign discount_percentage = discount_value | times: 1.0 | divided_by: compare_price | times: 100 | round %}
<span
id="Badge-{{ section_id }}-{{ card_product.id }}"
class="badge badge--bottom-left color-{{ settings.sale_badge_color_scheme }}"
>
{{ discount_percentage }}% OFF
<div class="debug">
compare_at_price: {{ card_product.compare_at_price }}
price: {{ card_product.price }}
discount_value: {{ discount_value }}
discount_percentage: {{ discount_percentage }}%
</div>
</span>
{%- else -%}
<span
id="Badge-{{ section_id }}-{{ card_product.id }}"
class="badge badge--bottom-left color-{{ settings.sale_badge_color_scheme }}"
>
{{ 'products.product.on_sale' | t }}
</span>
{%- endif -%}
{%- endif -%}
</div>Breaking Down the Code:
Let me walk you through how this solution works:
Availability Check:
If the product is not available (sold out), I display a “Sold Out” badge using:
{{ 'products.product.sold_out' | t }}Discount Calculation:
I use the compare_at_price and price values to calculate the discount. But to avoid Liquid rounding down the result, I force the calculation to use floating-point precision by multiplying by 1.0:
{% assign discount_percentage = discount_value | times: 1.0 | divided_by: compare_price | times: 100 | round %}Debugging Info
ncluding a debug block helped me verify the values during development:
<div class="debug">
compare_at_price: {{ card_product.compare_at_price }}
price: {{ card_product.price }}
discount_value: {{ discount_value }}
discount_percentage: {{ discount_percentage }}%
</div>I found this especially useful while troubleshooting, but of course, it’s important to remove these debug elements before pushing the code live.

Best Practices
Here are a few things I learned while working on this:
- Avoid Integer Division Pitfalls:
When working with Liquid, be cautious with division involving integers. If you need to work with decimals or percentages, always force floating-point precision by multiplying one of the values by1.0. - Use Debugging Tools:
I added debug blocks to check my assumptions. This simple practice saved me a lot of time during the troubleshooting process. - Performance Considerations:
Be mindful of how many calculations you perform in your Liquid templates. Since this discount calculation is relatively lightweight, it should work fine across the store. But if you’re adding more complex logic, consider caching or performing some calculations in the backend.