This repository has been archived on 2026-09-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
itsthevine/src/main/resources/templates/fragments/header.html
T
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

59 lines
3.0 KiB
HTML

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<!--/*
The sticky bar, and the mobile menu.
The menu is a <details> element. That is not a trick to avoid writing JavaScript for its own sake —
the React version needed an effect to close the panel on navigation, another to close it on Escape,
another to stop the page behind it scrolling, and a third to unmount it (a panel parked off-screen
still extends the scrollable area, which is how you used to be able to scroll sideways and find the
menu). A <details> closes on Escape by itself, and navigation is a new document, so it cannot survive
it. Nothing to remember, nothing to clean up.
The panel is `absolute` and starts below the bar (`top-full`), so the logo and the toggle stay put and
the panel needs no second copy of either. It must not be `fixed`: the header carries `backdrop-blur`,
and a backdrop-filter makes an element a containing block for fixed descendants — a fixed panel in
here would resolve against the 80px header box and get clipped to a sliver.
*/-->
<header th:fragment="header" class="sticky top-0 z-40 bg-bakery-50/90 backdrop-blur border-b border-bakery-200">
<div class="container mx-auto px-4">
<div class="flex items-center justify-between gap-2 h-20 md:h-24">
<div th:replace="~{fragments/lockup :: small('text-bakery-700')}"></div>
<nav class="hidden md:flex items-center gap-8">
<th:block th:replace="~{:: links('label text-bakery-700 hover:text-bakery-900 transition-colors')}"></th:block>
</nav>
<details class="group md:hidden shrink-0">
<summary class="p-2 cursor-pointer list-none" aria-label="Menu">
<svg class="h-6 w-6 text-bakery-700 group-open:hidden" fill="none" stroke-linecap="round"
stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path d="M4 6h16M4 12h16M4 18h16" />
</svg>
<svg class="h-6 w-6 text-bakery-700 hidden group-open:block" fill="none" stroke-linecap="round"
stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path d="M6 18L18 6M6 6l12 12" />
</svg>
</summary>
<div class="absolute inset-x-0 top-full z-30 h-[calc(100dvh-5rem)] bg-bakery-50">
<nav class="container mx-auto px-4 flex flex-col">
<th:block th:replace="~{:: links('text-bakery-800 hover:text-bakery-600 transition py-4 text-lg border-b border-bakery-100')}"></th:block>
</nav>
</div>
</details>
</div>
</div>
</header>
<!--/* One list of links, worn two ways — so the phone and the desktop can't end up offering different
pages. */-->
<th:block th:fragment="links(itemClass)">
<a href="/products" th:class="${itemClass}">Our Products</a>
<a href="/catering" th:class="${itemClass}">Catering</a>
<a href="/history" th:class="${itemClass}">Our Story</a>
<a href="/contact" th:class="${itemClass}">Contact</a>
</th:block>
</body>
</html>