{%- comment -%} MIT License · (c) 2026 Lintel · https://lintelagency.com/resources/shopify-sections {%- endcomment -%} {%- comment -%} FAQ accordion on native
/: every answer opens and closes without JavaScript. A small custom element adds the optional extras: one answer open at a time and a search filter. Questions can be grouped under headings by the block's group label. Optional FAQPage JSON-LD built from the blocks (checkbox, off by default). Depends on: snippets/lib-button.liquid {%- endcomment -%} {%- liquid assign s = section.settings assign heading_tag = s.heading_tag | default: 'h2' case heading_tag when 'h1' assign group_tag = 'h2' when 'h2' assign group_tag = 'h3' else assign group_tag = 'h4' endcase comment Group labels in order of first appearance, wrapped in a delimiter so "Ship" never matches "Shipping". Questions without a label are listed first, without a heading. endcomment assign sep = '|~|' assign group_list = sep assign has_ungrouped = false for block in section.blocks assign g = block.settings.group | strip if g == blank assign has_ungrouped = true else assign needle = sep | append: g | append: sep unless group_list contains needle assign group_list = group_list | append: g | append: sep endunless endif endfor assign use_groups = false if s.group_questions and group_list != sep assign use_groups = true endif assign has_header = false if s.heading != blank or s.text != blank assign has_header = true endif assign has_cta = false if s.cta_text != blank or s.button_label != blank assign has_cta = true endif assign list_name = 'lib-faq-' | append: section.id -%} {%- capture cta -%} {%- if has_cta -%}
{%- if s.cta_text != blank -%}
{{ s.cta_text }}
{%- endif -%} {%- render 'lib-button', label: s.button_label, url: s.button_url, style: s.button_style, new_tab: s.button_new_tab -%}
{%- endif -%} {%- endcapture -%}
{%- if has_header or s.layout == 'split' -%}
{%- if s.heading != blank -%} <{{ heading_tag }} class="lib-faq__heading lib-faq__heading--{{ s.heading_size }}"> {{- s.heading -}} {%- endif -%} {%- if s.text != blank -%}
{{ s.text }}
{%- endif -%} {%- if s.layout == 'split' -%} {{ cta | replace: 'CTA_POS', 'side' }} {%- endif -%}
{%- endif -%} {%- if s.show_search and section.blocks.size > 0 -%} {%- endif -%}
{%- if section.blocks.size == 0 -%}

Add question blocks to fill this FAQ.

