{%- comment -%} MIT License · (c) 2026 Lintel · https://lintelagency.com/resources/shopify-sections {%- endcomment -%} {%- comment -%} Store locator: one card per shop, stockist or showroom with photo, address, opening hours, phone, email and a "get directions" link that opens the address in Google Maps, Apple Maps or OpenStreetMap. No maps API, no key, no embedded map: just links. With more than one city a small script adds a city filter; without it every store shows. All text is typed by the merchant. Depends on: snippets/lib-image.liquid {%- endcomment -%} {%- liquid assign s = section.settings assign heading_tag = s.heading_tag | default: 'h2' assign item_tag = 'h3' if heading_tag == 'h3' or heading_tag == 'p' assign item_tag = 'h4' endif assign cities = section.blocks | map: 'settings' | map: 'city' | compact | uniq assign show_filter = false if s.show_filter and cities.size > 1 assign show_filter = true endif assign cols = s.columns | plus: 0 assign img_vw = 100 | divided_by: cols assign img_sizes = '(min-width: 990px) ' | append: img_vw | append: 'vw, 100vw' assign filter_id = 'LibStoreFilter-' | append: section.id -%}
{%- if s.heading != blank -%} <{{ heading_tag }} class="lib-stores__heading lib-stores__heading--{{ s.heading_size }}"> {{- s.heading -}} {%- endif -%} {%- if s.text != blank -%}
{{ s.text }}
{%- endif -%}
{%- if show_filter -%} {%- endif -%}

