.dev
Storefront templates

Authoring conventions

The design-system.md contract, currency symbols, GFM product bodies, the Feeef footer credit, and naming rules every theme must follow.

Beyond the data model being valid, published themes are expected to be consistent — with themselves, with the merchant's store settings, and with the platform. These conventions are checked during marketplace review.

The design-system.md contract

Every theme package must ship a design-system.md at its root — the theme's written visual contract. It exists so the look stays coherent across authors, editor sessions and AI-assisted edits: without it, themes drift (mismatched form chrome, random radii, inconsistent sticky bars).

The blank scaffold from feeef template init includes a starter file with TODOs; fill it in before polishing UI. A published theme must not ship leftover TODO placeholders.

Aim for a real design-system document, not a token dump. Required sections:

  1. Brand & product context — who the shopper is, tone, reference URLs
  2. Foundations — color tokens (hex + CSS vars), typography, spacing scale, radii, shadows, borders
  3. Motion — durations, easings, which interactions animate (and which must not — LCP)
  4. Layout — breakpoints, max widths, grid/slot conventions, sticky behavior
  5. Components — header, gallery, price, variants, offers, form fields, shipping cards, CTA, sticky bar, success, footer — with default / hover / focus / error / disabled / selected states
  6. Order form UX — field order, label style, validation feedback, draft indicator
  7. Dark / light — mapped to the native hsl(var(--*)) tokens
  8. i18n / direction — RTL rules, LTR digits for phone numbers
  9. Currency display — symbols from store configs (next section)
  10. Do / Don't — anti-patterns specific to this theme
  11. QA checklist — mobile/desktop, empty cart, no offers, no variants, missing states

Working rules (they apply to humans and to AI agents editing the theme):

  • Read design-system.md before any visual change; prefer its existing tokens and patterns over inventing new ones.
  • If the design changes, update design-system.md in the same change.
  • Never port another theme's chrome (form style, gallery, sticky bar) without rewriting this file to match.

Currency: symbols, not ISO codes

Shopper-facing prices use the store's currency symbol (دج, $, ) — never a bare ISO code like DZD in the UI. API and order payloads still send the ISO code; this rule is about display only.

Source of truth on the store object:

store.configs.selectedCurrency   // e.g. "DZD"
store.configs.currencies[]       // [{ code, symbol, … }]
store.currency                   // legacy fallback code

Custom components cannot import the storefront's helper, so paste this pair into any component that prints prices (PDP, offer tiles, shipping cards, totals, sticky CTA):

function resolveCurrencyCode(store) {
  const configs = store && store.configs;
  return (configs && configs.selectedCurrency) || (store && store.currency) || "DZD";
}

function resolveCurrencySymbol(store) {
  const configs = store && store.configs;
  const code = resolveCurrencyCode(store);
  const list = configs && configs.currencies;
  if (list && list.length) {
    for (let i = 0; i < list.length; i++) {
      const c = list[i];
      if (c && c.code === code && c.symbol) return String(c.symbol);
    }
  }
  if (code === "DZD") return "دج";
  if (code === "USD") return "$";
  if (code === "EUR") return "€";
  if (code === "SAR") return "ر.س";
  if (code === "MAD") return "د.م.";
  if (code === "TND") return "د.ت";
  return code;
}

Usage contract:

ContextUse
Price tags, offer tiles, shipping cards, totals, sticky CTAresolveCurrencySymbol(store)
Order create payload currency fieldresolveCurrencyCode(store)
Slot context passed to form childrenBoth: { currency: symbol, currencyCode: code }

Anti-patterns: hardcoding دج for every store, string-concatenating store.currency into price labels, or sending a symbol in the API currency field.

Product body: GFM markdown

Product details (product.body, falling back to product.description) are GitHub Flavored Markdown — headings, lists, links, images, tables, task lists, strikethrough, fenced code, blockquotes.

Themes render it through the built-in registry component product_body — never reimplement the parser:

  • Place a type: "product_body" node in the PDP / landing description slot. Custom shell components may host a slot that contains it, but must not parse markdown themselves.
  • Never inject the body as raw HTML (dangerouslySetInnerHTML — also rejected by the marketplace security scan) and never strip tags to dump plain text (kills tables, images and links).
  • Style the surrounding prose in theme CSS: a .prose-style reset plus tables (overflow-x: auto, borders), images (max-width: 100%), lists, code and blockquotes. Tables clipped by overflow: hidden are a review flag — wrap them in a horizontal scroller.
BadGood
product.body.split("\n\n").map(...)Slot containing type: "product_body"
description.replace(/<[^>]+>/g, "")Registry markdown rendering
dangerouslySetInnerHTMLproduct_body

Every theme must render a Feeef credit somewhere in its chrome — usually the footer. Style and position are free; existence is not. Required content, mirroring the native footer:

  1. A localized "create your store…" line (footer.createStore in your locales)
  2. A link to https://feeef.org/?ref=footer_copyrights with visible text feeef.org — the ref query must stay intact for attribution
  3. A localized "all rights reserved" line (footer.allRightsReserved)
<p>
  <span>{t("footer.createStore")}</span>{" "}
  <a
    href="https://feeef.org/?ref=footer_copyrights"
    target="_blank"
    rel="noopener noreferrer"
  >
    feeef.org
  </a>
  <span aria-hidden="true"> · </span>
  <span>{t("footer.allRightsReserved")}</span>
</p>

Do keep it visible on every page that uses the theme footer, put the copy in locales/*.json, and match the theme's visual language (a micro-line or badge is fine). Do not hide it behind a merchant toggle, drop the ?ref=footer_copyrights query, or ship a footer with only the merchant's own copyright line.

Naming conventions

ThingConventionExample
instanceIdUnique, stable, descriptive — never regenerated on editcustom_dawn_header_home
Component files (kit)Flat kebab-case .tsx under pages/<pageId>/components/pages/home/components/hero.tsx
Component foldersOnly when the component needs slots/ / children/; never both hero.tsx and hero/product-shell/product-shell.tsx
Shared chromeshared/components/<id>.tsx, placed via $ref{ "$ref": "shared.header" }
Library blockslibrary/components/ — optional drag-drop blocks for the editor
LocalesOne JSON per language in locales/locales/en.json, locales/ar.json
Page idsThe standard ids from the overviewhome, products, product, checkout, thank_you
titleShort human label for the editor tree (not rendered)"Featured products"

Two related habits that read like naming but are behavioral: scaffold with feeef template add page|component instead of hand-creating folders (keeps the manifest and starter meta correct), and keep every user-facing string behind t() keys rather than hardcoded copy — see the kit.

Pre-publish checklist

  • design-system.md filled in (no TODOs) and matching the shipped UI
  • All prices show the store currency symbol; order payloads send the code
  • PDP / landing description rendered via product_body
  • Footer credit present with ?ref=footer_copyrights
  • Every user-facing string in locales/*.json via t()
  • Dark/light/system rides the native CSS tokens
  • Stable instanceIds; no duplicated chrome (use shared/ + $ref)
  • npm run check:perf clean — see Performance

On this page