{%- endif -%} {%- liquid comment Pass 0 lists questions without a group label (or all questions when grouping is off); each following pass lists one group under its heading. endcomment if use_groups assign pass_str = group_list if has_ungrouped assign pass_str = '__none__' | append: group_list endif else assign pass_str = '__all__' endif assign passes = pass_str | split: sep assign first_done = false -%} {%- for pass in passes -%} {%- if pass == blank -%}{%- continue -%}{%- endif -%}
{%- if pass != '__none__' and pass != '__all__' -%} <{{ group_tag }} class="lib-faq__group-heading">{{ pass | escape }} {%- endif -%}
{%- for block in section.blocks -%} {%- liquid assign b = block.settings assign g = b.group | strip assign show = false if pass == '__all__' assign show = true elsif pass == '__none__' and g == blank assign show = true elsif g == pass assign show = true endif -%} {%- if show and b.question != blank -%}
{{ b.question | escape }} {%- if s.icon_style != 'none' -%} {%- endif -%}
{{ b.answer }}
{%- assign first_done = true -%} {%- endif -%} {%- endfor -%}
{%- endfor -%}
{{ cta | replace: 'CTA_POS', 'bottom' }}
{%- if s.structured_data and section.blocks.size > 0 -%} {%- assign ld_first = true -%} {%- endif -%} {% stylesheet %} .lib-faq { 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-faq__inner { max-width: 1200px; margin: 0 auto; display: grid; gap: clamp(1.75rem, 4vw, 2.75rem); } .lib-faq--full .lib-faq__inner { max-width: none; } .lib-faq[data-layout='single'] .lib-faq__inner { max-width: var(--lib-max); } .lib-faq[hidden], .lib-faq [hidden] { display: none !important; } @media (max-width: 749px) { .lib-faq { padding-top: var(--lib-pt-m); padding-bottom: var(--lib-pb-m); } .lib-faq[data-show='desktop'] { display: none; } } @media (min-width: 750px) { .lib-faq[data-show='mobile'] { display: none; } } @media (min-width: 990px) { .lib-faq[data-layout='split'] .lib-faq__inner { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: start; gap: clamp(2.5rem, 6vw, 5rem); } .lib-faq[data-layout='split'] .lib-faq__header { position: sticky; top: 2rem; } .lib-faq[data-layout='two'] .lib-faq__list { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 2.5rem; align-items: start; } .lib-faq[data-layout='two'][data-style='cards'] .lib-faq__list { column-gap: 0.75rem; } } /* Header and alignment: desktop, then mobile override. */ .lib-faq__header { display: flex; flex-direction: column; align-items: flex-start; text-align: left; } .lib-faq__header[data-align='center'] { align-items: center; text-align: center; } .lib-faq__header[data-align='right'] { align-items: flex-end; text-align: right; } @media (max-width: 749px) { .lib-faq__header[data-align-m='left'] { align-items: flex-start; text-align: left; } .lib-faq__header[data-align-m='center'] { align-items: center; text-align: center; } .lib-faq__header[data-align-m='right'] { align-items: flex-end; text-align: right; } } .lib-faq__heading { margin: 0; font-size: clamp(1.75rem, 3.5vw, 2.75rem); line-height: 1.1; letter-spacing: -0.02em; text-wrap: balance; } .lib-faq__heading--small { font-size: clamp(1.5rem, 2.5vw, 2rem); } .lib-faq__heading--large { font-size: clamp(2rem, 4.5vw, 3.5rem); } .lib-faq__heading--xlarge { font-size: clamp(2.5rem, 6vw, 4.5rem); } .lib-faq__intro { margin-top: 1rem; max-width: 38rem; font-size: 1.0625rem; line-height: 1.6; opacity: 0.75; } .lib-faq__intro > :first-child, .lib-faq__cta-text > :first-child, .lib-faq__answer > :first-child { margin-top: 0; } .lib-faq__intro > :last-child, .lib-faq__cta-text > :last-child, .lib-faq__answer > :last-child { margin-bottom: 0; } /* Search */ .lib-faq__search { margin-bottom: 1.5rem; } .lib-faq__search-label { display: block; margin-bottom: 0.5rem; font-size: 0.875rem; font-weight: 600; } .lib-faq__search-field { position: relative; } .lib-faq__search-icon { position: absolute; inset-inline-start: 1rem; top: 50%; translate: 0 -50%; opacity: 0.55; pointer-events: none; } .lib-faq__search-input { width: 100%; min-height: 3rem; padding: 0.75rem 1rem 0.75rem 2.75rem; border: 1px solid var(--lib-line); border-radius: var(--lib-radius); background: var(--lib-card); color: inherit; font: inherit; font-size: 1rem; } .lib-faq__search-input::placeholder { color: inherit; opacity: 0.5; } .lib-faq__search-input:focus-visible { outline: 2px solid var(--lib-accent); outline-offset: 2px; } .lib-faq__empty { margin: 1rem 0 0; opacity: 0.75; } .lib-faq__empty:empty { display: none; } /* Groups and items */ .lib-faq__groups { display: grid; gap: 2.5rem; } .lib-faq__group-heading { margin: 0 0 0.75rem; font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; opacity: 0.6; } .lib-faq__list { display: grid; } .lib-faq[data-style='dividers'] .lib-faq__list { border-top: 1px solid var(--lib-line); } .lib-faq[data-style='dividers'] .lib-faq__item { border-bottom: 1px solid var(--lib-line); } .lib-faq[data-style='cards'] .lib-faq__list { gap: 0.75rem; } .lib-faq[data-style='cards'] .lib-faq__item { background: var(--lib-card); border: 1px solid var(--lib-line); border-radius: var(--lib-radius); } @media (min-width: 990px) { /* Two columns with dividers: each column keeps its own top rule. */ .lib-faq[data-layout='two'][data-style='dividers'] .lib-faq__list { border-top: 0; } .lib-faq[data-layout='two'][data-style='dividers'] .lib-faq__item:nth-child(-n + 2) { border-top: 1px solid var(--lib-line); } } .lib-faq__question { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 1.25rem 0; font-size: 1.0625rem; font-weight: 600; line-height: 1.4; cursor: pointer; list-style: none; } .lib-faq__question::-webkit-details-marker { display: none; } .lib-faq__question::marker { content: ''; } .lib-faq[data-style='cards'] .lib-faq__question { padding-inline: 1.25rem; } .lib-faq__question:focus-visible { outline: 2px solid var(--lib-accent); outline-offset: 2px; border-radius: 4px; } .lib-faq__question:hover .lib-faq__question-text { text-decoration: underline; text-underline-offset: 0.2em; text-decoration-thickness: 1px; } .lib-faq__icon { flex: none; display: grid; place-items: center; width: 2rem; height: 2rem; border-radius: 50%; color: var(--lib-accent); background: color-mix(in srgb, var(--lib-accent) 10%, transparent); } .lib-faq__icon svg, .lib-faq__icon-v { transition: transform 0.25s ease; transform-origin: center; } .lib-faq__item[open] .lib-faq__icon-v { transform: rotate(90deg); opacity: 0; } .lib-faq[data-icon='chevron'] .lib-faq__item[open] .lib-faq__icon svg { transform: rotate(180deg); } .lib-faq__answer { padding: 0 0 1.25rem; max-width: 46rem; line-height: 1.65; opacity: 0.8; } .lib-faq[data-style='cards'] .lib-faq__answer { padding-inline: 1.25rem; } .lib-faq__answer a { color: inherit; text-underline-offset: 0.2em; } .lib-faq__answer a:focus-visible { outline: 2px solid var(--lib-accent); outline-offset: 2px; } .lib-faq__placeholder { margin: 0; padding: 2rem; border: 1px dashed var(--lib-line); border-radius: var(--lib-radius); text-align: center; opacity: 0.7; } /* Call to action */ .lib-faq__cta { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem 1.5rem; padding: 1.5rem; border-radius: var(--lib-radius); background: color-mix(in srgb, var(--lib-text) 5%, transparent); } .lib-faq__cta-text { line-height: 1.5; } /* Split layout: the box sits under the heading on desktop and after the questions on mobile. */ .lib-faq__cta--side { display: none; } @media (min-width: 990px) { .lib-faq[data-layout='split'] .lib-faq__cta--side { display: flex; } .lib-faq[data-layout='split'] .lib-faq__cta--bottom { display: none; } } .lib-faq__header .lib-faq__cta { margin-top: 2rem; padding: 0; background: none; flex-direction: column; align-items: inherit; } /* Motion: smooth open where the browser supports animating
content. */ @media (prefers-reduced-motion: no-preference) { .lib-faq[data-animate] { interpolate-size: allow-keywords; } .lib-faq[data-animate] .lib-faq__item::details-content { block-size: 0; overflow: hidden; transition: block-size 0.3s ease, content-visibility 0.3s allow-discrete; } .lib-faq[data-animate] .lib-faq__item[open]::details-content { block-size: auto; } } @media (prefers-reduced-motion: reduce) { .lib-faq__icon svg, .lib-faq__icon-v { transition: none; } } {% endstylesheet %} {% javascript %} if (!customElements.get('lib-faq-accordion')) { customElements.define( 'lib-faq-accordion', class extends HTMLElement { connectedCallback() { this.items = () => [...this.querySelectorAll('details.lib-faq__item')]; // One open at a time. Native
already does this in current browsers; // this covers older ones. if (this.hasAttribute('data-exclusive')) { this.addEventListener( 'toggle', (e) => { const opened = e.target; if (!(opened instanceof HTMLDetailsElement) || !opened.open) return; for (const item of this.items()) if (item !== opened && item.open) item.open = false; }, true ); } const search = this.querySelector('[data-search]'); const input = search && search.querySelector('input'); if (!input) return; const empty = search.querySelector('.lib-faq__empty'); search.hidden = false; input.addEventListener('input', () => this.filter(input.value, empty)); input.addEventListener('keydown', (e) => { if (e.key === 'Escape' && input.value) { input.value = ''; this.filter('', empty); } }); } filter(raw, empty) { const query = raw.trim().toLowerCase(); let shown = 0; for (const item of this.items()) { const match = !query || item.textContent.toLowerCase().includes(query); item.hidden = !match; if (match) shown++; } for (const group of this.querySelectorAll('[data-group]')) { group.hidden = !group.querySelector('details.lib-faq__item:not([hidden])'); } if (empty) empty.textContent = shown ? '' : empty.dataset.emptyText || ''; } } ); } {% endjavascript %} {% schema %} { "name": "Lib · FAQ", "tag": "section", "class": "lib-section", "max_blocks": 30, "settings": [ { "type": "header", "content": "Content" }, { "type": "inline_richtext", "id": "heading", "label": "Heading", "default": "Frequently asked questions" }, { "type": "select", "id": "heading_tag", "label": "Heading tag", "info": "Use H1 only when this is the page's main heading. Group headings use the next level down.", "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" }, { "value": "xlarge", "label": "Extra large" } ], "default": "medium" }, { "type": "richtext", "id": "text", "label": "Intro text", "default": "

Shipping, returns and caring for your pieces, answered.

" }, { "type": "header", "content": "Questions" }, { "type": "checkbox", "id": "group_questions", "label": "Group questions by their group label", "info": "Questions with the same group label are listed together under that label as a heading, in the order the labels first appear.", "default": false }, { "type": "checkbox", "id": "first_open", "label": "Open the first question", "default": false }, { "type": "checkbox", "id": "one_open", "label": "Only one answer open at a time", "default": false }, { "type": "select", "id": "icon_style", "label": "Icon", "options": [ { "value": "plus", "label": "Plus" }, { "value": "chevron", "label": "Chevron" }, { "value": "none", "label": "None" } ], "default": "plus" }, { "type": "select", "id": "item_style", "label": "Question style", "options": [ { "value": "dividers", "label": "Divider lines" }, { "value": "cards", "label": "Cards" } ], "default": "dividers" }, { "type": "range", "id": "radius", "label": "Corner radius (cards, search, contact box)", "min": 0, "max": 24, "step": 2, "unit": "px", "default": 12 }, { "type": "header", "content": "Search" }, { "type": "checkbox", "id": "show_search", "label": "Show search box", "info": "Filters the questions as visitors type. Needs JavaScript; without it the box stays hidden.", "default": false }, { "type": "text", "id": "search_label", "label": "Search label", "default": "Search questions", "visible_if": "{{ section.settings.show_search }}" }, { "type": "text", "id": "search_placeholder", "label": "Search placeholder", "default": "Try 'returns' or 'glaze'", "visible_if": "{{ section.settings.show_search }}" }, { "type": "text", "id": "no_results", "label": "No results message", "default": "No questions match your search. Try another word or contact us.", "visible_if": "{{ section.settings.show_search }}" }, { "type": "header", "content": "Contact box" }, { "type": "richtext", "id": "cta_text", "label": "Text", "default": "

Still have questions? Our team answers within one working day.

" }, { "type": "text", "id": "button_label", "label": "Button label", "default": "Contact us" }, { "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 link in a new tab", "default": false }, { "type": "header", "content": "Layout" }, { "type": "select", "id": "layout", "label": "Layout", "options": [ { "value": "single", "label": "Single column" }, { "value": "two", "label": "Two columns on desktop" }, { "value": "split", "label": "Heading left, questions right" } ], "default": "single" }, { "type": "select", "id": "section_width", "label": "Width", "options": [ { "value": "page", "label": "Page width" }, { "value": "full", "label": "Full width" } ], "default": "page" }, { "type": "range", "id": "content_width", "label": "Content max width", "info": "Single column layout only.", "min": 560, "max": 1200, "step": 20, "unit": "px", "default": 800, "visible_if": "{{ section.settings.layout == 'single' }}" }, { "type": "text_alignment", "id": "text_alignment", "label": "Heading alignment on desktop", "default": "left" }, { "type": "select", "id": "text_alignment_mobile", "label": "Heading alignment on mobile", "options": [ { "value": "left", "label": "Left" }, { "value": "center", "label": "Center" }, { "value": "right", "label": "Right" } ], "default": "left" }, { "type": "header", "content": "Colors" }, { "type": "color", "id": "color_bg", "label": "Background", "default": "#FAFAF9" }, { "type": "color_background", "id": "color_bg_gradient", "label": "Background gradient", "info": "Optional. Covers the background color." }, { "type": "color", "id": "color_text", "label": "Text", "default": "#1C1917" }, { "type": "color", "id": "color_accent", "label": "Accent (icons, button, focus ring)", "default": "#1C1917" }, { "type": "color", "id": "color_accent_text", "label": "Text on accent", "default": "#FAFAF9" }, { "type": "color", "id": "color_card", "label": "Card and search box background", "default": "#FFFFFF" }, { "type": "color", "id": "color_line", "label": "Dividers and borders", "default": "#E7E5E4" }, { "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": 80 }, { "type": "range", "id": "padding_bottom", "label": "Bottom padding on desktop", "min": 0, "max": 200, "step": 4, "unit": "px", "default": 80 }, { "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": "Animate answers opening", "info": "In browsers that support it. Always off when the visitor prefers reduced motion.", "default": true }, { "type": "header", "content": "Search engines" }, { "type": "checkbox", "id": "structured_data", "label": "Output FAQPage structured data", "info": "Adds schema.org FAQPage JSON-LD built from the questions, with formatting removed from answers. Use it on one FAQ per page, and only when your theme or an app does not already output FAQ markup.", "default": false } ], "blocks": [ { "type": "question", "name": "Question", "settings": [ { "type": "text", "id": "question", "label": "Question", "default": "How long does delivery take?" }, { "type": "richtext", "id": "answer", "label": "Answer", "default": "

Orders ship within two working days. Delivery usually takes three to five working days after that.

" }, { "type": "text", "id": "group", "label": "Group label", "info": "Optional. Used as a heading when \"Group questions\" is on." } ] } ], "presets": [ { "name": "Lib · FAQ", "blocks": [ { "type": "question", "settings": { "group": "Orders and delivery", "question": "How long does delivery take?", "answer": "

Orders ship from our studio within two working days. Delivery usually takes three to five working days after that, and you get a tracking link by email as soon as your parcel leaves.

" } }, { "type": "question", "settings": { "group": "Orders and delivery", "question": "Do you ship internationally?", "answer": "

Yes, to most of Europe, the UK, the US and Canada. Duties and shipping costs are shown at checkout before you pay.

" } }, { "type": "question", "settings": { "group": "Orders and delivery", "question": "Can I change or cancel my order?", "answer": "

Write to us within 12 hours of ordering and we will change or cancel it. After that your order is usually already packed.

" } }, { "type": "question", "settings": { "group": "Returns", "question": "What is your returns policy?", "answer": "

Unused pieces can be returned within 30 days for a full refund. Start a return from the link in your shipping email.

" } }, { "type": "question", "settings": { "group": "Returns", "question": "What if something arrives broken?", "answer": "

Send us a photo within seven days of delivery and we will replace the piece or refund it. You do not need to send the broken piece back.

" } }, { "type": "question", "settings": { "group": "Product care", "question": "Is the stoneware dishwasher safe?", "answer": "

Our glazed stoneware is dishwasher and microwave safe. Pieces with an unglazed base last longer when washed by hand.

" } }, { "type": "question", "settings": { "group": "Product care", "question": "Why does my piece look slightly different from the photo?", "answer": "

Every piece is thrown and glazed by hand, so colour and speckling vary from one to the next. No two are exactly alike.

" } } ] } ] } {% endschema %}