Skip to content

Commit 3f35a85

Browse files
Roméo MaignalRoméo Maignal
authored andcommitted
feat(navbar): the list of number viz# is now replaced by a drop-down menu
1 parent 6985edc commit 3f35a85

2 files changed

Lines changed: 158 additions & 4 deletions

File tree

src/components/NavigationBar.css

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,71 @@
7272
background: var(--accent-dim);
7373
}
7474

75+
/* Visualizations dropdown */
76+
.navbar-dropdown-wrapper {
77+
position: relative;
78+
list-style: none;
79+
}
80+
81+
.navbar-dropdown-trigger {
82+
cursor: default;
83+
display: flex;
84+
align-items: center;
85+
gap: 0.3rem;
86+
user-select: none;
87+
}
88+
89+
.dropdown-arrow {
90+
display: inline-block;
91+
transition: transform 0.2s ease;
92+
flex-shrink: 0;
93+
}
94+
95+
.dropdown-arrow.open {
96+
transform: rotate(180deg);
97+
}
98+
99+
.navbar-dropdown {
100+
display: none;
101+
position: absolute;
102+
top: 100%;
103+
left: 50%;
104+
transform: translateX(-50%);
105+
/* padding-top bridges the gap so the hover zone is unbroken */
106+
padding: 0.5rem 0.35rem 0.35rem;
107+
list-style: none;
108+
min-width: 110px;
109+
z-index: 200;
110+
flex-direction: column;
111+
gap: 0.1rem;
112+
}
113+
114+
.navbar-dropdown-inner {
115+
background: var(--nav-bg);
116+
backdrop-filter: blur(16px);
117+
-webkit-backdrop-filter: blur(16px);
118+
border: 1px solid var(--border-sep);
119+
border-radius: 8px;
120+
padding: 0.35rem;
121+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
122+
display: flex;
123+
flex-direction: column;
124+
gap: 0.1rem;
125+
}
126+
127+
.navbar-dropdown.open {
128+
display: flex;
129+
}
130+
131+
.navbar-dropdown .navbar-item {
132+
display: block;
133+
white-space: nowrap;
134+
}
135+
136+
.mobile-viz-item {
137+
display: none;
138+
}
139+
75140
.navbar-right {
76141
display: flex;
77142
align-items: center;
@@ -143,4 +208,13 @@
143208
font-size: 1.1rem;
144209
padding: 0.75rem 1.5rem;
145210
}
211+
212+
/* Hide desktop dropdown on mobile, show flat viz items instead */
213+
.navbar-dropdown-wrapper {
214+
display: none;
215+
}
216+
217+
.mobile-viz-item {
218+
display: block;
219+
}
146220
}

src/components/NavigationBar.tsx

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,31 @@
11
import { useEffect, useState } from 'react'
22
import './NavigationBar.css'
33

