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.
Your details
Your section file (sections/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">‹</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">›</button>
{%- endif -%}
{%- if mode == 'marquee' -%}
<button type="button" data-pause aria-pressed="false" aria-label="Pause announcements">❚❚</button>
{%- endif -%}
{%- if section.settings.dismissible -%}
<button type="button" data-close aria-label="Close announcements">✕</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
- 01
Duplicate your theme
In Online Store → Themes, click … next to your live theme and choose Duplicate. Work on the copy first.
- 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. - 03
Place it in the header
Open Customize. Under Header, click Add section and pick Announcement bar. Drag it above your header.
- 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/salefor 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.
Related tools
Last reviewed 30 September 2026.
Sources: Shopify: Section schema · Shopify: Input settings · Shopify: Integrate sections with the theme editor · Shopify Liquid: request object
