Internal reference

TierZero Styleguide

Design tokens, components and patterns for this theme. Every value below is read from theme settings, so it always matches the live storefront.

Colour tokens

Read live from theme settings. Use the CSS custom property, never a hex value โ€” hardcoding a colour breaks merchant theming.

  • var(--text-color) #616161
  • var(--heading-color) #16134a
  • var(--accent-color) #16134a
  • var(--link-color) #c8e9f2
  • var(--border-color) #e8e8e8
  • var(--background) #f5f5f5
  • var(--secondary-background) #ffffff

Typography

Headings use var(--heading-font-family), body copy uses var(--text-font-family). Base size is 16px.

Heading 1 โ€” Figtree

Heading 2

Heading 3

Heading 4

Heading 5

Body copy โ€” Figtree. The quick brown fox jumps over the lazy dog.

Small print and captions.

Buttons


Shared snippets

section-header

Standard heading block for content sections. Pass heading_tag: 'h2' below a hero.

{%- render 'section-header',
      eyebrow: section.settings.eyebrow,
      heading: section.settings.heading,
      subheading: section.settings.subheading,
      heading_tag: 'h2',
      alignment: 'left' -%}

promo-link-label

Produces an accessible name for an image-only link, so it does not fail WCAG 2.4.4.

{%- capture promo_label -%}
  {%- render 'promo-link-label', title: block.settings.title, image: block.settings.image, link: block.settings.link -%}
{%- endcapture -%}

Spacing & layout

Every custom section exposes padding_top and padding_bottom range settings in its schema, stepped by 4px. Follow that convention in new sections โ€” merchants expect to control vertical rhythm without a developer.

{ "type": "range", "id": "padding_top", "label": "Padding top",
  "min": 0, "max": 120, "step": 4, "unit": "px", "default": 48 }

Typical defaults in this theme: 48px for a content section, 40px for a dense one, 56px for a hero. Horizontal gutters come from .container; never add your own page-level side padding.

The fractional grid

Inherited from the Warehouse base theme and used by every product grid. Classes read as fraction--breakpoint and are passed to snippets as grid_classes. This syntax cannot be guessed, so it is documented here.

ClassMeaning
1/2--tabletHalf width from the tablet breakpoint up
1/3--lap-and-upOne third from laptop up
1/4--lapOne quarter at the laptop breakpoint
1/5--deskOne fifth at desktop
1/6--wideOne sixth on wide screens

Combine them to change per breakpoint โ€” they are additive, not exclusive.

Page sections

Built for editorial pages. All carry presets, so they can be added to any page from the theme editor โ€” not only the templates they were written for.

SectionBlocksUse it for
page-herometaPage h1 plus label/value pairs and a CTA
spec-tablerowStructured facts as a label/value table
grade-detailgradePaired cards with criteria and front/back photos
scale-comparisonsegmentComparing scales on one aligned track
grade-crosswalkmappingEquivalence table across systems
checklist-stepsstepNumbered walkthrough where order matters
rule-listruleSet of short titled rules
pre-order-herometa, trustPre-order page h1, release facts, CTA and reassurance row
release-countdownโ€”Release date plus a live countdown
product-lineupgroupThe few products a launch page is actually selling
release-timelineโ€”Ordered dates with passed/upcoming states; steps with no date are skipped
rarity-breakdownโ€”Proportional bar plus counts and percentages, from plain "Label: count" lines
set-overviewโ€”Long-form copy with a <details> read-more, for indexable text
styleguideโ€”This page

Only one section per page may render the h1. page-hero and pre-order-hero have a Use as page title toggle; everything below them uses h2. Watch the stock faq section: its separator block renders an h1, so a page that already has a hero must use question blocks only.

Data-driven sections

The constraint to understand first: a JSON page template shares its section settings with every page that uses it. Type a release date into a section and all pre-order pages show that date. So anything that varies per page cannot live in a setting โ€” it has to come from data.

The split this theme uses: layout, labels and copy stay in section settings, and per-release facts come from the collection's pre_order metafields. Sections name the fact they want; they never hold the value.