{%- if section.blocks.size > 0 -%} {%- endif -%}
{% stylesheet %} .lib-stores { display: block; 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-stores__inner { max-width: 1200px; margin: 0 auto; } .lib-stores--full .lib-stores__inner { max-width: none; } @media (max-width: 749px) { .lib-stores { padding-top: var(--lib-pt-m); padding-bottom: var(--lib-pb-m); } .lib-stores[data-show='desktop'] { display: none; } } @media (min-width: 750px) { .lib-stores[data-show='mobile'] { display: none; } } .lib-stores__header { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 1rem 2rem; margin-bottom: clamp(1.25rem, 3vw, 2rem); } .lib-stores__copy { min-width: 0; max-width: 40rem; } .lib-stores__heading { margin: 0; font-size: clamp(1.75rem, 3vw, 2.5rem); line-height: 1.15; letter-spacing: -0.02em; text-wrap: balance; } .lib-stores__heading--small { font-size: clamp(1.25rem, 1.8vw, 1.5rem); } .lib-stores__heading--large { font-size: clamp(2rem, 4vw, 3.25rem); } .lib-stores__text { margin-top: 0.6rem; font-size: 1rem; line-height: 1.6; opacity: 0.85; } .lib-stores__text > :first-child { margin-top: 0; } .lib-stores__text > :last-child { margin-bottom: 0; } .lib-stores__header[data-align='center'] { flex-direction: column; align-items: center; text-align: center; } @media (max-width: 749px) { .lib-stores__header[data-align-m='left'] { flex-direction: column; align-items: flex-start; text-align: left; } .lib-stores__header[data-align-m='center'] { flex-direction: column; align-items: center; text-align: center; } } .lib-stores__filter { display: flex; flex-direction: column; gap: 0.35rem; min-width: min(16rem, 100%); } .lib-stores__filter[hidden] { display: none; } .lib-stores__filter-label { font-size: 0.875rem; font-weight: 600; text-align: left; } .lib-stores__select { min-height: 2.75rem; padding: 0.5rem 2.25rem 0.5rem 0.875rem; border: 1.5px solid color-mix(in srgb, var(--lib-text) 35%, transparent); border-radius: 10px; background: var(--lib-card) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 6l4 4 4-4' fill='none' stroke='%23777' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat right 0.75rem center / 1rem; color: var(--lib-text); font: inherit; font-size: 0.9375rem; appearance: none; } .lib-stores__select:focus-visible { outline: 2px solid var(--lib-accent); outline-offset: 2px; } .lib-stores__count { margin: 0 0 1rem; font-size: 0.875rem; opacity: 0.8; } .lib-stores__count:empty { display: none; } .lib-stores__list { display: grid; grid-template-columns: repeat(var(--lib-cols), minmax(0, 1fr)); gap: clamp(1rem, 2.5vw, 1.75rem); margin: 0; padding: 0; list-style: none; } @media (max-width: 989px) { .lib-stores__list { grid-template-columns: repeat(min(var(--lib-cols), 2), minmax(0, 1fr)); } } @media (max-width: 749px) { .lib-stores__list { grid-template-columns: 1fr; } } .lib-stores__item { display: flex; min-width: 0; } .lib-stores__item[hidden] { display: none; } .lib-stores__card { display: flex; flex: 1; flex-direction: column; min-width: 0; border-radius: 14px; overflow: hidden; } .lib-stores[data-style='card'] .lib-stores__card { background: var(--lib-card); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--lib-text) 10%, transparent); } .lib-stores__media { aspect-ratio: 4 / 3; overflow: hidden; background: color-mix(in srgb, var(--lib-text) 6%, transparent); } .lib-stores[data-style='plain'] .lib-stores__media { border-radius: 14px; } .lib-stores__media[data-ratio='landscape'] { aspect-ratio: 16 / 9; } .lib-stores__media[data-ratio='square'] { aspect-ratio: 1; } .lib-stores__img { display: block; width: 100%; height: 100%; object-fit: cover; } .lib-stores__body { display: flex; flex: 1; flex-direction: column; gap: 0.5rem; padding: 1rem 0 0; } .lib-stores[data-style='card'] .lib-stores__body { padding: 1.125rem 1.25rem 1.25rem; } .lib-stores__city { margin: 0; font-size: 0.75rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--lib-accent); } .lib-stores__name { margin: 0; font-size: 1.1875rem; line-height: 1.3; } .lib-stores__address { font-style: normal; font-size: 0.9375rem; line-height: 1.55; } .lib-stores__hours { font-size: 0.875rem; line-height: 1.55; opacity: 0.85; } .lib-stores__hours > * { margin: 0; } .lib-stores__contact { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; margin: 0; padding: 0; list-style: none; font-size: 0.9375rem; } .lib-stores__contact a { display: inline-block; padding-block: 0.2rem; color: inherit; text-underline-offset: 0.2em; } .lib-stores__note { margin: 0; font-size: 0.875rem; opacity: 0.8; } .lib-stores__map { display: inline-flex; align-items: center; gap: 0.4rem; align-self: flex-start; min-height: 2.75rem; margin-top: auto; padding: 0.5rem 1rem; border-radius: 999px; background: var(--lib-accent); color: var(--lib-accent-text); font-size: 0.875rem; font-weight: 600; text-decoration: none; } .lib-stores__map:hover { filter: brightness(1.08); } .lib-stores__map:focus-visible, .lib-stores__contact a:focus-visible { outline: 2px solid var(--lib-accent); outline-offset: 2px; } .lib-stores__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } @supports (animation-timeline: view()) { @media (prefers-reduced-motion: no-preference) { .lib-stores[data-animate] .lib-stores__card { animation: lib-stores-rise linear both; animation-timeline: view(); animation-range: entry 0% entry 50%; } } } @keyframes lib-stores-rise { from { opacity: 0; transform: translateY(18px); } } {% endstylesheet %} {% javascript %} (() => { if (customElements.get('lib-store-locator')) return; class LibStoreLocator extends HTMLElement { connectedCallback() { const filter = this.querySelector('[data-filter]'); if (!filter) return; this.items = [...this.querySelectorAll('[data-city]')]; this.count = this.querySelector('[data-count]'); filter.hidden = false; filter.querySelector('select').addEventListener('change', (event) => this.apply(event.target.value)); } apply(city) { let shown = 0; this.items.forEach((item) => { item.hidden = Boolean(city) && item.dataset.city !== city; if (!item.hidden) shown += 1; }); const template = this.count.dataset.template || ''; this.count.textContent = city ? template.replace('{count}', shown).replace('{total}', this.items.length) : ''; } } customElements.define('lib-store-locator', LibStoreLocator); })(); {% endjavascript %} {% schema %} { "name": "Lib · Store locator", "tag": "section", "class": "lib-section", "max_blocks": 30, "settings": [ { "type": "header", "content": "Content" }, { "type": "inline_richtext", "id": "heading", "label": "Heading", "default": "Visit us" }, { "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": "header", "content": "City filter" }, { "type": "checkbox", "id": "show_filter", "label": "Show a city filter", "info": "Only when the stores are in more than one city.", "default": true }, { "type": "text", "id": "filter_label", "label": "Filter label", "default": "City" }, { "type": "text", "id": "all_label", "label": "All cities option", "default": "All cities" }, { "type": "text", "id": "count_label", "label": "Result message", "info": "{count} stores shown, {total} in all. Read out by screen readers too.", "default": "{count} of {total} stores" }, { "type": "checkbox", "id": "show_city", "label": "Show the city on each card", "default": true }, { "type": "header", "content": "Map links" }, { "type": "select", "id": "map_provider", "label": "Open addresses in", "info": "A link built from the store's name and address; a block's own map link wins.", "options": [ { "value": "google", "label": "Google Maps" }, { "value": "apple", "label": "Apple Maps" }, { "value": "osm", "label": "OpenStreetMap" } ], "default": "google" }, { "type": "text", "id": "map_label", "label": "Map link text", "info": "Leave empty to hide map links.", "default": "Get directions" }, { "type": "checkbox", "id": "map_new_tab", "label": "Open map links in a new tab", "default": true }, { "type": "text", "id": "new_tab_label", "label": "New tab notice (screen readers)", "default": "opens in a new tab" }, { "type": "header", "content": "Layout" }, { "type": "range", "id": "columns", "label": "Columns on desktop", "info": "Mobile shows one column.", "min": 1, "max": 4, "step": 1, "default": 3 }, { "type": "select", "id": "card_style", "label": "Card style", "options": [ { "value": "plain", "label": "Plain" }, { "value": "card", "label": "Card" } ], "default": "card" }, { "type": "select", "id": "image_ratio", "label": "Image shape", "options": [ { "value": "standard", "label": "4:3" }, { "value": "landscape", "label": "16:9" }, { "value": "square", "label": "Square" } ], "default": "standard" }, { "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": "Heading alignment on desktop", "options": [ { "value": "left", "label": "Left" }, { "value": "center", "label": "Center" } ], "default": "left" }, { "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": "#FFFFFF" }, { "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": "Accent (map links, cities)", "default": "#7A5A3A" }, { "type": "color", "id": "color_accent_text", "label": "Text on accent", "default": "#FFFFFF" }, { "type": "color", "id": "color_card", "label": "Card background", "default": "#FAF6F1" }, { "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": 64 }, { "type": "range", "id": "padding_bottom", "label": "Bottom padding on desktop", "min": 0, "max": 200, "step": 4, "unit": "px", "default": 64 }, { "type": "range", "id": "padding_top_mobile", "label": "Top padding on mobile", "min": 0, "max": 160, "step": 4, "unit": "px", "default": 40 }, { "type": "range", "id": "padding_bottom_mobile", "label": "Bottom padding on mobile", "min": 0, "max": 160, "step": 4, "unit": "px", "default": 40 }, { "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 cards in on scroll", "info": "Only in browsers that support scroll-driven animations; never when reduced motion is on.", "default": false } ], "blocks": [ { "type": "store", "name": "Store", "settings": [ { "type": "text", "id": "name", "label": "Store name", "default": "Store name" }, { "type": "text", "id": "city", "label": "City", "info": "Used by the city filter.", "default": "City" }, { "type": "textarea", "id": "address", "label": "Address", "info": "One line per address line." }, { "type": "richtext", "id": "hours", "label": "Opening hours", "default": "

Mon–Sat 10:00–18:00

" }, { "type": "text", "id": "phone", "label": "Phone" }, { "type": "text", "id": "email", "label": "Email" }, { "type": "text", "id": "note", "label": "Note", "info": "Optional, for example parking or step-free access." }, { "type": "url", "id": "map_url", "label": "Map link", "info": "Optional. Paste a share link from your maps app to use it instead." }, { "type": "image_picker", "id": "image", "label": "Image" }, { "type": "image_picker", "id": "image_mobile", "label": "Mobile image", "info": "Optional. Below 750px." } ] } ], "presets": [ { "name": "Lib · Store locator", "blocks": [{ "type": "store" }, { "type": "store" }] } ] } {% endschema %}