The shop knows when it is open, and says so — to visitors and to Google

Three things a bakery's site should do that this one couldn't, all from the same fact.

THE HOURS WERE MARKUP, so the only thing the site could do with them was print them. A visitor at half
past two on a Sunday had to work out for themselves that the shop was shut. They are data now (Shop.WEEK),
and `Hours` reads them two ways: as the list the footer and the contact page print — consecutive days with
the same times collapsed into one line, the way a sign does it, instead of three hand-written rows — and
as an answer. The header now says "Open until 2:00pm", or "Closed · opens Tuesday at 7:00am", worked out
on the server in the shop's own timezone. Right now, on a Sunday evening, it says the latter.

THE SHOP IS NOW DESCRIBED TO A SEARCH ENGINE: schema.org Bakery in the head, with the address, the phone
number and those same opening times. For a shop whose customers find it by searching its town, that is the
difference between a blue link and a listing that shows "Open ⋅ closes 2pm" — and it cannot drift from
what the page says, because it is built from the same record. Serialised with Jackson rather than
string-built, so the day somebody interpolates a name into it, it is not an injection. (Jackson 3 —
`tools.jackson` — which is what Boot 4 ships; the fasterxml import does not compile.)

A SHARED LINK NOW HAS A PICTURE. og:image, og:image:alt and a large summary card: it was a grey box with a
title, which is what every link to this site has looked like in a message.

AND IT PRINTS. The catering page is a price list and a bakery prints price lists — Ctrl-P now gives the
prices on paper rather than a screenshot of a website: no chrome, no sage bands eating a cartridge, cards
that don't split across a page break, and the buttons that only exist to be clicked marked `no-print`.

Five new tests, all of them free of Spring, because this is a calculation over a constant: the week
collapses as a sign would write it, the minute of opening counts as open and the minute of closing does
not, and a Saturday afternoon is told when Tuesday starts.

Also swept the last one-off styles the earlier pass missed — max-w-3xl/4xl, an inline letter-spacing the
.h2 class already carries, and the mobile menu's link string.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
2026-07-26 19:27:41 -05:00
co-authored by Claude Opus 5
parent 9e80590687
commit a657600078
13 changed files with 474 additions and 41 deletions
@@ -2,40 +2,40 @@
<html xmlns:th="http://www.thymeleaf.org">
<body>
<!--/*
The opening hours and the address, in one place.
The opening times and the address, printed from the shop's own record rather than from markup.
They were on the homepage and in the footer already, and the moment the contact page wanted them too
that became three copies of the one fact a customer most often comes here for. A shop that changes its
Saturday hours should change them once.
They were three hand-written lines repeated in three templates. Now `Hours` collapses the week into the
lines a sign would show — consecutive days that keep the same times become one row — and the same data
answers "are they open right now?" in the header and describes the shop to a search engine. A shop that
changes its Saturday hours changes them in Shop.java, once.
Both fragments take the classes that vary by where they sit — cream text in the footer, sage on white
in a card — so the content is shared without the styling being averaged into something that suits
neither.
Both fragments take the classes that vary by where they sit — cream in the footer, sage on white in a
card — so the content is shared without the styling being averaged into something that suits neither.
*/-->
<ul th:fragment="hours(rowClass)" class="space-y-3">
<li th:class="'flex justify-between gap-4 ' + ${rowClass}">
<span>Tuesday &ndash; Friday</span>
<span class="font-medium whitespace-nowrap">7:00am &ndash; 2:00pm</span>
</li>
<li th:class="'flex justify-between gap-4 ' + ${rowClass}">
<span>Saturday</span>
<span class="font-medium whitespace-nowrap">7:00am &ndash; 12:00pm</span>
</li>
<li th:class="'flex justify-between gap-4 ' + ${rowClass}">
<span>Sunday &ndash; Monday</span>
<span class="font-medium">Closed</span>
<li th:each="span : ${hours}" th:class="'flex justify-between gap-4 ' + ${rowClass}">
<span th:text="${span.days}">Tuesday Friday</span>
<span class="font-medium whitespace-nowrap" th:text="${span.hours}">7:00am 2:00pm</span>
</li>
</ul>
<address th:fragment="address(linkClass)" class="not-italic space-y-3">
<p>215 E Main Street<br>Princeville, IL 61559</p>
<p th:text="${shopStreet}">215 E Main Street</p>
<p th:text="${shopTown}">Princeville, IL 61559</p>
<p>
<a href="tel:+13097010660" th:class="${linkClass}">(309) 701-0660</a>
<a th:href="|tel:${shopPhone}|" th:class="${linkClass}" th:text="${shopPhoneSpoken}">(309) 701-0660</a>
</p>
<p class="break-words">
<a href="mailto:[email protected]" th:class="${linkClass}">[email protected]</a>
<a th:href="|mailto:${shopEmail}|" th:class="${linkClass}" th:text="${shopEmail}">[email protected]</a>
</p>
</address>
<!--/* "Open until 2:00pm", or when it opens next. The one thing a visitor at 2:30 on a Sunday wants. */-->
<span th:fragment="open-now(dotClass, textClass)"
th:class="'inline-flex items-center gap-2 ' + ${textClass}">
<span th:class="'h-2 w-2 rounded-full shrink-0 ' + ${dotClass}" aria-hidden="true"></span>
<span th:text="${openNow.summary}">Open until 2:00pm</span>
</span>
</body>
</html>