jelonyx
Section library · Section 03

Review carousel section
for Dawn and Horizon.

This review carousel scrolls horizontally on any Shopify template. You add reviews as blocks in the theme editor and configure the star rating, reviewer name, text, and optional product label. Visitors can drag to scroll by default, or you can turn on continuous auto-scroll that pauses on hover. It does not require a paid app or third-party scripts.

Asset typeSection workbench
Filesections/jlx-review-carousel.liquid
VerificationPage build checked
OfferShopify Section Build
Compatible with:Dawn 12+HorizonMost OS 2.0 themes
Merchant workbench

Preview the review carousel before editing code.

Tune the proof copy, star accent, and scroll mode. In Shopify, every review is a block, so a merchant can add, remove, and reorder cards.

Live storefront preview

What customers say

Manual proof cards for landing pages and PDPs.

★★★★★

The page finally answers what I needed before checkout. The layout feels clear on mobile.

Mia K.VerifiedHydration set
★★★★★

Easy to scan, fast to load, and the verified label gives the section more weight.

Leo R.VerifiedDaily cleanser
★★★★

The cards work well on product and landing pages without adding another review app.

Amara S.VerifiedNight cream
sections/jlx-review-carousel.liquid
Liquid / CSS / schema
{% comment %}
  Jelonyx · Review Carousel Section
  Compatible with Dawn 12+, Horizon, and most OS 2.0 themes
  Source: jelonyx.com/shopify/sections/review-carousel
{% endcomment %}

{%- if section.blocks.size > 0 -%}

<section
  id="jlx-rc-{{ section.id }}"
  class="jlx-rc{% if section.settings.auto_scroll %} jlx-rc--auto{% endif %}"
>
  <div class="jlx-rc__inner">

    {%- if section.settings.heading != blank -%}
      <h2 class="jlx-rc__heading">{{ section.settings.heading }}</h2>
    {%- endif -%}

    {%- if section.settings.subheading != blank -%}
      <p class="jlx-rc__subheading">{{ section.settings.subheading }}</p>
    {%- endif -%}

    <div
      class="jlx-rc__track"
      id="jlx-rc-track-{{ section.id }}"
      aria-label="{{ section.settings.heading | default: 'Customer reviews' | escape }}"
    >
      {%- for block in section.blocks -%}
        <article class="jlx-rc__card" {{ block.shopify_attributes }}>

          {%- assign rating = block.settings.rating | plus: 0 -%}
          <div class="jlx-rc__stars" aria-label="{{ rating }} out of 5 stars">
            {%- for i in (1..5) -%}
              <span class="jlx-rc__star{% if i <= rating %} jlx-rc__star--on{% endif %}" aria-hidden="true">★</span>
            {%- endfor -%}
          </div>

          {%- if block.settings.review_text != blank -%}
            <p class="jlx-rc__text">{{ block.settings.review_text }}</p>
          {%- endif -%}

          <footer class="jlx-rc__footer">
            <div class="jlx-rc__byline">
              {%- if block.settings.reviewer_name != blank -%}
                <span class="jlx-rc__name">{{ block.settings.reviewer_name }}</span>
              {%- endif -%}
              {%- if block.settings.verified -%}
                <span class="jlx-rc__verified">Verified</span>
              {%- endif -%}
            </div>
            {%- if block.settings.product_tag != blank -%}
              <span class="jlx-rc__tag">{{ block.settings.product_tag }}</span>
            {%- endif -%}
          </footer>

        </article>
      {%- endfor -%}
    </div>

  </div>
</section>

