Einen BFCM Countdown-Banner ohne App bauen

Zuletzt aktualisiert
Von Experten geprüft
5 Min. Lesezeit
Jacques Blom
Jacques Blom
CTO bei Fudge.

Key Takeaways

  • Du brauchst keine App für einen Countdown-Banner. Ein Timer ist eine einzige Section-Datei: etwas Liquid, ein bisschen CSS und ein kleines Inline-Script. Apps verlangen dafür monatliche Gebühren und belasten jede Seite mit ihrem eigenen Script-Gewicht.
  • Die Announcement Bar von Dawn hat keinen Countdown. Sie unterstützt bis zu 12 rotierende Textankündigungen, Social-Media-Icons und Sprach-/Länderauswahl, aber nichts Zeitbasiertes. Eine Custom Section ist also der app-freie Weg.1
  • Die Zeitzonen-Falle ist real. Ein Datums-String ohne UTC-Offset wird in der lokalen Zeitzone jedes Besuchers geparst, sodass dein Sale in jedem Land zu einem anderen Zeitpunkt “endet”. Pinne die Deadline mit einem expliziten Offset fest.
  • Ein Banner ist klassisches CLS-Territorium. Reserviere die Höhe im CSS und gib den Ziffern Boxen mit fester Breite, sonst verschiebt der Timer nach dem Laden deine gesamte Seite nach unten.

Ein Shopify Countdown-Banner ohne App ist eine einzige Section-Datei, die du in dein Theme einfügst: eine Nachricht, vier tickende Zahlen und eine Deadline, die im Theme-Editor festgelegt wird. Dieser Guide gibt dir die komplette Section und geht dann auf die drei Details ein, die bei den meisten Copy-Paste-Snippets falsch gemacht werden: Zeitzonen, Layout Shift und Screenreader.

Er wurde für BFCM geschrieben, denn dann greifen die meisten Shops zu einem Timer. Aber die Section funktioniert für jeden Sale mit einem echten Enddatum.

Warum du uns vertrauen kannst

Wir sind seit über vier Jahren im Shopify-Bereich tätig und haben mit Hunderten von Shopify-Brands an ihren Storefronts gearbeitet. Jacques hat über 15 Jahre Entwicklererfahrung. Wir bauen Fudge, einen AI Storefront Editor mit einer 4.9-Bewertung im Shopify App Store und “Built for Shopify”-Status. Das Schreiben von Theme Sections wie dieser ist also buchstäblich das, was unser Produkt den ganzen Tag macht.


Warum ein Countdown-Banner für BFCM funktioniert

Eine Deadline beantwortet die eine Frage, die sich jeder BFCM-Käufer im Stillen stellt: Kann ich mir das später noch überlegen? Wenn die ehrliche Antwort “Nein” lautet, sagt das ein sichtbarer Timer besser als jedes Adjektiv. Das ist Loss Aversion (Verlustaversion), die für dich verkauft.

Das Schlüsselwort ist “ehrlich”. Zähle bis zu einer echten Endzeit herunter und beende den Sale, wenn null erreicht ist. Evergreen-Timer, die sich beim Neuladen heimlich zurücksetzen, gewöhnen wiederkehrende Besucher daran, Dringlichkeit zu ignorieren, und bringen dich in einer wachsenden Liste von Rechtsprechungen auf die falsche Seite der Dark-Pattern-Regeln.

Ein Banner ist auch der billigste Ort, um Urgency zu erzeugen. Es sitzt auf jeder Seite über dem Header, erfordert kein Redesign und verschwindet in dem Moment, in dem der Sale endet.

Wenn du den Rest deiner BFCM-Vorbereitungen triffst, solltest du auch die Tracking-Seite abdecken: Unser Guide zum serverseitigen Meta-Tracking mit der Conversions API sorgt dafür, dass der Traffic, den dein Banner konvertiert, auch richtig zugeordnet wird.

Brauchst du wirklich eine Countdown-Timer-App?

Nein, und für ein so kleines Banner ist der Deal mit der App in jeder Hinsicht schlecht.