4-
const navItems = [
4+
const topNavItems = [
55
{ id: 'hero-section', label: 'Home' },
66
{ id: 'introduction-section', label: 'Introduction' },
7+
]
8+
9+
const vizItems = [
710
{ id: 'section1', label: 'Viz 1' },
811
{ id: 'section2', label: 'Viz 2' },
912
{ id: 'section3', label: 'Viz 3' },
1013
{ id: 'section4', label: 'Viz 4' },
1114
{ id: 'section5', label: 'Viz 5' },
1215
{ id: 'section6', label: 'Viz 6' },
16+
]
17+
18+
const bottomNavItems = [
1319
{ id: 'about-section', label: 'Team' },
1420
]
1521

22+
const allNavItems = [...topNavItems, ...vizItems, ...bottomNavItems]
23+
1624
const NavigationBar: React.FC = () => {
1725
const [isMenuOpen, setIsMenuOpen] = useState(false)
1826
const [scrolled, setScrolled] = useState(false)
1927
const [activeSection, setActiveSection] = useState('hero-section')
28+
const [isVizOpen, setIsVizOpen] = useState(false)
2029
const [theme, setTheme] = useState<'dark' | 'light'>(() => {
2130
const stored = localStorage.getItem('theme') as 'dark' | 'light' | null
2231
if (stored) return stored
@@ -51,9 +60,9 @@ const NavigationBar: React.FC = () => {
5160
setScrolled(window.scrollY > 20)
5261

5362
const viewportProbe = window.scrollY + window.innerHeight * 0.35
54-
let currentSection = navItems[0].id
63+
let currentSection = allNavItems[0].id
5564

56-
for (const item of navItems) {
65+
for (const item of allNavItems) {
5766
const section = document.getElementById(item.id)
5867
if (section && viewportProbe >= section.offsetTop) {
5968
currentSection = item.id
@@ -81,6 +90,8 @@ const NavigationBar: React.FC = () => {
8190
setIsMenuOpen(false)
8291
}
8392

93+
const vizIsActive = vizItems.some(v => v.id === activeSection)
94+
8495
return (
8596
<nav className={`navbar${scrolled ? ' scrolled' : ''}`}>
8697
<div className="navbar-container">
@@ -91,7 +102,76 @@ const NavigationBar: React.FC = () => {
91102
</a>
92103

93104
<ul className={`navbar-menu${isMenuOpen ? ' active' : ''}`}>
94-
{navItems.map((item) => (
105+
{topNavItems.map((item) => (
106+
<li key={item.id}>
107+
<a
108+
href={`#${item.id}`}
109+
className={`navbar-item${activeSection === item.id ? ' active' : ''}`}
110+
onClick={() => {
111+
setActiveSection(item.id)
112+
closeMenu()
113+
}}
114+
>
115+
{item.label}
116+
</a>
117+
</li>
118+
))}
119+
120+
{/* Desktop: hover dropdown */}
121+
<li
122+
className="navbar-dropdown-wrapper"
123+
onMouseEnter={() => setIsVizOpen(true)}
124+
onMouseLeave={() => setIsVizOpen(false)}
125+
>
126+
<span className={`navbar-item navbar-dropdown-trigger${vizIsActive ? ' active' : ''}`}>
127+
Visualizations
128+
<svg
129+
className={`dropdown-arrow${isVizOpen ? ' open' : ''}`}
130+
viewBox="0 0 24 24" width="12" height="12"
131+
fill="none" stroke="currentColor" strokeWidth="2"
132+
strokeLinecap="round" strokeLinejoin="round"
133+
>
134+
<path d="M6 9l6 6 6-6"/>
135+
</svg>
136+
</span>
137+
<ul className={`navbar-dropdown${isVizOpen ? ' open' : ''}`}>
138+
<div className="navbar-dropdown-inner">
139+
{vizItems.map((item) => (
140+
<li key={item.id}>
141+
<a
142+
href={`#${item.id}`}
143+
className={`navbar-item${activeSection === item.id ? ' active' : ''}`}
144+
onClick={() => {
145+
setActiveSection(item.id)
146+
closeMenu()
147+
setIsVizOpen(false)
148+
}}
149+
>
150+
{item.label}
151+
</a>
152+
</li>
153+
))}
154+
</div>
155+
</ul>
156+
</li>
157+
158+
{/* Mobile only: viz items shown flat */}
159+
{vizItems.map((item) => (
160+
<li key={`mob-${item.id}`} className="mobile-viz-item">
161+
<a
162+
href={`#${item.id}`}
163+
className={`navbar-item${activeSection === item.id ? ' active' : ''}`}
164+
onClick={() => {
165+
setActiveSection(item.id)
166+
closeMenu()
167+
}}
168+
>
169+
{item.label}
170+
</a>
171+
</li>
172+
))}
173+
174+
{bottomNavItems.map((item) => (
95175
<li key={item.id}>
96176
<a
97177
href={`#${item.id}`}

0 commit comments

Comments
 (0)