Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 103 additions & 28 deletions apps/web/src/components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,107 @@
import { NavLink } from "react-router";

import { ModeToggle } from "./mode-toggle";
import React, { useState } from "react";
import { Menu, X } from "lucide-react";
import SidebarItem from "@/components/sidebar-item";
import { ModeToggle } from "@/components/mode-toggle";

export default function Header() {
const links = [{ to: "/", label: "Home" }];
const [isMobileMenuOpen, setMobileMenuOpen] = useState(false);

return (
<>
{/* TOP HEADER */}
<header
className="flex justify-between items-center p-4 border-b-4"
style={{
background: "var(--surface)",
borderColor: "var(--border)",
}}
>
{/* LOGO + TEXT */}
<div className="flex items-center gap-2">
{/* Logo Image */}
<img
src="/logo.png" // πŸ‘ˆ Replace with your logo path
alt="IIITBuzz Logo"
className="h-8 w-8" // Adjust size as needed
/>
{/* Logo Text */}
<span
className="font-bold text-lg pixel-font"
style={{ color: "var(--primary)" }}
>
IIITBuzz
</span>
</div>

{/* Right Side (ModeToggle + Hamburger) */}
<div className="flex items-center gap-2">
{/* Mode Toggle Dropdown (desktop & mobile) */}
<ModeToggle />

{/* Hamburger Button (mobile only) */}
<button
className="md:hidden p-2 rounded border-2 pixel-font"
style={{
background: "var(--surface)",
color: "var(--primary)",
borderColor: "var(--primary)",
}}
aria-label="Toggle Menu"
onClick={() => setMobileMenuOpen(true)}
>
<Menu size={20} />
</button>
</div>
</header>

{/* MOBILE DRAWER */}
<div
className={`fixed inset-y-0 left-0 w-64 z-40 transition-transform duration-300 ease-in-out shadow-pixel
${isMobileMenuOpen ? "translate-x-0" : "-translate-x-full"}`}
style={{
background: "var(--surface)",
color: "var(--text-primary)",
borderRight: "4px solid var(--border)",
}}
>
{/* Drawer Header */}
<div className="flex justify-between items-center p-4 border-b-4" style={{ borderColor: "var(--border)" }}>
<div className="font-bold pixel-font" style={{ color: "var(--primary)" }}>
Menu
</div>
<button
className="p-2 rounded border-2 pixel-font"
style={{
background: "var(--surface)",
color: "var(--primary)",
borderColor: "var(--primary)",
}}
aria-label="Close Menu"
onClick={() => setMobileMenuOpen(false)}
>
<X size={20} />
</button>
</div>

{/* Drawer Navigation */}
<nav className="flex flex-col gap-2 p-4" aria-label="Mobile Navigation">
<SidebarItem icon="🏠" label="Home" active />
<SidebarItem icon="πŸ—‚οΈ" label="Categories" />
<SidebarItem icon="🏷️" label="Tags" />
<SidebarItem icon="πŸ“’" label="Announcements" />
<SidebarItem icon="πŸ†" label="Leaderboard" />
<SidebarItem icon="ℹ️" label="About" />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The navigation links in the mobile drawer are hardcoded. This is less maintainable than defining them as a data structure (like an array of objects) and rendering them with .map(). This approach makes it much easier to add, remove, or reorder navigation items in the future.

For example:

const navItems = [
  { icon: "🏠", label: "Home", active: true },
  { icon: "πŸ—‚οΈ", label: "Categories" },
  { icon: "🏷️", label: "Tags" },
  { icon: "πŸ“’", label: "Announcements" },
  { icon: "πŸ†", label: "Leaderboard" },
  { icon: "ℹ️", label: "About" },
];

// ... inside the component's return
<nav className="flex flex-col gap-2 p-4" aria-label="Mobile Navigation">
  {navItems.map(item => <SidebarItem key={item.label} {...item} />)}
</nav>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

</nav>
</div>

return (
<div>
<div className="flex flex-row items-center justify-between px-2 py-1">
<nav className="flex gap-4 text-lg">
{links.map(({ to, label }) => {
return (
<NavLink
key={to}
to={to}
className={({ isActive }) => (isActive ? "font-bold" : "")}
end
>
{label}
</NavLink>
);
})}
</nav>
<div className="flex items-center gap-2">
<ModeToggle />
</div>
</div>
<hr />
</div>
);
{/* BACKDROP */}
{isMobileMenuOpen && (
<div
className="fixed inset-0 z-30 bg-black/50"
onClick={() => setMobileMenuOpen(false)}
aria-hidden="true"
></div>
)}
</>
);
}
18 changes: 18 additions & 0 deletions apps/web/src/components/sidebar-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export default function SidebarItem({ icon, label, active = false }: { icon: string; label: string; active?: boolean }) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The component's props are typed inline. For better readability and to adhere to common React/TypeScript best practices, it's better to define a separate type or interface for the props.

Example:

type SidebarItemProps = {
  icon: string;
  label: string;
  active?: boolean;
};

export default function SidebarItem({ icon, label, active = false }: SidebarItemProps) { /* ... */ }

return (
<div
className={`flex items-center gap-3 px-3 py-2 rounded font-bold pixel-font text-sm cursor-pointer w-full
${active ? "border-l-4" : "hover:bg-[var(--bg)]"}
`}
style={{
background: active ? "var(--bg)" : "transparent",
color: active ? "var(--primary)" : "var(--text-secondary)",
borderColor: active ? "var(--primary)" : undefined,
}}
title={label}
>
<span className="text-lg flex-shrink-0">{icon}</span>
<span className="break-words">{label}</span>
</div>
);
}
Loading