Header / Footer Builder

The Header / Footer Builder lets a merchant compose a storefront header and footer visually, with no code. The builder lives in the Shop Dashboard (Online Shop → Headers and Online Shop → Footers). What it produces is a self-contained block of HTML + CSS that the storefront injects verbatim — so a header/footer carries no runtime dependencies and renders identically everywhere.

How it is stored

A header or footer entity holds a settings object with three keys:

{
  "config":  { "...": "what the builder edits" },
  "content": "<header>…</header>",
  "styles":  ".ch-scope … { … }"
}
  • config — the structured model the builder reads and writes.
  • content — generated HTML, scoped under .ch-scope (header) / .cf-scope (footer).
  • styles — generated CSS, namespaced to the same scope so it never leaks into the page.

content and styles are always generated from config on save. Never edit them by hand — edit config (via the builder) and they recompile.

The storefront renders from content + styles only.

Announcement bar (above the header)

A header can carry a slim strip rendered above the header bar. It is a single optional announcement object on the header config — not an element, so it does not belong to a zone:

Like the header itself it has three columns, each an ordered list of elements:

{
  "announcement": {
    "enabled": true,
    "background": "#111827",
    "color": "#ffffff",
    "size": "normal",
    "sticky": false,
    "elements": {
      "left":   [{ "id": "p1", "type": "phone", "label": "Order:", "number": "+8801700000000" }],
      "center": [
        { "id": "r1", "type": "rotate", "interval": 4,
          "messages": ["Free delivery over ৳2,000", "Cash on delivery nationwide"] },
        { "id": "t1", "type": "text", "text": "Use EID40", "strong": true, "motion": "flash", "url": "/sale" }
      ],
      "right":  [{ "id": "s1", "type": "social", "links": [
        { "network": "facebook", "url": "https://facebook.com/yourshop" },
        { "network": "whatsapp", "url": "https://wa.me/8801700000000" }
      ]}]
    }
  }
}

Element types

type Fields Notes
text text, url?, strong?, motion? motion is none | flash | fade | slide-up
rotate messages[], interval, url? Text slider — cycles in place. Messages share one grid cell, so the width never jumps
marquee text, speed, direction, url? speed = seconds per pass (floored at 4); direction is left (right-to-left) or right
phone number, label? Renders a tel: link with a phone icon
social links[] of { network, url } facebook | instagram | twitter | youtube | whatsapp. Entries with a blank URL are skipped
  • Omit the object, or set enabled: false, and nothing at all is emitted — headers authored before this existed compile byte-for-byte as before.
  • size is compact (30px) | normal (38px) | tall (48px).
  • When the bar and the header are both sticky, the compiler offsets the header by the bar's height so the two stack instead of overlapping.
  • Legacy single-message bars keep working. A config with text/url/ linkLabel/align and no elements is migrated on read into one text element in the column its align chose, and renders as it always did.

Everything animates in pure CSS

A compiled header carries no JavaScript, so every moving part above is a CSS keyframe animation. That is a real constraint — but it also means they keep working with JS disabled and cost nothing at runtime.

It is also why there is no close button: a dismissal could not be remembered and the bar would return on every page. For a closeable bar use the announcement-bar page widget, which is a React component.

All motion is disabled under @media (prefers-reduced-motion: reduce) — a permanently scrolling bar is an accessibility problem (WCAG 2.2.2), so a shopper whose OS asks for less motion sees the first message, static.

There is no close button here, by design. A compiled header is HTML plus scoped CSS with no JavaScript, so a dismissal could not be remembered and the bar would return on every page. For a closeable bar use the announcement-bar page widget — that one is a React component and remembers the choice per shopper.

Use When
Header announcement The message belongs to the site — shipping terms, a hotline — and should sit above the header on every page
announcement-bar widget The message belongs to one page's content, or needs to be dismissible

Header model — zones and elements

A header has three zones — left, center, right — and each zone holds an ordered list of elements.

{
  "elements": {
    "left": [
      { "id": "logo-1", "type": "logo", "logoType": "text", "text": "Aurora", "height": 32 }
    ],
    "center": [
      {
        "id": "menu-1",
        "type": "menu",
        "variant": "plain",
        "source": "static",
        "gap": 28,
        "items": [
          { "label": "Home", "url": "/" },
          { "label": "Shop", "url": "/shop" }
        ]
      }
    ],
    "right": [
      { "id": "search-1", "type": "search", "variant": "icon", "placeholder": "Search products…", "categories": [] },
      { "id": "act-1", "type": "action", "action": "account" },
      { "id": "act-3", "type": "action", "action": "cart" },
      { "id": "btn-1", "type": "button", "variant": "solid", "label": "Sale", "url": "/shop?sort=sale", "background": "#e11d48", "color": "#ffffff" }
    ]
  }
}

Element types: logo, menu, search, button, text, text-link, icon-link, image, action (account / wishlist / cart), phone.

A menu element can be "source": "static" (custom links) or "source": "dynamic", bound to a shop menu by menuSlug — dynamic menus stay in sync with the menu manager.

A footer is a grid of columns, and each column holds an ordered list of widgets. The number of columns is the merchant's choice via columnCount.

{
  "columnCount": 4,
  "cols": [
    {
      "id": "col-1",
      "elements": [
        { "id": "h-1", "type": "heading", "text": "Aurora" },
        { "id": "t-1", "type": "text", "text": "Quality products, delivered fast." },
        { "id": "s-1", "type": "social", "facebook": "https://facebook.com/aurora", "instagram": "https://instagram.com/aurora" }
      ]
    },
    {
      "id": "col-2",
      "elements": [
        { "id": "h-2", "type": "heading", "text": "Shop" },
        { "id": "l-1", "type": "links", "links": [
          { "label": "All products", "url": "/shop" },
          { "label": "New arrivals", "url": "/shop?sort=new" }
        ] }
      ]
    },
    {
      "id": "col-4",
      "elements": [
        { "id": "n-1", "type": "newsletter", "title": "Newsletter", "placeholder": "Enter your email", "buttonLabel": "Subscribe" }
      ]
    }
  ],
  "copyright": "© {year} Aurora. All rights reserved."
}

Widget types: heading, text, links, menu (static or dynamic shop menu), image, newsletter, social, html (raw HTML escape hatch).

Choosing the column count

columnCount controls the grid:

  • 0 (or omitted) — responsive auto-fit; columns size themselves to the content.
  • 16 — that many equal-width columns per row.

The grid automatically collapses to 2 columns on tablet (≤ 768px) and a single column on mobile (≤ 480px).

copyright renders as a bottom bar beneath the grid. {year} is replaced with the current year. Leave it empty to hide the bar.

Authoring guidelines

  • Header: keep it light — a logo, one menu, search, and a couple of action icons. More than ~5 elements in a zone tends to wrap on smaller screens.
  • Footer: 3–5 columns reads best. Lead with a brand column (heading + text + social), follow with link columns, and end with a newsletter.
  • Colors: set background, color, and mutedColor on the footer config. Link and social colors derive from mutedColor and brighten to color on hover.
  • Images: logo and footer image widgets store a media slug in src. The builder resolves it to a full media URL at compile time — do not paste absolute URLs.
  • Set as default: use the Set as default action to make a header/footer the storefront default for all pages.

Sample presets

Complete, ready-to-use samples (full { config, content, styles }) ship in the theme kit:

  • 10-theme-preset-kit/src/themes/custom/header-footer/sample-header.json
  • 10-theme-preset-kit/src/themes/custom/header-footer/sample-footer.json

To reuse one, create a header/footer in the builder and seed its settings with the sample config, then save — the builder regenerates content and styles.