Apps kosten Geld für etwas, das ein Theme nativ tun kann. Countdown-Banner-Apps verlangen Abos für etwas, das auf eine einzige Section-Datei hinausläuft. Für komplexe Funktionen ist das in Ordnung; ein Timer ist keine komplexe Funktion.

Apps kosten Geschwindigkeit auf jeder Seite, das ganze Jahr über. App Embeds injizieren ihr eigenes JavaScript und CSS in deine Storefront, und die meisten bleiben geladen, nachdem der Sale endet, oder sogar nach der Deinstallation, wenn die App dein Theme bearbeitet hat. Während BFCM, wenn jeder Besucher zählt, ist das zusätzliche Skript-Gewicht von Drittanbietern das Letzte, was deine verkehrsstärkste Woche braucht.

Eine native Section hingegen besteht aus wenigen Kilobytes, die über Shopifys CDN mit deinem Theme ausgeliefert, im selben Theme-Editor wie alles andere bearbeitet und mit einem Klick gelöscht wird, wenn der Dezember kommt.

Was kann der Theme-Editor ganz ohne Code?

Es lohnt sich, das vor dem Bauen zu prüfen, denn die Antwort hat sich 2025-2026 geändert.

Dawn und die meisten kostenlosen Themes: kein Countdown. Die Announcement Bar Section von Dawn unterstützt bis zu 12 rotierende Ankündigungsblöcke mit Links, Auto-Rotate, Social-Media-Icons, einer Trennlinie sowie Länder- und Sprachauswahl. Im Schema gibt es nirgends eine zeitbasierte Einstellung.1 Eine statische Nachricht wie “Sale endet am Montag” ist das Höchste der Gefühle.

Horizon und KI-generierte Blöcke: ja, irgendwie schon. Der Shopify Theme-Editor kann jetzt über Shopify Magic einen benutzerdefinierten Block (Custom Block) aus einem Text-Prompt generieren. Du klickst auf “Block hinzufügen”, wählst “Generieren” und beschreibst den gewünschten Timer. Das funktioniert über Theme-Store-Themes hinweg in den meisten Plänen, mit einem Vorbehalt, den man sich zweimal durchlesen sollte: Der Shopify-Support hilft nicht bei der Anpassung oder Fehlersuche des generierten Codes, und die Qualität liegt ausdrücklich in deiner Verantwortung.2

Dieser Vorbehalt ist der Grund, warum eine handgeprüfte Section für BFCM immer noch der bessere Weg ist. Du bekommst Code, den du gelesen hast, mit genau den Einstellungen, die du willst, und keinen Überraschungen um 23 Uhr an Thanksgiving. Wenn du noch nie eine Section hinzugefügt hast, überfliege zuerst wie man Homepage-Sections in Shopify hinzufügt; die Mechanik ist identisch.


Bau es ein: die komplette Countdown-Banner-Section

Gehe in deinem Adminbereich auf Onlineshop → Themes → ⋯ → Code bearbeiten, dupliziere zuerst das Theme, erstelle dann unter sections/ eine neue Datei namens countdown-banner.liquid und füge den gesamten Code unten ein.

<div
  id="CountdownBanner-{{ section.id }}"
  class="countdown-banner"
  data-end="{{ section.settings.end_date | escape }}T{{ section.settings.end_time | escape }}:00{{ section.settings.utc_offset | escape }}"
  data-expiry="{{ section.settings.expiry_behavior | escape }}"
  style="background: {{ section.settings.bg_color }}; color: {{ section.settings.text_color }};"
