# How it works: free Shopify section

> Numbered steps with icons joined by a line, for subscriptions, made-to-order products or services: a row on desktop, stacked on mobile, with a button.

How it works is a free Shopify section that explains a process in numbered steps: each step has a number or an icon, a title and a short text, joined by a line, in a row on desktop (or a column) and stacked on mobile. The numbers follow the order of the blocks, so reordering steps renumbers them, and an optional button after the last step leads to the next action. It suits subscriptions, made-to-order and personalised products, services and returns. Built as an ordered list in pure Liquid and CSS for Online Store 2.0 themes, without an app.

- **Category:** Content and storytelling
- **Use on:** Home, Product, Landing pages
- **Reads:** Content only
- **Size:** 22 KB in 2 files, no JavaScript
- **License:** MIT, © 2026 Lintel
- **Page:** https://lintelagency.com/resources/shopify-sections/how-it-works
- **Live demo:** https://lintelagency.com/resources/shopify-sections/demo/lib-how-it-works.html
- **Added:** 2026-11-02

## What it’s for

- How a subscription works: choose, receive, pause or skip
- How made-to-order or personalised products are made and delivered
- How returns or repairs work, on a help or policy page

## Editor settings

In the theme editor the section is called “Lib · How it works”. IDs, types, labels and defaults come from the section’s schema; the labels are what the theme editor shows.

### Content

| ID | Type | Label | Default | Options or range |
| --- | --- | --- | --- | --- |
| `heading` | `inline_richtext` | Heading | How it works |  |
| `heading_tag` | `select` | Heading tag | h2 | `h1`, `h2`, `h3`, `p` |
| `heading_size` | `select` | Heading size | medium | `small`, `medium`, `large` |
| `text` | `richtext` | Intro text | – |  |
| `button_label` | `text` | Button text | – |  |
| `button_url` | `url` | Button link | – |  |
| `button_style` | `select` | Button style | primary | `primary`, `outline`, `text` |
| `button_new_tab` | `checkbox` | Open button link in a new tab | `false` |  |

### Steps

| ID | Type | Label | Default | Options or range |
| --- | --- | --- | --- | --- |
| `layout` | `select` | Layout on desktop | row | `row`, `column` |
| `marker` | `select` | Marker | number | `number`, `icon` |
| `step_label` | `text` | Step label with icons | Step |  |
| `step_style` | `select` | Step style | plain | `plain`, `card` |
| `show_line` | `checkbox` | Join the steps with a line | `true` |  |
| `section_width` | `select` | Width | page | `page`, `full` |
| `text_alignment` | `select` | Alignment on desktop | center | `left`, `center` |
| `text_alignment_mobile` | `select` | Heading alignment on mobile | left | `left`, `center` |

### Colors

| ID | Type | Label | Default | Options or range |
| --- | --- | --- | --- | --- |
| `color_bg` | `color` | Background | #FAF6F1 |  |
| `color_bg_gradient` | `color_background` | Background gradient | – |  |
| `color_text` | `color` | Text | #1F1B17 |  |
| `color_accent` | `color` | Markers and button | #7A5A3A |  |
| `color_accent_text` | `color` | Text on markers and button | #FFFFFF |  |
| `color_card` | `color` | Card background | #FFFFFF |  |
| `color_line` | `color` | Line | #DCCFBF |  |

### Spacing and visibility

| ID | Type | Label | Default | Options or range |
| --- | --- | --- | --- | --- |
| `padding_top` | `range` | Top padding on desktop | `72` | 0–200 px, step 4 |
| `padding_bottom` | `range` | Bottom padding on desktop | `72` | 0–200 px, step 4 |
| `padding_top_mobile` | `range` | Top padding on mobile | `48` | 0–160 px, step 4 |
| `padding_bottom_mobile` | `range` | Bottom padding on mobile | `48` | 0–160 px, step 4 |
| `show_on` | `select` | Show on | all | `all`, `desktop`, `mobile` |
| `anchor_id` | `text` | Anchor ID | – |  |

### Motion

| ID | Type | Label | Default | Options or range |
| --- | --- | --- | --- | --- |
| `animate` | `checkbox` | Fade steps in on scroll | `false` |  |

## Blocks

Up to 6 blocks in total.

### Step (`step`)

