Skip to content

Commit 66f4cce

Browse files
committed
Add responsive hamburger menu for mobile navigation
Replace broken mobile header with a proper hamburger menu that shows/hides navigation items. Uses pure CSS animations and Svelte state management without additional dependencies.
1 parent fcb8246 commit 66f4cce

File tree

1 file changed

+199
-16
lines changed

1 file changed

+199
-16
lines changed

src/Main.svelte

Lines changed: 199 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,94 @@
33
import Providers from "./Providers.svelte";
44
55
let activeTab: "models" | "providers" = "models";
6+
let mobileMenuOpen = false;
67
78
const GITHUB_URL = "https://github.com/BerriAI/litellm";
89
const DOCS_URL = "https://docs.litellm.ai";
10+
11+
function toggleMobileMenu() {
12+
mobileMenuOpen = !mobileMenuOpen;
13+
}
14+
15+
function closeMobileMenu() {
16+
mobileMenuOpen = false;
17+
}
18+
19+
function selectTab(tab: "models" | "providers") {
20+
activeTab = tab;
21+
closeMobileMenu();
22+
}
923
</script>
1024

1125
<div class="app-container">
1226
<!-- Header -->
1327
<header class="header">
1428
<div class="header-content">
15-
<div class="left-section">
16-
<div class="logo-section-header">
17-
<span class="logo-emoji">🚅</span>
18-
<span class="logo-text-header">LiteLLM</span>
19-
</div>
29+
<div class="logo-section-header">
30+
<span class="logo-emoji">🚅</span>
31+
<span class="logo-text-header">LiteLLM</span>
32+
</div>
33+
34+
<!-- Desktop Navigation -->
35+
<div class="desktop-nav">
2036
<div class="tabs">
21-
<button
22-
class="tab"
37+
<button
38+
class="tab"
2339
class:active={activeTab === "models"}
24-
on:click={() => activeTab = "models"}
40+
on:click={() => selectTab("models")}
2541
>
2642
Models
2743
</button>
28-
<button
29-
class="tab"
44+
<button
45+
class="tab"
3046
class:active={activeTab === "providers"}
31-
on:click={() => activeTab = "providers"}
47+
on:click={() => selectTab("providers")}
3248
>
3349
AI Gateway - Endpoints & Providers
3450
</button>
3551
</div>
52+
<nav class="nav-links">
53+
<a href={DOCS_URL} target="_blank" rel="noopener noreferrer" class="nav-link">Docs</a>
54+
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer" class="nav-link">GitHub</a>
55+
</nav>
3656
</div>
37-
<nav class="nav-links">
38-
<a href={DOCS_URL} target="_blank" rel="noopener noreferrer" class="nav-link">Docs</a>
39-
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer" class="nav-link">GitHub</a>
40-
</nav>
57+
58+
<!-- Mobile Menu Button -->
59+
<button
60+
class="mobile-menu-btn"
61+
on:click={toggleMobileMenu}
62+
aria-label="Toggle menu"
63+
aria-expanded={mobileMenuOpen}
64+
>
65+
<span class="hamburger" class:open={mobileMenuOpen}></span>
66+
</button>
4167
</div>
68+
69+
<!-- Mobile Menu -->
70+
{#if mobileMenuOpen}
71+
<div class="mobile-menu">
72+
<div class="mobile-tabs">
73+
<button
74+
class="mobile-tab"
75+
class:active={activeTab === "models"}
76+
on:click={() => selectTab("models")}
77+
>
78+
Models
79+
</button>
80+
<button
81+
class="mobile-tab"
82+
class:active={activeTab === "providers"}
83+
on:click={() => selectTab("providers")}
84+
>
85+
AI Gateway - Endpoints & Providers
86+
</button>
87+
</div>
88+
<div class="mobile-links">
89+
<a href={DOCS_URL} target="_blank" rel="noopener noreferrer" class="mobile-link" on:click={closeMobileMenu}>Docs</a>
90+
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer" class="mobile-link" on:click={closeMobileMenu}>GitHub</a>
91+
</div>
92+
</div>
93+
{/if}
4294
</header>
4395

4496
<!-- Content -->
@@ -98,7 +150,7 @@
98150
justify-content: space-between;
99151
}
100152
101-
.left-section {
153+
.desktop-nav {
102154
display: flex;
103155
align-items: center;
104156
gap: 2rem;
@@ -162,5 +214,136 @@
162214
.nav-link:hover {
163215
color: var(--litellm-primary);
164216
}
217+
218+
/* Mobile Menu Button - Hidden on desktop */
219+
.mobile-menu-btn {
220+
display: none;
221+
background: none;
222+
border: none;
223+
cursor: pointer;
224+
padding: 0.5rem;
225+
z-index: 101;
226+
}
227+
228+
.hamburger {
229+
display: block;
230+
width: 24px;
231+
height: 2px;
232+
background-color: #1a1a1a;
233+
position: relative;
234+
transition: background-color 0.2s ease;
235+
}
236+
237+
.hamburger::before,
238+
.hamburger::after {
239+
content: "";
240+
position: absolute;
241+
width: 24px;
242+
height: 2px;
243+
background-color: #1a1a1a;
244+
left: 0;
245+
transition: transform 0.3s ease;
246+
}
247+
248+
.hamburger::before {
249+
top: -7px;
250+
}
251+
252+
.hamburger::after {
253+
top: 7px;
254+
}
255+
256+
/* Hamburger animation when open */
257+
.hamburger.open {
258+
background-color: transparent;
259+
}
260+
261+
.hamburger.open::before {
262+
transform: rotate(45deg) translate(5px, 5px);
263+
}
264+
265+
.hamburger.open::after {
266+
transform: rotate(-45deg) translate(5px, -5px);
267+
}
268+
269+
/* Mobile Menu Panel */
270+
.mobile-menu {
271+
display: none;
272+
background: #ffffff;
273+
border-top: 1px solid #e5e7eb;
274+
padding: 1rem;
275+
}
276+
277+
.mobile-tabs {
278+
display: flex;
279+
flex-direction: column;
280+
gap: 0.5rem;
281+
margin-bottom: 1rem;
282+
padding-bottom: 1rem;
283+
border-bottom: 1px solid #e5e7eb;
284+
}
285+
286+
.mobile-tab {
287+
padding: 0.75rem 1rem;
288+
border: none;
289+
background: transparent;
290+
color: #6b7280;
291+
font-weight: 500;
292+
font-size: 1rem;
293+
cursor: pointer;
294+
text-align: left;
295+
border-radius: 8px;
296+
transition: all 0.2s ease;
297+
}
298+
299+
.mobile-tab:hover {
300+
background-color: #f3f4f6;
301+
color: #1a1a1a;
302+
}
303+
304+
.mobile-tab.active {
305+
background-color: #f3f4f6;
306+
color: #1a1a1a;
307+
}
308+
309+
.mobile-links {
310+
display: flex;
311+
flex-direction: column;
312+
gap: 0.5rem;
313+
}
314+
315+
.mobile-link {
316+
padding: 0.75rem 1rem;
317+
color: #1a1a1a;
318+
text-decoration: none;
319+
font-weight: 500;
320+
font-size: 1rem;
321+
border-radius: 8px;
322+
transition: all 0.2s ease;
323+
}
324+
325+
.mobile-link:hover {
326+
background-color: #f3f4f6;
327+
color: var(--litellm-primary);
328+
}
329+
330+
/* Mobile Responsive */
331+
@media (max-width: 768px) {
332+
.header-content {
333+
padding: 0.75rem 1rem;
334+
}
335+
336+
.desktop-nav {
337+
display: none;
338+
}
339+
340+
.mobile-menu-btn {
341+
display: block;
342+
}
343+
344+
.mobile-menu {
345+
display: block;
346+
}
347+
}
165348
</style>
166349

0 commit comments

Comments
 (0)