>
  <div class="countdown-banner__inner">
    <p class="countdown-banner__message">
      {%- if section.settings.link != blank -%}
        <a href="{{ section.settings.link }}">{{ section.settings.message | escape }}</a>
      {%- else -%}
        {{ section.settings.message | escape }}
      {%- endif -%}
    </p>

    <div class="countdown-banner__timer" role="timer" aria-live="off">
      <span class="countdown-banner__sr-only">
        Angebot endet am {{ section.settings.end_date }} um {{ section.settings.end_time }}
      </span>
      <span class="countdown-banner__unit" aria-hidden="true">
        <span class="countdown-banner__digits" data-unit="days"></span>
        <small>Tage</small>
      </span>
      <span class="countdown-banner__unit" aria-hidden="true">
        <span class="countdown-banner__digits" data-unit="hours"></span>
        <small>Std</small>
      </span>
      <span class="countdown-banner__unit" aria-hidden="true">
        <span class="countdown-banner__digits" data-unit="minutes"></span>
        <small>Min</small>
      </span>
      <span class="countdown-banner__unit" aria-hidden="true">
        <span class="countdown-banner__digits" data-unit="seconds"></span>
        <small>Sek</small>
      </span>
    </div>

    <p class="countdown-banner__expired" hidden>
      {{ section.settings.expired_message | escape }}
    </p>
  </div>
</div>

{% style %}
  #CountdownBanner-{{ section.id }} {
    min-height: 3.25rem; /* Platz reservieren: kein Layout Shift, wenn JS eingefüllt wird */
    display: flex;
    align-items: center;
  }
  #CountdownBanner-{{ section.id }} .countdown-banner__inner {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.25rem;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: 0.5rem 1.5rem;
    text-align: center;
  }
  #CountdownBanner-{{ section.id }} .countdown-banner__message {
    margin: 0;
    font-size: 0.9rem;
    letter-spacing: 0.03em;
  }
  #CountdownBanner-{{ section.id }} .countdown-banner__message a {
    color: inherit;
  }
  #CountdownBanner-{{ section.id }} .countdown-banner__timer {
    display: flex;
    gap: 0.75rem;
  }
  #CountdownBanner-{{ section.id }} .countdown-banner__unit {
    display: inline-flex;
    flex-direction: column;
    line-height: 1.1;
  }
  #CountdownBanner-{{ section.id }} .countdown-banner__digits {
    font-variant-numeric: tabular-nums; /* jede Ziffer gleich breit: kein Ruckeln */
    min-width: 2ch;
    font-weight: 700;
    font-size: 1rem;
  }
  #CountdownBanner-{{ section.id }} .countdown-banner__unit small {
    font-size: 0.6rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    opacity: 0.75;
  }
  #CountdownBanner-{{ section.id }} .countdown-banner__expired {
    margin: 0;
    width: 100%;
  }
  .countdown-banner__sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  @media (prefers-reduced-motion: no-preference) {
    #CountdownBanner-{{ section.id }} .countdown-banner__digits {
      transition: opacity 150ms ease;
    }
  }
{% endstyle %}

<script>
  (function () {
    function initCountdown() {
      var banner = document.getElementById('CountdownBanner-{{ section.id }}');
      if (!banner || banner.dataset.initialized) return;
      banner.dataset.initialized = 'true';

      var timer = banner.querySelector('.countdown-banner__timer');
      var expired = banner.querySelector('.countdown-banner__expired');

      var end = new Date(banner.dataset.end).getTime();
      if (isNaN(end)) {
        // Schlechtes oder fehlendes Datum: niemals NaN anzeigen, auf statische Nachricht zurückfallen
        timer.hidden = true;
        expired.hidden = false;
        return;
      }

      var units = {};
      banner.querySelectorAll('[data-unit]').forEach(function (el) {
        units[el.dataset.unit] = el;
      });

      function pad(n) {
        return n < 10 ? '0' + n : String(n);
      }

      var tick;

      function render() {
        if (!banner.isConnected) {
          clearInterval(tick);
          return;
        }
        var diff = end - Date.now();
        if (diff <= 0) {
          clearInterval(tick);
          if (banner.dataset.expiry === 'hide') {
            banner.hidden = true;
          } else {
            timer.hidden = true;
            expired.hidden = false;
          }
          return;
        }
        var s = Math.floor(diff / 1000);
        units.days.textContent = String(Math.floor(s / 86400));
        units.hours.textContent = pad(Math.floor((s % 86400) / 3600));
        units.minutes.textContent = pad(Math.floor((s % 3600) / 60));
        units.seconds.textContent = pad(s % 60);
      }

      render();
      tick = setInterval(render, 1000);
    }

    initCountdown();

    // Theme-Editor: Neu initialisieren, wenn die Section neu geladen wird
    document.addEventListener('shopify:section:load', function (event) {
      if (event.detail && event.detail.sectionId === '{{ section.id }}') {
        initCountdown();
      }
    });
  })();