| ID | Type | Label | Default | Options or range |
| --- | --- | --- | --- | --- |
| `icon` | `select` | Icon | check | `check`, `cart`, `pencil`, `chat`, `hand`, `box`, `truck`, `calendar`, `repeat`, `return`, `star` |
| `icon_image` | `image_picker` | Custom icon | – |  |
| `title` | `text` | Title | Step title |  |
| `text` | `richtext` | Text | `<p>One or two sentences about this step.</p>` |  |

## Install

### In Shopify admin

1. Work on a copy, not your live theme: in Shopify admin go to **Online Store > Themes**, open the **…** menu on your current theme and choose **Duplicate**.
2. On the copy, open the **…** menu and choose **Edit code**.
3. In the `sections` folder, add a new file named `lib-how-it-works.liquid` and paste the contents of `sections/lib-how-it-works.liquid` from the source below. Save.
4. In the `snippets` folder, add `lib-button.liquid` the same way. If a snippet is already there from another Lib section, it is the same file; keep one copy.
5. Open **Customize** on the copy, choose **Add section** and pick **Lib · How it works**. Replace the sample content with your own and set the colors.
6. Preview the copy, then publish it when it looks right.

### With Shopify CLI

From your local theme folder, download the files and push only them to an unpublished theme (find its ID with `shopify theme list`):

```sh
curl -fsSL -o sections/lib-how-it-works.liquid https://lintelagency.com/resources/shopify-sections/files/lib-how-it-works.liquid
curl -fsSL -o snippets/lib-button.liquid https://lintelagency.com/resources/shopify-sections/files/lib-button.liquid
shopify theme push --theme <unpublished-theme-id> --only sections/lib-how-it-works.liquid --only snippets/lib-button.liquid
```

Never push to the live theme: always pass the ID of an unpublished theme and never use `--live` or `--allow-live`. Then add the section in the theme editor as above.

## Files

