Content that fills itself in
A blog index, start to finish
Copy page
A real one, start to finish. Three pieces: the articles, a card, and the loop.
The shape
Your articles live under one address, and the page that lists them is an ordinary page — see Articles.
Blog /blog an ordinary page, with the loop on it
A year of sourdough /articles/a-year-of-sourdough
Opening on Sundays /articles/opening-on-sundays
The Blog page does not own them. Rename it, move it, add a second one, and the articles stay where they are.
The loop
On the /blog page:
{% assign posts = collections.articles | sort: "date" | reverse %}
{% for post in posts limit: 6 %}
<article>
<a href="{{ post.url }}">
<h2>{{ post.title }}</h2>
<p>{{ post.excerpt }}</p>
</a>
</article>
{% else %}
<p>No articles yet.</p>
{% endfor %}
Read it in order:
assignbuilds the list: every article on the site, sorted by date, newest first. Without the sort the list comes in the order you arrange the articles in, which is the right thing for a portfolio and the wrong one for a blog.forwalks it, at most six.elseis what shows when the list is empty — worth writing, because a brand new blog is empty and a bare heading with nothing under it looks broken.
Assign first, always
The filters have to go on the assign line, not the for line. This is the one
trap in the whole language and it is covered on its own page:
Sorting and filtering.
The card, as a block
Six articles means six copies of the card design in one page. Move the card into a block — a piece of markup kept as its own document — and the loop renders that instead:
{% assign posts = collections.articles | sort: "date" | reverse %}
{% for post in posts limit: 6 %}
{% render "aB3x9kQ2mN01", post: post %}
{% endfor %}
post: post is the part that matters. It hands the block the article it is
currently on, so the block can be written once and used by the blog index and a
homepage strip without becoming two near-identical copies.
Inside the block, the thing handed in is called post because that is what the
call named it:
<article>
<a href="{{ post.url }}">
{% if post.image %}
<img src="{{ post.image.src }}" alt="{{ post.image.alt }}">
{% endif %}
<h3>{{ post.title }}</h3>
<p>{{ post.excerpt }}</p>
</a>
</article>
The {% if post.image %} guard is not optional politeness. An article with no
picture has image set to nothing at all, and without the guard you get an
empty <img> on every card that lacks one.
The block works for pages too. It reads url, title, excerpt and image,
which both kinds have — so the same card can list a section’s child pages
without being written twice.
You do not have to write any of this
Ask for it. “Make the blog index list the articles automatically, newest first, six at a time” is a request the AI handles, and it writes the markup above.
This page exists so you can read what it wrote, change it, and know what is possible to ask for.