</script>

{% schema %}
{
  "name": "Countdown Banner",
  "settings": [
    {
      "type": "text",
      "id": "message",
      "label": "Nachricht",
      "default": "Black Friday: bis zu 40 % Rabatt auf alles"
    },
    {
      "type": "url",
      "id": "link",
      "label": "Link (optional)"
    },
    {
      "type": "text",
      "id": "end_date",
      "label": "Enddatum (JJJJ-MM-TT)",
      "default": "2026-11-30"
    },
    {
      "type": "text",
      "id": "end_time",
      "label": "Endzeit, 24h (HH:MM)",
      "default": "23:59"
    },
    {
      "type": "text",
      "id": "utc_offset",
      "label": "Shop UTC-Offset",
      "default": "-05:00",
      "info": "Pinnt die Deadline auf einen globalen Moment fest. Nutze den Offset der Zeitzone deines Shops, z. B. +01:00 für Berlin im November, -05:00 für New York."
    },
    {
      "type": "select",
      "id": "expiry_behavior",
      "label": "Wenn der Countdown endet",
      "options": [
        { "value": "hide", "label": "Banner ausblenden" },
        { "value": "message", "label": "Ablaufmeldung anzeigen" }
      ],
      "default": "message"
    },
    {
      "type": "text",
      "id": "expired_message",
      "label": "Ablaufmeldung",
      "default": "Der Sale ist beendet. Bis zum nächsten Jahr!"
    },
    {
      "type": "color",
      "id": "bg_color",
      "label": "Hintergrund",
      "default": "#111111"
    },
    {
      "type": "color",
      "id": "text_color",
      "label": "Text",
      "default": "#ffffff"
    }
  ],
  "presets": [{ "name": "Countdown Banner" }]
}
{% endschema %}

Speichern, den Theme-Editor öffnen und Countdown Banner zur Section-Gruppe Header hinzufügen, damit es auf jeder Seite über dem Header gerendert wird. Datum, Zeit und Offset einstellen, das Verhalten nach Ablauf wählen und fertig. Die gleiche Platzierung der Section-Gruppe funktioniert für eine Free Shipping Bar, die während BFCM gut zu einem Countdown passt.

Möchtest du das Banner lieber beschreiben und die Section in dein Theme schreiben lassen? Genau das ist Fudge.
Try Fudge for Free

Die Zeitzonen-Falle

Das ist der Fehler in den meisten Copy-Paste-Timern. new Date('2026-11-30T23:59:00'), ohne Offset, wird in der lokalen Zeitzone des Besuchers geparst. Ein Käufer in Sydney würde sehen, dass dein Sale 16 Stunden früher endet als ein Käufer in Los Angeles, und irgendjemand macht immer einen Screenshot davon.

Die Section vermeidet dies, indem sie die Einstellung utc_offset an den Datums-String anhängt, sodass 2026-11-30T23:59:00-05:00 überall auf der Erde derselbe Moment ist. Zwei Dinge, die du bei der Konfiguration prüfen solltest:

CLS-sicher machen

Ein Banner, das oben auf der Seite eingefügt wird, ist die klassische Ursache für einen schlechten Cumulative Layout Shift (CLS) Score: Alles darunter springt nach unten, wenn es erscheint. Diese Section schützt auf zwei Arten davor.

Die Höhe wird reserviert, bevor JavaScript ausgeführt wird. Das min-height: 3.25rem auf dem Wrapper bedeutet, dass der Platz beim First Paint vorhanden ist und sich durch das Einfüllen der Ziffern nichts am Layout ändert. Wenn du die Schriftart vergrößerst, miss neu und passe die reservierte Höhe entsprechend an.

Die Ziffern können nicht wackeln. font-variant-numeric: tabular-nums gibt jeder Ziffer die gleiche Breite, und min-width: 2ch auf jeder Zahlenbox verhindert, dass der Timer “atmet”, wenn aus 10 eine 09 wird. Ohne das würde das Banner bei einigen Schriftarten jede Sekunde neu fließen (Reflow).