- [sections/lib-how-it-works.liquid](https://lintelagency.com/resources/shopify-sections/files/lib-how-it-works.liquid): goes in `sections/`
- [snippets/lib-button.liquid](https://lintelagency.com/resources/shopify-sections/files/lib-button.liquid): goes in `snippets/`

## Questions

### How many steps can I add?

Up to six, one block each. In the row layout they share the width evenly; for more detail per step, use the column layout.

### Can I use icons instead of numbers?

Yes. Switch the marker to Icon: each step picks one of eleven line icons or your own image, and an optional label such as "Step 2" keeps the order visible.

### Do the numbers update when I reorder steps?

Yes. Numbers come from the list order, and the steps are an ordered list in the HTML, so screen readers announce the step count and position too.

### Can I add a call to action?

Yes, one button after the last step with solid, outline or text style and an optional new tab, for example "Start your order".

## Source

### sections/lib-how-it-works.liquid

```liquid
{%- comment -%}
  MIT License · (c) 2026 Lintel · https://lintelagency.com/resources/shopify-sections
{%- endcomment -%}
{%- comment -%}
  How it works: numbered steps with an icon, a title and a short text, joined by a line, for
  subscriptions, made-to-order products, services or returns. Steps sit in a row on desktop (or a
  column) and stack on mobile; the numbers come from the list order, so reordering blocks
  renumbers them. Optional button after the last step. Pure Liquid and CSS; no JavaScript.
  Depends on: snippets/lib-button.liquid
{%- endcomment -%}

{%- liquid
  assign s = section.settings
  assign heading_tag = s.heading_tag | default: 'h2'
  assign step_tag = 'h3'
  if heading_tag == 'h3' or heading_tag == 'p'
    assign step_tag = 'h4'
  endif
-%}

<div
  class="lib-steps lib-steps--{{ s.section_width }}"
  {% if s.anchor_id != blank %}
    id="{{ s.anchor_id | handleize }}"
  {% endif %}
  data-show="{{ s.show_on }}"
  data-layout="{{ s.layout }}"
  data-marker="{{ s.marker }}"
  data-style="{{ s.step_style }}"
  {% if s.show_line %}
    data-line
  {% endif %}
  {% if s.animate %}
    data-animate
  {% endif %}
  style="
    --lib-bg: {{ s.color_bg }};
    {% if s.color_bg_gradient != blank %}--lib-bg-image: {{ s.color_bg_gradient }};{% endif %}
    --lib-text: {{ s.color_text }};
    --lib-accent: {{ s.color_accent }};
    --lib-accent-text: {{ s.color_accent_text }};
    --lib-card: {{ s.color_card }};
    --lib-line: {{ s.color_line }};
    --lib-count: {{ section.blocks.size }};
    --lib-pt: {{ s.padding_top }}px;
    --lib-pb: {{ s.padding_bottom }}px;
    --lib-pt-m: {{ s.padding_top_mobile }}px;
    --lib-pb-m: {{ s.padding_bottom_mobile }}px;
  "
>
  <div class="lib-steps__inner" data-align="{{ s.text_alignment }}" data-align-m="{{ s.text_alignment_mobile }}">
    {%- if s.heading != blank or s.text != blank -%}
      <header class="lib-steps__header">
        {%- if s.heading != blank -%}
          <{{ heading_tag }} class="lib-steps__heading lib-steps__heading--{{ s.heading_size }}">
            {{- s.heading -}}
          </{{ heading_tag }}>
        {%- endif -%}
        {%- if s.text != blank -%}
          <div class="lib-steps__text">{{ s.text }}</div>
        {%- endif -%}
      </header>
    {%- endif -%}

    {%- if section.blocks.size > 0 -%}
      <ol class="lib-steps__list" role="list">
        {%- for block in section.blocks -%}
          {%- assign b = block.settings -%}
          <li class="lib-steps__item" {{ block.shopify_attributes }}>
            <span class="lib-steps__marker" aria-hidden="true">
              {%- if s.marker == 'icon' -%}
                {%- if b.icon_image != blank -%}
                  {{ b.icon_image | image_url: width: 96 | image_tag: class: 'lib-steps__icon-img', loading: 'lazy', alt: '' }}
                {%- else -%}
                  <svg viewBox="0 0 24 24" width="24" height="24" focusable="false" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
                    {%- case b.icon -%}
                      {%- when 'cart' -%}
                        <path d="M3 4h2.5l2.2 10.5h10.6L20.5 7H7"/><circle cx="9.5" cy="19" r="1.5"/><circle cx="17" cy="19" r="1.5"/>
                      {%- when 'pencil' -%}
                        <path d="M4 20h4L19 9l-4-4L4 16z"/><path d="m13.5 6.5 4 4"/>
                      {%- when 'chat' -%}
                        <path d="M4 5h16v11H9l-5 4z"/>
                      {%- when 'hand' -%}
                        <path d="M8 12V5.5a1.5 1.5 0 0 1 3 0V11M11 10V4.5a1.5 1.5 0 0 1 3 0V11M14 10.5V6a1.5 1.5 0 0 1 3 0v7c0 4-2.5 7-6.5 7S5 17.5 4.5 15L3.6 12a1.5 1.5 0 0 1 2.8-1.1L8 14"/>
                      {%- when 'box' -%}
                        <path d="m12 3 8 4v10l-8 4-8-4V7z"/><path d="m4 7 8 4 8-4M12 11v10"/>
                      {%- when 'truck' -%}
                        <path d="M3 6h11v10H3zM14 9h4l3 3v4h-7"/><circle cx="7" cy="17.5" r="1.8"/><circle cx="17" cy="17.5" r="1.8"/>
                      {%- when 'calendar' -%}
                        <rect x="4" y="5.5" width="16" height="14" rx="2"/><path d="M4 10h16M8.5 3.5v4M15.5 3.5v4"/>
                      {%- when 'repeat' -%}
                        <path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/>
                      {%- when 'return' -%}
                        <path d="M9 14 5 10l4-4"/><path d="M5 10h9a5 5 0 0 1 0 10h-3"/>
                      {%- when 'star' -%}
                        <path d="m12 3.5 2.6 5.3 5.9.9-4.3 4.1 1 5.8-5.2-2.7-5.2 2.7 1-5.8-4.3-4.1 5.9-.9z"/>
                      {%- else -%}
                        <circle cx="12" cy="12" r="8.5"/><path d="m8.5 12 2.5 2.5 4.5-5"/>
                    {%- endcase -%}
                  </svg>
                {%- endif -%}
              {%- else -%}
                {{- forloop.index -}}
              {%- endif -%}
            </span>
            <div class="lib-steps__body">
              {%- if s.marker == 'icon' and s.step_label != blank -%}
                <p class="lib-steps__label">{{ s.step_label | escape }} {{ forloop.index }}</p>
              {%- endif -%}
              {%- if b.title != blank -%}
                <{{ step_tag }} class="lib-steps__title">{{ b.title | escape }}</{{ step_tag }}>
              {%- endif -%}
              {%- if b.text != blank -%}
                <div class="lib-steps__copy">{{ b.text }}</div>
              {%- endif -%}
            </div>
          </li>
        {%- endfor -%}
      </ol>
    {%- endif -%}

    {%- if s.button_label != blank -%}
      <div class="lib-steps__cta">
        {%- render 'lib-button', label: s.button_label, url: s.button_url, style: s.button_style, new_tab: s.button_new_tab -%}
      </div>
    {%- endif -%}
  </div>
</div>

{% stylesheet %}
  .lib-steps {
    background-color: var(--lib-bg);
    background-image: var(--lib-bg-image, none);
    color: var(--lib-text);
    padding: var(--lib-pt) 1rem var(--lib-pb);
  }
  .lib-steps__inner { max-width: 1200px; margin: 0 auto; }
  .lib-steps--full .lib-steps__inner { max-width: none; }
  @media (max-width: 749px) {
    .lib-steps { padding-top: var(--lib-pt-m); padding-bottom: var(--lib-pb-m); }
    .lib-steps[data-show='desktop'] { display: none; }
  }
  @media (min-width: 750px) {
    .lib-steps[data-show='mobile'] { display: none; }
  }

  .lib-steps__header { max-width: 42rem; margin-bottom: clamp(1.75rem, 4vw, 3rem); }
  .lib-steps__heading {
    margin: 0;
    font-size: clamp(1.75rem, 3vw, 2.5rem);
    line-height: 1.15;
    letter-spacing: -0.02em;
    text-wrap: balance;
  }
  .lib-steps__heading--small { font-size: clamp(1.25rem, 1.8vw, 1.5rem); }
  .lib-steps__heading--large { font-size: clamp(2rem, 4vw, 3.25rem); }
  .lib-steps__text { margin-top: 0.6rem; font-size: 1rem; line-height: 1.6; opacity: 0.85; }
  .lib-steps__text > :first-child { margin-top: 0; }
  .lib-steps__text > :last-child { margin-bottom: 0; }

  /* Column (and every layout on mobile): marker on the left, line down the markers. */
  .lib-steps__list {
    --lib-marker: 2.75rem;
    position: relative;
    display: grid;
    gap: clamp(1.25rem, 3vw, 2rem);
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .lib-steps__item { position: relative; display: flex; gap: 1rem; min-width: 0; }
  .lib-steps[data-style='card'] .lib-steps__item { padding: clamp(1rem, 2.5vw, 1.5rem); border-radius: 14px; background: var(--lib-card); }
  .lib-steps__marker {
    position: relative;
    z-index: 1;
    flex: none;
    display: grid;
    place-items: center;
    width: var(--lib-marker);
    height: var(--lib-marker);
    border-radius: 50%;
    background: var(--lib-accent);
    color: var(--lib-accent-text);
    font-size: 1.0625rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
  }
  .lib-steps__marker svg,
  .lib-steps__icon-img { width: 1.375rem; height: 1.375rem; object-fit: contain; }
  .lib-steps[data-line] .lib-steps__item:not(:last-child)::after {
    content: '';
    position: absolute;
    top: var(--lib-marker);
    bottom: calc(clamp(1.25rem, 3vw, 2rem) * -1);
    left: calc(var(--lib-marker) / 2 - 1px);
    width: 2px;
    background: var(--lib-line);
  }
  .lib-steps[data-line][data-style='card'] .lib-steps__item:not(:last-child)::after { display: none; }
  .lib-steps__body { min-width: 0; padding-top: 0.35rem; }
  .lib-steps__label { margin: 0 0 0.2rem; font-size: 0.75rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; opacity: 0.7; }
  .lib-steps__title { margin: 0; font-size: clamp(1.0625rem, 1.5vw, 1.25rem); line-height: 1.3; }
  .lib-steps__copy { margin-top: 0.35rem; font-size: 0.9375rem; line-height: 1.6; opacity: 0.85; }
  .lib-steps__copy > * { margin: 0; }
  .lib-steps__copy > * + * { margin-top: 0.5em; }
  .lib-steps__copy a { color: inherit; text-underline-offset: 0.2em; }

  /* Row: steps side by side, the line runs through the markers. */
  @media (min-width: 750px) {
    .lib-steps[data-layout='row'] .lib-steps__list { grid-template-columns: repeat(var(--lib-count), minmax(0, 1fr)); }
    .lib-steps[data-layout='row'] .lib-steps__item { flex-direction: column; }
    .lib-steps[data-layout='row'] .lib-steps__body { padding-top: 0; }
    .lib-steps[data-layout='row'][data-line] .lib-steps__item:not(:last-child)::after {
      top: calc(var(--lib-marker) / 2 - 1px);
      bottom: auto;
      left: var(--lib-marker);
      right: calc(clamp(1.25rem, 3vw, 2rem) * -1);
      width: auto;
      height: 2px;
    }
  }

  .lib-steps__cta { display: flex; margin-top: clamp(1.75rem, 4vw, 2.75rem); }

  .lib-steps__inner[data-align='center'] .lib-steps__header { margin-inline: auto; text-align: center; }
  .lib-steps__inner[data-align='center'] .lib-steps__cta { justify-content: center; }
  @media (min-width: 750px) {
    .lib-steps[data-layout='row'] .lib-steps__inner[data-align='center'] .lib-steps__item { align-items: center; text-align: center; }
    .lib-steps[data-layout='row'][data-line] .lib-steps__inner[data-align='center'] .lib-steps__item:not(:last-child)::after {
      left: calc(50% + var(--lib-marker) / 2);
      right: calc(-50% - clamp(1.25rem, 3vw, 2rem) + var(--lib-marker) / 2);
    }
  }
  @media (max-width: 749px) {
    .lib-steps__inner[data-align-m='left'] .lib-steps__header { margin-inline: 0; text-align: left; }
    .lib-steps__inner[data-align-m='left'] .lib-steps__cta { justify-content: flex-start; }
    .lib-steps__inner[data-align-m='center'] .lib-steps__header { margin-inline: auto; text-align: center; }
    .lib-steps__inner[data-align-m='center'] .lib-steps__cta { justify-content: center; }
  }

  @supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
      .lib-steps[data-animate] .lib-steps__item {
        animation: lib-steps-rise linear both;
        animation-timeline: view();
        animation-range: entry 0% entry 55%;
      }
    }
  }
  @keyframes lib-steps-rise {
    from { opacity: 0; transform: translateY(16px); }
  }
{% endstylesheet %}

{% schema %}
{
  "name": "Lib · How it works",
  "tag": "section",
  "class": "lib-section",
  "max_blocks": 6,
  "settings": [
    { "type": "header", "content": "Content" },
    { "type": "inline_richtext", "id": "heading", "label": "Heading", "default": "How it works" },
    {
      "type": "select",
      "id": "heading_tag",
      "label": "Heading tag",
      "info": "Use H1 only when this is the page's main heading.",
      "options": [
        { "value": "h1", "label": "H1" },
        { "value": "h2", "label": "H2" },
        { "value": "h3", "label": "H3" },
        { "value": "p", "label": "Paragraph" }
      ],
      "default": "h2"
    },
    {
      "type": "select",
      "id": "heading_size",
      "label": "Heading size",
      "options": [
        { "value": "small", "label": "Small" },
        { "value": "medium", "label": "Medium" },
        { "value": "large", "label": "Large" }
      ],
      "default": "medium"
    },
    { "type": "richtext", "id": "text", "label": "Intro text" },
    { "type": "text", "id": "button_label", "label": "Button text" },
    { "type": "url", "id": "button_url", "label": "Button link" },
    {
      "type": "select",
      "id": "button_style",
      "label": "Button style",
      "options": [
        { "value": "primary", "label": "Solid" },
        { "value": "outline", "label": "Outline" },
        { "value": "text", "label": "Text link" }
      ],
      "default": "primary"
    },
    { "type": "checkbox", "id": "button_new_tab", "label": "Open button link in a new tab", "default": false },

    { "type": "header", "content": "Steps" },
    {
      "type": "select",
      "id": "layout",
      "label": "Layout on desktop",
      "info": "Mobile always stacks the steps.",
      "options": [
        { "value": "row", "label": "Row" },
        { "value": "column", "label": "Column" }
      ],
      "default": "row"
    },
    {
      "type": "select",
      "id": "marker",
      "label": "Marker",
      "options": [
        { "value": "number", "label": "Number" },
        { "value": "icon", "label": "Icon" }
      ],
      "default": "number"
    },
    { "type": "text", "id": "step_label", "label": "Step label with icons", "info": "Shown above each title with the step's number, for example \"Step 2\". Leave empty to hide.", "default": "Step" },
    {
      "type": "select",
      "id": "step_style",
      "label": "Step style",
      "options": [
        { "value": "plain", "label": "Plain" },
        { "value": "card", "label": "Card" }
      ],
      "default": "plain"
    },
    { "type": "checkbox", "id": "show_line", "label": "Join the steps with a line", "info": "Plain style only.", "default": true },
    {
      "type": "select",
      "id": "section_width",
      "label": "Width",
      "options": [
        { "value": "page", "label": "Page width" },
        { "value": "full", "label": "Full width" }
      ],
      "default": "page"
    },
    {
      "type": "select",
      "id": "text_alignment",
      "label": "Alignment on desktop",
      "options": [
        { "value": "left", "label": "Left" },
        { "value": "center", "label": "Center" }
      ],
      "default": "center"
    },
    {
      "type": "select",
      "id": "text_alignment_mobile",
      "label": "Heading alignment on mobile",
      "options": [
        { "value": "left", "label": "Left" },
        { "value": "center", "label": "Center" }
      ],
      "default": "left"
    },

    { "type": "header", "content": "Colors" },
    { "type": "color", "id": "color_bg", "label": "Background", "default": "#FAF6F1" },
    { "type": "color_background", "id": "color_bg_gradient", "label": "Background gradient", "info": "Optional. Covers the background color." },
    { "type": "color", "id": "color_text", "label": "Text", "default": "#1F1B17" },
    { "type": "color", "id": "color_accent", "label": "Markers and button", "default": "#7A5A3A" },
    { "type": "color", "id": "color_accent_text", "label": "Text on markers and button", "default": "#FFFFFF" },
    { "type": "color", "id": "color_card", "label": "Card background", "default": "#FFFFFF" },
    { "type": "color", "id": "color_line", "label": "Line", "default": "#DCCFBF" },

    { "type": "header", "content": "Spacing and visibility" },
    { "type": "range", "id": "padding_top", "label": "Top padding on desktop", "min": 0, "max": 200, "step": 4, "unit": "px", "default": 72 },
    { "type": "range", "id": "padding_bottom", "label": "Bottom padding on desktop", "min": 0, "max": 200, "step": 4, "unit": "px", "default": 72 },
    { "type": "range", "id": "padding_top_mobile", "label": "Top padding on mobile", "min": 0, "max": 160, "step": 4, "unit": "px", "default": 48 },
    { "type": "range", "id": "padding_bottom_mobile", "label": "Bottom padding on mobile", "min": 0, "max": 160, "step": 4, "unit": "px", "default": 48 },
    {
      "type": "select",
      "id": "show_on",
      "label": "Show on",
      "options": [
        { "value": "all", "label": "All devices" },
        { "value": "desktop", "label": "Desktop only" },
        { "value": "mobile", "label": "Mobile only" }
      ],
      "default": "all"
    },
    { "type": "text", "id": "anchor_id", "label": "Anchor ID", "info": "Optional. Link to this section with #your-id." },

    { "type": "header", "content": "Motion" },
    { "type": "checkbox", "id": "animate", "label": "Fade steps in on scroll", "info": "Only in browsers that support scroll-driven animations; never when reduced motion is on.", "default": false }
  ],
  "blocks": [
    {
      "type": "step",
      "name": "Step",
      "settings": [
        {
          "type": "select",
          "id": "icon",
          "label": "Icon",
          "info": "Shown when the section's marker is Icon.",
          "options": [
            { "value": "check", "label": "Check" },
            { "value": "cart", "label": "Cart" },
            { "value": "pencil", "label": "Pencil (customise)" },
            { "value": "chat", "label": "Chat" },
            { "value": "hand", "label": "Hand (made by hand)" },
            { "value": "box", "label": "Package" },
            { "value": "truck", "label": "Delivery" },
            { "value": "calendar", "label": "Calendar" },
            { "value": "repeat", "label": "Repeat (subscription)" },
            { "value": "return", "label": "Return" },
            { "value": "star", "label": "Star" }
          ],
          "default": "check"
        },
        { "type": "image_picker", "id": "icon_image", "label": "Custom icon", "info": "Optional. Replaces the icon above." },
        { "type": "text", "id": "title", "label": "Title", "default": "Step title" },
        { "type": "richtext", "id": "text", "label": "Text", "default": "<p>One or two sentences about this step.</p>" }
      ]
    }
  ],
  "presets": [
    {
      "name": "Lib · How it works",
      "blocks": [
        { "type": "step", "settings": { "icon": "cart", "title": "Choose your pieces" } },
        { "type": "step", "settings": { "icon": "hand", "title": "We make them to order" } },
        { "type": "step", "settings": { "icon": "truck", "title": "Delivered to your door" } }
      ]
    }
  ]
}
{% endschema %}
```

### snippets/lib-button.liquid

```liquid
{%- comment -%}
  MIT License · (c) 2026 Lintel · https://lintelagency.com/resources/shopify-sections
{%- endcomment -%}
{% doc %}
  Animated call-to-action link. Light sweep on hover, arrow slides right.
  Colors come from the parent section's --lib-accent / --lib-accent-text / --lib-text vars.
  Inspired by Uiverse.io button patterns; rewritten for themes, no dependencies.

  @param {string} label - Button text
  @param {string} [url] - Link target; renders a disabled-looking span when blank
  @param {string} [style] - 'primary' (default), 'outline' or 'text'
  @param {boolean} [new_tab] - Open the link in a new tab
  @param {boolean} [arrow] - Show the trailing arrow (default true)

  @example
  {% render 'lib-button', label: section.settings.button_label, url: section.settings.button_url, style: 'outline' %}
{% enddoc %}

{%- liquid
  assign btn_style = style | default: 'primary'
  assign show_arrow = true
  if arrow == false
    assign show_arrow = false
  endif
-%}

{%- unless label == blank -%}
{%- capture inner -%}
  <span class="lib-btn__label">{{ label | escape }}</span>
  {%- if show_arrow -%}
    <svg class="lib-btn__arrow" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" focusable="false">
      <path d="M3 8h9M8.5 4.5 12 8l-3.5 3.5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
    </svg>
  {%- endif -%}
{%- endcapture -%}

{%- if url != blank -%}
  <a
    class="lib-btn lib-btn--{{ btn_style }}"
    href="{{ url }}"
    {% if new_tab %}
      target="_blank" rel="noopener"
    {% endif %}
  >
    {{- inner -}}
  </a>
{%- else -%}
  <span class="lib-btn lib-btn--{{ btn_style }}" aria-disabled="true">{{ inner }}</span>
{%- endif -%}
{%- endunless -%}

{% stylesheet %}
  .lib-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
    padding: 0.85em 1.5em;
    border-radius: var(--lib-radius, 999px);
    font: inherit;
    font-weight: 600;
    line-height: 1;
    text-decoration: none;
    overflow: hidden;
    isolation: isolate;
    transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
  }
  .lib-btn--primary {
    background: var(--lib-accent, #111);
    color: var(--lib-accent-text, #fff);
    box-shadow: 0 1px 2px rgb(0 0 0 / 0.12);
  }
  .lib-btn--outline {
    background: transparent;
    color: var(--lib-text, #111);
    box-shadow: inset 0 0 0 1.5px currentColor;
  }
  .lib-btn--text {
    padding-inline: 0;
    background: none;
    color: var(--lib-text, #111);
    text-decoration: underline;
    text-underline-offset: 0.3em;
    overflow: visible;
  }
  .lib-btn--text::before { display: none; }
  .lib-btn--text:hover { transform: none; box-shadow: none; }
  .lib-btn::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(110deg, transparent 30%, rgb(255 255 255 / 0.35) 50%, transparent 70%);
    transform: translateX(-120%);
    transition: transform 0.6s ease;
  }
  .lib-btn--outline::before {
    background: linear-gradient(110deg, transparent 30%, rgb(127 127 127 / 0.18) 50%, transparent 70%);
  }
  .lib-btn:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 20px -8px rgb(0 0 0 / 0.35);
  }
  .lib-btn:hover::before { transform: translateX(120%); }
  .lib-btn__arrow { transition: transform 0.2s ease; }
  .lib-btn:hover .lib-btn__arrow { transform: translateX(3px); }
  .lib-btn:focus-visible {
    outline: 2px solid var(--lib-accent, #111);
    outline-offset: 3px;
  }
  .lib-btn[aria-disabled='true'] { pointer-events: none; }
  @media (prefers-reduced-motion: reduce) {
    .lib-btn, .lib-btn::before, .lib-btn__arrow { transition: none; }
    .lib-btn:hover { transform: none; }
  }
{% endstylesheet %}
```

## License

MIT License, © 2026 Lintel. Free to use, change and ship in your own store or in client work; keep the license line at the top of each file.
