Sections and cards: three grids, two quiet panels and five headings become three names
build-and-publish / build (pull_request) Successful in 2m4s

Counted first, as with the last sweep. A grid of cards was written three times with three different sets
of breakpoints and gaps, so cards on different pages lined up differently for no reason anyone chose. A
tinted aside card existed twice, hand-rolled, with different borders. A section heading existed five ways:
centred with one margin, centred with another, left-aligned with a blurb, left without.

Now `card-grid`, `panel-quiet` and a `section-head` fragment (with a cream-on-sage twin for the dark
bands). The catering small print and the contact page's "ordering for a crowd?" card are the same object
now, which is what they always were.

TWO THINGS THE SCREENSHOTS DECIDED, not taste:

Three prices go three-up from md, while the shared card grid waits for lg. A grid of photos is fine
two-wide; three prices are a comparison, and 2 + 1 on a laptop strands the third size on its own row.

And the per-table column count is entirely in one classappend rather than overriding `card-grid`, because
Tailwind emits grid-cols-1, -2, -3 in that order — so a `grid-cols-3` from the shared class beats a
`grid-cols-2` written beside it in the markup, whatever the markup says. Two classes for one property,
winner decided by the stylesheet: the same trap that stopped `hidden` from hiding the open-now line, and I
had already written it before catching it. A one-size and a two-size table were both wrong; proved the fix
by building a real two-size table through the admin and looking at it, then deleting it again.

