Leave a star on GitHub! ⭐


Docs
Header

Header

These components are used to build the header of the website.

Installation

pnpm add clsx @headlessui/react @heroicons/react

Copy and paste the following code of header into your project.

components/eldoraui/header.tsx

"use client";
import {
  Disclosure,
  DisclosureButton,
  DisclosurePanel,
} from "@headlessui/react";
import { Bars2Icon } from "@heroicons/react/24/solid";
import { motion } from "framer-motion";
import { Link } from "@/components/ui/link";
import {
  PlusGrid,
  PlusGridItem,
  PlusGridRow,
} from "@/components/eldoraui/plusgrid";
import { Logo } from "@/components/logo";
import { siteConfig } from "@/config/site";
 
const links = [
  { href: "/pricing", label: "Pricing" },
  { href: "/company", label: "Company" },
  { href: "/login", label: "Login" },
];
 
function DesktopNav() {
  return (
    <nav className="relative hidden lg:flex">
      {links.map(({ href, label }) => (
        <PlusGridItem key={href} className="relative flex">
          <Link
            href={href}
            className="flex items-center px-4 py-3 text-base font-medium text-gray-950  bg-blend-multiply data-[hover]:bg-black/[2.5%] dark:text-white"
          >
            {label}
          </Link>
        </PlusGridItem>
      ))}
    </nav>
  );
}
 
function MobileNavButton() {
  return (
    <DisclosureButton
      className="flex size-12 items-center justify-center self-center rounded-lg data-[hover]:bg-black/5 lg:hidden"
      aria-label="Open main menu"
    >
      <Bars2Icon className="size-6" />
    </DisclosureButton>
  );
}
 
function MobileNav() {
  return (
    <DisclosurePanel className="lg:hidden">
      <div className="flex flex-col gap-6 py-4">
        {links.map(({ href, label }, linkIndex) => (
          <motion.div
            initial={{ opacity: 0, rotateX: -90 }}
            animate={{ opacity: 1, rotateX: 0 }}
            transition={{
              duration: 0.15,
              ease: "easeInOut",
              rotateX: { duration: 0.3, delay: linkIndex * 0.1 },
            }}
            key={href}
          >
            <Link
              href={href}
              className="text-base font-medium text-gray-950 dark:text-white"
            >
              {label}
            </Link>
          </motion.div>
        ))}
      </div>
      <div className="absolute left-1/2 w-screen -translate-x-1/2">
        <div className="absolute inset-x-0 top-0 border-t border-black/5 dark:border-white/5" />
        <div className="absolute inset-x-0 top-2 border-t border-black/5 dark:border-white/5" />
      </div>
    </DisclosurePanel>
  );
}
 
export function Navbar({ banner }: { banner?: React.ReactNode }) {
  return (
    <Disclosure as="header" className="pt-12 sm:pt-16">
      <PlusGrid>
        <PlusGridRow className="relative flex justify-between">
          <div className="relative flex gap-6">
            <PlusGridItem className="py-3">
              <Link href="/" title="Home">
                <div className="flex items-center space-x-2">
                  {" "}
                  {/* Flex container to align items horizontally */}
                  <div className="-mt-1">
                    <Logo className="mr-2 size-6" />
                  </div>
                  <span className="hidden font-bold md:inline-block">
                    {siteConfig.name}
                  </span>
                </div>
              </Link>
            </PlusGridItem>
            {banner && (
              <div className="relative hidden items-center py-3 lg:flex">
                {banner}
              </div>
            )}
          </div>
          <DesktopNav />
          <MobileNavButton />
        </PlusGridRow>
      </PlusGrid>
      <MobileNav />
    </Disclosure>
  );
}

Copy and paste the following code of plusgrid into your project.

components/eldoraui/plusgrid.tsx

import { clsx } from "clsx";
 
export function PlusGrid({
  className = "",
  children,
}: {
  className?: string;
  children: React.ReactNode;
}) {
  return <div className={className}>{children}</div>;
}
 
export function PlusGridRow({
  className = "",
  children,
}: {
  className?: string;
  children: React.ReactNode;
}) {
  return (
    <div
      className={clsx(
        className,
        "group/row relative isolate pt-[calc(theme(spacing.2)+1px)] last:pb-[calc(theme(spacing.2)+1px)]",
      )}
    >
      <div
        aria-hidden="true"
        className="absolute inset-y-0 left-1/2 -z-10 w-screen -translate-x-1/2"
      >
        <div className="absolute inset-x-0 top-0 border-t border-black/5 dark:border-white/10"></div>
        <div className="absolute inset-x-0 top-2 border-t border-black/5 dark:border-white/10"></div>
        <div className="absolute inset-x-0 bottom-0 hidden border-b border-black/5 group-last/row:block dark:border-white/10"></div>
        <div className="absolute inset-x-0 bottom-2 hidden border-b border-black/5  group-last/row:block dark:border-white/10"></div>
      </div>
      {children}
    </div>
  );
}
 
export function PlusGridItem({
  className = "",
  children,
}: {
  className?: string;
  children: React.ReactNode;
}) {
  return (
    <div className={clsx(className, "group/item relative")}>
      <PlusGridIcon
        placement="top left"
        className="hidden  group-first/item:block"
      />
      <PlusGridIcon placement="top right" />
      <PlusGridIcon
        placement="bottom left"
        className="hidden group-last/row:group-first/item:block"
      />
      <PlusGridIcon
        placement="bottom right"
        className="hidden group-last/row:block"
      />
      {children}
    </div>
  );
}
 
export function PlusGridIcon({
  className = "",
  placement,
}: {
  className?: string;
  placement: `${"top" | "bottom"} ${"right" | "left"}`;
}) {
  let [yAxis, xAxis] = placement.split(" ");
 
  let yClass = yAxis === "top" ? "-top-2" : "-bottom-2";
  let xClass = xAxis === "left" ? "-left-2" : "-right-2";
 
  return (
    <svg
      viewBox="0 0 15 15"
      aria-hidden="true"
      className={clsx(
        className,
        "absolute size-[15px] fill-black/10 dark:fill-white/15",
        yClass,
        xClass,
      )}
    >
      <path d="M8 0H7V7H0V8H7V15H8V8H15V7H8V0Z" />
    </svg>
  );
}

Copy and paste the following code of link into your project.

components/eldoraui/link.tsx

import * as Headless from "@headlessui/react";
import NextLink, { type LinkProps } from "next/link";
import { forwardRef } from "react";
 
export const Link = forwardRef(function Link(
  props: LinkProps & React.ComponentPropsWithoutRef<"a">,
  ref: React.ForwardedRef<HTMLAnchorElement>,
) {
  return (
    <Headless.DataInteractive>
      <NextLink ref={ref} {...props} />
    </Headless.DataInteractive>
  );
});

Props Table

PropTypeDescriptionDefault
bannerReact.ReactNodeAn optional banner element to display within the navbar.undefined
classNamestringOptional CSS class for custom styling of the grid.""
childrenReact.ReactNodeChild elements to be rendered inside the grid, row, or item.undefined
hrefstringThe destination URL of the link.undefined