Inject a powerful sense of urgency into your site and watch conversions climb. Our dynamic countdown timer instantly signals to shoppers that time is running out, motivating them to complete their purchase before they miss out.

Use it to highlight your next-day delivery cutoff so customers know exactly how many hours remain to place an order and receive it by tomorrow. Leverage it during flash sales or limited-time promotions to make every second count for example, only two hours left to save 20 percent.

By clearly displaying the remaining time, you eliminate hesitation, reduce cart abandonment and transform casual browsers into committed buyers. Whether you are pushing last-minute holiday gifts, end-of-season markdowns or exclusive bundle deals, this countdown timer turns thinking into action.

Features

Edit Title
Enter a clear, concise headline that tells customers exactly what the countdown is for.

Edit Subtitle
Add a brief subtitle to provide additional context or details.

Choose Colour Scheme
Select a colour palette that complements your site’s overall theme.

Set Countdown End Time
Specify the exact end time in 24-hour format (e.g., 18:00).

How to Add this to your Theme

Create a snippet in your snippets theme folder and add the following code:

{%- comment -%}
  Countdown Timer Component
  Usage:
  {% render 'parallel-countdown-timer',
    title: block.settings.title,
    subtitle: block.settings.subtitle,
    end_time: block.settings.end_time,
    color_scheme: block.settings.color_scheme
  %}
{%- endcomment -%}

{%- liquid
  assign title = title | default: 'Limited Time Offer'
  assign subtitle = subtitle | default: 'Ends in'
  assign end_time = end_time | default: '23:59'
  assign color_scheme = color_scheme | default: 'scheme-1'
-%}

<div class="countdown-timer-wrapper color-{{ color_scheme }} gradient">
  {%- if title != blank -%}
    <h2 class="countdown-title h2">{{ title }}</h2>
  {%- endif -%}

  {%- if subtitle != blank -%}
    <div class="countdown-subtitle subtitle">{{ subtitle }}</div>
  {%- endif -%}

  <div class="countdown-timer" data-end-time="{{ end_time }}">
    <div class="countdown-unit">
      <div class="countdown-value-wrapper">
        <span class="countdown-value hours">00</span>
      </div>
      <span class="countdown-label">Hours</span>
    </div>
    <div class="countdown-unit">
      <div class="countdown-value-wrapper">
        <span class="countdown-value minutes">00</span>
      </div>
      <span class="countdown-label">Minutes</span>
    </div>
    <div class="countdown-unit">
      <div class="countdown-value-wrapper">
        <span class="countdown-value seconds">00</span>
      </div>
      <span class="countdown-label">Seconds</span>
    </div>
  </div>
</div>

<style>
  .countdown-timer-wrapper {
    padding: 2rem;
    border-radius: 0.5rem;
    margin: 1rem 0;
    text-align: center;
  }

  .countdown-title {
    margin: 0 0 0.5rem;
  }

  .countdown-subtitle {
    margin: 0 0 1rem;
    opacity: 0.8;
  }

  .countdown-timer {
    display: flex;
    gap: 1rem;
    justify-content: center;
    align-items: center;
  }

  .countdown-unit {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 80px;
  }

  .countdown-value-wrapper {
    background: rgba(247, 247, 247, 0.5);
    padding: 0.5rem 1rem;
    border-radius: 0.25rem;
    min-width: 60px;
    text-align: center;
  }

  .countdown-value {
    font-size: 2rem;
    font-weight: bold;
    line-height: 1;
  }

  .countdown-label {
    margin-top: 0.5rem;
    font-size: 0.875rem;
    opacity: 0.8;
  }

  @media screen and (max-width: 749px) {
    .countdown-timer-wrapper {
      padding: 1.5rem;
    }

    .countdown-timer {
      gap: 0.5rem;
    }

    .countdown-unit {
      min-width: 60px;
    }

    .countdown-value-wrapper {
      min-width: 45px;
      padding: 0.25rem 0.5rem;
    }

    .countdown-value {
      font-size: 1.5rem;
    }

    .countdown-label {
      font-size: 0.75rem;
    }
  }

  /* Animation classes */
  .countdown-value-wrapper {
    transition: transform 0.2s ease;
  }

  .countdown-value-wrapper.pulse {
    animation: pulse 0.5s ease;
  }

  @keyframes pulse {
    0% {
      transform: scale(1);
    }
    50% {
      transform: scale(1.1);
    }
    100% {
      transform: scale(1);
    }
  }

  .countdown-timer-wrapper.fade-out {
    animation: fadeOut 0.5s ease forwards;
  }

  @keyframes fadeOut {
    from {
      opacity: 1;
    }
    to {
      opacity: 0;
    }
  }
