Skip to content

Commit 0cd257a

Browse files
feat(web): Replace bespoke navigation menu with shadcn navigation menu (#11117)
Co-authored-by: Roo Code <roomote@roocode.com>
1 parent d8f65b6 commit 0cd257a

File tree

4 files changed

+293
-100
lines changed

4 files changed

+293
-100
lines changed

apps/web-roo-code/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
},
1414
"dependencies": {
1515
"@radix-ui/react-dialog": "^1.1.15",
16+
"@radix-ui/react-navigation-menu": "^1.2.14",
1617
"@radix-ui/react-slot": "^1.2.4",
1718
"@roo-code/evals": "workspace:^",
1819
"@roo-code/types": "^1.108.0",

apps/web-roo-code/src/components/chromes/nav-bar.tsx

Lines changed: 136 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,17 @@ import { EXTERNAL_LINKS } from "@/lib/constants"
1313
import { useLogoSrc } from "@/lib/hooks/use-logo-src"
1414
import { ScrollButton } from "@/components/ui"
1515
import ThemeToggle from "@/components/chromes/theme-toggle"
16-
import { Brain, ChevronDown, Cloud, Puzzle, Slack, X } from "lucide-react"
16+
import { Brain, Cloud, Puzzle, Slack, X } from "lucide-react"
17+
import {
18+
NavigationMenu,
19+
NavigationMenuContent,
20+
NavigationMenuItem,
21+
NavigationMenuLink,
22+
NavigationMenuList,
23+
NavigationMenuTrigger,
24+
navigationMenuTriggerStyle,
25+
} from "@/components/ui/navigation-menu"
26+
import { cn } from "@/lib/utils"
1727

1828
function LinearIcon({ className }: { className?: string }) {
1929
return (
@@ -30,115 +40,142 @@ interface NavBarProps {
3040

3141
export function NavBar({ stars, downloads }: NavBarProps) {
3242
const [isMenuOpen, setIsMenuOpen] = useState(false)
33-
const [openDropdown, setOpenDropdown] = useState<string | null>(null)
3443
const logoSrc = useLogoSrc()
3544

3645
return (
3746
<header className="sticky font-light top-0 z-50 border-b border-border bg-background/80 backdrop-blur-md">
3847
<div className="container flex h-16 items-center justify-between px-4 sm:px-6 lg:px-8">
39-
<div className="flex items-center">
48+
<div className="flex items-center flex-shrink-0">
4049
<Link href="/" className="flex items-center">
4150
<Image src={logoSrc} alt="Roo Code Logo" width={130} height={24} className="h-[24px] w-auto" />
4251
</Link>
4352
</div>
4453

4554
{/* Desktop Navigation */}
46-
<nav className="grow ml-6 hidden text-sm md:flex md:items-center">
47-
{/* Product Dropdown */}
48-
<div
49-
className="relative"
50-
onMouseEnter={() => setOpenDropdown("product")}
51-
onMouseLeave={() => setOpenDropdown(null)}>
52-
<button className="flex items-center px-4 py-6 gap-1 transition-transform duration-200 hover:scale-105 hover:text-foreground">
53-
Product
54-
<ChevronDown className="size-3 ml-1 mt-0.5" />
55-
</button>
56-
<div
57-
className={`absolute left-0 top-12 mt-2 w-[260px] rounded-md border border-border bg-background py-1 shadow-lg transition-all duration-200 ${openDropdown === "product" ? "opacity-100 translate-y-0 pointer-events-auto" : "opacity-0 -translate-y-2 pointer-events-none"}`}>
58-
<Link
59-
href="/extension"
60-
className="block px-4 py-2 text-sm transition-colors hover:bg-accent hover:text-foreground"
61-
onClick={() => setOpenDropdown(null)}>
62-
<Puzzle className="size-3 inline mr-2 -mt-0.5" />
63-
Roo Code VS Code Extension
64-
</Link>
65-
<Link
66-
href="/cloud"
67-
className="block px-4 py-2 text-sm transition-colors hover:bg-accent hover:text-foreground"
68-
onClick={() => setOpenDropdown(null)}>
69-
<Cloud className="size-3 inline mr-2 -mt-0.5" />
70-
Roo Code Cloud
71-
</Link>
72-
<Link
73-
href="/slack"
74-
className="block px-4 py-2 text-sm transition-colors hover:bg-accent hover:text-foreground"
75-
onClick={() => setOpenDropdown(null)}>
76-
<Slack className="size-3 inline mr-2 -mt-0.5" />
77-
Roo Code for Slack
78-
</Link>
79-
<Link
80-
href="/linear"
81-
className="block px-4 py-2 text-sm transition-colors hover:bg-accent hover:text-foreground"
82-
onClick={() => setOpenDropdown(null)}>
83-
<LinearIcon className="size-3 inline mr-2 -mt-0.5" />
84-
Roo Code for Linear
85-
</Link>
86-
<Link
87-
href="/provider"
88-
className="block px-4 py-2 text-sm transition-colors hover:bg-accent hover:text-foreground"
89-
onClick={() => setOpenDropdown(null)}>
90-
<Brain className="size-3 inline mr-2 -mt-0.5" />
91-
Roo Code Router
92-
</Link>
93-
</div>
94-
</div>
95-
{/* Resources Dropdown */}
96-
<div
97-
className="relative"
98-
onMouseEnter={() => setOpenDropdown("resources")}
99-
onMouseLeave={() => setOpenDropdown(null)}>
100-
<button className="flex items-center px-4 py-6 gap-1 transition-transform duration-200 hover:scale-105 hover:text-foreground">
101-
Resources
102-
<ChevronDown className="size-3 ml-1 mt-0.5" />
103-
</button>
104-
<div
105-
className={`absolute left-0 top-12 mt-2 w-40 rounded-md border border-border bg-background py-1 shadow-lg transition-all duration-200 ${openDropdown === "resources" ? "opacity-100 translate-y-0 pointer-events-auto" : "opacity-0 -translate-y-2 pointer-events-none"}`}>
106-
<Link
107-
href="/evals"
108-
className="block px-4 py-2 text-sm transition-colors hover:bg-accent hover:text-foreground"
109-
onClick={() => setOpenDropdown(null)}>
110-
Evals
111-
</Link>
112-
<a
113-
href={EXTERNAL_LINKS.DISCORD}
114-
target="_blank"
115-
rel="noopener noreferrer"
116-
className="block px-4 py-2 text-sm transition-colors hover:bg-accent hover:text-foreground"
117-
onClick={() => setOpenDropdown(null)}>
118-
Discord
119-
</a>
120-
<a
121-
href={EXTERNAL_LINKS.SECURITY}
122-
target="_blank"
123-
rel="noopener noreferrer"
124-
className="block px-4 py-2 text-sm transition-colors hover:bg-accent hover:text-foreground"
125-
onClick={() => setOpenDropdown(null)}>
126-
Trust Center
127-
</a>
128-
</div>
129-
</div>
130-
<a
131-
href={EXTERNAL_LINKS.DOCUMENTATION}
132-
target="_blank"
133-
className="px-4 py-6 transition-transform duration-200 hover:scale-105 hover:text-foreground">
134-
Docs
135-
</a>
136-
<Link
137-
href="/pricing"
138-
className="px-4 py-6 transition-transform duration-200 hover:scale-105 hover:text-foreground">
139-
Pricing
140-
</Link>
141-
</nav>
55+
<NavigationMenu className="grow ml-6 hidden text-sm md:flex">
56+
<NavigationMenuList>
57+
{/* Product Dropdown */}
58+
<NavigationMenuItem>
59+
<NavigationMenuTrigger className="bg-transparent font-light">Product</NavigationMenuTrigger>
60+
<NavigationMenuContent>
61+
<ul className="grid min-w-[260px] gap-1 p-2">
62+
<li>
63+
<NavigationMenuLink asChild>
64+
<Link
65+
href="/extension"
66+
className="flex items-center select-none rounded-md px-3 py-2 text-sm leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground">
67+
<Puzzle className="size-3 mr-2" />
68+
Roo Code VS Code Extension
69+
</Link>
70+
</NavigationMenuLink>
71+
</li>
72+
<li>
73+
<NavigationMenuLink asChild>
74+
<Link
75+
href="/cloud"
76+
className="flex items-center select-none rounded-md px-3 py-2 text-sm leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground">
77+
<Cloud className="size-3 mr-2" />
78+
Roo Code Cloud
79+
</Link>
80+
</NavigationMenuLink>
81+
</li>
82+
<li>
83+
<NavigationMenuLink asChild>
84+
<Link
85+
href="/slack"
86+
className="flex items-center select-none rounded-md px-3 py-2 text-sm leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground">
87+
<Slack className="size-3 mr-2" />
88+
Roo Code for Slack
89+
</Link>
90+
</NavigationMenuLink>
91+
</li>
92+
<li>
93+
<NavigationMenuLink asChild>
94+
<Link
95+
href="/linear"
96+
className="flex items-center select-none rounded-md px-3 py-2 text-sm leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground">
97+
<LinearIcon className="size-3 mr-2" />
98+
Roo Code for Linear
99+
</Link>
100+
</NavigationMenuLink>
101+
</li>
102+
<li>
103+
<NavigationMenuLink asChild>
104+
<Link
105+
href="/provider"
106+
className="flex items-center select-none rounded-md px-3 py-2 text-sm leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground">
107+
<Brain className="size-3 mr-2" />
108+
Roo Code Router
109+
</Link>
110+
</NavigationMenuLink>
111+
</li>
112+
</ul>
113+
</NavigationMenuContent>
114+
</NavigationMenuItem>
115+
116+
{/* Resources Dropdown */}
117+
<NavigationMenuItem>
118+
<NavigationMenuTrigger className="bg-transparent font-light">
119+
Resources
120+
</NavigationMenuTrigger>
121+
<NavigationMenuContent>
122+
<ul className="grid min-w-[260px] gap-1 p-2">
123+
<li>
124+
<NavigationMenuLink asChild>
125+
<Link
126+
href="/evals"
127+
className="block select-none rounded-md px-3 py-2 text-sm leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground">
128+
Evals
129+
</Link>
130+
</NavigationMenuLink>
131+
</li>
132+
<li>
133+
<NavigationMenuLink asChild>
134+
<a
135+
href={EXTERNAL_LINKS.DISCORD}
136+
target="_blank"
137+
rel="noopener noreferrer"
138+
className="block select-none rounded-md px-3 py-2 text-sm leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground">
139+
Discord
140+
</a>
141+
</NavigationMenuLink>
142+
</li>
143+
<li>
144+
<NavigationMenuLink asChild>
145+
<a
146+
href={EXTERNAL_LINKS.SECURITY}
147+
target="_blank"
148+
rel="noopener noreferrer"
149+
className="block select-none rounded-md px-3 py-2 text-sm leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground">
150+
Trust Center
151+
</a>
152+
</NavigationMenuLink>
153+
</li>
154+
</ul>
155+
</NavigationMenuContent>
156+
</NavigationMenuItem>
157+
158+
{/* Docs Link */}
159+
<NavigationMenuItem>
160+
<NavigationMenuLink
161+
asChild
162+
className={cn(navigationMenuTriggerStyle(), "bg-transparent font-light")}>
163+
<a href={EXTERNAL_LINKS.DOCUMENTATION} target="_blank">
164+
Docs
165+
</a>
166+
</NavigationMenuLink>
167+
</NavigationMenuItem>
168+
169+
{/* Pricing Link */}
170+
<NavigationMenuItem>
171+
<NavigationMenuLink
172+
asChild
173+
className={cn(navigationMenuTriggerStyle(), "bg-transparent font-light")}>
174+
<Link href="/pricing">Pricing</Link>
175+
</NavigationMenuLink>
176+
</NavigationMenuItem>
177+
</NavigationMenuList>
178+
</NavigationMenu>
142179

143180
<div className="hidden md:flex md:items-center md:space-x-4 flex-shrink-0 font-medium">
144181
<div className="flex flex-row space-x-2 flex-shrink-0">
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import * as React from "react"
2+
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
3+
import { cva } from "class-variance-authority"
4+
import { ChevronDown } from "lucide-react"
5+
6+
import { cn } from "@/lib/utils"
7+
8+
const NavigationMenu = React.forwardRef<
9+
React.ElementRef<typeof NavigationMenuPrimitive.Root>,
10+
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
11+
>(({ className, children, ...props }, ref) => (
12+
<NavigationMenuPrimitive.Root
13+
ref={ref}
14+
className={cn("relative z-10 flex max-w-max flex-1 items-center", className)}
15+
{...props}>
16+
{children}
17+
<NavigationMenuViewport />
18+
</NavigationMenuPrimitive.Root>
19+
))
20+
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName
21+
22+
const NavigationMenuList = React.forwardRef<
23+
React.ElementRef<typeof NavigationMenuPrimitive.List>,
24+
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
25+
>(({ className, ...props }, ref) => (
26+
<NavigationMenuPrimitive.List
27+
ref={ref}
28+
className={cn("group flex flex-1 list-none items-center space-x-1", className)}
29+
{...props}
30+
/>
31+
))
32+
NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
33+
34+
const NavigationMenuItem = NavigationMenuPrimitive.Item
35+
36+
const navigationMenuTriggerStyle = cva(
37+
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=open]:text-accent-foreground data-[state=open]:bg-accent/50 data-[state=open]:hover:bg-accent data-[state=open]:focus:bg-accent",
38+
)
39+
40+
const NavigationMenuTrigger = React.forwardRef<
41+
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
42+
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
43+
>(({ className, children, ...props }, ref) => (
44+
<NavigationMenuPrimitive.Trigger
45+
ref={ref}
46+
className={cn(navigationMenuTriggerStyle(), "group", className)}
47+
{...props}>
48+
{children}{" "}
49+
<ChevronDown
50+
className="relative top-[1px] ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180"
51+
aria-hidden="true"
52+
/>
53+
</NavigationMenuPrimitive.Trigger>
54+
))
55+
NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName
56+
57+
const NavigationMenuContent = React.forwardRef<
58+
React.ElementRef<typeof NavigationMenuPrimitive.Content>,
59+
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
60+
>(({ className, ...props }, ref) => (
61+
<NavigationMenuPrimitive.Content
62+
ref={ref}
63+
className={cn(
64+
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ",
65+
className,
66+
)}
67+
{...props}
68+
/>
69+
))
70+
NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName
71+
72+
const NavigationMenuLink = NavigationMenuPrimitive.Link
73+
74+
const NavigationMenuViewport = React.forwardRef<
75+
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
76+
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
77+
>(({ className, ...props }, ref) => (
78+
<div className={cn("absolute left-0 top-full flex justify-center")}>
79+
<NavigationMenuPrimitive.Viewport
80+
className={cn(
81+
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
82+
className,
83+
)}
84+
ref={ref}
85+
{...props}
86+
/>
87+
</div>
88+
))
89+
NavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName
90+
91+
const NavigationMenuIndicator = React.forwardRef<
92+
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
93+
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
94+
>(({ className, ...props }, ref) => (
95+
<NavigationMenuPrimitive.Indicator
96+
ref={ref}
97+
className={cn(
98+
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
99+
className,
100+
)}
101+
{...props}>
102+
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
103+
</NavigationMenuPrimitive.Indicator>
104+
))
105+
NavigationMenuIndicator.displayName = NavigationMenuPrimitive.Indicator.displayName
106+
107+
export {
108+
navigationMenuTriggerStyle,
109+
NavigationMenu,
110+
NavigationMenuList,
111+
NavigationMenuItem,
112+
NavigationMenuContent,
113+
NavigationMenuTrigger,
114+
NavigationMenuLink,
115+
NavigationMenuIndicator,
116+
NavigationMenuViewport,
117+
}

0 commit comments

Comments
 (0)