The Mobile Menu Trilogy: One Button, Three Fixes, Mild Emotional Damage
A humorous but technical dev log about fixing CryptoBacktest mobile navigation across multiple pages, with real commit snippets and the exact bugs that kept coming back.
Series: CRYPTOBACKTEST B.LOG
- 1. 8 years of Bitcoin data taught me more than any trading book
- 2. Ship Week Diaries: How v8.5 Turned into a Product Identity Rewrite
- 3. The Mobile Menu Trilogy: One Button, Three Fixes, Mild Emotional Damage โ you are here
- 4. The i18n + SEO Cleanup Chronicles: Canonical Chaos, hreflang Therapy, and Other Adventures
- 5. Why Backtesting Matters: Or, How I Stopped Trusting My Gut and Started Trusting Data

This post is about a single button.
A tiny hamburger menu button.
A button so small you could miss it with your thumb.
That button consumed multiple commits and a non-trivial portion of my sanity.
๐บ Episode List (Yes, There Were Episodes)
The timeline:
0b5c0fe(2026-02-17): top tab + mobile menu functionality pass8ee6bab(2026-02-17): mobile hamburger still broken on specific pagesf472d14(2026-02-17): missing hamburger HTML on mobile
If this sounds repetitive, that's because it was.
๐ Root Cause Pattern
We didn't have one bug. We had a class of consistency bugs:
- JS toggle logic existed, but not uniformly
- CSS states existed, but not consistently applied
- Some pages were missing required HTML nodes
Each local fix looked correct inside its file, but globally the system remained fragile.
โ๏ธ The Actual Toggle Logic We Added
From 0b5c0fe (js/app.js):
const mobileMenuBtn = document.getElementById('mobile-menu-btn');
const navLinks = document.querySelector('.nav-links');
if (mobileMenuBtn && navLinks) {
mobileMenuBtn.addEventListener('click', () => {
navLinks.classList.toggle('mobile-open');
});
document.addEventListener('click', (e) => {
if (!mobileMenuBtn.contains(e.target) && !navLinks.contains(e.target)) {
navLinks.classList.remove('mobile-open');
}
});
}Nothing exotic. Just normal, reasonable UI behavior.
And still, we had breakage. Why? Because behavior logic is only half the contract.
๐จ The CSS State Styling We Were Missing
Here's the CSS that should have been consistently applied across all pages (from 8ee6bab):
/* Mobile menu container base state */
.nav-links {
display: flex;
flex-direction: column;
position: absolute;
top: 60px;
right: 0;
background-color: #fff;
border: 1px solid #e0e0e0;
border-radius: 4px;
/* Default: hidden on mobile */
max-height: 0;
overflow: hidden;
opacity: 0;
transition: max-height 0.3s ease, opacity 0.2s ease;
z-index: 1000;
}
/* Active/open state โ toggled by .mobile-open class */
.nav-links.mobile-open {
max-height: 500px;
opacity: 1;
}
/* Hamburger button styling */
#mobile-menu-btn {
background: none;
border: none;
cursor: pointer;
padding: 8px;
display: none; /* hidden on desktop */
}
/* Show hamburger only below 768px breakpoint */
@media (max-width: 768px) {
#mobile-menu-btn {
display: block;
}
.nav-links {
flex-direction: column;
gap: 0;
}
.nav-links a {
padding: 12px 16px;
border-bottom: 1px solid #f0f0f0;
}
}
/* Tablet breakpoint โ slightly larger spacing */
@media (max-width: 1024px) and (min-width: 769px) {
#mobile-menu-btn {
display: none; /* revert to desktop layout */
}
.nav-links {
position: static;
flex-direction: row;
max-height: auto;
opacity: 1;
}
}The problem? Not all pages had this exact CSS block. Some inherited it, some had stale versions, some had nothing.
Result: the toggle logic would fire, the class would get added, but the CSS state change never happened. Users saw no menu appear.
๐จ The Missing HTML Problem
In f472d14, the fix was embarrassingly simple:
- some pages literally did not include the hamburger button HTML block
The diff between pages that worked vs. pages that broke:
<!-- Working page (home): -->
+ <button id="mobile-menu-btn" aria-label="Menu">
+ <span class="hamburger-icon">โฐ</span>
+ </button>
+ <nav class="nav-links">
+ <a href="/app">App</a>
+ <a href="/finder">Finder</a>
+ </nav>
<!-- Broken page (finder): -->
- <!-- button entirely missing! -->
- <!-- nav-links div present but orphaned -->
<nav class="nav-links">
<a href="/app">App</a>
<a href="/finder">Finder</a>
</nav>Meaning:
- JS listener setup ran
- queried element returned
null - UI did not expose menu entry at all
This is a perfect "works on my page" trap. The code can be correct and still fail if page structures drift.
๐ Why This Bug Was Sticky
The navigation layer existed in many HTML files.
Any time you run multi-page static layouts without strict templating, you risk:
- structural drift
- stale copies
- one-file hotfixes that never propagate
๐ฑ The Viewport Meta Tag Gotcha We Nearly Missed
While tracking down these issues, we almost overlooked something simple but critical on mobile:
<!-- Without this, mobile CSS queries won't fire correctly -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">If this line is missing or malformed (e.g., width=980px instead of width=device-width), your media queries for @media (max-width: 768px) will not trigger properly.
We found one test page missing this entirely. It had all the CSS media queries written correctly, but the browser was rendering at desktop width on mobile devices. The hamburger button never showed because max-width: 768px was evaluating against a 980px viewport.
The lesson: always validate your viewport meta tag on every page template. It's invisible, but it controls whether mobile breakpoints even work.
๐ From the Outside vs. Inside
From the outside, it looks like one bug. From the inside, it's a distribution problem.
๐ก The Non-Developer Lesson I Learned the Hard Way
I used to think UI bugs were mostly about "bad JS."
Now I think mobile UI bugs are often about this triangle:
- structure (HTML)
- behavior (JS)
- state styling (CSS)
If one side is inconsistent, the triangle collapses and your users get a broken menu.
โ How We Reduced Repeat Incidents
Not perfect, but better:
- standardized nav structure across pages
- repeated toggle logic where needed
- added visual pass on multiple routes, not just home
- created a checklist: viewport meta tag, hamburger button HTML,
.mobile-openCSS state
And yes, that last one matters. Because bugs love pages no one manually tests at midnight.
๐ผ๏ธ Screenshot Reality Check

The finder page is exactly where these bugs hide:
- complex layout
- enough content to distract you
- different scroll and viewport behavior
If nav works on home but fails here, users still call it broken.
๐ฏ Tangent (But Useful): Why This Is Actually Product Work
I know, this sounds like "just front-end bugfixing."
But from user perspective:
- if nav fails, trust drops
- if trust drops, strategy outputs feel less credible
- if outputs feel less credible, your core value is discounted
So yes, a hamburger button can impact perceived product quality way beyond navigation.
โ๏ธ Final Notes from the Button Wars
What I would do if restarting:
- centralized nav partial/template from day one
- route checklist for responsive controls
- tiny e2e mobile smoke test on critical pages
- validate viewport meta tag on every page template early
Because the menu bug is never "just one bug." It is usually your architecture politely telling you to standardize before you add more features.
And yes, I still stare at hamburger buttons like they owe me money.
2026.02.17
Written by
Jay
Licensed Pharmacist ยท Senior Researcher
Building production-grade AI tools across medicine, finance, and productivity โ without a CS degree. Domain expertise first, code second.
About the author โRelated posts