</style>

<script>
  class CountdownTimer {
    constructor(element) {
      this.element = element;
      this.endTime = element.dataset.endTime;
      this.elements = {
        hours: element.querySelector('.hours'),
        minutes: element.querySelector('.minutes'),
        seconds: element.querySelector('.seconds'),
      };

      // Validate time format
      if (!this.validateTimeFormat(this.endTime)) {
        console.error('Invalid time format. Please use 24-hour format (HH:MM)');
        return;
      }

      // Start the countdown
      this.tick();
      this.interval = setInterval(() => this.tick(), 1000);
    }

    validateTimeFormat(time) {
      const timeRegex = /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/;
      return timeRegex.test(time);
    }

    tick() {
      const now = new Date();
      const [hours, minutes] = this.endTime.split(':').map(Number);
      const endDate = new Date(now);
      endDate.setHours(hours, minutes, 0, 0);

      // If end time has passed, set to next day
      if (now > endDate) {
        endDate.setDate(endDate.getDate() + 1);
      }

      const timeLeft = endDate - now;
      const hoursLeft = Math.floor(timeLeft / (1000 * 60 * 60));
      const minutesLeft = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60));
      const secondsLeft = Math.floor((timeLeft % (1000 * 60)) / 1000);

      this.updateDisplay(hoursLeft, minutesLeft, secondsLeft);

      if (timeLeft <= 0) {
        this.end();
      }
    }

    updateDisplay(hours, minutes, seconds) {
      const updateValue = (element, newValue) => {
        const currentValue = element.textContent;
        const paddedValue = newValue.toString().padStart(2, '0');

        if (currentValue !== paddedValue) {
          element.textContent = paddedValue;
          element.closest('.countdown-value-wrapper').classList.add('pulse');
          setTimeout(() => {
            element.closest('.countdown-value-wrapper').classList.remove('pulse');
          }, 500);
        }
      };

      updateValue(this.elements.hours, hours);
      updateValue(this.elements.minutes, minutes);
      updateValue(this.elements.seconds, seconds);
    }

    end() {
      clearInterval(this.interval);
      this.element.closest('.countdown-timer-wrapper').classList.add('fade-out');
      setTimeout(() => {
        this.element.closest('.countdown-timer-wrapper').style.display = 'none';
      }, 500);
    }
  }

  // Initialise all countdown timers on the page
  document.querySelectorAll('.countdown-timer').forEach((timer) => {
    new CountdownTimer(timer);
  });
</script>

Add this to your main-product.liquid schema:

    {
      "type": "parallel-countdown-timer",
      "name": "Parallel Countdown Timer",
      "limit": 1,
      "settings": [
        {
          "type": "text",
          "id": "title",
          "label": "Title",
          "default": "Limited Time Offer"
        },
        {
          "type": "text",
          "id": "subtitle",
          "label": "Subtitle",
          "default": "Ends in"
        },
        {
          "type": "text",
          "id": "end_time",
          "label": "End Time",
          "default": "23:59",
          "info": "Enter time in 24-hour format (HH:MM)"
        },
        {
          "type": "color_scheme",
          "id": "color_scheme",
          "label": "Color scheme",
          "default": "scheme-1"
        }
      ]
    },

Add this within your main-product.liquids after {% when ‘custom_liquid’ %}, just before the next {% when ‘…….’ %}.

{%- when 'parallel-countdown-timer' -%}
{% render 'parallel-countdown-timer',
title: block.settings.title,
subtitle: block.settings.subtitle,
end_time: block.settings.end_time,
color_scheme: block.settings.color_scheme
%}

Congratulations 🎉

You now have a countdown timer you can add to your product page. This is a pretty versatile little snippet that can easily be expanded to do a lot more. Enjoy.

Matt

Leave a Reply

Your email address will not be published. Required fields are marked *