<style>
  #jlx-rc-{{ section.id }} {
    padding: clamp(40px, 8vw, 80px) 0;
    background: var(--color-background, #fff);
  }
  .jlx-rc__inner {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 20px;
  }
  .jlx-rc__heading {
    font-size: clamp(22px, 3.5vw, 34px);
    font-weight: 700;
    line-height: 1.2;
    color: var(--color-foreground, #1a1a1a);
    margin: 0 0 10px;
  }
  .jlx-rc__subheading {
    font-size: 15px;
    line-height: 1.6;
    color: var(--color-foreground-secondary, #6b6b6b);
    margin: 0 0 28px;
  }
  .jlx-rc__track {
    display: flex;
    gap: 16px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -ms-overflow-style: none;
    padding-bottom: 4px;
    cursor: grab;
  }
  .jlx-rc__track:active { cursor: grabbing; }
  .jlx-rc__track::-webkit-scrollbar { display: none; }
  .jlx-rc--auto .jlx-rc__track {
    scroll-snap-type: none;
    cursor: default;
  }
  .jlx-rc__card {
    flex: 0 0 clamp(240px, 30vw, 320px);
    scroll-snap-align: start;
    background: var(--color-background-2, rgba(0,0,0,0.03));
    border: 1px solid var(--color-border, rgba(0,0,0,0.08));
    border-radius: 10px;
    padding: 22px 22px 18px;
    display: flex;
    flex-direction: column;
    gap: 12px;
  }
  .jlx-rc__stars {
    display: flex;
    gap: 2px;
    line-height: 1;
  }
  .jlx-rc__star {
    font-size: 16px;
    color: var(--color-border, rgba(0,0,0,0.18));
  }
  .jlx-rc__star--on { color: #f5a623; }
  .jlx-rc__text {
    font-size: 14px;
    line-height: 1.65;
    color: var(--color-foreground, #1a1a1a);
    margin: 0;
    flex: 1;
  }
  .jlx-rc__footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: auto;
    padding-top: 14px;
    border-top: 1px solid var(--color-border, rgba(0,0,0,0.08));
  }
  .jlx-rc__byline {
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .jlx-rc__name {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-foreground, #1a1a1a);
  }
  .jlx-rc__verified {
    font-size: 11px;
    font-weight: 500;
    color: #1a9463;
  }
  .jlx-rc__tag {
    font-size: 11px;
    font-weight: 500;
    color: var(--color-foreground-secondary, #6b6b6b);
    background: var(--color-background, #fff);
    border: 1px solid var(--color-border, rgba(0,0,0,0.1));
    border-radius: 2px;
    padding: 2px 7px;
    white-space: nowrap;
  }
</style>

<script>
(function() {
  var el = document.getElementById('jlx-rc-track-{{ section.id }}');
  if (!el) return;
  {%- if section.settings.auto_scroll -%}
  var pps = {{ section.settings.scroll_speed | default: 50 }};
  var paused = false, last = null;
  el.addEventListener('mouseenter', function() { paused = true; });
  el.addEventListener('mouseleave', function() { paused = false; });
  el.addEventListener('touchstart', function() { paused = true; }, { passive: true });
  el.addEventListener('touchend', function() { paused = false; });
  function tick(ts) {
    if (!paused) {
      if (last) {
        el.scrollLeft += pps * (ts - last) / 1000;
        if (el.scrollLeft + el.clientWidth >= el.scrollWidth - 2) el.scrollLeft = 0;
      }
      last = ts;
    } else { last = null; }
    requestAnimationFrame(tick);
  }
  requestAnimationFrame(tick);
  {%- else -%}
  var down = false, startX, startScroll;
  el.addEventListener('mousedown', function(e) { down = true; startX = e.pageX - el.offsetLeft; startScroll = el.scrollLeft; });
  el.addEventListener('mouseleave', function() { down = false; });
  el.addEventListener('mouseup', function() { down = false; });
  el.addEventListener('mousemove', function(e) {
    if (!down) return;
    e.preventDefault();
    el.scrollLeft = startScroll - (e.pageX - el.offsetLeft - startX);
  });
  {%- endif -%}
})();
</script>

{%- else -%}
  {%- if request.design_mode -%}
    <div style="padding:60px 20px;text-align:center;border:2px dashed rgba(0,0,0,0.12);border-radius:8px;">
      <p style="color:#888;font-size:14px;margin:0;">Add review blocks in the section settings to build your carousel.</p>
    </div>
  {%- endif -%}
{%- endif -%}

{% schema %}
{
  "name": "Review carousel",
  "tag": "section",
  "class": "section",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "placeholder": "What customers say"
    },
    {
      "type": "text",
      "id": "subheading",
      "label": "Subheading"
    },
    { "type": "header", "content": "Scroll behaviour" },
    {
      "type": "checkbox",
      "id": "auto_scroll",
      "label": "Auto-scroll",
      "default": false,
      "info": "Scrolls the carousel continuously. Pauses on hover or touch."
    },
    {
      "type": "range",
      "id": "scroll_speed",
      "label": "Scroll speed",
      "min": 20,
      "max": 100,
      "step": 10,
      "default": 50,
      "info": "Pixels per second. Lower is slower."
    }
  ],
  "blocks": [
    {
      "type": "review",
      "name": "Review",
      "settings": [
        {
          "type": "text",
          "id": "reviewer_name",
          "label": "Reviewer name",
          "placeholder": "Sarah M."
        },
        {
          "type": "select",
          "id": "rating",
          "label": "Star rating",
          "options": [
            { "value": "5", "label": "5 stars" },
            { "value": "4", "label": "4 stars" },
            { "value": "3", "label": "3 stars" },
            { "value": "2", "label": "2 stars" },
            { "value": "1", "label": "1 star" }
          ],
          "default": "5"
        },
        {
          "type": "textarea",
          "id": "review_text",
          "label": "Review text",
          "placeholder": "Exactly what I was looking for. Arrived quickly and the quality is great."
        },
        {
          "type": "checkbox",
          "id": "verified",
          "label": "Show verified buyer badge",
          "default": true
        },
        {
          "type": "text",
          "id": "product_tag",
          "label": "Product label",
          "placeholder": "Dawn Serum 50ml",
          "info": "Optional. Shows which product this review is for."
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "Review carousel",
      "category": "Social proof"
    }
  ]
}
{% endschema %}

How to add the section

  1. 01
    Open your theme code editor.

    In Shopify Admin, go to Online Store → Themes. On your active theme, click the three-dot menu and select Edit code.

  2. 02
    Create a new section file.

    Under the Sections folder, click Add a new section. Name it jlx-review-carousel and click Done.

  3. 03
    Paste the full section code.

    Delete any placeholder content in the new file, paste the entire code block above, and save.

  4. 04
    Add the section to a template.

    Open the theme editor (Customize). Navigate to the template where you want the carousel, such as the homepage, product page, or a landing page. Click Add sectionand select Review carousel.

  5. 05
    Add reviews as blocks.

    Inside the section panel, click Add block for each review. Set the reviewer name, rating, and review text. Check Verified buyer to show the badge. Add a product label if the review is for a specific product.

  6. 06
    Choose drag-scroll or auto-scroll.

    In the section settings, Auto-scroll is off by default, so visitors drag or swipe through the cards. Toggle it on for continuous scrolling. Adjust the speed slider to taste. Save.

Schema settings

Section-level settings:

SettingTypeNotes
headingtextOptional heading above the carousel.
subheadingtextOptional subheading below the heading.
auto_scrollcheckboxEnables rAF-based continuous scroll. Off by default, so visitors drag or swipe.
scroll_speedrangeSpeed in pixels per second when auto_scroll is on. Range 20–100, default 50.

Block settings (per review):

SettingTypeNotes
reviewer_nametextName shown at the bottom of the card. e.g. "Sarah M."
ratingselectStar rating 1–5. Renders filled gold stars up to the selected value.
review_texttextareaThe review body. No character limit in the schema, but shorter reviews look better in cards.
verifiedcheckboxShows a green "Verified" badge next to the reviewer name when checked.
product_tagtextOptional product label in the card footer. Useful when reviews span multiple products.

How it works

The track is a flex container with overflow-x: auto and scroll-snap-type: x mandatory. Each card has scroll-snap-align: start, so scrolling snaps to card boundaries. On touch devices this works natively without JavaScript. On desktop, the mouse-drag handler enables click-and-drag scrolling.

When auto_scroll is enabled, the section adds a class jlx-rc--auto which disables scroll-snap-type (so programmatic scrolling is not interrupted by snapping). A requestAnimationFrame loop increments scrollLeft by scroll_speed pixels per second. When the track reaches the end, it resets to zero.

Auto-scroll pauses on mouseenter and touchstart so visitors can read a card without it jumping away. It resumes on mouseleave and touchend. The rAF loop uses the timestamp delta, so the speed is consistent regardless of frame rate.

Star ratings are stored as a select with string values "1""5". Liquid converts the string to an integer with | plus: 0, then a {% for i in (1..5) %} loop compares each index against the rating to apply the filled class.

Compatibility

Tested against Dawn 12+ and Horizon. The section reads --color-background, --color-foreground, --color-border, and --color-foreground-secondary from the theme. Card backgrounds use --color-background-2, which both Dawn and Horizon define as a subtle off-white or near-black depending on the colour scheme.

requestAnimationFrame and CSS scroll snap are supported in all modern browsers. scrollbar-width: none hides the scrollbar in Firefox; the ::-webkit-scrollbar rule handles Chrome and Safari.

Limitations

  • No continuous loop: auto-scroll jumps back to the start when the track reaches the end rather than wrapping smoothly. A continuous marquee requires duplicating the blocks in the DOM, which adds markup weight. The jump is fast enough at normal speeds to be unobtrusive.
  • No prev/next buttons: the section relies on native scroll (drag, swipe, or auto-scroll). Adding arrow buttons requires a small additional JS handler and two button elements in the markup.
  • Static content only: reviews are entered manually as blocks. The section does not pull from the Shopify Product Reviews app or any reviews API. For dynamic review data, the section code would need a metafield or app block integration.
  • Auto-scroll and drag conflict: when auto-scroll is on, the drag handler is not loaded. Visitors can still touch-scroll on mobile (which temporarily pauses auto-scroll via touchstart), but cannot mouse-drag.
Shopify Section Build

Need this built for your store?

If you want the carousel styled to match your brand, extended with a continuous loop, prev/next buttons, or connected to live review data, we can scope and deliver that.