refactor(layout): floating sidebar + floating top header
Operator feedback 2026-06-04: - "pull out the left nav to make it look like it is its own floating section versus attached to the top-left" - "this is also true for the header for the pages" - "the divider that runs between the right side and the sidebar. let's make that divider go away so it all feels like one large page" Changes: components/Layout.js - Outer flex shell: `md:p-4 md:gap-4` pulls the sidebar AND the main column in from the viewport edges on desktop. Mobile (< 768px) is unaffected because the sidebar is `hidden md:flex` already and the TopSearchBar / page content still run flush there. - Desktop sidebar: `md:rounded-3xl md:overflow-hidden` so the rail reads as a floating chip. Dropped `var(--rim-light-outer)` from its box-shadow stack — that 1px outer ring was literally the "vertical divider between the sidebar and the right side" the operator wanted gone. Chip edges are now defined by `var(--elevation-ambient)` (drop shadow) + the glass-surface-mid tint against the body gradient that shows through `md:gap-4`. - Main column: added `md:gap-4 min-w-0`. The gap separates the floating TopSearchBar from the page content vertically so they read as two distinct floating chrome elements; min-w-0 prevents long card titles / table cells from blowing past the column width. components/ui/TopSearchBar.js - `md:rounded-2xl` so the header reads as a floating chip on desktop. Mobile stays square (header is edge-to-edge there). - Box-shadow: `rim-light-inner` + `elevation-ambient`. Deliberately no `rim-light-outer` — the prior "remove the divider" feedback applies to any visible 1px edge, and a hairline outer ring would re-introduce one on mobile where the bar abuts content directly. Tests: - npm run test:run: 113/113 - npm run lint: clean - npm run build: green Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
7a01469e5f
commit
3cfdab25c3
2 changed files with 44 additions and 13 deletions
|
|
@ -664,7 +664,18 @@ export default function Layout({ children, user = null, showSearch = false }) {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen">
|
// Outer shell — `md:p-4 md:gap-4` pulls both the sidebar and the
|
||||||
|
// main column in from the viewport edges so the sidebar and the
|
||||||
|
// top header read as floating chrome chips (operator feedback
|
||||||
|
// 2026-06-04: "pull out the left nav to make it look like it is
|
||||||
|
// its own floating section versus attached to the top-left"). The
|
||||||
|
// `md:gap-4` on this row also creates the visible page-body
|
||||||
|
// background between the sidebar's right edge and the main
|
||||||
|
// column, which replaces the previous hard divider — "all feels
|
||||||
|
// like one large page" is achieved by the hearth gradient
|
||||||
|
// showing through the gap. Mobile (< 768px) keeps the current
|
||||||
|
// flush layout because the sidebar is `hidden md:flex` anyway.
|
||||||
|
<div className="flex h-screen md:p-4 md:gap-4">
|
||||||
{/* Mobile Navigation - Bottom bar for mobile */}
|
{/* Mobile Navigation - Bottom bar for mobile */}
|
||||||
<MobileNavigation
|
<MobileNavigation
|
||||||
onMenuOpen={() => setIsMobileMenuOpen(true)}
|
onMenuOpen={() => setIsMobileMenuOpen(true)}
|
||||||
|
|
@ -815,14 +826,21 @@ export default function Layout({ children, user = null, showSearch = false }) {
|
||||||
|
|
||||||
{/* Desktop Sidebar - Hidden on mobile. Liquid Glass mid-tint, ambient
|
{/* Desktop Sidebar - Hidden on mobile. Liquid Glass mid-tint, ambient
|
||||||
elevation + rim-light edges; the page background visibly cools
|
elevation + rim-light edges; the page background visibly cools
|
||||||
through the rail. */}
|
through the rail. 2026-06-04 floating-chrome pass: rounded-3xl
|
||||||
|
+ overflow-hidden so the rail reads as its own floating chip;
|
||||||
|
`var(--rim-light-outer)` dropped from the box-shadow stack
|
||||||
|
because that 1px outer ring was the "vertical divider between
|
||||||
|
the sidebar and the right side" the operator wanted gone. The
|
||||||
|
chip's edge is now defined solely by `var(--elevation-ambient)`
|
||||||
|
(drop shadow) + the glass-surface-mid tint against the body
|
||||||
|
gradient that shows through `md:gap-4` on the outer flex. */}
|
||||||
<div
|
<div
|
||||||
className="hidden md:flex md:static inset-y-0 left-0 w-64"
|
className="hidden md:flex md:static inset-y-0 left-0 w-64 md:rounded-3xl md:overflow-hidden"
|
||||||
style={{
|
style={{
|
||||||
background: 'var(--glass-surface-mid)',
|
background: 'var(--glass-surface-mid)',
|
||||||
backdropFilter: 'blur(var(--glass-blur-mid)) saturate(var(--glass-saturate))',
|
backdropFilter: 'blur(var(--glass-blur-mid)) saturate(var(--glass-saturate))',
|
||||||
WebkitBackdropFilter: 'blur(var(--glass-blur-mid)) saturate(var(--glass-saturate))',
|
WebkitBackdropFilter: 'blur(var(--glass-blur-mid)) saturate(var(--glass-saturate))',
|
||||||
boxShadow: 'var(--rim-light-inner), var(--rim-light-outer), var(--elevation-ambient)',
|
boxShadow: 'var(--rim-light-inner), var(--elevation-ambient)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="p-4 h-full flex flex-col w-full">
|
<div className="p-4 h-full flex flex-col w-full">
|
||||||
|
|
@ -933,8 +951,14 @@ export default function Layout({ children, user = null, showSearch = false }) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main Content */}
|
{/* Main Content. `md:gap-4` adds vertical breathing room between
|
||||||
<div className="flex-1 flex flex-col pb-16 md:pb-0">
|
the floating TopSearchBar header and the page content so they
|
||||||
|
read as two distinct floating chrome elements over the body
|
||||||
|
gradient (rather than the header sitting flush against the
|
||||||
|
content). `min-w-0` prevents flex children from blowing past
|
||||||
|
the column width when long card titles or table cells refuse
|
||||||
|
to wrap. */}
|
||||||
|
<div className="flex-1 flex flex-col pb-16 md:pb-0 md:gap-4 min-w-0">
|
||||||
{/* Global top bar — redesign-v2 sub-convoy #3 (2026-06-04).
|
{/* Global top bar — redesign-v2 sub-convoy #3 (2026-06-04).
|
||||||
Rendered for ALL authenticated pages so the search +
|
Rendered for ALL authenticated pages so the search +
|
||||||
notifications + user-menu chrome is consistent everywhere
|
notifications + user-menu chrome is consistent everywhere
|
||||||
|
|
|
||||||
|
|
@ -62,17 +62,24 @@ export default function TopSearchBar({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header
|
<header
|
||||||
className="px-4 sm:px-6 py-3 flex items-center gap-3 sm:gap-4"
|
// Floating-chrome pass 2026-06-04: `md:rounded-2xl` so the bar
|
||||||
|
// reads as its own floating chip on desktop (Layout's
|
||||||
|
// `md:p-4 md:gap-4` puts visible body gradient on all four sides
|
||||||
|
// of it). Mobile keeps square corners since the header runs
|
||||||
|
// edge-to-edge there. The shadow stack pairs `rim-light-inner`
|
||||||
|
// (subtle top inset highlight kept from the prior pass) with
|
||||||
|
// `elevation-ambient` (drop shadow) so it floats over the body
|
||||||
|
// gradient. We deliberately did NOT add `rim-light-outer` here:
|
||||||
|
// the operator's "remove the divider" feedback from the prior
|
||||||
|
// pass applies to any visible 1px edge, and a hairline outer
|
||||||
|
// ring would re-introduce one on mobile where the bar abuts
|
||||||
|
// content directly.
|
||||||
|
className="px-4 sm:px-6 py-3 flex items-center gap-3 sm:gap-4 md:rounded-2xl"
|
||||||
style={{
|
style={{
|
||||||
background: 'var(--glass-surface-mid)',
|
background: 'var(--glass-surface-mid)',
|
||||||
backdropFilter: 'blur(12px) saturate(180%)',
|
backdropFilter: 'blur(12px) saturate(180%)',
|
||||||
WebkitBackdropFilter: 'blur(12px) saturate(180%)',
|
WebkitBackdropFilter: 'blur(12px) saturate(180%)',
|
||||||
// The hairline border-bottom (`0 1px 0 var(--border)`) was
|
boxShadow: 'var(--rim-light-inner), var(--elevation-ambient)',
|
||||||
// removed 2026-06-04 per operator feedback ("almost looks
|
|
||||||
// seamless between that header and the rest of that page
|
|
||||||
// content"). The rim-light-inner highlight stays so the bar
|
|
||||||
// still reads as elevated chrome against the gradient body.
|
|
||||||
boxShadow: 'var(--rim-light-inner)',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Search affordance — read-only-ish input that opens the
|
{/* Search affordance — read-only-ish input that opens the
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue