Article card
A blog post card with cover image, category, date and reading time.
<div class="grid">
<article class="post">
<a class="cover" href="/journal/linen-care/">
<img src="https://placehold.co/800x450/e8ecf4/8593ab?text=Linen+care" alt="">
</a>
<div class="body">
<a class="tag" href="/journal/care/">Care</a>
<h3><a href="/journal/linen-care/">How to wash linen without ruining it</a></h3>
<p class="excerpt">Linen softens with every wash, as long as you keep it away from the two things that
destroy it. Neither is the washing machine.</p>
<footer class="meta">
<img class="avatar" src="https://placehold.co/64x64/3b4fe4/ffffff?text=RE" alt="">
<span class="who">Ruth Ellery</span>
<span class="dot">·</span>
<time datetime="2026-04-02">2 April</time>
<span class="dot">·</span>
<span>5 min read</span>
</footer>
</div>
</article>
<article class="post">
<a class="cover" href="/journal/wool-summer/">
<img src="https://placehold.co/800x450/e6efe9/7ba88f?text=Wool+in+summer" alt="">
</a>
<div class="body">
<a class="tag" href="/journal/cloth/">Cloth</a>
<h3><a href="/journal/wool-summer/">Wool is a summer fabric, actually</a></h3>
<p class="excerpt">Everything you were told about wool being for winter comes from one specific kind of
wool, and it is not the one worth wearing in July.</p>
<footer class="meta">
<img class="avatar" src="https://placehold.co/64x64/1f8a5b/ffffff?text=JM" alt="">
<span class="who">Jon Maher</span>
<span class="dot">·</span>
<time datetime="2026-03-18">18 March</time>
<span class="dot">·</span>
<span>7 min read</span>
</footer>
</div>
</article>
</div>* { box-sizing: border-box; margin: 0; }
body { font: 16px/1.6 system-ui, -apple-system, sans-serif; background: #f6f8fb; padding: 24px; }
.grid {
display: grid;
gap: 20px;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.post {
display: flex;
flex-direction: column;
background: #fff;
border: 1px solid #e2e7ef;
border-radius: 14px;
overflow: hidden;
}
.cover { display: block; background: #eef1f6; }
.cover img {
display: block;
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
transition: transform .3s ease;
}
.post:hover .cover img { transform: scale(1.03); }
.body { padding: 18px 20px 20px; display: flex; flex-direction: column; flex: 1; }
.tag {
align-self: flex-start;
font-size: .74rem;
font-weight: 500;
letter-spacing: .01em;
padding: 3px 10px;
border-radius: 999px;
background: #eef0fe;
color: #2a3abf;
text-decoration: none;
}
.tag:hover { background: #dfe3fd; }
.body h3 { margin-top: 12px; font-size: 1.15rem; line-height: 1.32; letter-spacing: -0.01em; }
.body h3 a { color: #16202e; text-decoration: none; }
.body h3 a:hover { text-decoration: underline; text-underline-offset: 3px; }
.excerpt { margin-top: 9px; color: #5b6b83; font-size: .93rem; }
.meta {
margin-top: auto;
padding-top: 16px;
display: flex;
align-items: center;
gap: 7px;
font-size: .81rem;
color: #8593ab;
}
.avatar { width: 26px; height: 26px; border-radius: 50%; margin-right: 2px; }
.who { color: #16202e; font-weight: 500; }
.dot { color: #c9d3e2; }
@media (max-width: 480px) {
/* the byline wraps badly at small sizes; drop the reading time */
.meta > :nth-last-child(-n+2) { display: none; }
}
@media (prefers-reduced-motion: reduce) {
.cover img { transition: none; }
.post:hover .cover img { transform: none; }
}How it works
The cover image alt is empty, on purpose. The headline immediately below says the same thing, and the image is a link to the same place. An alt repeating the headline means a screen reader user hears the title twice in a row. alt="" marks it decorative and it is skipped. The same reasoning applies to the avatar — the author's name is written next to it.
margin-top: auto on the byline pushes it to the bottom of the card regardless of how long the headline and excerpt are, so bylines line up across a row. This is the flexbox equivalent of the sticky-footer trick.
The zoom is on the image, not the card. overflow: hidden on the card plus transform: scale() on the image gives the effect without the card itself moving, which would nudge its neighbours.
A real time element. <time datetime="2026-04-02"> carries the machine-readable date while showing whatever human format you like. Search engines and feed readers use the attribute.
Accessibility notes
The whole card is not a link. Only the image, the tag and the headline are. Wrapping an entire card in an anchor means a screen reader reads the full text as one enormous link name, and it makes the category tag unreachable because you cannot nest links.
If you want the whole card clickable, the usual technique is a pseudo-element stretched over the card from the headline link, with the tag raised above it using position: relative.
Making it yours
auto-fit here rather than auto-fill: with two cards, auto-fit stretches them to fill the row while auto-fill would leave a gap where a third would go. For a feed with an unpredictable number of posts, auto-fit is usually what you want.
For a featured post, give the first card grid-column: 1 / -1 and switch its body to a two-column layout.
Add Article structured data to the post pages themselves — not to the cards, which are only links.
Related templates
Product card
An e-commerce card with image, price, rating and an add-to-basket action.
HTMLCSSTailwindPricing table
Three tiers with a highlighted plan and a monthly-to-yearly toggle.
HTMLCSSJavaScriptProfile card
A team or author card with avatar, role, bio and social links.
HTMLCSSHoly grail layout
Header, footer, two sidebars and a main column — in nine lines of grid.
HTMLCSSCheck your version
Once you have edited this, the HTML validator will catch any tag you left unclosed, and the formatter will tidy the indentation. Both run in your browser.