fix: one menu, at the left, holding whatever the page has
The previous attempt split it in two — a session button on the left, the site menu on the right — which is not what was asked for and is worse: a second menu an inch from the first is a choice nobody wants to make. One button. It sits at the left-hand end of the bar on a phone, where it always fits, and what is behind it changes with where you are: the site's sections always, plus the questions in this session, or the contents of the article you are reading. A control that changes what it holds must at least not change where it is. Reading registers its contents with the same mechanism a session uses for its questions, so the drawer grows the tab and nothing else has to know. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
This commit is contained in:
parent
9658ddb207
commit
658482c53a
3 changed files with 49 additions and 46 deletions
|
|
@ -1,4 +1,6 @@
|
|||
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||
import useMediaQuery from '../hooks/useMediaQuery'
|
||||
import { useSessionDrawer } from '../context/SessionDrawer'
|
||||
import RichText from './RichText'
|
||||
/**
|
||||
* Article prose goes through the same renderer as everything else.
|
||||
|
|
@ -111,6 +113,16 @@ export default function ArticleReader({
|
|||
// you consult, and a wall of prose hides the one heading you came for.
|
||||
const [openIds, setOpenIds] = useState({})
|
||||
const [drawerOpen, setDrawerOpen] = useState(false)
|
||||
// On a phone the contents live behind the same button as the site menu, the
|
||||
// way a session's questions do: one control at the top left whose contents
|
||||
// change with where you are, rather than a second menu to hunt for. The rail
|
||||
// is on screen above 1150px, so there is nothing to open there.
|
||||
const narrow = useMediaQuery('(max-width: 1150px)')
|
||||
const { register: registerDrawer } = useSessionDrawer()
|
||||
useEffect(() => {
|
||||
if (!narrow || bare) return undefined
|
||||
return registerDrawer(() => setDrawerOpen(true))
|
||||
}, [narrow, bare, registerDrawer])
|
||||
// Collapsing the contents rail hands its width to the prose.
|
||||
const [railOpen, setRailOpen] = useState(() => stored(RAIL_KEY, 'open') !== 'closed')
|
||||
const [size, setSize] = useState(() => (SIZES.includes(stored(SIZE_KEY, 'm')) ? stored(SIZE_KEY, 'm') : 'm'))
|
||||
|
|
|
|||
|
|
@ -281,17 +281,37 @@ export default function Navbar({ onSignIn, onRegister, onSearch }) {
|
|||
for stays put; where you are going gets out of the way while you read. */}
|
||||
<div className="navbar-primary">
|
||||
<div className="container navbar-inner">
|
||||
{/* While a session is open on a phone, the questions get their own
|
||||
control on the left and the site menu keeps the right. One burger
|
||||
that changed meaning depending on where you were meant the two
|
||||
menus took turns, and the one you wanted was the other one. */}
|
||||
{sessionDrawer.opener && (
|
||||
<button type="button" className="nav-session-burger"
|
||||
aria-label="Questions in this session" onClick={sessionDrawer.opener}>
|
||||
<span aria-hidden="true">☰</span>
|
||||
</button>
|
||||
)}
|
||||
<Link to="/" className="logo" onClick={() => setMenuOpen(false)}
|
||||
{/* One menu, at the left-hand end on a phone. What is inside it
|
||||
changes with where you are — the site's sections always, plus the
|
||||
questions in this session, or the contents of what you are
|
||||
reading — but it is always the same button in the same place. Two
|
||||
buttons was the previous attempt and it was wrong: a second menu
|
||||
an inch from the first is a choice nobody wants to make.
|
||||
|
||||
It opens the page's own drawer where there is one, because the
|
||||
site menu is a tab inside that; otherwise the site menu itself. */}
|
||||
<button
|
||||
className="nav-burger"
|
||||
onClick={() => (sessionDrawer.opener ? sessionDrawer.opener() : setMenuOpen(v => !v))}
|
||||
aria-label={sessionDrawer.opener ? 'Menu and contents' : 'Menu'}
|
||||
aria-expanded={sessionDrawer.opener ? undefined : menuOpen}
|
||||
>
|
||||
<span style={{
|
||||
display: 'block', width: 22, height: 2, background: 'currentColor',
|
||||
borderRadius: 2, transition: 'transform 0.2s, opacity 0.2s',
|
||||
transform: menuOpen ? 'translateY(7px) rotate(45deg)' : 'none',
|
||||
}} />
|
||||
<span style={{
|
||||
display: 'block', width: 22, height: 2, background: 'currentColor',
|
||||
borderRadius: 2, opacity: menuOpen ? 0 : 1, transition: 'opacity 0.2s',
|
||||
}} />
|
||||
<span style={{
|
||||
display: 'block', width: 22, height: 2, background: 'currentColor',
|
||||
borderRadius: 2, transition: 'transform 0.2s, opacity 0.2s',
|
||||
transform: menuOpen ? 'translateY(-7px) rotate(-45deg)' : 'none',
|
||||
}} />
|
||||
</button>
|
||||
<Link to="/" className="logo" onClick={() => setMenuOpen(false)}
|
||||
onMouseEnter={() => setPeek(true)} onFocus={() => setPeek(true)}>🏥 PedsHub</Link>
|
||||
{user && <GlobalSearch onOpenOverlay={onSearch} />}
|
||||
|
||||
|
|
@ -300,31 +320,7 @@ export default function Navbar({ onSignIn, onRegister, onSearch }) {
|
|||
<FeedbackBadge />
|
||||
<JobsBadge jobs={jobs} />
|
||||
<AccountMenu user={user} onLogout={logout} />
|
||||
{/* Always the site menu. It used to become the session's list of
|
||||
questions while a session was open, which put both menus on
|
||||
the same button and neither where the thumb expected it; the
|
||||
session now has its own control at the other end of the bar. */}
|
||||
<button
|
||||
className="nav-burger"
|
||||
onClick={() => setMenuOpen(v => !v)}
|
||||
aria-label="Menu"
|
||||
aria-expanded={menuOpen}
|
||||
>
|
||||
<span style={{
|
||||
display: 'block', width: 22, height: 2, background: 'currentColor',
|
||||
borderRadius: 2, transition: 'transform 0.2s, opacity 0.2s',
|
||||
transform: menuOpen ? 'translateY(7px) rotate(45deg)' : 'none',
|
||||
}} />
|
||||
<span style={{
|
||||
display: 'block', width: 22, height: 2, background: 'currentColor',
|
||||
borderRadius: 2, opacity: menuOpen ? 0 : 1, transition: 'opacity 0.2s',
|
||||
}} />
|
||||
<span style={{
|
||||
display: 'block', width: 22, height: 2, background: 'currentColor',
|
||||
borderRadius: 2, transition: 'transform 0.2s, opacity 0.2s',
|
||||
transform: menuOpen ? 'translateY(-7px) rotate(-45deg)' : 'none',
|
||||
}} />
|
||||
</button>
|
||||
|
||||
</div>
|
||||
) : (
|
||||
/* Logged-out: Sign In + Register */
|
||||
|
|
|
|||
|
|
@ -218,15 +218,6 @@ html, body { overflow-x: hidden; }
|
|||
|
||||
@media (prefers-reduced-motion: reduce) { .navbar-sections { transition: none; } }
|
||||
[data-theme="markdown"] .navbar .logo { color: #d4a96a; }
|
||||
/* The session's own control, at the left-hand end of the bar. Off the desktop
|
||||
layout entirely: there the list of questions is a rail beside the question,
|
||||
and a button for it would open a drawer over something already on screen. */
|
||||
.nav-session-burger {
|
||||
display: none; margin-right: 2px; padding: 6px 8px; cursor: pointer;
|
||||
border: 1px solid rgba(255, 255, 255, .22); border-radius: 8px;
|
||||
background: none; color: var(--navbar-fg); font-size: 1rem; line-height: 1;
|
||||
}
|
||||
.nav-session-burger:hover { border-color: rgba(255, 255, 255, .45); }
|
||||
.nav-burger { display: none; background: none !important; border: 0 !important; cursor: pointer; color: var(--navbar-fg); padding: 6px; flex-direction: column; gap: 5px; opacity: 0.85; }
|
||||
/* Scoped to the dark bar. These were `.navbar a` and `.navbar button`, which
|
||||
also caught everything in the light section bar beneath it — near-white text
|
||||
|
|
@ -810,8 +801,12 @@ html, body { overflow-x: hidden; }
|
|||
/* The burger carries the sections on a phone; the strip would be a second
|
||||
copy of the same links competing for the same thumb. */
|
||||
.nav-burger { display: flex; }
|
||||
.nav-session-burger { display: inline-flex; align-items: center; }
|
||||
.nav-logout { display: none; }
|
||||
/* At the left-hand end, before the logo — it is the first child of the bar
|
||||
now, so nothing here has to reorder it. A control that changes what it
|
||||
holds must at least not change where it is: reaching for the top right and
|
||||
finding the account chip is how a menu gets a reputation. */
|
||||
.navbar-inner > .nav-burger { margin-right: 2px; }
|
||||
/* Hide the whole strip, wrapper and arrows, not just the link row. */
|
||||
.navbar-sections .ss-wrap { display: none; }
|
||||
.navbar-sections, .navbar-sections-inner, .navbar-sections:focus-within { height: 42px; }
|
||||
|
|
|
|||
Loading…
Reference in a new issue