feat(web): bottom tab bar navigation (M4)
- Mobile bottom tab bar: Chat · Hands · Mind · More. "More" opens the drawer for the long tail (Journal, Log, Settings, sessions); hamburger retired. - Auto-hides while the keyboard is open (body.kb) so the input pins to the keyboard; mobile-only (desktop keeps its header nav). - Removed now-redundant Mind/Hands from the drawer + their listeners. - Bottom-fill fix: #chat uses 100dvh (the visible viewport) — 100vh/inset:0 reach into the home-indicator zone iOS won't comfortably show, clipping the bar; dvh/svh exclude it. Tab bar is flex:none with a small fixed bottom padding (safe-area padding double-counts at dvh height), and the body bg matches the bar so any strip below #chat is seamless. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -677,6 +677,9 @@ select:hover {
|
||||
/* Wordmark + status dot — shown only in the mobile header (media query below) */
|
||||
.brand, .brand-dot { display: none; }
|
||||
|
||||
/* Bottom tab bar — mobile only (shown in the media query) */
|
||||
#tabbar { display: none; }
|
||||
|
||||
/* Hamburger Menu */
|
||||
.hamburger-menu {
|
||||
display: none;
|
||||
@@ -784,21 +787,26 @@ select:hover {
|
||||
@media screen and (max-width: 768px) {
|
||||
body {
|
||||
padding: 0;
|
||||
background: var(--bg-elev); /* matches the tab bar so any strip below #chat is seamless */
|
||||
}
|
||||
|
||||
#chat {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0; left: 0; right: 0;
|
||||
width: 100%;
|
||||
/* Height follows the *visible* viewport (above the keyboard); offsetTop
|
||||
shift is applied via JS transform so it tracks iOS's viewport panning. */
|
||||
height: var(--app-height, 100dvh);
|
||||
transform: translateY(var(--app-offset, 0px));
|
||||
height: 100dvh; /* the *visible* viewport (excludes the home-indicator zone);
|
||||
overrides the base 95vh. Body bg matches the bar below it. */
|
||||
background: var(--bg-dark);
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
}
|
||||
/* Only while the keyboard is open do we follow the *visible* viewport: release
|
||||
the bottom anchor and size from the top by the measured visible height. */
|
||||
body.kb #chat {
|
||||
bottom: auto;
|
||||
height: var(--app-height, 100dvh);
|
||||
transform: translateY(var(--app-offset, 0px));
|
||||
}
|
||||
|
||||
/* Show hamburger, hide desktop header controls */
|
||||
.hamburger-menu {
|
||||
@@ -857,14 +865,51 @@ select:hover {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* Input area - bigger touch targets */
|
||||
/* Input area - bigger touch targets. The tab bar owns the bottom safe-area
|
||||
inset now (the input is no longer the bottom-most element). */
|
||||
#input {
|
||||
padding: 12px;
|
||||
padding-bottom: calc(12px + env(safe-area-inset-bottom));
|
||||
padding-left: calc(12px + env(safe-area-inset-left));
|
||||
padding-right: calc(12px + env(safe-area-inset-right));
|
||||
}
|
||||
|
||||
/* Bottom tab bar */
|
||||
#tabbar {
|
||||
display: flex;
|
||||
flex: none; /* never let it be compressed/clipped by the flex column */
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--bg-elev);
|
||||
padding-bottom: 6px; /* 100dvh already excludes the home-indicator zone */
|
||||
padding-left: env(safe-area-inset-left);
|
||||
padding-right: env(safe-area-inset-right);
|
||||
}
|
||||
#tabbar .tab {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 3px;
|
||||
padding: 7px 0 5px;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
color: var(--text-fade);
|
||||
font-family: var(--font-console);
|
||||
text-decoration: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
#tabbar .tab:hover { background: none; }
|
||||
#tabbar .tab:active { background: var(--accent-soft); }
|
||||
#tabbar .tab .ti { font-size: 1.3rem; line-height: 1; filter: grayscale(.45); }
|
||||
#tabbar .tab .tl { font-size: .64rem; letter-spacing: .3px; }
|
||||
#tabbar .tab.active { color: var(--accent); }
|
||||
#tabbar .tab.active .ti { filter: none; }
|
||||
body.kb #tabbar { display: none; } /* keyboard open ⇒ hide so input pins to keyboard */
|
||||
|
||||
/* The "More" tab is the menu trigger now — retire the hamburger. */
|
||||
.hamburger-menu { display: none !important; }
|
||||
|
||||
#userInput {
|
||||
font-size: 16px; /* Prevents zoom on iOS */
|
||||
padding: 12px;
|
||||
|
||||
Reference in New Issue
Block a user