Ein bewusster Kompromiss: Die Option beim Ablauf ausblenden entfernt das Banner und verschiebt die Seite einmalig. Wenn dein Sale inmitten von viel Traffic endet, solltest du die Option mit der Ablaufmeldung bevorzugen, da diese die Höhe beibehält. Layout Shift hat mehr Fehlerquellen als nur Banner; unser Guide zum Beheben von CLS in Shopify deckt den Rest ab.

Barrierefreiheit: Ein Timer, der ruhig bleibt

Ein Ticker, der im Sekundentakt läuft, ist das Schlimmste, was einem Screenreader angekündigt werden kann. Deshalb ist die Section so gebaut, dass sie es einmal ansagt und dann die Klappe hält:

Bevor du veröffentlichst

Überprüfe das Banner auf dem Desktop und auf dem Smartphone, da die Nachricht und der Timer bei schmalen Breiten unterschiedlich umbrechen. Lege ein Enddatum in der Vergangenheit fest, um zu bestätigen, dass der abgelaufene Status das tut, was du konfiguriert hast, und stelle dann das echte Datum wieder ein. Öffne die Section im Theme-Editor und ändere eine Einstellung, um zu prüfen, ob der Timer auch nach dem Neuladen der Section weiterläuft.


Wann stattdessen ein Page Builder Sinn macht

Ein einzelnes Banner ist kein Grund, irgendetwas zu installieren. Aber das Kalkül ändert sich, wenn der Countdown nur ein Teil eines größeren Kampagnen-Builds ist: eine BFCM-Landingpage mit Timern, gestaffelten Angeboten, beweglichen Elementen über verschiedene Templates hinweg und einem Marketingteam, das stündlich iterieren will, ohne Code anzufassen. An diesem Punkt verdient ein Page Builder oder ein spezielles Kampagnen-Tool sein Abo, zumindest für November.

Der mittlere Weg ist der, für den wir Fudge gebaut haben. Du beschreibst das Banner oder die gesamte Kampagnen-Section in einfacher Sprache, und Fudge schreibt es als natives Liquid, CSS und JavaScript in dein Theme, mitsamt Einstellungen und einem Diff, das du vor der Veröffentlichung lesen kannst. Du erhältst das app-freie Ergebnis, ohne es von Hand programmieren zu müssen; die Shopify Store Editor-Seite zeigt, wie das funktioniert. Nichts muss im Dezember deinstalliert werden, weil es von vornherein keine App-Schicht gab.

FAQ

Kann ich einen Countdown-Banner in Shopify ohne eine App hinzufügen?

Ja. Ein Countdown-Banner ist eine einzige Custom Section: Liquid-Markup, ein Schema mit einer Einstellung für das Enddatum, etwas CSS und ein kleines Inline-Script. Du fügst es in den sections/-Ordner im Code-Editor ein, fügst es über den Theme-Editor hinzu und konfigurierst die Deadline wie jede andere Section-Einstellung. Kein Abo, kein Drittanbieter-Script.

Hat das Dawn-Theme einen eingebauten Countdown-Timer?

Nein. Dawns Announcement Bar unterstützt bis zu 12 rotierende Textankündigungen mit Links, Auto-Rotate, Social-Media-Icons sowie Länder- und Sprachauswahl, aber nichts Zeitbasiertes. Um in Dawn einen Countdown zu erhalten, musst du eine Custom Section hinzufügen oder die KI-Blockgenerierung des Theme-Editors nutzen, für deren Output der Shopify-Support keine Fehlersuche anbietet.

Warum zeigt mein Countdown in verschiedenen Ländern unterschiedliche Zeiten an?

Weil der Datums-String keinen UTC-Offset hat. JavaScript parst eine reine Datums-/Zeitangabe wie 2026-11-30T23:59:00 in der lokalen Zeitzone jedes Besuchers, sodass die Deadline weltweit um bis zu einen Tag abweicht. Hänge einen expliziten Offset wie -05:00 an, um das Ende des Sales auf einen globalen Zeitpunkt festzunageln.

