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/head.html
T
austin e10da662a0 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.
2026-07-26 20:53:06 -05:00

53 lines
3.2 KiB
HTML

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<!--/*
The page head. `title`, `description`, `path` and (on the homepage) `preload` come from the model —
see SiteController, where each route states its own.
This is what the SPA could not do. PageMetaController used to rewrite the shell's <title> and four
meta tags with regular expressions on the way out, and its test read the real index.html so that
reformatting that file failed the build rather than silently breaking the rewriting. A page that is
rendered on the server just writes its own head, so all of that is gone.
*/-->
<head th:fragment="head(title, description, path)">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" media="(prefers-color-scheme: light)" href="/images/resources/logo_L.png">
<link rel="icon" media="(prefers-color-scheme: dark)" href="/images/resources/logo_dark.png">
<!--/* Open the connection to the photo bucket while the head is still parsing. */-->
<link rel="preconnect" th:href="${assetOrigin}" crossorigin>
<!--/* All three are above the fold — the wordmark, every heading, and the body — and all three together
are now smaller than the display face alone used to be. Without preloading, each page opens in
the fallback and then reflows into the brand. */-->
<link rel="preload" as="font" type="font/woff2" href="/fonts/raleway-latin.woff2" crossorigin>
<link rel="preload" as="font" type="font/woff2" href="/fonts/LeJour-Script.woff2" crossorigin>
<link rel="preload" as="font" type="font/woff2" href="/fonts/AdBhashitha.woff2" crossorigin>
<link th:if="${preload}" rel="preload" as="image" th:href="${preload}" fetchpriority="high">
<title th:text="${title}">The Vine Coffeehouse + Bakery</title>
<meta name="description" th:content="${description}">
<meta property="og:title" th:content="${title}">
<meta property="og:description" th:content="${description}">
<meta property="og:url" th:content="${baseUrl + (path == '/' ? '' : path)}">
<!--/* Without an image a shared link is a grey box with a title. This is the storefront. */-->
<meta property="og:image" th:content="${shareImage}">
<meta property="og:image:alt" content="The Vine Coffeehouse + Bakery on Main Street, Princeville">
<meta name="twitter:card" content="summary_large_image">
<meta property="og:type" content="website">
<meta property="og:locale" content="en_US">
<meta name="keywords" content="bakery, coffeehouse, pastries, custom cakes, cinnamon rolls, paninis, Princeville IL">
<!--/* ?v= is the commit sha in a deployment. One hand-written stylesheet has no content hash in its
name, so without it a returning visitor keeps the CSS they cached before the deploy. */-->
<link rel="stylesheet" th:href="|/css/site.css?v=${build}|">
<!--/* The shop as a search engine reads it: a Bakery, its address, its phone number and its opening
times, from the same record the page prints. It is how a local shop gets "Open ⋅ closes 2pm"
next to its listing. Unescaped because it is JSON, written by Jackson — see StructuredData. */-->
<script type="application/ld+json" th:utext="${structuredData}"></script>
</head>
<body></body>
</html>