StepWhat happens
1. Resolve the collectionPage's pre_order.collection metafield, else collections[page.handle], else the section's fallback setting. The page metafield wins, because it is the only per-page one.
2. Name a factEach block has a Value from select โ€” release_date, price, formatsโ€ฆ
3. Resolve itsnippets/pre-order-value.liquid maps the name to the metafield and outputs text, or nothing if unset.
4. Fall backMetafield โ†’ the block's written value โ†’ a visible โ€œTo confirmโ€ flag. Never a blank cell.
{%- liquid
  assign po_collection = page.metafields.pre_order.collection.value
  if po_collection == blank
    assign po_collection = collections[page.handle]
  endif
  if po_collection == blank
    assign po_collection = section.settings.collection
  endif
-%}

{%- capture row_value -%}
  {%- render 'pre-order-value',
        po_collection: po_collection,
        source: block.settings.source,
        date_format: section.settings.date_format -%}
{%- endcapture -%}
{%- assign row_value = row_value | strip -%}

render has isolated scope, so a snippet cannot assign variables for its caller. That is why the resolver outputs a value and the caller captures it โ€” and why the eight-line collection lookup is repeated in each section rather than shared.

Adding a new fact is two edits: a when branch in the resolver and one option in each schema that should offer it. spec-table defaults every row to Written below, so existing pages are unaffected.

Do not use featured-collection on a shared template. Its collection is a section setting, so every page using the template would show the same collection. Sections that need the page's own collection must resolve it as above.

Repeatable per-page content cannot be blocks either. Blocks live in the template, so they are shared too. A per-set rarity list or overview has to arrive as data: rarity-breakdown parses plain Label: count lines and set-overview splits a multi-line metafield into paragraphs. Plain text beats JSON or metaobjects here โ€” someone copying four numbers off a publisher page should not have to learn a syntax.

Check for structured data the theme already emits. snippets/microdata-schema.liquid puts a BreadcrumbList in the document head on every page. A second one from a section produced two competing trails for one URL, so pre-order-hero renders visible breadcrumbs by default and keeps its JSON-LD behind an off-by-default setting.

Pills

snippets/pill.liquid โ€” badge for grade codes, region tags and short status labels. Variants tie a pill to the scale it belongs to.

NM EX LP Muted Accent NMNear Mint
{%- render 'pill', code: 'NM', label: 'Near Mint', variant: 'na' -%}

Component states

A component is not done until every state is handled. Check these before shipping.

StateRequirement
DefaultMeets 4.5:1 contrast for text
HoverVisible change that is not colour alone
FocusNever remove the outline without an equally visible replacement
Active / pressedReflect it in aria-pressed where it is a toggle
DisabledUse the real disabled attribute, not just styling
EmptySay what is missing and how to fix it โ€” never render blank
ErrorMessage next to the field, not only a red border

Form elements

Every input needs a <label for> pointing at its id. A placeholder is not a label. Build ids from something stable โ€” an id built from an out-of-scope Liquid variable renders empty and silently breaks the association.

Icons

cart search close minus check heart filter arrow-right
{%- render 'icon', icon: 'cart' -%}

Icons are decorative by default. If an icon is the only content of a button or link, the control still needs an accessible name via aria-label.

Names must match the when cases in snippets/icon.liquid. An unknown name renders nothing at all โ€” silently, with no error. Available include: cart, big-cart, search, close, close-2, minus, check, heart, account, filter, grid, list, lock, package, address, play, timer, zoom, hamburger, arrow-left, arrow-right, tail-left, tail-right, plus the social icons.

Accessibility rules

CI fails on link-name and aria-allowed-attr. These are the rules behind that, each one written from a defect actually found in this theme.

RuleWhy
Every link has an accessible name15 image-only links shipped with empty text and alt=""
aria-expanded only on interactive elementsIt was on a plain div, which is invalid
Table row labels use <th scope="row"><td> loses the label/value pairing
Never carry meaning by colour aloneAdd a border, icon or text as well โ€” WCAG 1.4.1
Respect prefers-reduced-motionKeep the state change, drop the movement
Contrast: 4.5:1 normal, 3:1 large textThe brand blue currently fails at 2.89:1