Blog Post Grid

Section

Grid of recent blog posts from a selected blog, with placeholder fallback and ItemList JSON-LD.

ImageSeo RichDynamic
Edit
Copied 0 times
Preview
Accent
Section code
<section class="sl-blog-grid" style="padding:clamp(32px,6vw,64px) 1rem;color:inherit;font-family:inherit;">
  <div style="max-width:1200px;margin:0 auto;">
    <h2 style="margin:0 0 1.5rem;font-size:clamp(1.5rem,3vw,2rem);text-align:center;">{{ section.settings.heading }}</h2>
    {%- assign blog = blogs[section.settings.blog] -%}
    {%- assign limit = section.settings.number | default: 3 -%}
    <div style="display:grid;gap:1.5rem;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));">
      {%- if blog.articles.size > 0 -%}
        {%- for article in blog.articles limit: limit -%}
          <article style="border:1px solid currentColor;border-radius:.5rem;overflow:hidden;display:flex;flex-direction:column;">
            {%- if article.image -%}
              <a href="{{ article.url }}" style="display:block;">
                <img src="{{ article.image | image_url: width: 600 }}" alt="{{ article.image.alt | default: article.title }}" loading="lazy" style="width:100%;height:auto;aspect-ratio:16/9;object-fit:cover;display:block;" />
              </a>
            {%- endif -%}
            <div style="padding:1rem;display:flex;flex-direction:column;gap:.5rem;flex:1;">
              <time datetime="{{ article.published_at | date: '%Y-%m-%d' }}" style="font-size:.8rem;opacity:.7;">{{ article.published_at | date: '%B %-d, %Y' }}</time>
              <h3 style="margin:0;font-size:1.15rem;"><a href="{{ article.url }}" style="color:inherit;text-decoration:none;">{{ article.title }}</a></h3>
              <p style="margin:0;opacity:.85;line-height:1.5;font-size:.95rem;">{{ article.excerpt_or_content | strip_html | truncate: 140 }}</p>
              <a href="{{ article.url }}" style="margin-top:auto;color:var(--sl-accent,#0a5cff);text-decoration:none;font-weight:600;font-size:.9rem;">{{ section.settings.read_more | default: 'Read more' }} &rarr;</a>
            </div>
          </article>
        {%- endfor -%}
      {%- else -%}
        {%- for i in (1..3) -%}
          <article style="border:1px solid currentColor;border-radius:.5rem;overflow:hidden;display:flex;flex-direction:column;">
            <img src="https://picsum.photos/seed/blog{{ i }}/600/340" alt="{{ section.settings.placeholder_alt | default: 'Article placeholder' }}" loading="lazy" style="width:100%;aspect-ratio:16/9;object-fit:cover;display:block;" />
            <div style="padding:1rem;display:flex;flex-direction:column;gap:.5rem;flex:1;">
              <time datetime="2026-01-0{{ i }}" style="font-size:.8rem;opacity:.7;">January {{ i }}, 2026</time>
              <h3 style="margin:0;font-size:1.15rem;">Sample article {{ i }}</h3>
              <p style="margin:0;opacity:.85;line-height:1.5;font-size:.95rem;">A short excerpt of the article goes here to entice readers to click through.</p>
              <a href="#" style="margin-top:auto;color:var(--sl-accent,#0a5cff);text-decoration:none;font-weight:600;font-size:.9rem;">Read more &rarr;</a>
            </div>
          </article>
        {%- endfor -%}
      {%- endif -%}
    </div>
  </div>
  {%- if blog.articles.size > 0 -%}
    <script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "ItemList",
      "itemListElement": [
        {%- for article in blog.articles limit: limit -%}
        { "@type": "ListItem", "position": {{ forloop.index }}, "item": { "@type": "Article", "headline": {{ article.title | json }}, "datePublished": "{{ article.published_at | date: '%Y-%m-%d' }}", "url": "{{ shop.url }}{{ article.url }}" } }{%- unless forloop.last -%},{%- endunless -%}
        {%- endfor -%}
      ]
    }
    </script>
  {%- endif -%}
</section>
{% schema %}
{
  "name": "Blog Post Grid",
  "settings": [
    { "type": "text", "id": "heading", "label": "Heading", "default": "From the journal" },
    { "type": "blog", "id": "blog", "label": "Blog" },
    { "type": "range", "id": "number", "label": "Posts to show", "min": 3, "max": 6, "step": 1, "default": 3 },
    { "type": "text", "id": "read_more", "label": "Read more text", "default": "Read more" },
    { "type": "text", "id": "placeholder_alt", "label": "Alt text (placeholder)", "default": "Article placeholder" }
  ],
  "presets": [{ "name": "Blog Post Grid" }]
}
{% endschema %}

Internal notesTeam only

Freeform markdown notes visible only to signed-in team members. Separate from the public description.