Wird ein Countdown-Banner meinen Core Web Vitals schaden?

Nur wenn es ohne reservierten Platz eingefügt wird. Gib dem Banner im CSS eine feste min-height, damit die Seite beim First Paint korrekt gelayoutet wird, und verwende tabular-nums mit einer min-width für die Ziffern, damit der Timer nicht jede Sekunde neu berechnet werden muss (Reflow). Auf diese Weise minimiert das Banner Layout Shifts.

Was sollte passieren, wenn der Countdown null erreicht?

Wähle eines von zwei Verhaltensweisen und mache es zu einer Einstellung: Blende das Banner komplett aus oder tausche den Timer gegen eine Ablaufmeldung. Die Option mit der Nachricht ist besser, wenn der Sale endet, während Traffic auf der Seite ist, da sie die Höhe des Banners beibehält und späte Layout Shifts vermeidet. Egal wofür du dich entscheidest, beende den Sale dann auch wirklich.

Sind sich automatisch zurücksetzende Evergreen-Countdown-Timer eine gute Idee?

Nein. Timer, die sich bei jedem Besuch zurücksetzen, trainieren wiederkehrende Käufer darauf, Dringlichkeit zu ignorieren. Zudem werden Fake-Deadlines in einigen Rechtsprechungen als irreführendes Design gemäß den Verbraucherschutzregeln behandelt. Zähle auf eine echte Endzeit herunter und halte dich daran; ehrliche Dringlichkeit konvertiert und bleibt auch so.

Wie zeige ich das Banner auf jeder Seite meines Shops an?

Füge die Section im Theme-Editor zur Section-Gruppe „Header“ hinzu und nicht zu einem einzelnen Template. Sections in der Header-Gruppe werden über dem Header auf allen Seiten eines Online Store 2.0-Themes gerendert, sodass eine einzige Platzierung den gesamten Storefront abdeckt und mit einem Klick nach dem Sale entfernt werden kann.

Jacques's signature
Beschreibe das Banner. Erhalte Theme-Code, keine App.

Footnotes

  1. Shopify, Dawn-Theme-Source, sections/announcement-bar.liquid - Das Section-Schema unterstützt Ankündigungsblöcke (bis zu 12) mit Text und Link, Auto-Rotate mit einer Geschwindigkeit von 3-10 Sekunden, ein Farbschema, eine Trennlinie, Social-Icons sowie Länder- und Sprachauswahl; es existiert kein Countdown oder zeitbasierte Einstellung. https://github.com/Shopify/dawn/blob/main/sections/announcement-bar.liquid 2

  2. Shopify Help Center, „Automatische Generierung von Theme-Blöcken im Theme-Editor“ – Shopify Magic generiert über Block hinzufügen → Generieren aus einem Text-Prompt einen in sich geschlossenen Theme-Block, funktioniert mit Theme-Store-Themes in den Plänen Free Trial, Basic, Growth, Advanced und Plus, und „Die Anpassung oder Fehlerbehebung für Code, der von Shopify Magic für Blöcke generiert wurde, wird vom Shopify Support nicht unterstützt.“ https://help.shopify.com/en/manual/online-store/themes/customizing-themes/theme-editor/shopify-magic/generate-blocks

You might also be interested in

Server-Side Tagging für BFCM: Meta CAPI auf Shopify
Was der Meta-Kanal von Shopify serverseitig sendet, wann du serverseitiges GTM oder Stape benötigst, event_id-Deduplizierung und wie man die Event Match Quality überprüft.
Shopify Sales Funnel: Ein Praxis-Guide für DTC
Wie du einen Shopify Sales Funnel baust, der Cold Traffic in Erstkäufer und Erstkäufer in treue Bestandskunden verwandelt – Phasen, Tools und die wichtigsten Metriken.
Shopify Markets einrichten: Eine Schritt-für-Schritt-Anleitung
Richte Shopify Markets Schritt für Schritt ein: Erstelle Märkte, weise Regionen zu, wähle eine Domain-Struktur, füge Kataloge, Preislisten, Sprachen und Zölle hinzu.