refactor(ui): rework top bar nav as cockpit switch row (#697)

Replace the sliding accent pill and blurred underline with material-at-
rest tabs that speak the cyan identity language used elsewhere in the
chrome. Each tab is a full-height button with a tracked-mono uppercase
label, and the active tab carries a crisp 2px cyan rail flush with the
bar's bottom edge.

Drops the <Highlight> primitive, the springs import, and the unused
navTabValue memo/prop from EditorLayout.
This commit is contained in:
Anso
2026-04-19 15:11:16 -04:00
committed by GitHub
parent 9483a09cee
commit ef4455f68d
2 changed files with 27 additions and 39 deletions
-4
View File
@@ -378,9 +378,6 @@ export default function EditorLayout() {
return items; return items;
}, [isAdmin, isPaid, license?.variant, can]); }, [isAdmin, isPaid, license?.variant, can]);
// Only highlight a tab if activeView matches a nav item
const navTabValue = navItems.some(i => i.value === activeView) ? activeView : undefined;
// Reset editor state (extracted from Home button onClick) // Reset editor state (extracted from Home button onClick)
const resetEditorState = () => { const resetEditorState = () => {
setSelectedFile(null); setSelectedFile(null);
@@ -2484,7 +2481,6 @@ export default function EditorLayout() {
<TopBar <TopBar
activeView={activeView} activeView={activeView}
navItems={navItems} navItems={navItems}
navTabValue={navTabValue}
onNavigate={handleNavigate} onNavigate={handleNavigate}
mobileNavOpen={mobileNavOpen} mobileNavOpen={mobileNavOpen}
onMobileNavOpenChange={setMobileNavOpen} onMobileNavOpenChange={setMobileNavOpen}
+27 -35
View File
@@ -3,8 +3,6 @@ import type { LucideIcon } from 'lucide-react';
import { Menu } from 'lucide-react'; import { Menu } from 'lucide-react';
import { Button } from './ui/button'; import { Button } from './ui/button';
import { Sheet, SheetContent, SheetTrigger } from './ui/sheet'; import { Sheet, SheetContent, SheetTrigger } from './ui/sheet';
import { Highlight, HighlightItem } from './animate-ui/primitives/effects/highlight';
import { springs } from '@/lib/motion';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
export interface TopBarNavItem { export interface TopBarNavItem {
@@ -16,7 +14,6 @@ export interface TopBarNavItem {
interface TopBarProps { interface TopBarProps {
activeView: string; activeView: string;
navItems: TopBarNavItem[]; navItems: TopBarNavItem[];
navTabValue: string | undefined;
onNavigate: (value: string) => void; onNavigate: (value: string) => void;
mobileNavOpen: boolean; mobileNavOpen: boolean;
onMobileNavOpenChange: (open: boolean) => void; onMobileNavOpenChange: (open: boolean) => void;
@@ -27,7 +24,6 @@ interface TopBarProps {
export function TopBar({ export function TopBar({
activeView, activeView,
navItems, navItems,
navTabValue,
onNavigate, onNavigate,
mobileNavOpen, mobileNavOpen,
onMobileNavOpenChange, onMobileNavOpenChange,
@@ -46,37 +42,33 @@ export function TopBar({
<div className="flex-1 min-w-0" /> <div className="flex-1 min-w-0" />
{/* CENTER ZONE: Navigation (hidden on mobile) */} {/* CENTER ZONE: Navigation (hidden on mobile) */}
<nav aria-label="Primary" className="hidden md:flex justify-center"> <nav aria-label="Primary" className="hidden md:flex self-stretch items-stretch">
<Highlight {navItems.map(({ value, label, icon: Icon }) => {
className="inset-0 rounded-md bg-accent" const isActive = activeView === value;
value={navTabValue} return (
controlledItems <button
mode="children" key={value}
click={false} onClick={() => onNavigate(value)}
transition={springs.snappy} aria-label={label}
> aria-current={isActive ? 'page' : undefined}
<div className="inline-flex items-center rounded-lg p-1 gap-0.5"> className={cn(
{navItems.map(({ value, label, icon: Icon }) => ( 'relative inline-flex h-full items-center gap-2 px-4',
<HighlightItem key={value} value={value}> 'font-mono text-[10px] uppercase tracking-[0.18em] transition-colors',
<button 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand/50',
onClick={() => onNavigate(value)} isActive ? 'text-foreground' : 'text-muted-foreground hover:text-foreground',
aria-label={label} )}
aria-current={activeView === value ? 'page' : undefined} >
className={cn( <Icon className="w-4 h-4 shrink-0" strokeWidth={1.5} />
'relative inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md transition-colors', <span className="hidden xl:inline">{label}</span>
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand/50', {isActive && (
activeView === value <span
? 'text-foreground after:absolute after:bottom-0 after:left-1/4 after:right-1/4 after:h-[2px] after:rounded-full after:bg-brand after:blur-[2px]' aria-hidden
: 'text-muted-foreground hover:text-foreground', className="pointer-events-none absolute inset-x-0 -bottom-px h-[2px] bg-brand"
)} />
> )}
<Icon className="w-4 h-4 shrink-0" strokeWidth={1.5} /> </button>
<span className="hidden xl:inline">{label}</span> );
</button> })}
</HighlightItem>
))}
</div>
</Highlight>
</nav> </nav>
{/* RIGHT ZONE: Utilities + identity pin */} {/* RIGHT ZONE: Utilities + identity pin */}