Archived
Photographs hold still, and every page opens the same way
HOVER IS OFF THE IMAGES. The product cards lifted, the homepage's three cards lifted and zoomed their
photo 5% on the way. None of those cards is a link, so the lift was promising a click that isn't there,
and a photograph of a cake does not benefit from moving. `panel-lift` is still defined for the day
something IS clickable; nothing on the public site uses it.
EVERY PAGE NOW OPENS THE SAME: a small-caps line, the title in the display face, one sentence saying what
this is. Two dresses for it — `photographed` for the pages that sell or tell (catering, the story), where a
blurred photo under the sage wash does what the homepage hero does; `plain` for the pages you arrive at
already knowing what you want, where a photograph would be in front of the thing you came for. It was four
pieces of markup that happened to look similar; it is one fragment with two variants. Products and Contact
gained the eyebrow they were missing ("From the counter", "Say hello").
THE HOMEPAGE'S ORDER was hero → what people come in for → story → catering → visit: two cream sections
butted together at the end, with the one block that sells something buried between them. Now hero →
what people come in for → catering (on the deeper cream, so two light sections still read as two) → story
→ visit. Dark, light, light-but-different, dark, light.
TWO BUGS THE SCREENSHOTS FOUND, both invisible at the width I had been checking:
- Between 1024 and about 1200 the wordmark, the nav gaps AND the open-now line all stepped up at once, so
the script overflowed its box and painted over the branch mark. They step up at xl now, separately.
- The open-now fragment hardcoded `inline-flex` while the header passed `hidden lg:…`, which is two rules
for one property — the winner decided by Tailwind's output order rather than by the markup, which is why
"hidden" wasn't hiding it. The caller owns display now.
Checked at 390, 800, 900, 1000, 1024, 1100, 1200, 1280 and 1440. 60 tests green.
This commit is contained in:
@@ -3,27 +3,11 @@
|
||||
<body>
|
||||
<div th:fragment="content">
|
||||
|
||||
<!--/*
|
||||
A short photographic band rather than the plain cream header the other pages use. This is the one page
|
||||
that is selling something, and the same treatment as the homepage hero — blurred photo, sage wash —
|
||||
carries the brand rather than inventing a new look for it.
|
||||
*/-->
|
||||
<section class="band relative flex items-center bg-bakery-900 text-white overflow-hidden">
|
||||
<img th:src="${photos.of('gallery/Cakes.webp')}" alt=""
|
||||
class="absolute inset-0 w-full h-full object-cover blur-[3px] scale-110">
|
||||
<div class="absolute inset-0 bg-bakery-900/80"></div>
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-bakery-900 via-bakery-800/60 to-bakery-900/80"></div>
|
||||
|
||||
<div class="relative container w-full min-w-0">
|
||||
<div class="measure text-center">
|
||||
<p class="eyebrow text-bakery-200">Order ahead</p>
|
||||
<h1 class="h1 mt-3 text-bakery-50">Goodie boxes & catering</h1>
|
||||
<p class="lede mt-5 text-bakery-100">
|
||||
Boxes for the office, packages for a party, and cakes and desserts for a wedding.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div th:replace="~{fragments/opening :: photographed(
|
||||
'Order ahead',
|
||||
'Goodie boxes & catering',
|
||||
'Boxes for the office, packages for a party, and cakes and desserts for a wedding.',
|
||||
'gallery/Cakes.webp')}"></div>
|
||||
|
||||
<div class="bg-bakery-50">
|
||||
<div class="container pt-14 pb-16 md:pt-16 md:pb-24">
|
||||
@@ -33,11 +17,7 @@
|
||||
mind, and we will bake to it.
|
||||
</p>
|
||||
|
||||
<!--/* The branch from the wordmark, as a divider. It is a mask over currentColor, so it takes the
|
||||
colour of the text around it — see .mark in tokens.css. */-->
|
||||
<div class="mt-10 flex justify-center" aria-hidden="true">
|
||||
<span class="mark mark-r w-12 h-12 text-bakery-400"></span>
|
||||
</div>
|
||||
<div class="mt-10" th:replace="~{fragments/opening :: divider}"></div>
|
||||
|
||||
<div class="mt-12 max-w-6xl mx-auto space-y-16 md:space-y-20">
|
||||
|
||||
@@ -67,7 +47,7 @@
|
||||
: (${#lists.size(table.tiers) == 2} ? 'sm:grid-cols-2 max-w-3xl mx-auto'
|
||||
: 'sm:grid-cols-2 lg:grid-cols-3')">
|
||||
<article th:each="tier, t : ${table.tiers}"
|
||||
class="relative flex flex-col overflow-hidden panel-lift">
|
||||
class="relative flex flex-col overflow-hidden panel">
|
||||
<!--/* The wordmark's branch, at watermark weight. */-->
|
||||
<span class="panel-mark -top-3 -right-4 w-24 h-24 rotate-12" aria-hidden="true"></span>
|
||||
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
<body>
|
||||
<div th:fragment="content" class="min-h-screen bg-bakery-50">
|
||||
|
||||
<header class="page-head">
|
||||
<h1 class="h1 text-bakery-900">Contact us</h1>
|
||||
<p class="mt-4 text-bakery-800 max-w-xl mx-auto leading-relaxed">
|
||||
Ask about a cake, a date, or an order for a crowd. We read everything that comes in.
|
||||
</p>
|
||||
</header>
|
||||
<div th:replace="~{fragments/opening :: plain(
|
||||
'Say hello',
|
||||
'Contact us',
|
||||
'Ask about a cake, a date, or an order for a crowd. We read everything that comes in.')}"></div>
|
||||
|
||||
<!--/*
|
||||
The form, and beside it the things somebody on this page is usually actually after: when we are open,
|
||||
|
||||
@@ -21,14 +21,19 @@
|
||||
<div class="flex items-center justify-between gap-2 h-20 md:h-24">
|
||||
<div th:replace="~{fragments/lockup :: small('text-bakery-700')}"></div>
|
||||
|
||||
<div class="hidden md:flex items-center gap-8">
|
||||
<nav class="flex items-center gap-8">
|
||||
<!--/*
|
||||
The status line is the first thing to go when the bar gets tight: at the width of a small laptop
|
||||
the wordmark and the nav were overlapping, because "Closed · opens Tuesday at 7:00am" is as wide as
|
||||
two nav items. It comes back at lg, and the mobile menu carries it at every width below md.
|
||||
*/-->
|
||||
<div class="hidden md:flex items-center gap-6 xl:gap-8">
|
||||
<nav class="flex items-center gap-6 xl:gap-8">
|
||||
<th:block th:replace="~{:: links('label text-bakery-700 hover:text-bakery-900 transition-colors')}"></th:block>
|
||||
</nav>
|
||||
<!--/* Worked out on the server from the same opening times the footer prints. */-->
|
||||
<p th:replace="~{fragments/visit :: open-now(
|
||||
${openNow.open} ? 'bg-bakery-500' : 'bg-bakery-300',
|
||||
'text-sm text-bakery-600 whitespace-nowrap')}"></p>
|
||||
'hidden xl:inline-flex text-sm text-bakery-600 whitespace-nowrap')}"></p>
|
||||
</div>
|
||||
|
||||
<details class="group md:hidden shrink-0">
|
||||
@@ -47,7 +52,7 @@
|
||||
<th:block th:replace="~{:: links('text-bakery-800 hover:text-bakery-600 transition-colors py-4 text-lg border-b border-bakery-100')}"></th:block>
|
||||
<p th:replace="~{fragments/visit :: open-now(
|
||||
${openNow.open} ? 'bg-bakery-500' : 'bg-bakery-300',
|
||||
'text-sm text-bakery-600 py-4')}"></p>
|
||||
'inline-flex text-sm text-bakery-600 py-4')}"></p>
|
||||
</nav>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<a th:fragment="small(tint)" href="/"
|
||||
th:class="'flex items-center gap-1.5 sm:gap-2 min-w-0 shrink ' + ${tint}"
|
||||
aria-label="The Vine Coffeehouse + Bakery, home">
|
||||
<th:block th:replace="~{:: inner('w-12 h-12 sm:w-14 sm:h-14', 'text-xl sm:text-2xl md:text-3xl', 'text-[0.6rem] sm:text-xs tracking-[0.18em]')}"></th:block>
|
||||
<th:block th:replace="~{:: inner('w-12 h-12 sm:w-14 sm:h-14', 'text-xl sm:text-2xl xl:text-3xl', 'text-[0.6rem] sm:text-xs tracking-[0.18em]')}"></th:block>
|
||||
</a>
|
||||
|
||||
<!--/* The hero, on the homepage — where a link back to "/" would be pointless. */-->
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<body>
|
||||
<!--/*
|
||||
How a page begins. Every page, the same three things in the same order: a small-caps line, the title in
|
||||
the display face, and one sentence saying what this is.
|
||||
|
||||
Two dresses for it. `photographed` is for the pages that are selling or telling — catering and the story
|
||||
— where a blurred photo under the sage wash does the work the homepage hero does. `plain` is for the
|
||||
pages you arrive at already knowing what you want, where a photograph would be in the way of the thing
|
||||
you came for: the catalogue, the contact form.
|
||||
|
||||
Before this the four openings were four different pieces of markup that happened to look similar.
|
||||
*/-->
|
||||
|
||||
<section th:fragment="photographed(eyebrow, title, lede, image)"
|
||||
class="band relative flex items-center bg-bakery-900 text-white overflow-hidden">
|
||||
<img th:src="${photos.of(image)}" alt=""
|
||||
class="absolute inset-0 w-full h-full object-cover blur-[3px] scale-110">
|
||||
<!--/* Sage wash rather than a neutral black scrim — the tint is the identity. */-->
|
||||
<div class="absolute inset-0 bg-bakery-900/80"></div>
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-bakery-900 via-bakery-800/60 to-bakery-900/80"></div>
|
||||
|
||||
<div class="relative container w-full min-w-0">
|
||||
<div class="measure text-center">
|
||||
<p class="eyebrow text-bakery-200" th:text="${eyebrow}">Order ahead</p>
|
||||
<h1 class="h1 mt-3 text-bakery-50" th:text="${title}">Goodie boxes & catering</h1>
|
||||
<p class="lede mt-5 text-bakery-100" th:text="${lede}">One line about the page.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<header th:fragment="plain(eyebrow, title, lede)" class="page-head">
|
||||
<p class="eyebrow text-bakery-500" th:text="${eyebrow}">From the counter</p>
|
||||
<h1 class="h1 mt-3 text-bakery-900" th:text="${title}">Our products</h1>
|
||||
<p class="lede mt-4 measure text-bakery-800" th:text="${lede}">One line about the page.</p>
|
||||
</header>
|
||||
|
||||
<!--/* 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>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -32,8 +32,11 @@
|
||||
</address>
|
||||
|
||||
<!--/* "Open until 2:00pm", or when it opens next. The one thing a visitor at 2:30 on a Sunday wants. */-->
|
||||
<!--/* The caller passes the display too (`inline-flex`, or `hidden lg:inline-flex`): setting it here as
|
||||
well would be two rules for one property, and the winner would depend on Tailwind's output order
|
||||
rather than on the markup. */-->
|
||||
<span th:fragment="open-now(dotClass, textClass)"
|
||||
th:class="'inline-flex items-center gap-2 ' + ${textClass}">
|
||||
th:class="'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>
|
||||
|
||||
@@ -3,24 +3,11 @@
|
||||
<body>
|
||||
<div th:fragment="content" class="bg-bakery-50">
|
||||
|
||||
<!--/* The shop's own front, which is what the story is about. Same band as the catering page. */-->
|
||||
<section class="band relative flex items-center bg-bakery-900 text-white overflow-hidden">
|
||||
<img th:src="${photos.of('gallery/Outside.webp')}" alt=""
|
||||
class="absolute inset-0 w-full h-full object-cover object-bottom blur-[3px] scale-110">
|
||||
<div class="absolute inset-0 bg-bakery-900/80"></div>
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-bakery-900 via-bakery-800/60 to-bakery-900/80"></div>
|
||||
<div class="relative container w-full min-w-0">
|
||||
<div class="measure text-center">
|
||||
<p class="eyebrow text-bakery-200">Since 2024</p>
|
||||
<h1 class="h1 mt-3 text-bakery-50">Our story</h1>
|
||||
<p class="lede mt-5 text-bakery-100">
|
||||
215 E Main Street, in the middle of downtown Princeville.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<header class="container pt-14 pb-2 md:pt-16 text-center"></header>
|
||||
<div th:replace="~{fragments/opening :: photographed(
|
||||
'Since 2024',
|
||||
'Our story',
|
||||
'215 E Main Street, in the middle of downtown Princeville.',
|
||||
'gallery/Outside.webp')}"></div>
|
||||
|
||||
<div class="container pb-16 md:pb-24">
|
||||
<div class="measure space-y-12">
|
||||
|
||||
@@ -48,18 +48,42 @@
|
||||
<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 th:each="item : ${ {'Cinnamon Rolls', 'Sugar Cookies', 'Cakes'} }"
|
||||
class="group panel-lift overflow-hidden text-center hover:-translate-y-1">
|
||||
<div class="overflow-hidden">
|
||||
<!--/* The filenames carry the spaces in these names; SitePhotos encodes them. */-->
|
||||
<img th:src="${photos.of('gallery/' + item + '.webp')}" th:alt="${item}" width="600" height="400" loading="lazy"
|
||||
class="w-full h-56 object-cover transition-transform duration-500 group-hover:scale-105">
|
||||
</div>
|
||||
class="panel overflow-hidden text-center">
|
||||
<!--/* The filenames carry the spaces in these names; SitePhotos encodes them. */-->
|
||||
<img th:src="${photos.of('gallery/' + item + '.webp')}" th:alt="${item}" width="600" height="400" loading="lazy"
|
||||
class="w-full h-56 object-cover">
|
||||
<h3 class="panel-head border-b-0 border-t h4 md:text-2xl text-bakery-800 py-6" th:text="${item}">Cinnamon Rolls</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!--/*
|
||||
Ordering ahead. The catering page exists and the homepage said nothing about it — a customer only
|
||||
found it by reading the nav. Photo on one side, the offer on the other, and the three tables named so
|
||||
the link is worth following.
|
||||
*/-->
|
||||
<section class="section bg-bakery-100">
|
||||
<div class="container">
|
||||
<div class="grid md:grid-cols-2 gap-8 md:gap-12 items-center measure-wide">
|
||||
<img th:src="${photos.of('gallery/Sugar Cookies.webp')}" alt="Iced sugar cookies from The Vine"
|
||||
width="800" height="600" loading="lazy"
|
||||
class="photo w-full h-64 md:h-80 object-cover">
|
||||
<div>
|
||||
<h2 class="h2 text-bakery-900">Ordering for a crowd?</h2>
|
||||
<p class="mt-4 text-bakery-800 leading-relaxed">
|
||||
Goodie boxes for the office, packages for a party, and cakes and desserts for a wedding —
|
||||
with prices, sizes and what each one includes, so you can see where to start.
|
||||
</p>
|
||||
<a href="/catering"
|
||||
class="pill-sage mt-6">
|
||||
Goodie boxes & catering
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!--/* Our story */-->
|
||||
<section class="section bg-bakery-800 text-white">
|
||||
<div class="container">
|
||||
@@ -76,32 +100,6 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!--/*
|
||||
Ordering ahead. The catering page exists and the homepage said nothing about it — a customer only
|
||||
found it by reading the nav. Photo on one side, the offer on the other, and the three tables named so
|
||||
the link is worth following.
|
||||
*/-->
|
||||
<section class="section bg-bakery-50">
|
||||
<div class="container">
|
||||
<div class="grid md:grid-cols-2 gap-8 md:gap-12 items-center measure-wide">
|
||||
<img th:src="${photos.of('gallery/Sugar Cookies.webp')}" alt="Iced sugar cookies from The Vine"
|
||||
width="800" height="600" loading="lazy"
|
||||
class="photo w-full h-64 md:h-80 object-cover">
|
||||
<div>
|
||||
<h2 class="h2 text-bakery-900">Ordering for a crowd?</h2>
|
||||
<p class="mt-4 text-bakery-800 leading-relaxed">
|
||||
Goodie boxes for the office, packages for a party, and cakes and desserts for a wedding —
|
||||
with prices, sizes and what each one includes, so you can see where to start.
|
||||
</p>
|
||||
<a href="/catering"
|
||||
class="pill-sage mt-6">
|
||||
Goodie boxes & catering
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!--/* Visit us */-->
|
||||
<section id="visit" class="section bg-bakery-50 scroll-mt-24">
|
||||
<div class="container">
|
||||
|
||||
@@ -3,13 +3,10 @@
|
||||
<body>
|
||||
<div th:fragment="content" class="min-h-screen bg-bakery-50">
|
||||
|
||||
<header class="page-head">
|
||||
<h1 class="h1 text-bakery-900">Our products</h1>
|
||||
<p class="mt-4 text-bakery-800 measure leading-relaxed">
|
||||
What comes out of the kitchen on Main Street. Cakes and decorated cookies are made to order, so
|
||||
most of what follows started as somebody describing what they wanted.
|
||||
</p>
|
||||
</header>
|
||||
<div th:replace="~{fragments/opening :: plain(
|
||||
'From the counter',
|
||||
'Our products',
|
||||
'What comes out of the kitchen on Main Street. Cakes and decorated cookies are made to order, so most of what follows started as somebody describing what they wanted.')}"></div>
|
||||
|
||||
<div class="container pb-16">
|
||||
<!--/*
|
||||
@@ -31,7 +28,7 @@
|
||||
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 th:each="product : ${products}"
|
||||
class="group panel-lift overflow-hidden">
|
||||
class="panel overflow-hidden">
|
||||
<div class="relative w-full overflow-hidden">
|
||||
<div th:replace="~{:: gallery(${product})}"></div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user