feat(header): mobile hamburger menu + alignment & typography pass

- Header: offset the 4px green ::before bar so content centres in the
  visible dark area, not the geometric box (fixes wordmark/nav/icon
  sitting ~2px high — the root cause behind the whole alignment saga)
- Mobile (≤768px): collapse nav into a slide-down hamburger panel
  (standalone js/nav.js); inline nav retained on desktop
- Nav: use the display serif in sentence case so it shares the
  wordmark's anatomy and aligns naturally (no optical nudge needed)
- Typography tokens: three-tier display tracking (--tracking-display,
  --tracking-display-sm), --tracking-caps, --color-error, paper-glass
  overlays, --text-story/--leading-story; applied across components
- trip-dates: move inline styles to a class

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BftDn9vu9SonFAY4vxu4uk
This commit is contained in:
2026-06-27 18:54:56 +02:00
co-authored by Claude Opus 4.8
parent ac38ae2a54
commit d7a772ae02
5 changed files with 207 additions and 44 deletions
+140 -34
View File
@@ -39,7 +39,10 @@ body::after {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 var(--space-5);
box-sizing: border-box;
/* 4px top offsets the green ::before gradient (top 4px of the bar) so all
content centres in the VISIBLE dark area, not the geometric header box. */
padding: 4px var(--space-5) 0;
height: var(--site-header-height);
background: var(--color-canvas);
border-bottom: 1px solid var(--color-border);
@@ -63,7 +66,7 @@ body::after {
font-family: var(--font-display);
font-size: var(--text-xl);
font-weight: 400;
letter-spacing: 0.06em;
letter-spacing: var(--tracking-display-sm);
text-decoration: none;
color: var(--color-ink);
line-height: 1;
@@ -76,9 +79,12 @@ body::after {
}
.site-nav a {
font-family: var(--font-ui);
font-size: var(--text-sm);
font-weight: 500;
/* Nav uses the display serif (not the UI sans) so it shares the wordmark's
anatomy: with one typeface, logo and nav centre identically — no optical
nudge needed. Sentence case keeps it quiet support beneath the wordmark. */
font-family: var(--font-display);
font-size: var(--text-base);
font-weight: 400;
color: var(--color-ink-2);
text-decoration: none;
padding: var(--space-2) var(--space-3);
@@ -90,11 +96,69 @@ body::after {
}
.site-nav a:hover { color: var(--color-ink); background: var(--color-paper); }
.site-nav a[aria-current="page"] { color: var(--color-accent); font-weight: 600; }
.site-nav a[aria-current="page"] { color: var(--color-accent); }
@media (max-width: 380px) {
.site-title { font-size: var(--text-md); }
.site-nav a { padding: var(--space-2); font-size: 0.8rem; }
/* ── Hamburger toggle ────────────────────────────────────────────────────────── */
.nav-toggle {
display: none; /* shown only on mobile, via the media query below */
flex-direction: column;
justify-content: center;
gap: 5px;
width: 44px;
height: 44px;
padding: 11px 10px;
margin-right: -10px; /* cancel inner padding so the bars align to the bar edge */
background: none;
border: 0;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.nav-toggle__bar {
display: block;
width: 100%;
height: 2px;
border-radius: 2px;
background: var(--color-ink-2);
transition: transform 0.25s ease, opacity 0.2s ease, background 0.15s;
}
.nav-toggle:hover .nav-toggle__bar { background: var(--color-ink); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
@media (max-width: 768px) {
.nav-toggle { display: flex; }
/* Nav collapses into a slide-down panel beneath the header bar. */
.site-nav {
position: absolute;
top: 100%;
left: 0;
right: 0;
flex-direction: column;
align-items: stretch;
gap: 0;
padding: var(--space-2) var(--space-5) var(--space-4);
background: var(--color-canvas);
border-bottom: 1px solid var(--color-border);
box-shadow: var(--shadow-lg);
transform: translateY(-8px);
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
}
.site-nav[data-open="true"] {
transform: translateY(0);
opacity: 1;
visibility: visible;
}
.site-nav a {
font-size: var(--text-lg);
padding: var(--space-3) 0;
min-height: 0;
border-radius: 0;
}
.site-nav a:hover { background: none; color: var(--color-ink); }
}
/* ── Feed ────────────────────────────────────────────────────────────────────── */
@@ -142,6 +206,7 @@ body::after {
font-size: var(--text-xl);
font-weight: 400;
line-height: var(--leading-snug);
letter-spacing: var(--tracking-display);
color: var(--color-ink);
margin-bottom: var(--space-3);
transition: color 0.15s;
@@ -173,6 +238,7 @@ body::after {
font-size: var(--text-xl);
font-weight: 400;
line-height: var(--leading-snug);
letter-spacing: var(--tracking-display);
color: var(--color-ink);
margin-bottom: var(--space-2);
}
@@ -479,6 +545,8 @@ body::after {
font-family: var(--font-display);
font-size: var(--text-2xl);
font-weight: 400;
line-height: var(--leading-tight);
letter-spacing: var(--tracking-display);
margin-bottom: var(--space-8);
color: var(--color-ink);
}
@@ -517,7 +585,7 @@ body::after {
font-weight: 600;
color: var(--color-ink-muted);
text-transform: uppercase;
letter-spacing: 0.07em;
letter-spacing: var(--tracking-caps);
}
.stats-countries {
@@ -534,7 +602,7 @@ body::after {
color: var(--color-ink);
text-transform: uppercase;
font-size: var(--text-xs);
letter-spacing: 0.07em;
letter-spacing: var(--tracking-caps);
}
.stats-note {
@@ -662,6 +730,8 @@ body::after {
font-family: var(--font-display);
font-size: var(--text-xl);
font-weight: 400;
line-height: var(--leading-snug);
letter-spacing: var(--tracking-display);
margin-bottom: var(--space-6);
color: var(--color-ink);
}
@@ -731,13 +801,13 @@ body::after {
/* Inline field validation */
.post-form-wrap .field-invalid {
border-color: #c0392b !important;
outline-color: #c0392b !important;
border-color: var(--color-error) !important;
outline-color: var(--color-error) !important;
}
.post-form-wrap .field-error {
display: block;
color: #c0392b;
color: var(--color-error);
font-size: var(--text-sm);
margin-top: var(--space-1);
}
@@ -775,7 +845,7 @@ body::after {
}
.form-status--ok { color: var(--color-accent); }
.form-status--err { color: #B44A2A; }
.form-status--err { color: var(--color-error); }
/* ── Accessibility & motion ──────────────────────────────────────────────────── */
@@ -863,10 +933,18 @@ body::after {
font-family: var(--font-display);
font-size: var(--text-2xl);
font-weight: 400;
line-height: var(--leading-tight);
letter-spacing: var(--tracking-display);
color: var(--color-ink);
margin-bottom: var(--space-2);
}
.trip-dates {
font-size: var(--text-sm);
color: var(--color-ink-muted);
margin: var(--space-1) 0 var(--space-2);
}
.home-trip-counts {
font-size: var(--text-sm);
color: var(--color-ink-muted);
@@ -975,6 +1053,8 @@ body::after {
font-family: var(--font-display);
font-size: var(--text-2xl);
font-weight: 400;
line-height: var(--leading-tight);
letter-spacing: var(--tracking-display);
color: var(--color-ink);
margin-bottom: var(--space-8);
}
@@ -1028,6 +1108,8 @@ body::after {
font-family: var(--font-display);
font-size: var(--text-xl);
font-weight: 400;
line-height: var(--leading-snug);
letter-spacing: var(--tracking-display);
color: var(--color-ink);
margin-bottom: var(--space-2);
}
@@ -1069,7 +1151,7 @@ body::after {
font-size: var(--text-xs);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
letter-spacing: var(--tracking-caps);
color: var(--color-ink-muted);
margin-bottom: var(--space-3);
}
@@ -1119,7 +1201,7 @@ body::after {
font-size: var(--text-xs);
font-weight: 600;
font-variant: small-caps;
letter-spacing: 0.08em;
letter-spacing: var(--tracking-caps);
color: var(--color-accent);
margin-bottom: var(--space-2);
}
@@ -1216,6 +1298,8 @@ body::after {
.trip-cycling-title {
font-family: var(--font-display);
font-size: var(--text-lg);
line-height: var(--leading-snug);
letter-spacing: var(--tracking-display-sm);
color: var(--color-ink);
}
@@ -1245,7 +1329,7 @@ body::after {
font-size: var(--text-sm);
font-weight: 500;
color: rgba(255,255,255,0.85);
background: rgba(26,24,20,0.55);
background: var(--color-paper-glass-high);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
border: 1px solid rgba(255,255,255,0.12);
@@ -1254,7 +1338,7 @@ body::after {
text-decoration: none;
transition: background 0.2s;
}
.story-escape:hover { background: rgba(26,24,20,0.8); }
.story-escape:hover { background: var(--color-paper-glass-hover); }
/* Hero */
.story-hero { position: relative; }
@@ -1303,7 +1387,7 @@ body::after {
.story-hero__title {
font-family: var(--font-display);
font-size: clamp(2.2rem, 6vw, 4.5rem);
font-weight: 900;
font-weight: 400;
line-height: 1.08;
letter-spacing: -0.02em;
margin-bottom: var(--space-3);
@@ -1354,13 +1438,27 @@ body::after {
}
.story-body p {
font-family: var(--font-ui);
font-size: 1.0625rem;
line-height: 1.85;
font-size: var(--text-story);
line-height: var(--leading-story);
color: var(--color-ink-2);
margin-bottom: var(--space-6);
}
.story-body h2, .story-body h3 {
.story-body h2 {
font-family: var(--font-display);
font-size: var(--text-xl);
font-weight: 400;
line-height: var(--leading-snug);
letter-spacing: var(--tracking-display);
color: var(--color-ink);
margin-top: var(--space-10);
margin-bottom: var(--space-4);
}
.story-body h3 {
font-family: var(--font-display);
font-size: var(--text-lg);
font-weight: 400;
line-height: var(--leading-snug);
letter-spacing: var(--tracking-display-sm);
color: var(--color-ink);
margin-top: var(--space-10);
margin-bottom: var(--space-4);
@@ -1404,7 +1502,7 @@ body::after {
font-family: var(--font-ui);
font-size: var(--text-sm);
color: var(--color-ink-muted);
letter-spacing: 0.06em;
letter-spacing: var(--tracking-caps);
text-transform: uppercase;
}
@@ -1421,7 +1519,7 @@ body::after {
font-family: var(--font-display);
font-size: var(--text-lg);
font-weight: 400;
letter-spacing: -0.01em;
letter-spacing: var(--tracking-display-sm);
color: var(--color-ink);
white-space: nowrap;
max-width: 40%;
@@ -1505,7 +1603,7 @@ body::after {
align-items: center;
gap: var(--space-2);
padding: var(--space-10) var(--space-12);
background: rgba(26,24,20,0.25);
background: var(--color-paper-glass-mid);
backdrop-filter: blur(18px) saturate(1.4);
-webkit-backdrop-filter: blur(18px) saturate(1.4);
border: 1px solid rgba(255,255,255,0.12);
@@ -1533,8 +1631,9 @@ body::after {
.chapter-break__title {
font-family: var(--font-display);
font-size: clamp(1.6rem, 4vw, 2.6rem);
font-weight: 700;
font-weight: 400;
line-height: 1.15;
letter-spacing: var(--tracking-display);
color: #fff;
text-shadow: 0 2px 16px rgba(0,0,0,0.4);
margin: 0;
@@ -1623,8 +1722,8 @@ body::after {
}
.scrolly-step__inner p {
font-family: var(--font-ui);
font-size: 1.05rem;
line-height: 1.8;
font-size: var(--text-story);
line-height: var(--leading-story);
color: var(--color-ink-2);
margin-bottom: var(--space-3);
}
@@ -1671,7 +1770,7 @@ body::after {
padding: var(--space-10) var(--space-8);
backdrop-filter: blur(14px) saturate(1.3);
-webkit-backdrop-filter: blur(14px) saturate(1.3);
background: rgba(26,24,20,0.08);
background: var(--color-paper-glass-low);
text-align: center;
}
.pull-quote__inner--no-image {
@@ -1692,6 +1791,7 @@ body::after {
font-family: var(--font-display);
font-size: clamp(1.2rem, 3vw, 1.6rem);
font-style: italic;
letter-spacing: var(--tracking-display-sm);
color: #fff;
line-height: 1.5;
margin: var(--space-4) 0;
@@ -1843,6 +1943,8 @@ body::after {
font-family: var(--font-display);
font-size: var(--text-2xl);
font-weight: 400;
line-height: var(--leading-tight);
letter-spacing: var(--tracking-display);
color: var(--color-ink);
margin-bottom: 0;
}
@@ -1875,7 +1977,7 @@ body::after {
font-size: var(--text-xs);
color: var(--color-ink-muted);
text-transform: uppercase;
letter-spacing: 0.06em;
letter-spacing: var(--tracking-caps);
margin-bottom: var(--space-1);
}
.story-card__location {
@@ -1889,9 +1991,10 @@ body::after {
font-family: var(--font-display);
font-size: var(--text-lg);
font-weight: 400;
line-height: var(--leading-snug);
letter-spacing: var(--tracking-display-sm);
color: var(--color-ink);
margin-bottom: var(--space-3);
line-height: var(--leading-snug);
}
.story-card__cta {
font-family: var(--font-ui);
@@ -1916,6 +2019,8 @@ body::after {
font-family: var(--font-display);
font-size: var(--text-2xl);
font-weight: 400;
line-height: var(--leading-tight);
letter-spacing: var(--tracking-display);
color: var(--color-ink);
margin-bottom: var(--space-2);
}
@@ -1973,7 +2078,7 @@ body::after {
font-size: var(--text-xs);
font-weight: 600;
font-variant: small-caps;
letter-spacing: 0.08em;
letter-spacing: var(--tracking-caps);
color: var(--color-accent);
}
@@ -1985,9 +2090,10 @@ body::after {
font-family: var(--font-display);
font-size: var(--text-lg);
font-weight: 400;
line-height: var(--leading-snug);
letter-spacing: var(--tracking-display-sm);
color: var(--color-ink);
text-decoration: none;
line-height: 1.3;
}
.home-highlight-title:hover { color: var(--color-accent); }
+14
View File
@@ -13,6 +13,13 @@
--color-accent-on: #FFFFFF; /* text on accent surfaces */
--color-surface-raised: #2A2720; /* elevated surfaces: tooltips, hover */
--color-ink-inverse: #17171A; /* text on accent-coloured buttons */
--color-error: #c0392b; /* validation errors, form error status */
/* ── Glass overlays (paper colour at opacity, for story components) ── */
--color-paper-glass-low: color-mix(in srgb, var(--color-paper) 8%, transparent);
--color-paper-glass-mid: color-mix(in srgb, var(--color-paper) 25%, transparent);
--color-paper-glass-high: color-mix(in srgb, var(--color-paper) 55%, transparent);
--color-paper-glass-hover: color-mix(in srgb, var(--color-paper) 80%, transparent);
/* ── Fonts ───────────────────────────────────────────────── */
--font-display: 'DM Serif Display', Georgia, serif;
@@ -27,11 +34,18 @@
--text-xl: 1.75rem;
--text-2xl: 2.25rem;
--text-3xl: 3rem;
--text-story: 1.0625rem; /* long-form reading size: story body + scrolly steps */
/* ── Leading ─────────────────────────────────────────────── */
--leading-tight: 1.2;
--leading-snug: 1.35;
--leading-normal: 1.65;
--leading-story: 1.85; /* relaxed reading rhythm for story long-form */
/* ── Tracking ────────────────────────────────────────────── */
--tracking-caps: 0.07em; /* uppercase UI labels: dates, badges, sidebar headings */
--tracking-display: -0.015em; /* display headings: text-xl and text-2xl */
--tracking-display-sm: -0.01em; /* display headings: text-lg and smaller; site logotype */
/* ── Spacing (4px grid) ──────────────────────────────────── */
--space-1: 0.25rem;
+37
View File
@@ -0,0 +1,37 @@
/* Mobile nav: hamburger toggle for the slide-down menu panel.
Standalone (not part of the esbuild bundle) — a small progressive
enhancement for the header. Loaded via base.html.twig. */
(function () {
var toggle = document.querySelector('.nav-toggle');
var nav = document.getElementById('site-nav');
if (!toggle || !nav) return;
function setOpen(open) {
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
toggle.setAttribute('aria-label', open ? 'Close menu' : 'Open menu');
nav.setAttribute('data-open', open ? 'true' : 'false');
}
toggle.addEventListener('click', function (e) {
e.stopPropagation();
setOpen(toggle.getAttribute('aria-expanded') !== 'true');
});
/* Tapping a link closes the panel before navigating. */
nav.addEventListener('click', function (e) {
if (e.target.closest('a')) setOpen(false);
});
/* Close on outside tap or Escape. */
document.addEventListener('click', function (e) {
if (!nav.contains(e.target) && !toggle.contains(e.target)) setOpen(false);
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') setOpen(false);
});
/* Reset to closed if the viewport grows back to desktop. */
window.addEventListener('resize', function () {
if (window.innerWidth > 768) setOpen(false);
});
})();
@@ -8,6 +8,7 @@
{% do assets.addCss('theme://css/style.css') %}
{% do assets.addCss('theme://css-compiled/main.css') %}
{% do assets.addJs('theme://js/main.js', {group: 'bottom'}) %}
{% do assets.addJs('theme://js/nav.js', {group: 'bottom'}) %}
{% block map_assets %}{% endblock %}
{{ assets.css()|raw }}
{{ assets.js()|raw }}
@@ -17,7 +18,12 @@
<header class="site-header">
<a class="site-title" href="{{ base_url_absolute }}">into the east</a>
{% block nav %}
<nav class="site-nav" aria-label="Main navigation">
<button class="nav-toggle" type="button" aria-label="Open menu" aria-controls="site-nav" aria-expanded="false">
<span class="nav-toggle__bar"></span>
<span class="nav-toggle__bar"></span>
<span class="nav-toggle__bar"></span>
</button>
<nav class="site-nav" id="site-nav" aria-label="Main navigation">
<a href="{{ base_url_absolute }}"{% if page.template == 'home' %} aria-current="page"{% endif %}>Home</a>
<a href="{{ base_url_absolute }}/trips"{% if page.template == 'trips' %} aria-current="page"{% endif %}>Past Trips</a>
</nav>
+1 -1
View File
@@ -71,7 +71,7 @@
<div class="home-trip-header">
<h1 class="home-trip-name">{{ page.title }}</h1>
{% if page.header.date_start %}
<p class="trip-dates" style="font-size:var(--text-sm);color:var(--color-ink-muted);margin:var(--space-1) 0 var(--space-2);">
<p class="trip-dates">
{{ page.header.date_start|date('d M Y') }}
{% if page.header.date_end %}{{ page.header.date_end|date('d M Y') }}{% else %} — Ongoing{% endif %}
</p>