fix(desktop-header): stop layout jumping via 3-column grid

The header used flex with the capsule centered by mx-auto, so the capsule's
auto-margins recomputed whenever anything in the row changed (and compounded
with framer-motion's shared-layout measurement on route change) — making the
whole row, incl. the language button, twitch when navigating (notably into
/admin).

Switch the inner row to grid grid-cols-[1fr_auto_1fr]: the capsule is anchored
to the true viewport center by the symmetric side columns, and the actions are
pinned to the right column (justify-self-end). A width change in one zone can no
longer shift another, so nothing jumps.
This commit is contained in:
c0mrade
2026-06-05 17:29:55 +03:00
parent 7de9b3b51b
commit 9fc681b56d

View File

@@ -191,9 +191,17 @@ export function AppShell({ children }: AppShellProps) {
{/* Desktop Header */} {/* Desktop Header */}
<header className="fixed left-0 right-0 top-0 z-50 hidden border-b border-dark-800/50 bg-dark-950/95 lg:block"> <header className="fixed left-0 right-0 top-0 z-50 hidden border-b border-dark-800/50 bg-dark-950/95 lg:block">
<div className="mx-auto flex h-14 max-w-[1600px] items-center gap-5 px-6"> {/* 3-зонный grid: лого | капсула | действия. Колонки 1fr_auto_1fr держат
капсулу строго по центру вьюпорта НЕЗАВИСИМО от ширины лого/действий,
а действия — у правого края. Поэтому ничего не «скачет» при переходах
(в т.ч. в админку): смена ширины в одной зоне не двигает другие. */}
<div className="mx-auto grid h-14 max-w-[1600px] grid-cols-[1fr_auto_1fr] items-center gap-4 px-6">
{/* Logo */} {/* Logo */}
<Link to="/" className="flex shrink-0 items-center gap-2.5" onClick={handleNavClick}> <Link
to="/"
className="flex shrink-0 items-center gap-2.5 justify-self-start"
onClick={handleNavClick}
>
<div className="relative flex h-8 w-8 flex-shrink-0 items-center justify-center overflow-hidden rounded-lg bg-dark-800"> <div className="relative flex h-8 w-8 flex-shrink-0 items-center justify-center overflow-hidden rounded-lg bg-dark-800">
<span <span
className={cn( className={cn(
@@ -218,9 +226,9 @@ export function AppShell({ children }: AppShellProps) {
</Link> </Link>
{/* Navigation — единая «капсула» (segmented control): все пункты видны {/* Navigation — единая «капсула» (segmented control): все пункты видны
всегда, без скролла/сжатия/сворачивания. Капсула центрируется в всегда, без скролла/сжатия/сворачивания. Центрируется средней
свободном пространстве между логотипом и кнопками (mx-auto). */} колонкой grid (justify-self-center), а не auto-margin'ами. */}
<nav className="mx-auto flex items-center gap-0.5 rounded-full border border-dark-800/70 bg-dark-900/50 p-1 shadow-sm backdrop-blur-sm"> <nav className="flex items-center gap-0.5 justify-self-center rounded-full border border-dark-800/70 bg-dark-900/50 p-1 shadow-sm backdrop-blur-sm">
{desktopNav.map((item) => renderNavLink(item.path, item.label, item.icon))} {desktopNav.map((item) => renderNavLink(item.path, item.label, item.icon))}
{isAdmin && ( {isAdmin && (
<> <>
@@ -230,8 +238,8 @@ export function AppShell({ children }: AppShellProps) {
)} )}
</nav> </nav>
{/* Right side actions — pinned right (nav centers via mx-auto), never shrink */} {/* Right side actions — правая колонка grid, прижата к краю, не сжимается */}
<div className="flex shrink-0 items-center gap-2"> <div className="flex shrink-0 items-center gap-2 justify-self-end">
<button <button
onClick={() => { onClick={() => {
haptic.impact('light'); haptic.impact('light');