Commit Graph
8 Commits
Author SHA1 Message Date
austinandClaude Opus 5 0300e5a54f 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]>
2026-07-26 21:15:37 -05:00
austinandClaude Opus 5 057deec47c Three bugs and a lighter page: 367 KB of fonts became 57
THE BUGS.

Tapping any field zoomed the page in on iOS, because Safari does that to anything under 16px and `field`
was 14px. Every enquiry typed on a phone began by throwing the layout sideways. 16px on phones, the denser
size back at sm where no browser does it.

The focus ring vanished on the sage bands: one colour for the whole site, and sage-500 on sage-800 is
about 2.5:1 — under the 3:1 a focus indicator needs. It went missing exactly where the page's main calls
to action live. Cream on those sections now.

THE FONTS. AdBhashitha shipped 1605 glyphs to set 187 codepoints, as .woff, unpreloaded: 306 KB for the
face every heading uses, arriving after first paint so each page opened in a fallback serif and then
reflowed. Subset to Latin and the punctuation it actually has, every layout feature kept, compressed to
woff2: **306 KB → 8 KB**, and a pixel diff of the catering page before and after is exactly zero. The
script wordmark is only recompressed, never subset — its swashes come from alternates and the logo is the
one thing that must not change shape. All three faces are preloaded now and together weigh less than the
display face did alone.

THE REST OF THE LIST. The products filter wrapped to three rows on a phone with "PASTRIES" stranded on the
last; it is a scrolling snap row below sm, wrapping and centred above it, and the negative margin lets a
half-chip show at the edge so it is obviously scrollable. Chips are 44px tall and the gallery arrows 44px
wide — both were under the thumb-sized minimum. The contact form has a honeypot: a real field with a
plausible name, off-page rather than display:none, skipped by the tab key. A filled one gets the same
thank-you a person gets, because telling a bot it failed only teaches it to try again — and nothing is
recorded or sent, which the test proves against the enquiry table.

And the catering small print, which was loose text floating between two rows of cards, is a soft panel
belonging to the table above it.

61 tests. Verified at 390 and 900.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-26 20:53:06 -05:00
austinandClaude Opus 5 e2b9af56f1 Say where you are, let the keyboard past, and give the story pictures
THE NAV NEVER SAID WHICH PAGE YOU WERE ON. A four-page site gets away with that right up until somebody
clicks Catering, lands on a page of prices, and cannot tell whether the click worked. The current page now
carries aria-current for a screen reader and a sage rule under the word for everyone else — a rule rather
than a darker colour, because the nav is already sage on cream and darkening it reads as a hover. The list
is generated from one array now instead of four hand-written anchors, which is what makes the marking
possible at all.

THE KEYBOARD HAD NO WAY PAST THE NAV. Every page begins with a skip link — off-screen until it has focus,
then a real button in the corner, which is the half of that pattern everyone forgets.

THE STORY WAS FOUR BLOCKS OF TEXT. It has photographs between the sections now, captioned in small caps,
and an opening paragraph set with a drop cap in the display face. Once, on that page: a drop cap on every
page would be a costume rather than a voice.

60 tests. Two new ones, both on things that fail silently rather than loudly: the active link (a wrong
model attribute would mark nothing, which looks exactly like a page with no active link) and the skip
link's target existing.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-26 19:33:06 -05:00
austinandClaude Opus 5 9e80590687 Everything on the shared names — the copy-paste is gone
The ported markup was written a page at a time, so the same thing was said several ways. Counted before
touching anything: nine variants of the page gutter, seven max-widths, six link styles, two definitions of
an input, and one stray shadow.

Now: `container` (which already centres and pads), `measure`/`measure-wide`, `link`/`link-plain`/
`link-on-dark`, one `field`, `photo`, `page-head`. Nothing in a template writes `bg-white rounded-…
shadow-…` or `underline underline-offset-4` any more — 119 uses of `panel`, 82 of `label`, 52 of
`panel-lift`, 17 of `link`.

TWO REAL FIXES FELL OUT OF IT, not just tidying:

- Every section said `container mx-auto px-4`: `mx-auto` twice, and a px-4 that beat the responsive
  padding inside `container`. The gutter was 16px at every width, including on a 27-inch screen. Removing
  the copy-paste restores sm:px-6 lg:px-8, which is most of why the pages now breathe.
- The public form and the admin had grown two different inputs — cream vs white, ring-2 vs ring-1, py-2.5
  vs py-2, and different text sizes. There is one `field` now, and the same focus ring as everything else.

The admin uses the same names where they are the same thing (`link`, `field`, `h4`, `--radius-*`) and
keeps its own denser buttons, because it is a tool and not a shop front.

53 tests green. Every class used across all nine pages resolves in the compiled stylesheet — that check is
what makes a sweep this size safe to do with a script.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-26 18:43:40 -05:00
austinandClaude Opus 5 c4cd19e175 Corners: 12px, and named once
24px (Tailwind's rounded-3xl) was the roundest thing on the page and read as a phone app. A printed card
has a corner you can see.

Two tokens rather than a find-and-replace, so this is answered once and not per template:
`--radius-panel` (12px) for anything with an edge — cards, photos mounted on them, the admin's boxes — and
`--radius-field` (8px) for inputs, which were three different radii across the public form and the admin.
Buttons stay fully round: the pill is the brand's button shape and always was.

53 tests green, every class on all seven checked pages still resolves.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-26 18:32:29 -05:00
austinandClaude Opus 5 5448cbded1 Make a card look like it came from the shop, not from a component library
The cards were white boxes with a grey drop shadow and no edge — the default card of every web app,
sitting on a page that is otherwise cream, sage and a hand-drawn vine. Four changes, all of them things
the brand already owns:

- A WARM HAIRLINE. A card now has an edge, so it reads as stock lying on a counter rather than as a panel
  floating over a screen.
- A SHADOW IN SAGE, not black — rgba(35,43,30,…), the darkest colour in the palette. Grey shadows are what
  made them feel borrowed.
- A CREAM HEAD (`panel-head`). On a catering card the size, price and "About 6–8 people" sit on the tint
  with the contents below, the way a printed menu separates what it costs from what you get. Product cards
  use the same band for their caption, so the photo reads as mounted on the card instead of being it.
- THE BRANCH, at watermark weight, in the corner of every price card. It is the one mark that is
  unmistakably this shop, and it costs nothing: the mask was already there for the wordmark.

The lift is a lift now, two pixels, rather than the shadow just darkening.

TWO THINGS TAILWIND v4 MADE ME FIX ALONG THE WAY. `.mark`, `.mark-l`, `.mark-r` and `.no-scrollbar` were
plain classes in @layer components, and only a registered utility can be @applied by another rule — the
watermark builds on the branch mark, so they became @utility like everything else here. And rewriting that
file dropped the `summary { list-style: none }` reset, which would have put a disclosure triangle beside
the hamburger on every phone; caught it by grepping the compiled CSS rather than by luck.

53 tests green, every class on every page resolves, and the price cards are in the screenshot.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-26 18:29:05 -05:00
austinandClaude Opus 5 6b6ddcea42 A design pass: the type ladder from the logo, and one name per thing
TYPOGRAPHY, taken from the lockup and walked down to something you can read a paragraph in. "The Vine" in
LeJour Script over COFFEEHOUSE + BAKERY in AdBhashitha is the whole brand voice, so: the script stays in
the wordmark and nowhere else (it is unreadable at length); AdBhashitha carries h1–h4 and the price, and
keeps the wordmark's 0.01em letter-spacing so a heading sits on the same rhythm as the logo above it;
`eyebrow` and `label` are the hinge — sans, but letterspaced like the tagline — and then plain Raleway.
Sizes are declared once in type.css instead of being re-chosen per template, and none of the type classes
set a colour, because the same heading appears in sage on cream and in cream on sage.

BUTTONS AND SURFACES have names now. `panel` is the white box that was "bg-white rounded-3xl shadow-xs"
written out in nine places (which is how two of them end up with different corners); `panel-lift` is one
that reacts. `pill` is one shape in four colourways, which exist because this site puts buttons on cream
AND on sage and they cannot be the same colour on both. `chip`/`chip-on` are the products filter.

RHYTHM: `section` (py-16 md:py-24) and `band` (py-14 md:py-20, tighter because a coloured strip is already
doing the separating) replace nine hand-picked paddings.

FOCUS: one visible ring for everything reachable by keyboard, as an outline rather than a shadow so it
works on cream, on white and on the sage bands, at zero specificity so a utility can still win.

Z-INDEX: written down in base.css, all two of it — 40 the sticky header, 30 the mobile panel, which is a
child of the header and so can only ever sit under the bar. Everything else stacks in document order. The
point of the comment is that a third layer doesn't get invented at 9999.

DROPDOWNS: the admin's category selects were drawing the platform's own arrow in the platform's grey; they
get the site's chevron and room for it.

53 tests green. Every class used across all nine pages — five public, four admin — resolves in the
compiled stylesheet, so nothing here is styled by a name that doesn't exist.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-26 18:10:47 -05:00
austinandClaude Opus 5 8d54db7c30 Give the other pages somewhere to go, and move the stylesheets under src/main
MARKETING, not decoration — each of these was a page that stopped:

- The HOMEPAGE never mentioned catering. A customer found the page by reading the nav. It now has a
  section with a photo, what the three tables cover, and a button.
- PRODUCTS was a title and a grid. It gets a line saying what it is ("cakes and decorated cookies are
  made to order, so most of what follows started as somebody describing what they wanted") and a closing
  band with somewhere to go — an order, or the catering page.
- CONTACT was a form and nothing else. The things somebody on that page actually wants — when we're open,
  where we are, the phone number — were on the homepage and in the footer but not there. Now beside the
  form, with a card pointing at catering.
- OUR STORY got the shop's own front as a band and an ending, so the story leads somewhere.
- The FOOTER gained the opening hours, which is what people come to a bakery's site for and is now on
  every page.

CLEANED UP ON THE WAY. The hours and the address were about to exist in three places, so they are one
fragment (fragments/visit.html) that takes the classes that vary by where it sits — cream in the footer,
sage on white in a card. A shop that changes its Saturday hours should change them once.

STYLES MOVED to src/main/styles, with the rest of the app's source, and split by what each file is for:
theme.css (the fonts and the palette — names, nothing drawn), base.css (bare elements), components.css
(the site's own classes), admin.css (the admin's controls), and site.css as the entry that imports them in
cascade order and declares what Tailwind scans. It was one grab-bag file called tokens.css in a folder at
the repo root. node_modules has to stay beside them — Tailwind resolves `@import "tailwindcss"` by walking
up from the CSS file — so the whole npm project moved together, and the pom, .gitignore and README moved
with it.

53 tests green, `mvn package` builds the stylesheet from its new home, and every class used on all five
pages resolves in the compiled CSS (185/159/175/148/140, none missing).

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-26 17:56:00 -05:00