Shopify Announcement Bar Generator

Build a static, rotating or scrolling announcement bar and get it as a Shopify section you can edit in the theme editor.

Describe the result you need. AI will set up the tool and ask for any missing details.

Configure with AI

Enter to submit · Shift + Enter for a new line

Sent to AI. Leave out passwords and private customer data.

Your details

Messages

Add a link after a | sign, for example: Summer sale | /collections/sale. Up to 10 messages.

s
Behavior

It stays closed for the rest of their visit.

Style
px

Your section file (sections/custom-announcement-bar.liquid)

custom-announcement-bar.liquid
{%- comment -%}
  Announcement bar section. Static, rotating or marquee messages, each with an optional link.
  Add it to your Header group in the theme editor.
{%- endcomment -%}
{%- liquid
  assign show_bar = true
  if section.settings.show_on != 'all' and request.page_type != section.settings.show_on
    assign show_bar = false
  endif
  if request.design_mode
    assign show_bar = true
  endif
  assign mode = section.settings.mode
-%}

{%- if show_bar and section.blocks.size > 0 -%}
  {%- style -%}
    #AnnouncementBar-{{ section.id }}{display:block;background:{{ section.settings.background }};color:{{ section.settings.text_color }};font-size:{{ section.settings.font_size }}px;line-height:1.4}
    #AnnouncementBar-{{ section.id }}[hidden]{display:none}
    #AnnouncementBar-{{ section.id }} .ea-ab__inner{display:flex;align-items:center;gap:.25rem;max-width:1200px;margin:0 auto;padding:.55em .75em;min-height:2.6em}
    #AnnouncementBar-{{ section.id }} .ea-ab__viewport{flex:1;min-width:0;text-align:center}
    #AnnouncementBar-{{ section.id }} .ea-ab__track{display:flex;flex-wrap:wrap;justify-content:center;gap:.25em 2.5em}
    #AnnouncementBar-{{ section.id }} .ea-ab__msg{margin:0}
    #AnnouncementBar-{{ section.id }} .ea-ab__msg[hidden]{display:none}
    #AnnouncementBar-{{ section.id }} a,#AnnouncementBar-{{ section.id }} .ea-ab__fake-link{color:inherit;text-decoration:underline;text-underline-offset:2px}
    #AnnouncementBar-{{ section.id }} button{background:none;border:0;color:inherit;font:inherit;cursor:pointer;padding:.2em .5em;line-height:1;opacity:.85}
    #AnnouncementBar-{{ section.id }} button:hover{opacity:1}
    #AnnouncementBar-{{ section.id }}.ea-ab--rotate .ea-ab__msg.is-active{animation:ea-ab-fade .45s ease}
    #AnnouncementBar-{{ section.id }}.ea-ab--marquee .ea-ab__viewport{overflow:hidden}
    #AnnouncementBar-{{ section.id }}.ea-ab--marquee .ea-ab__rail{display:flex;width:max-content;animation:ea-ab-marquee {{ section.settings.marquee_speed }}s linear infinite}
    #AnnouncementBar-{{ section.id }}.ea-ab--marquee .ea-ab__track{flex-wrap:nowrap;flex-shrink:0;gap:3em;padding-right:3em}
    #AnnouncementBar-{{ section.id }}.ea-ab--marquee .ea-ab__msg{white-space:nowrap}
    #AnnouncementBar-{{ section.id }}.ea-ab--marquee:hover .ea-ab__rail,#AnnouncementBar-{{ section.id }}.ea-ab--marquee:focus-within .ea-ab__rail,#AnnouncementBar-{{ section.id }}.is-paused .ea-ab__rail{animation-play-state:paused}
    @keyframes ea-ab-fade{from{opacity:0}to{opacity:1}}
    @keyframes ea-ab-marquee{to{transform:translateX(-50%)}}
    @media (prefers-reduced-motion:reduce){
    #AnnouncementBar-{{ section.id }} .ea-ab__rail{animation:none!important;width:auto!important}
    #AnnouncementBar-{{ section.id }}.ea-ab--marquee .ea-ab__track{flex-wrap:wrap;padding-right:0;gap:.25em 2.5em}
    #AnnouncementBar-{{ section.id }}.ea-ab--marquee .ea-ab__msg{white-space:normal}
    #AnnouncementBar-{{ section.id }} .ea-ab__track[aria-hidden="true"],#AnnouncementBar-{{ section.id }} [data-pause]{display:none}
    #AnnouncementBar-{{ section.id }} .ea-ab__msg.is-active{animation:none}
    }
  {%- endstyle -%}

  <ea-announcement-bar
    id="AnnouncementBar-{{ section.id }}"
    class="ea-ab ea-ab--{{ mode }}"
    data-mode="{{ mode }}"
    data-speed="{{ section.settings.speed }}"
    {% if section.settings.dismissible %}data-dismissible{% endif %}
    role="region"
    aria-label="Announcements"
  >
    <div class="ea-ab__inner">
      {%- if mode == 'rotate' and section.blocks.size > 1 -%}
        <button type="button" data-prev aria-label="Previous announcement">&#8249;</button>
      {%- endif -%}
      <div class="ea-ab__viewport">
        <div class="ea-ab__rail">
          <div class="ea-ab__track">
            {%- for block in section.blocks -%}
              <p class="ea-ab__msg{% if forloop.first %} is-active{% endif %}" data-slide{% if mode == 'rotate' %}{% unless forloop.first %} hidden{% endunless %}{% endif %} {{ block.shopify_attributes }}>
                {%- if block.settings.link != blank -%}
                  <a href="{{ block.settings.link | escape }}">{{ block.settings.text | escape }}</a>
                {%- else -%}
                  {{- block.settings.text | escape -}}
                {%- endif -%}
              </p>
            {%- endfor -%}
          </div>
          {%- if mode == 'marquee' -%}
            <div class="ea-ab__track" aria-hidden="true">
              {%- for block in section.blocks -%}
                <p class="ea-ab__msg">
                  {%- if block.settings.link != blank -%}
                    <span class="ea-ab__fake-link">{{ block.settings.text | escape }}</span>
                  {%- else -%}
                    {{- block.settings.text | escape -}}
                  {%- endif -%}
                </p>
              {%- endfor -%}
            </div>
          {%- endif -%}
        </div>
      </div>
      {%- if mode == 'rotate' and section.blocks.size > 1 -%}
        <button type="button" data-next aria-label="Next announcement">&#8250;</button>
      {%- endif -%}
      {%- if mode == 'marquee' -%}
        <button type="button" data-pause aria-pressed="false" aria-label="Pause announcements">&#10074;&#10074;</button>
      {%- endif -%}
      {%- if section.settings.dismissible -%}
        <button type="button" data-close aria-label="Close announcements">&#10005;</button>
      {%- endif -%}
    </div>
  </ea-announcement-bar>

  <script>
    if (!customElements.get('ea-announcement-bar')) {
      customElements.define('ea-announcement-bar', class extends HTMLElement {
        connectedCallback() {
          const editing = window.Shopify && window.Shopify.designMode;
          const key = 'ea-ab-closed-' + this.id;
          if (this.hasAttribute('data-dismissible') && !editing) {
            try {
              if (sessionStorage.getItem(key)) { this.hidden = true; return; }
            } catch (e) {}
          }
          const close = this.querySelector('[data-close]');
          if (close) close.onclick = () => {
            this.hidden = true;
            try { sessionStorage.setItem(key, '1'); } catch (e) {}
          };
          const pause = this.querySelector('[data-pause]');
          if (pause) pause.onclick = () => {
            const paused = this.classList.toggle('is-paused');
            pause.setAttribute('aria-pressed', String(paused));
          };
          this.slides = Array.from(this.querySelectorAll('[data-slide]'));
          if (this.dataset.mode !== 'rotate' || this.slides.length < 2) return;
          this.index = 0;
          this.querySelector('[data-prev]').onclick = () => { this.stop(); this.show(this.index - 1); };
          this.querySelector('[data-next]').onclick = () => { this.stop(); this.show(this.index + 1); };
          this.onmouseenter = () => { this.hovered = true; };
          this.onmouseleave = () => { this.hovered = false; };
          if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
          const delay = Math.max(2, Number(this.dataset.speed) || 5) * 1000;
          this.stop();
          this.timer = setInterval(() => {
            if (!this.hovered && !this.contains(document.activeElement)) this.show(this.index + 1);
          }, delay);
        }
        disconnectedCallback() { this.stop(); }
        stop() { clearInterval(this.timer); }
        show(i) {
          const count = this.slides.length;
          this.slides[this.index].hidden = true;
          this.slides[this.index].classList.remove('is-active');
          this.index = (i + count) % count;
          this.slides[this.index].hidden = false;
          this.slides[this.index].classList.add('is-active');
        }
      });
    }
  </script>
{%- endif -%}

