Klarity Theme Markup

Lightweight tags for rendering products, collections, categories, variables, and conditionals — without exposing backend internals.

Quickstart

Insert tags into any theme template or CMS page body. The engine expands tags and safely escapes variables.

<h3>Trending in [[ page.category ]]</h3>

[[# products category="snacks" limit=4 sort="price_desc" ]]
  <a class="card" href="/products/[[ handle ]]" aria-label="[[ title ]]">
    <img src="[[ image ]]" alt="[[ title ]]" />
    <div class="title">[[ title ]]</div>
    <div class="price">[[ price_formatted ]]</div>
  </a>
[[^ products ]]
  <p class="muted">No products to show yet.</p>
[[/ products ]]

Variables

  • [[ site.name ]] — your site title.
  • [[ page.route ]] — e.g., home, category, products.
  • [[ page.category_parts.N ]] — URL parts after /category/. Example: /category/snacks/chips["snacks","chips"].
  • [[ product.* ]] and top-level aliases inside loops: [[ title ]], [[ image ]], [[ price_formatted ]], etc.
<h1>Welcome to [[ site.name ]]</h1>
<p>You are viewing: [[ page.route ]]</p>

Core Tags

Variables

Insert values: [[ key ]] or dot access [[ page.title ]].

<h1>[[ page.title ]]</h1>

Partials

Include a theme partial: [[> sections/hero ]]

[[> sections/hero ]]

Products

Loop products by category, collection, handle(s), or search.

[[# products category="drinks" limit=6 sort="newest" ]]
  <div>[[ title ]] — [[ price_formatted ]]</div>
[[/ products ]]

Conditionals

Use inline comparisons or truthiness.

[[# if compare_at_price is not "" ]]
  <span class="sale">Sale: [[ price_formatted ]]</span>
[[/ if ]]

[[^ if in_stock is not "true" ]]
  <span class="soldout">Sold out</span>
[[/ if ]]

Products Loop

  • By category: [[# products category="snacks" limit=8 sort="newest" ]] ... [[/ products ]]
  • By collection: [[# products collection="summer" limit=4 ]]
  • By single handle: [[# products handle="sea-salt-chips" ]]
  • By multiple handles: [[# products handles="sea-salt-chips, chili-crisps" ]]
  • By search: [[# products search="chips" limit=6 ]]
[[# products category="[[ page.category_parts.0 ]]" limit=4 sort="price_asc" ]]
  <a href="/products/[[ handle ]]" class="card">
    <img src="[[ image ]]" alt="[[ title ]]" />
    <div class="title">[[ title ]]</div>
    <div class="price">[[ price_formatted ]]</div>
  </a>
[[^ products ]]
  <p class="muted">Nothing here yet.</p>
[[/ products ]]

Images (by Asset library ID)

Use your image IDs from your image asset library. The engine calls the API, resolving image URL, slug (caption-like), and alt. Works as a single image or a simple Splide carousel.

Single image

[[ image id=42 style="cover" ]]
[[ image id="99" alt="bag" style="contain" ]]

<!-- Advanced: custom wrapper -->
<figure class="product-figure">
  [[ image id=42 alt="Classic Tee — blue" style="cover" ]]
  <figcaption>Classic Tee (blue)</figcaption>
</figure>
  • id — asset ID.
  • alt — overrides DB alt; if omitted, DB alt/slug are used when present.
  • stylecover (default) or contain; sets object-fit.

Carousel of images by IDs

Accepts any of these list forms: [1,2,3], "[1, 2, 3]", "1,2,3".

[[# carousel image-id=[1,2,3] id="hero-rail" per_page=1 pagination=true arrows=true ]]
  <!-- Inside, you can reference [[ url ]], [[ alt ]], [[ slug ]] for the current slide -->
  <img src="[[ url ]]" alt="[[ alt ]]" style="width:100%;height:420px;object-fit:cover;border-radius:12px;" />
[[/ carousel ]]

Troubleshooting

Got only 2 slides when you listed 3 IDs?
Ensure your list is clean JSON/CSV (the parser now accepts [1,2,3], "[1, 2, 3]", or "1,2,3"). Avoid stray characters like [3 at the start.

Pagination

Show a subset per page and build numeric links. Use page-limit in the products block to override limit. Links preserve current filters.

Basic Usage

<!-- Products: page-limit overrides limit -->
[[# products category="[[ page.category_parts.2 ]]" page-limit="12" sort="newest" ]]
  <a class="card" href="/products/[[ handle ]]">
    <img src="[[ image ]]" alt="[[ title ]]" />
    <div class="title">[[ title ]]</div>
    <div class="price">[[ price_formatted ]]</div>
  </a>
[[^ products ]]
  <p class="muted">No items found.</p>
[[/ products ]]

<!-- Numeric pagination (centered) -->
[[# if pagination.pages_count > 1 ]]
  <nav class="pagination" aria-label="Pagination"
       data-cur="[[ pagination.page ]]"
       data-pages="[[ pagination.pages_count ]]"
       data-per="[[ pagination.per_page ]]">
    <span class="pages js-pager"></span>
  </nav>
[[/ if ]]

Examples

  • Show 3 per page: page-limit="3"
  • Force via URL (debug): append ?limit=1
  • Sort descending price: sort="price_desc"

Debugging

Print the live pagination meta (same API call):

<div class="muted">
  page=[[ pagination.page ]] of [[ pagination.pages_count ]],
  per=[[ pagination.per_page ]], total=[[ pagination.total ]]
</div>
Tips
• If you see per=24 unexpectedly, your template likely didn’t include page-limit on the actual rendered products block. Add ?limit=1 to the URL to confirm the pipeline.
page-limit overrides any limit inside the same block.
• Links keep existing query params (like sort) and are centered by default here.

Operators

  • Equality: is, ==, is not, !=
  • Numeric: <, <=, >, >=
  • Set: is in (comma list)
[[# if collection is in "featured,summer" ]]
  <span class="flag">Featured</span>
[[/ if ]]

[[# if price_cents >= 5000 ]]
  <span class="flag premium">Premium</span>
[[/ if ]]

Markup Roadmap

  • Loops over collections/categories (e.g., list all collections).
  • Built-in money/number filters (formatting helpers in markup).
  • Inline image transforms (thumb sizes, aspect locks).
  • Pagination blocks for category pages.
  • Partial parameters ([[> sections/card with="product"]] style).
  • Internationalization hooks for text snippets.
Have a suggestion? Share it with your Klarity contact so we can prioritise it.