feat: responsive nav — hamburger menu + wider container (900px breakpoint)
- Widen nav container from 72rem to 80rem (1280px) — matches Zillow's nav container width, more breathing room for items on large monitors - Raise collapse breakpoint from 768px to 899px — links stay visible until the screen is actually too narrow - Add hamburger button (SVG 3-line icon) visible at < 900px - Add mobile drop-down panel with all nav links grouped under Plan / Explore / Account sections; overlay + Escape key close it Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Nav: hamburger menu on screens < 900px — clicking opens a full-width mobile panel with all nav links; overlay click and Escape key close it
|
||||||
|
- Nav: mobile panel groups links under "Plan", "Explore", and "Account" section headers
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Nav: widen container from 72rem (1152px) to 80rem (1280px) — matches Zillow's nav container width, more breathing room for nav items on large monitors
|
||||||
|
- Nav: collapse breakpoint raised from 768px to 899px — nav links no longer hide until the screen is actually too narrow
|
||||||
|
- Nav: remove redundant inline `style="display:grid;grid-template-columns:1fr auto 1fr"` on `.nav-inner` (already in CSS)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- i18n: improve German nav labels — "Verzeichnis" → "Anbieterverzeichnis", "Planer" → "Kostenrechner"
|
- i18n: improve German nav labels — "Verzeichnis" → "Anbieterverzeichnis", "Planer" → "Kostenrechner"
|
||||||
- CI: add missing env vars to `.env` heredoc — `WAITLIST_MODE`, `LEADS_EMAIL`, `UMAMI_API_URL`; make Paddle vars optional (`:-`) so they don't break deploys when unset
|
- CI: add missing env vars to `.env` heredoc — `WAITLIST_MODE`, `LEADS_EMAIL`, `UMAMI_API_URL`; make Paddle vars optional (`:-`) so they don't break deploys when unset
|
||||||
|
|||||||
@@ -113,9 +113,9 @@
|
|||||||
border-bottom: 1px solid #E2E8F0;
|
border-bottom: 1px solid #E2E8F0;
|
||||||
}
|
}
|
||||||
.nav-inner {
|
.nav-inner {
|
||||||
max-width: 72rem;
|
max-width: 80rem;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0 1rem;
|
padding: 0 1.5rem;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr auto 1fr;
|
grid-template-columns: 1fr auto 1fr;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -172,9 +172,84 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
@media (max-width: 768px) {
|
/* Hamburger button — hidden on desktop */
|
||||||
|
.nav-hamburger {
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
.nav-hamburger:hover { background: #F1F5F9; }
|
||||||
|
.nav-hamburger svg { display: block; }
|
||||||
|
/* Mobile menu panel — hidden by default */
|
||||||
|
.nav-mobile {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 56px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: #fff;
|
||||||
|
border-bottom: 1px solid #E2E8F0;
|
||||||
|
box-shadow: 0 8px 24px rgba(0,0,0,0.08);
|
||||||
|
z-index: 49;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0.75rem 1.5rem 1rem;
|
||||||
|
}
|
||||||
|
.nav-bar[data-navopen="true"] .nav-mobile {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.nav-mobile a,
|
||||||
|
.nav-mobile button.nav-auth-btn,
|
||||||
|
.nav-mobile a.nav-auth-btn {
|
||||||
|
display: block;
|
||||||
|
padding: 0.625rem 0;
|
||||||
|
border-bottom: 1px solid #F1F5F9;
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #475569;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.15s;
|
||||||
|
}
|
||||||
|
.nav-mobile a:last-child { border-bottom: none; }
|
||||||
|
.nav-mobile a:hover { color: #1D4ED8; }
|
||||||
|
.nav-mobile a.nav-auth-btn,
|
||||||
|
.nav-mobile button.nav-auth-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
border-bottom: none;
|
||||||
|
width: auto;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
.nav-mobile .nav-mobile-section {
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.07em;
|
||||||
|
color: #94A3B8;
|
||||||
|
padding: 0.75rem 0 0.25rem;
|
||||||
|
}
|
||||||
|
/* Overlay behind mobile menu */
|
||||||
|
.nav-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 48;
|
||||||
|
}
|
||||||
|
.nav-bar[data-navopen="true"] ~ .nav-overlay {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
@media (max-width: 899px) {
|
||||||
.nav-links { display: none; }
|
.nav-links { display: none; }
|
||||||
.nav-inner { justify-content: center; }
|
.nav-hamburger { display: flex; }
|
||||||
|
.nav-inner { grid-template-columns: auto 1fr auto; }
|
||||||
|
.nav-bar { position: sticky; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Page container */
|
/* Page container */
|
||||||
|
|||||||
@@ -38,8 +38,8 @@
|
|||||||
{% block head %}{% endblock %}
|
{% block head %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="nav-bar">
|
<nav class="nav-bar" id="main-nav">
|
||||||
<div class="nav-inner" style="display:grid;grid-template-columns:1fr auto 1fr">
|
<div class="nav-inner">
|
||||||
<!-- Left: demand / buy side -->
|
<!-- Left: demand / buy side -->
|
||||||
<div class="nav-links nav-links--left">
|
<div class="nav-links nav-links--left">
|
||||||
<a href="{{ url_for('planner.index') }}">{{ t.nav_planner }}</a>
|
<a href="{{ url_for('planner.index') }}">{{ t.nav_planner }}</a>
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
<span style="font-family:'Bricolage Grotesque',sans-serif;font-weight:800;font-size:1.125rem;color:#0F172A;letter-spacing:-0.02em">padelnomics</span>
|
<span style="font-family:'Bricolage Grotesque',sans-serif;font-weight:800;font-size:1.125rem;color:#0F172A;letter-spacing:-0.02em">padelnomics</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- Right: supply side + auth -->
|
<!-- Right: supply side + auth (desktop only) -->
|
||||||
<div class="nav-links nav-links--right">
|
<div class="nav-links nav-links--right">
|
||||||
<a href="{{ url_for('directory.index') }}">{{ t.nav_directory }}</a>
|
<a href="{{ url_for('directory.index') }}">{{ t.nav_directory }}</a>
|
||||||
<a href="{{ url_for('content.markets') }}">{{ t.nav_markets }}</a>
|
<a href="{{ url_for('content.markets') }}">{{ t.nav_markets }}</a>
|
||||||
@@ -88,10 +88,72 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('auth.login') }}" class="nav-auth-btn">{{ t.nav_signin }}</a>
|
<a href="{{ url_for('auth.login') }}" class="nav-auth-btn">{{ t.nav_signin }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Hamburger (mobile only) -->
|
||||||
|
<button type="button" class="nav-hamburger" id="nav-hamburger-btn" aria-label="Open navigation" aria-expanded="false">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||||
|
<rect y="3" width="20" height="2" rx="1" fill="#0F172A"/>
|
||||||
|
<rect y="9" width="20" height="2" rx="1" fill="#0F172A"/>
|
||||||
|
<rect y="15" width="20" height="2" rx="1" fill="#0F172A"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile menu panel -->
|
||||||
|
<div class="nav-mobile" id="nav-mobile-panel">
|
||||||
|
<span class="nav-mobile-section">Plan</span>
|
||||||
|
<a href="{{ url_for('planner.index') }}">{{ t.nav_planner }}</a>
|
||||||
|
<a href="{{ url_for('leads.quote_request') }}">{{ t.nav_quotes }}</a>
|
||||||
|
|
||||||
|
<span class="nav-mobile-section">Explore</span>
|
||||||
|
<a href="{{ url_for('directory.index') }}">{{ t.nav_directory }}</a>
|
||||||
|
<a href="{{ url_for('content.markets') }}">{{ t.nav_markets }}</a>
|
||||||
|
<a href="{{ url_for('public.suppliers') }}">{{ t.nav_suppliers }}</a>
|
||||||
|
<a href="{{ url_for('public.landing') }}#faq">{{ t.nav_help }}</a>
|
||||||
|
|
||||||
|
{% if user %}
|
||||||
|
<span class="nav-mobile-section">Account</span>
|
||||||
|
<a href="{{ url_for('dashboard.index') }}">{{ t.nav_dashboard }}</a>
|
||||||
|
{% if is_admin %}
|
||||||
|
<a href="{{ url_for('admin.index') }}">{{ t.nav_admin }}</a>
|
||||||
|
{% endif %}
|
||||||
|
<form method="post" action="{{ url_for('auth.logout') }}" style="margin:0.5rem 0 0;padding:0">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
|
<button type="submit" class="nav-auth-btn">{{ t.nav_signout }}</button>
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ url_for('auth.login') }}" class="nav-auth-btn" style="margin-top:0.5rem">{{ t.nav_signin }}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<!-- Overlay — closes mobile menu when clicked -->
|
||||||
|
<div class="nav-overlay" id="nav-overlay"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
var nav = document.getElementById('main-nav');
|
||||||
|
var btn = document.getElementById('nav-hamburger-btn');
|
||||||
|
var overlay = document.getElementById('nav-overlay');
|
||||||
|
function open() {
|
||||||
|
nav.setAttribute('data-navopen', 'true');
|
||||||
|
btn.setAttribute('aria-expanded', 'true');
|
||||||
|
}
|
||||||
|
function close() {
|
||||||
|
nav.removeAttribute('data-navopen');
|
||||||
|
btn.setAttribute('aria-expanded', 'false');
|
||||||
|
}
|
||||||
|
btn.addEventListener('click', function() {
|
||||||
|
nav.hasAttribute('data-navopen') ? close() : open();
|
||||||
|
});
|
||||||
|
overlay.addEventListener('click', close);
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
if (e.key === 'Escape') close();
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
<!-- Flash messages -->
|
<!-- Flash messages -->
|
||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
|
|||||||
Reference in New Issue
Block a user