{% schema %}
{
  "name": "Announcement bar",
  "tag": "section",
  "class": "ea-announcement-bar-section",
  "max_blocks": 10,
  "settings": [
    {
      "type": "select",
      "id": "mode",
      "label": "Style",
      "options": [
        {
          "value": "static",
          "label": "Static"
        },
        {
          "value": "rotate",
          "label": "Rotating"
        },
        {
          "value": "marquee",
          "label": "Marquee"
        }
      ],
      "default": "rotate"
    },
    {
      "type": "range",
      "id": "speed",
      "label": "Seconds per message",
      "min": 3,
      "max": 10,
      "step": 1,
      "unit": "s",
      "default": 5,
      "info": "Rotating style only."
    },
    {
      "type": "range",
      "id": "marquee_speed",
      "label": "Marquee loop length",
      "min": 10,
      "max": 60,
      "step": 5,
      "unit": "s",
      "default": 30,
      "info": "Marquee style only. Higher is slower."
    },
    {
      "type": "checkbox",
      "id": "dismissible",
      "label": "Let shoppers close the bar",
      "default": false
    },
    {
      "type": "select",
      "id": "show_on",
      "label": "Show on",
      "options": [
        {
          "value": "all",
          "label": "Every page"
        },
        {
          "value": "index",
          "label": "Home page only"
        },
        {
          "value": "product",
          "label": "Product pages only"
        },
        {
          "value": "collection",
          "label": "Collection pages only"
        }
      ],
      "default": "all"
    },
    {
      "type": "header",
      "content": "Colors and text"
    },
    {
      "type": "color",
      "id": "background",
      "label": "Background",
      "default": "#1f2a44"
    },
    {
      "type": "color",
      "id": "text_color",
      "label": "Text",
      "default": "#ffffff"
    },
    {
      "type": "range",
      "id": "font_size",
      "label": "Text size",
      "min": 11,
      "max": 20,
      "step": 1,
      "unit": "px",
      "default": 13
    }
  ],
  "blocks": [
    {
      "type": "message",
      "name": "Message",
      "settings": [
        {
          "type": "text",
          "id": "text",
          "label": "Text",
          "default": "Free shipping on orders over $75"
        },
        {
          "type": "text",
          "id": "link",
          "label": "Link",
          "info": "A path such as /collections/sale, or a full URL. Leave empty for no link."
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "Announcement bar",
      "blocks": [
        {
          "type": "message",
          "settings": {
            "text": "Free shipping on orders over $75",
            "link": "/collections/all"
          }
        },
        {
          "type": "message",
          "settings": {
            "text": "New: the autumn knitwear collection",
            "link": "/collections/knitwear"
          }
        },
        {
          "type": "message",
          "settings": {
            "text": "30-day free returns"
          }
        }
      ]
    }
  ]
}
{% endschema %}

To install: in Shopify admin go to Online Store → Themes, click … next to your theme → Edit code. Under Sections choose Add a new section, name it as shown above, replace the contents with this code and save. Then open Customize and add the section where you want it. Try it on a duplicate of your theme first. Announcement bars belong in the Header group: in Customize, click Add section under Header.

If your theme already shows its own announcement bar, hide or remove it so shoppers don't see two.

Live preview

What the generator builds

You get one complete section file, sections/custom-announcement-bar.liquid. It has a {% schema %} with settings and a preset, so after you add the file it appears under Add section in the theme editor like any built-in section.

  • Each message is a block, with its own text and optional link. You can add, reorder or remove messages in the theme editor without touching code.
  • Three styles. Static shows every message at once. Rotating shows one at a time with previous and next buttons. Marquee scrolls them in a loop.
  • Scoped CSS. Every rule starts with the section's own id, so it can't restyle anything else in your theme.
  • Small, self-contained script. A custom element handles rotation, pause and close. It keeps working when you edit the section in the theme editor.

How to add it to your Shopify theme

  1. 01

    Duplicate your theme

    In Online Store → Themes, click … next to your live theme and choose Duplicate. Work on the copy first.

  2. 02

    Add the section file

    Click … → Edit code. Under Sections, choose Add a new section, name it custom-announcement-bar, replace everything in it with the generated code and save.

  3. 03

    Place it in the header

    Open Customize. Under Header, click Add section and pick Announcement bar. Drag it above your header.

  4. 04

    Hide the old bar

    If your theme already has an announcement bar, hide it so shoppers don't see two.

Accessibility and pitfalls

  • Motion. Shoppers who ask their device for reduced motion get no scrolling and no auto-rotation. The marquee also has a pause button, and rotation stops as soon as someone uses the arrows.
  • Keep it short. One offer per message. Long text wraps to two lines on phones and pushes the page down.
  • Links. Use a path such as /collections/sale for pages on your store. It keeps working if you change domains.
  • Closing is per visit. A closed bar stays closed until the shopper's browser session ends, then shows again.

Questions people ask

Do I need an app for an announcement bar?

No. This is plain theme code: a section file with no app, no external scripts and no monthly fee.

Will it work with my theme?

It works with any Online Store 2.0 theme, including Dawn and the other free Shopify themes. Its styles are scoped to the section, so it doesn't clash with your theme's CSS.

Can I show the bar on some pages only?

Yes. Use the Show on setting to limit it to the home page, product pages or collection pages. In the theme editor it always shows so you can edit it.

How do I translate the messages?

The message text is an ordinary text setting, so it appears in Shopify's Translate & Adapt app alongside the rest of your theme content.

Can I add a countdown to the bar?

Use the Shopify countdown timer generator and place its section right under the bar in the header.

Want this builtinto your store?

I'm Emmanuel, a Shopify and WooCommerce developer. If this tool pointed at something you'd rather have fixed for you (a faster store, cleaner product data, a migration, a custom feature), send me a note.

Emmanuel Asika

Emmanuel Asika

Shopify & WooCommerce developer, Dublin

I build and fix online stores: new builds, migrations, speed, SEO and the custom bits apps can't do.

hello@emmanuelasika.com