Task ID: task-048
Date: 2026-02-18
Status: Planning Complete
- Homepage (mobile 375px): See
/Users/mastercontrol/.openclaw/media/browser/f2bb3492-0f69-48d6-9e81-f021e7497be5.png - Dashboard/Login page (mobile 375px): See
/Users/mastercontrol/.openclaw/media/browser/8e007fa9-3d43-4089-b7ea-0b7f0e141e4b.png
HTML (rendered via JavaScript in app.js):
// Logged out nav
nav.innerHTML = `
<a href="#/" class="logo">H</a>
<div class="nav-links">
<a href="#/blog">Blog</a>
<a href="#/skill">For Agents</a>
<a href="#/chat">Chat</a>
<a href="#/login">Login</a>
<a href="#/signup">Sign Up</a>
</div>
`;
// Logged in nav (has even more items!)
nav.innerHTML = `
<a href="#/" class="logo">H</a>
<div class="nav-links">
<a href="#/dashboard">Dashboard</a>
<a href="#/blog">Blog</a>
<a href="#/skill">For Agents</a>
<a href="#/chat">Chat</a>
<button onclick="logout()" class="btn-secondary">Logout</button>
</div>
`;Current CSS:
nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background: var(--bg);
border-bottom: 1px solid var(--border);
}
.logo {
font-size: 1.5rem;
font-weight: 600;
color: var(--primary);
text-decoration: none;
}
.nav-links {
display: flex;
gap: 1.5rem;
align-items: center;
}
.nav-links a {
color: var(--text-muted);
text-decoration: none;
font-size: 0.95rem;
transition: color 0.2s;
}- There is NO
@mediaquery specifically for the main navigation - The only mobile nav CSS (lines 1209-1268) styles the dashboard tabs, not the top nav
- Links just get smaller padding at 768px but no structural change
- Mobile viewport: ~375px
- Nav links: 5 items × ~60-80px each + gaps = ~350-450px needed
- Result: Links are crushed together or may overflow
- Logged-in state: 5 nav items + Logout button = 6 items (even worse)
- Missing: hamburger menu, slide-out drawer, or collapsible nav
- Missing: mobile-specific menu toggle button
- Missing: hidden menu state for small screens
- Dashboard tabs have a nice mobile bottom bar treatment
- Top navigation lacks any mobile-specific treatment
- Feels inconsistent between the two navs
- Links may be too close together for comfortable touch (should be 44px min)
- No minimum touch target size defined
Approach: Add a hamburger menu that appears on mobile, toggling a slide-down or modal menu.
- Hamburger menu: Standard pattern, users expect it
- Bottom sheet: Would conflict with existing dashboard bottom tabs
- Simplified nav: Would hide important links (Login/Signup)
- Dropdown: Doesn't scale well with 5-6 items
File: /public/app.js - Update both updateNav() variants
Logged-out nav:
nav.innerHTML = `
<a href="#/" class="logo">H</a>
<button class="nav-toggle" aria-label="Toggle navigation" aria-expanded="false">
<span class="hamburger"></span>
</button>
<div class="nav-links">
<a href="#/blog">Blog</a>
<a href="#/skill">For Agents</a>
<a href="#/chat">Chat</a>
<a href="#/login">Login</a>
<a href="#/signup" class="btn-primary">Sign Up</a>
</div>
`;Logged-in nav:
nav.innerHTML = `
<a href="#/" class="logo">H</a>
<button class="nav-toggle" aria-label="Toggle navigation" aria-expanded="false">
<span class="hamburger"></span>
</button>
<div class="nav-links">
<a href="#/dashboard">Dashboard</a>
<a href="#/blog">Blog</a>
<a href="#/skill">For Agents</a>
<a href="#/chat">Chat</a>
<button onclick="logout()" class="btn-secondary">Logout</button>
</div>
`;File: /public/app.js - Add after updateNav() function
// Toggle mobile navigation
function toggleMobileNav() {
const nav = document.getElementById('nav');
const toggle = nav.querySelector('.nav-toggle');
const isOpen = nav.classList.toggle('nav-open');
toggle.setAttribute('aria-expanded', isOpen);
}
// Close mobile nav when clicking a link
function closeMobileNav() {
const nav = document.getElementById('nav');
const toggle = nav.querySelector('.nav-toggle');
nav.classList.remove('nav-open');
if (toggle) toggle.setAttribute('aria-expanded', 'false');
}
// Initialize mobile nav listeners (call after updateNav)
function initMobileNav() {
const nav = document.getElementById('nav');
const toggle = nav.querySelector('.nav-toggle');
const links = nav.querySelectorAll('.nav-links a');
if (toggle) {
toggle.addEventListener('click', toggleMobileNav);
}
// Close menu when clicking a link
links.forEach(link => {
link.addEventListener('click', closeMobileNav);
});
}Then call initMobileNav() at the end of updateNav():
function updateNav() {
// ... existing innerHTML code ...
// Initialize mobile nav toggle
initMobileNav();
}File: /public/styles.css - Add after the existing nav styles
/* ============================================
MOBILE NAVIGATION
============================================ */
/* Hamburger button - hidden on desktop */
.nav-toggle {
display: none;
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
z-index: 1001;
}
.hamburger {
display: block;
width: 24px;
height: 2px;
background: var(--text);
position: relative;
transition: all 0.2s ease;
}
.hamburger::before,
.hamburger::after {
content: '';
position: absolute;
width: 24px;
height: 2px;
background: var(--text);
left: 0;
transition: all 0.2s ease;
}
.hamburger::before {
top: -7px;
}
.hamburger::after {
top: 7px;
}
/* Hamburger animation when open */
.nav-open .hamburger {
background: transparent;
}
.nav-open .hamburger::before {
top: 0;
transform: rotate(45deg);
}
.nav-open .hamburger::after {
top: 0;
transform: rotate(-45deg);
}
/* Mobile breakpoint */
@media (max-width: 768px) {
/* Show hamburger button */
.nav-toggle {
display: block;
}
/* Reduce nav padding on mobile */
nav {
padding: 0.75rem 1rem;
position: relative;
}
/* Position nav links as dropdown */
.nav-links {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: var(--bg);
border-bottom: 1px solid var(--border);
flex-direction: column;
padding: 0;
gap: 0;
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease, padding 0.3s ease;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
z-index: 1000;
}
/* Open state */
.nav-open .nav-links {
max-height: 400px;
padding: 1rem 0;
}
/* Style individual links */
.nav-links a,
.nav-links button {
width: 100%;
padding: 0.875rem 1.5rem;
text-align: left;
border-bottom: 1px solid var(--border);
font-size: 1rem;
}
.nav-links a:hover,
.nav-links button:hover {
background: var(--bg-alt);
}
.nav-links a:last-child,
.nav-links button:last-child {
border-bottom: none;
}
/* Style buttons in mobile menu */
.nav-links .btn-primary,
.nav-links .btn-secondary {
margin: 0.5rem 1rem;
width: calc(100% - 2rem);
text-align: center;
border-radius: var(--radius);
}
}
/* Extra small screens */
@media (max-width: 480px) {
nav {
padding: 0.5rem 0.75rem;
}
.logo {
font-size: 1.25rem;
}
}Add to CSS when menu is open:
/* Prevent body scroll when mobile menu is open */
body.nav-menu-open {
overflow: hidden;
}Add to JavaScript:
function toggleMobileNav() {
const nav = document.getElementById('nav');
const toggle = nav.querySelector('.nav-toggle');
const isOpen = nav.classList.toggle('nav-open');
toggle.setAttribute('aria-expanded', isOpen);
// Prevent body scroll when menu is open
document.body.classList.toggle('nav-menu-open', isOpen);
}
function closeMobileNav() {
const nav = document.getElementById('nav');
const toggle = nav.querySelector('.nav-toggle');
nav.classList.remove('nav-open');
if (toggle) toggle.setAttribute('aria-expanded', 'false');
document.body.classList.remove('nav-menu-open');
}Keep visible links but prioritize essential ones:
@media (max-width: 600px) {
.nav-links {
gap: 0.5rem;
}
.nav-links a:not(.nav-primary) {
display: none;
}
/* Show only Login/Signup and maybe Dashboard */
}Rejected because: Hides important navigation options, confusing UX.
Would conflict with existing dashboard bottom tabs on mobile.
| File | Changes |
|---|---|
/public/styles.css |
Add hamburger button styles + mobile nav breakpoint (~80 lines) |
/public/app.js |
Update updateNav() function + add toggleMobileNav(), closeMobileNav(), initMobileNav() (~40 lines) |
After implementation, verify:
- Hamburger button appears at mobile breakpoint (768px)
- Hamburger animates to X when open
- Menu slides down smoothly
- All nav links are accessible and readable
- Touch targets are 44px+ minimum
- Keyboard navigation works (Tab, Enter, Escape)
- Menu closes when clicking a link
- Menu closes when pressing Escape
- Works on both logged-in and logged-out states
- Dashboard bottom tabs don't conflict
- No horizontal scroll on mobile
- Works in Safari, Chrome, Firefox
- ARIA attributes:
aria-label,aria-expandedon toggle button - Keyboard: Escape should close menu
- Focus management: Move focus to first link when menu opens
- Screen reader: Announce menu state (expanded/collapsed)
- Minimal: CSS-only animations (no heavy JS libraries)
- No blocking resources: Pure CSS hamburger
- Accessibility-first: ARIA and keyboard support built-in
- Add CSS for hamburger button and mobile menu (styles.css)
- Update
updateNav()to include hamburger button (app.js) - Add toggle functions (app.js)
- Test on mobile viewport
- Test accessibility (keyboard nav, screen reader)
- Commit changes
- CSS changes: ~80 lines, 15-20 min
- JavaScript changes: ~40 lines, 15-20 min
- Testing: 15-20 min
- Total: ~45-60 minutes