61 tests.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
2026-07-26 21:15:37 -05:00
co-authored by Claude Opus 5
parent 057deec47c
commit 0300e5a54f
6 changed files with 59 additions and 16 deletions
+15 -9
View File
@@ -34,18 +34,24 @@
alignment the aggregate guarantees.
*/-->
<section th:each="table : ${menu.packages}">
<header class="measure text-center">
<h2 class="h2 text-bakery-900" th:text="${table.name}">Office boxes</h2>
<p th:if="${table.blurb}" class="mt-3 text-bakery-700 leading-relaxed" th:text="${table.blurb}">
Mini pastries for a morning meeting.
</p>
</header>
<div th:replace="~{fragments/opening :: section-head(${table.name}, ${table.blurb})}"></div>
<!--/* Three sizes is the common case; a table with one or two shouldn't stretch to fill the row. */-->
<div class="mt-8 grid gap-6 md:gap-8"
<!--/*
Three sizes go three-up from md, not lg like the shared `card-grid`: a grid of PHOTOS is fine
two-wide, but three prices are a comparison, and landing 2 + 1 on a laptop strands the third
size on its own row where it reads as an afterthought.
The column count is entirely in the classappend rather than overriding `card-grid`, and that
is not a style preference: Tailwind emits grid-cols-1, -2, -3 in that order, so `grid-cols-3`
from the shared class would beat a `grid-cols-2` written next to it in the markup. Two classes
for one property, with the winner decided by the stylesheet's order — the same trap that made
`hidden` fail to hide the open-now line. Never both.
*/-->
<div class="grid gap-6 md:gap-8 mt-8"
th:classappend="${#lists.size(table.tiers) == 1} ? 'max-w-sm mx-auto'
: (${#lists.size(table.tiers) == 2} ? 'sm:grid-cols-2 max-w-3xl mx-auto'
: 'sm:grid-cols-2 lg:grid-cols-3')">
: 'sm:grid-cols-2 md:grid-cols-3')">
<article th:each="tier, t : ${table.tiers}"
class="relative flex flex-col overflow-hidden panel">
<!--/* The wordmark's branch, at watermark weight. */-->
@@ -89,7 +95,7 @@
three cards led to the same place, since an enquiry is about the table rather than the size,
and the label read "Ask about the 1520 people" on the tables whose sizes are headcounts.
*/-->
<div class="mt-8 measure rounded-panel border border-bakery-200/70 bg-bakery-50/60 px-6 py-6 text-center">
<div class="panel-quiet mt-8 measure px-6 py-6 text-center">
<div th:if="${!#lists.isEmpty(table.notes)}">
<h3 class="label text-bakery-500">Good to know</h3>
<ul class="mt-3 space-y-2 text-sm text-bakery-700">
+1 -1
View File
@@ -89,7 +89,7 @@
<div class="mt-4 text-bakery-800"
th:replace="~{fragments/visit :: address('link-plain')}"></div>
</div>
<div class="rounded-panel border border-bakery-200 p-6 md:p-8">
<div class="panel-quiet p-6 md:p-8">
<h2 class="h4 text-bakery-900">Ordering for a crowd?</h2>
<p class="mt-2 text-sm text-bakery-700 leading-relaxed">
Goodie boxes, party packages and wedding desserts have their own page, with sizes and prices.
@@ -36,6 +36,24 @@
<p class="lede mt-4 measure text-bakery-800" th:text="${lede}">One line about the page.</p>
</header>
<!--/*
How a section begins, as opposed to a page: the title, centred, and optionally a line under it. Pass an
empty string for no line.
It was five different pieces of markup — centred with one margin, centred with another, left-aligned
with a blurb — for what is one idea repeated down every page.
*/-->
<div th:fragment="section-head(title, blurb)" class="measure text-center">
<h2 class="h2 text-bakery-900" th:text="${title}">What people come in for</h2>
<p th:if="${!#strings.isEmpty(blurb)}" class="mt-3 text-bakery-700 leading-relaxed" th:text="${blurb}">A line.</p>
</div>
<!--/* The same, on the sage bands, where the type is cream. */-->
<div th:fragment="section-head-dark(title, blurb)" class="measure text-center">
<h2 class="h2" th:text="${title}">Before you order</h2>
<p th:if="${!#strings.isEmpty(blurb)}" class="mt-3 text-bakery-100 leading-relaxed" th:text="${blurb}">A line.</p>
</div>
<!--/* The branch from the wordmark, between one part of a page and the next. */-->
<div th:fragment="divider" class="flex justify-center" aria-hidden="true">
<span class="mark mark-r w-12 h-12 text-bakery-400"></span>
+5 -5
View File
@@ -45,8 +45,8 @@
<!--/* What people come in for */-->
<section class="section bg-bakery-50">
<div class="container">
<h2 class="h2 text-center text-bakery-900 mb-12">What people come in for</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6 md:gap-8">
<div class="mb-12" th:replace="~{fragments/opening :: section-head('What people come in for', '')}"></div>
<div class="card-grid">
<div th:each="item : ${ {'Cinnamon Rolls', 'Sugar Cookies', 'Cakes'} }"
class="panel overflow-hidden text-center">
<!--/* The filenames carry the spaces in these names; SitePhotos encodes them. */-->
@@ -88,8 +88,8 @@
<section class="section bg-bakery-800 text-white">
<div class="container">
<div class="measure text-center">
<h2 class="h2 mb-8">Our story</h2>
<p class="lede text-bakery-100 mb-8">
<h2 class="h2">Our story</h2>
<p class="lede mt-4 text-bakery-100 mb-8">
Morissa Bennett opened The Vine in 2024. We bake in our own kitchen on Main Street:
cinnamon rolls, cookies, custom cakes, sandwiches, paninis, and coffee.
</p>
@@ -103,7 +103,7 @@
<!--/* Visit us */-->
<section id="visit" class="section bg-bakery-50 scroll-mt-24">
<div class="container">
<h2 class="h2 text-center text-bakery-900 mb-12">Visit us</h2>
<div class="mb-12" th:replace="~{fragments/opening :: section-head('Visit us', '')}"></div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-12 measure-wide">
<div class="panel p-6 md:p-8">
<h2 class="h3 text-bakery-900 mb-6">Our hours</h2>
+1 -1
View File
@@ -26,7 +26,7 @@
<!--/* Deliberately unanimated, as before: filtering used to run a layout reflow plus an enter/exit
fade on every card, which on a 40-card grid reads as the page lurching rather than
responding. The only motion left is the shadow on hover. */-->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<div class="card-grid">
<div th:each="product : ${products}"
class="panel overflow-hidden">
<div class="relative w-full overflow-hidden">
+19
View File
@@ -147,6 +147,25 @@
@apply bg-bakery-50/70 border-b border-bakery-200/70;
}
/*
* A card that is holding an aside rather than the thing you came for: the small print under a price
* table, a pointer to another page. Tinted and bordered, no shadow, so it sits ON the page instead of
* above it and does not compete with the cards it follows. There were two hand-rolled versions of this
* before it had a name, and they had different borders.
*/
@utility panel-quiet {
@apply rounded-panel border border-bakery-200/70 bg-bakery-50/60;
}
/*
* A grid of cards. It was written three times with three different sets of breakpoints and gaps — the
* homepage went to three columns at md, the catering tables at lg, the catalogue at lg with a wider gap —
* so cards on different pages lined up differently for no reason anyone chose.
*/
@utility card-grid {
@apply grid gap-6 md:gap-8 sm:grid-cols-2 lg:grid-cols-3;
}
/*
* The branch, laid into a corner at the weight of a watermark. It is the one mark that is unmistakably
* this shop, and at this size and colour it is texture rather than decoration.