Web InnoventixFreeCode

Pill Navbar

Original · free

floating pill navbar

byWeb InnoventixReact + Tailwind
navxpillnavbars
Open

Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.

npx shadcn@latest add https://webinnoventix.com/r/navx-pill.json
navx-pill.tsx
"use client";

import { useEffect, useId, useRef, useState } from "react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";

type NavItem = {
  id: string;
  label: string;
};

type Section = {
  id: string;
  eyebrow: string;
  title: string;
  body: string;
};

const NAV_ITEMS: NavItem[] = [
  { id: "overview", label: "Overview" },
  { id: "features", label: "Features" },
  { id: "pricing", label: "Pricing" },
  { id: "customers", label: "Customers" },
  { id: "faq", label: "FAQ" },
];

const SECTIONS: Section[] = [
  {
    id: "overview",
    eyebrow: "Deploy intelligence",
    title: "Ship with a steady pulse",
    body: "Halcyon collapses every pipeline, rollback, and on-call ping into one quiet timeline your whole team can read at a glance — no dashboards to babysit.",
  },
  {
    id: "features",
    eyebrow: "What you get",
    title: "Three tools, zero war rooms",
    body: "A flight recorder for every deploy, blast-radius maps before you merge, and alert grouping that mutes the noise so the one signal that matters gets through.",
  },
  {
    id: "pricing",
    eyebrow: "Fair by design",
    title: "Pricing that scales down, not just up",
    body: "Start free for small teams. Pay per active developer only when you grow — no seat minimums, no annual lock-in, and every plan keeps 30 days of deploy history.",
  },
  {
    id: "customers",
    eyebrow: "Proof",
    title: "Teams that stopped dreading Fridays",
    body: "From two-person seed startups to platform teams shipping 400 times a day, Halcyon runs quietly underneath the release process nobody wants to think about.",
  },
  {
    id: "faq",
    eyebrow: "Details",
    title: "The questions we get most",
    body: "How it connects, what it can see, and what happens at 2am when a deploy goes sideways. Short answers, no sales gloss.",
  },
];

const FEATURES = [
  {
    title: "Flight recorder",
    copy: "Every deploy captured frame by frame, so a 2am rollback takes seconds, not a five-person incident call.",
  },
  {
    title: "Blast-radius maps",
    copy: "See exactly which services a change touches before you merge — not after the pager starts buzzing.",
  },
  {
    title: "Quiet by default",
    copy: "Smart grouping mutes duplicate alerts and surfaces the single event that actually needs a human.",
  },
];

const FAQS = [
  {
    q: "How long does it take to connect?",
    a: "Around ten minutes. Halcyon reads from your existing CI and cloud provider through a scoped, read-only token — nothing to install on your servers.",
  },
  {
    q: "What can it actually see?",
    a: "Deploy events, service metadata, and health signals. It never reads application data, secrets, or customer records. You choose the scopes per environment.",
  },
  {
    q: "Does it replace our incident tool?",
    a: "No — it sits in front of it. Halcyon decides what deserves a page, then hands the clean context to whatever you already use for on-call.",
  },
];

export default function NavxPill() {
  const reduceMotion = useReducedMotion();
  const [active, setActive] = useState<string>(NAV_ITEMS[0].id);
  const [menuOpen, setMenuOpen] = useState<boolean>(false);
  const [searchOpen, setSearchOpen] = useState<boolean>(false);
  const [query, setQuery] = useState<string>("");
  const [elevated, setElevated] = useState<boolean>(false);

  const panelId = useId();
  const searchId = useId();
  const toggleRef = useRef<HTMLButtonElement>(null);
  const searchInputRef = useRef<HTMLInputElement>(null);
  const firstMobileItemRef = useRef<HTMLAnchorElement>(null);
  const sentinelRef = useRef<HTMLDivElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);

  // Scroll-spy: mark the section nearest the top as active.
  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        for (const entry of entries) {
          if (entry.isIntersecting) {
            setActive(entry.target.id);
          }
        }
      },
      { rootMargin: "-45% 0px -50% 0px", threshold: 0 },
    );
    for (const item of NAV_ITEMS) {
      const el = document.getElementById(item.id);
      if (el) observer.observe(el);
    }
    return () => observer.disconnect();
  }, []);

  // Elevate the pill once the page scrolls away from the very top.
  useEffect(() => {
    const node = sentinelRef.current;
    if (!node) return;
    const observer = new IntersectionObserver(
      ([entry]) => setElevated(!entry.isIntersecting),
      { threshold: 0 },
    );
    observer.observe(node);
    return () => observer.disconnect();
  }, []);

  // Global keys: "/" opens search, Escape closes overlays.
  useEffect(() => {
    function onKey(event: KeyboardEvent) {
      const el = document.activeElement;
      const typing =
        el instanceof HTMLElement &&
        (el.tagName === "INPUT" ||
          el.tagName === "TEXTAREA" ||
          el.isContentEditable);
      if (event.key === "/" && !typing) {
        event.preventDefault();
        setSearchOpen(true);
      } else if (event.key === "Escape") {
        setSearchOpen(false);
        setMenuOpen((open) => {
          if (open) toggleRef.current?.focus();
          return false;
        });
      }
    }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  // Close the mobile sheet on an outside pointer press.
  useEffect(() => {
    if (!menuOpen) return;
    function onDown(event: PointerEvent) {
      if (
        containerRef.current &&
        !containerRef.current.contains(event.target as Node)
      ) {
        setMenuOpen(false);
      }
    }
    window.addEventListener("pointerdown", onDown);
    return () => window.removeEventListener("pointerdown", onDown);
  }, [menuOpen]);

  // Focus management for the expandable controls.
  useEffect(() => {
    if (searchOpen) searchInputRef.current?.focus();
  }, [searchOpen]);

  useEffect(() => {
    if (menuOpen) firstMobileItemRef.current?.focus();
  }, [menuOpen]);

  function goTo(id: string) {
    setActive(id);
    setMenuOpen(false);
    const el = document.getElementById(id);
    if (el) {
      el.scrollIntoView({
        behavior: reduceMotion ? "auto" : "smooth",
        block: "start",
      });
    }
  }

  const focusRing =
    "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-indigo-400 dark:focus-visible:ring-offset-slate-950";

  const softTransition = reduceMotion
    ? { duration: 0 }
    : { duration: 0.18, ease: "easeOut" as const };

  return (
    <section className="relative w-full overflow-hidden bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100">
      <style>{`
        @keyframes navxpill-pulse {
          0%, 100% { opacity: 0.35; transform: scale(1); }
          50% { opacity: 0.7; transform: scale(1.12); }
        }
        @keyframes navxpill-sheen {
          0% { transform: translateX(-130%); }
          55%, 100% { transform: translateX(230%); }
        }
        .navxpill-pulse { animation: navxpill-pulse 3.8s ease-in-out infinite; }
        .navxpill-sheen { animation: navxpill-sheen 5s ease-in-out infinite; }
        @media (prefers-reduced-motion: reduce) {
          .navxpill-pulse, .navxpill-sheen { animation: none; }
        }
      `}</style>

      {/* Ambient background wash */}
      <div
        aria-hidden="true"
        className="pointer-events-none absolute inset-0 opacity-70 dark:opacity-100"
        style={{
          background:
            "radial-gradient(60rem 32rem at 50% -8rem, rgba(99,102,241,0.14), transparent 70%)",
        }}
      />

      <div className="relative mx-auto w-full max-w-6xl px-4 sm:px-6 lg:px-8">
        <div ref={sentinelRef} aria-hidden="true" className="h-px w-full" />

        {/* Floating pill navbar */}
        <div ref={containerRef} className="sticky top-4 z-50">
          <nav
            aria-label="Primary"
            className={[
              "relative flex items-center justify-between gap-2 rounded-full border pl-3 pr-2 backdrop-blur-xl transition-all duration-300",
              "border-slate-200/80 bg-white/80 dark:border-white/10 dark:bg-slate-900/70",
              elevated
                ? "py-1.5 shadow-[0_18px_50px_-18px_rgba(15,23,42,0.45)] dark:shadow-[0_18px_60px_-20px_rgba(0,0,0,0.8)]"
                : "py-2 shadow-[0_10px_34px_-16px_rgba(15,23,42,0.35)] dark:shadow-[0_10px_40px_-18px_rgba(0,0,0,0.7)]",
            ].join(" ")}
          >
            {/* Brand */}
            <a
              href="#overview"
              onClick={(e) => {
                e.preventDefault();
                goTo("overview");
              }}
              className={`group flex shrink-0 items-center gap-2 rounded-full px-1.5 py-1 ${focusRing}`}
            >
              <span className="relative grid h-8 w-8 place-items-center">
                <span
                  aria-hidden="true"
                  className="navxpill-pulse absolute inset-0 rounded-full bg-indigo-500/40 blur-md"
                />
                <span className="relative grid h-8 w-8 place-items-center rounded-full bg-gradient-to-br from-indigo-500 to-violet-600 text-white shadow-inner">
                  <svg
                    viewBox="0 0 24 24"
                    className="h-4 w-4"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth="2.2"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    aria-hidden="true"
                  >
                    <path d="M3 13h4l2.5 6L14 5l2.5 8H21" />
                  </svg>
                </span>
              </span>
              <span className="text-[15px] font-semibold tracking-tight text-slate-900 dark:text-white">
                Halcyon
              </span>
            </a>

            {/* Center links (desktop) */}
            <ul className="hidden items-center gap-0.5 md:flex">
              {NAV_ITEMS.map((item) => {
                const isActive = active === item.id;
                return (
                  <li key={item.id} className="relative">
                    <a
                      href={`#${item.id}`}
                      aria-current={isActive ? "location" : undefined}
                      onClick={(e) => {
                        e.preventDefault();
                        goTo(item.id);
                      }}
                      className={[
                        "relative flex items-center rounded-full px-3.5 py-2 text-sm font-medium transition-colors",
                        focusRing,
                        isActive
                          ? "text-slate-900 dark:text-white"
                          : "text-slate-500 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white",
                      ].join(" ")}
                    >
                      {isActive && (
                        <motion.span
                          layoutId="navxpill-active"
                          aria-hidden="true"
                          className="absolute inset-0 -z-10 rounded-full bg-white shadow-sm ring-1 ring-slate-900/5 dark:bg-white/10 dark:ring-white/10"
                          transition={
                            reduceMotion
                              ? { duration: 0 }
                              : { type: "spring", stiffness: 420, damping: 34 }
                          }
                        />
                      )}
                      {item.label}
                    </a>
                  </li>
                );
              })}
            </ul>

            {/* Right actions */}
            <div className="flex shrink-0 items-center gap-1">
              {/* Expandable search (desktop) */}
              <div
                role="search"
                className="relative hidden items-center md:flex"
              >
                <AnimatePresence initial={false} mode="wait">
                  {searchOpen ? (
                    <motion.div
                      key="field"
                      initial={reduceMotion ? false : { width: 40, opacity: 0 }}
                      animate={{ width: 208, opacity: 1 }}
                      exit={reduceMotion ? undefined : { width: 40, opacity: 0 }}
                      transition={softTransition}
                      className="relative overflow-hidden"
                    >
                      <label htmlFor={searchId} className="sr-only">
                        Search Halcyon
                      </label>
                      <svg
                        viewBox="0 0 24 24"
                        className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400"
                        fill="none"
                        stroke="currentColor"
                        strokeWidth="2"
                        strokeLinecap="round"
                        strokeLinejoin="round"
                        aria-hidden="true"
                      >
                        <circle cx="11" cy="11" r="7" />
                        <path d="m20 20-3.2-3.2" />
                      </svg>
                      <input
                        ref={searchInputRef}
                        id={searchId}
                        type="text"
                        value={query}
                        onChange={(e) => setQuery(e.target.value)}
                        onBlur={() => {
                          if (query.trim() === "") setSearchOpen(false);
                        }}
                        placeholder="Search docs, deploys…"
                        className={`w-full rounded-full border border-slate-200 bg-white/90 py-2 pl-9 pr-3 text-sm text-slate-900 placeholder:text-slate-400 dark:border-white/10 dark:bg-slate-800/80 dark:text-white dark:placeholder:text-slate-500 ${focusRing}`}
                      />
                    </motion.div>
                  ) : (
                    <motion.button
                      key="button"
                      type="button"
                      onClick={() => setSearchOpen(true)}
                      aria-label="Open search"
                      initial={reduceMotion ? false : { opacity: 0 }}
                      animate={{ opacity: 1 }}
                      exit={reduceMotion ? undefined : { opacity: 0 }}
                      transition={softTransition}
                      className={`grid h-9 w-9 place-items-center rounded-full text-slate-500 transition-colors hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-white/10 dark:hover:text-white ${focusRing}`}
                    >
                      <svg
                        viewBox="0 0 24 24"
                        className="h-[18px] w-[18px]"
                        fill="none"
                        stroke="currentColor"
                        strokeWidth="2"
                        strokeLinecap="round"
                        strokeLinejoin="round"
                        aria-hidden="true"
                      >
                        <circle cx="11" cy="11" r="7" />
                        <path d="m20 20-3.2-3.2" />
                      </svg>
                    </motion.button>
                  )}
                </AnimatePresence>
              </div>

              <a
                href="#overview"
                onClick={(e) => {
                  e.preventDefault();
                  goTo("overview");
                }}
                className={`hidden rounded-full px-3.5 py-2 text-sm font-medium text-slate-600 transition-colors hover:text-slate-900 lg:inline-flex dark:text-slate-300 dark:hover:text-white ${focusRing}`}
              >
                Sign in
              </a>

              <a
                href="#pricing"
                onClick={(e) => {
                  e.preventDefault();
                  goTo("pricing");
                }}
                className={`relative hidden items-center gap-1.5 overflow-hidden rounded-full bg-gradient-to-r from-indigo-600 to-violet-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition-transform hover:-translate-y-px active:translate-y-0 sm:inline-flex ${focusRing}`}
              >
                <span
                  aria-hidden="true"
                  className="navxpill-sheen pointer-events-none absolute inset-y-0 -left-full w-1/2 bg-gradient-to-r from-transparent via-white/30 to-transparent"
                />
                Start free
                <svg
                  viewBox="0 0 24 24"
                  className="h-4 w-4"
                  fill="none"
                  stroke="currentColor"
                  strokeWidth="2.2"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  aria-hidden="true"
                >
                  <path d="M5 12h14M13 6l6 6-6 6" />
                </svg>
              </a>

              {/* Mobile toggle */}
              <button
                ref={toggleRef}
                type="button"
                onClick={() => setMenuOpen((open) => !open)}
                aria-expanded={menuOpen}
                aria-controls={panelId}
                aria-label={menuOpen ? "Close menu" : "Open menu"}
                className={`grid h-9 w-9 place-items-center rounded-full text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900 md:hidden dark:text-slate-300 dark:hover:bg-white/10 dark:hover:text-white ${focusRing}`}
              >
                {menuOpen ? (
                  <svg
                    viewBox="0 0 24 24"
                    className="h-5 w-5"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth="2"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    aria-hidden="true"
                  >
                    <path d="M6 6l12 12M18 6 6 18" />
                  </svg>
                ) : (
                  <svg
                    viewBox="0 0 24 24"
                    className="h-5 w-5"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth="2"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    aria-hidden="true"
                  >
                    <path d="M4 7h16M4 12h16M4 17h16" />
                  </svg>
                )}
              </button>
            </div>
          </nav>

          {/* Mobile sheet */}
          <AnimatePresence>
            {menuOpen && (
              <motion.div
                id={panelId}
                initial={
                  reduceMotion ? { opacity: 1 } : { opacity: 0, y: -8, scale: 0.98 }
                }
                animate={{ opacity: 1, y: 0, scale: 1 }}
                exit={
                  reduceMotion ? { opacity: 0 } : { opacity: 0, y: -8, scale: 0.98 }
                }
                transition={softTransition}
                className="absolute left-0 right-0 top-full z-50 mt-2 origin-top rounded-3xl border border-slate-200/80 bg-white/90 p-3 shadow-xl backdrop-blur-xl md:hidden dark:border-white/10 dark:bg-slate-900/85"
              >
                <ul className="flex flex-col">
                  {NAV_ITEMS.map((item, index) => {
                    const isActive = active === item.id;
                    return (
                      <li key={item.id}>
                        <a
                          ref={index === 0 ? firstMobileItemRef : undefined}
                          href={`#${item.id}`}
                          aria-current={isActive ? "location" : undefined}
                          onClick={(e) => {
                            e.preventDefault();
                            goTo(item.id);
                          }}
                          className={[
                            "flex items-center justify-between rounded-2xl px-4 py-3 text-[15px] font-medium transition-colors",
                            focusRing,
                            isActive
                              ? "bg-indigo-50 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-200"
                              : "text-slate-700 hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5",
                          ].join(" ")}
                        >
                          {item.label}
                          {isActive && (
                            <span
                              aria-hidden="true"
                              className="h-1.5 w-1.5 rounded-full bg-indigo-500 dark:bg-indigo-300"
                            />
                          )}
                        </a>
                      </li>
                    );
                  })}
                </ul>
                <a
                  href="#pricing"
                  onClick={(e) => {
                    e.preventDefault();
                    goTo("pricing");
                  }}
                  className={`mt-2 flex items-center justify-center gap-1.5 rounded-2xl bg-gradient-to-r from-indigo-600 to-violet-600 px-4 py-3 text-[15px] font-semibold text-white ${focusRing}`}
                >
                  Start free
                  <svg
                    viewBox="0 0 24 24"
                    className="h-4 w-4"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth="2.2"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    aria-hidden="true"
                  >
                    <path d="M5 12h14M13 6l6 6-6 6" />
                  </svg>
                </a>
              </motion.div>
            )}
          </AnimatePresence>
        </div>

        {/* Demo content — gives the floating nav something to sit above */}
        <div className="pb-24 pt-10">
          {SECTIONS.map((section) => (
            <section
              key={section.id}
              id={section.id}
              aria-labelledby={`${section.id}-title`}
              className="scroll-mt-24 border-b border-slate-200/70 py-16 last:border-0 dark:border-white/10"
            >
              <p className="text-xs font-semibold uppercase tracking-[0.18em] text-indigo-600 dark:text-indigo-400">
                {section.eyebrow}
              </p>
              <h2
                id={`${section.id}-title`}
                className="mt-3 max-w-2xl text-3xl font-semibold tracking-tight text-slate-900 sm:text-4xl dark:text-white"
              >
                {section.title}
              </h2>
              <p className="mt-4 max-w-2xl text-base leading-relaxed text-slate-600 dark:text-slate-300">
                {section.body}
              </p>

              {section.id === "features" && (
                <div className="mt-8 grid gap-4 sm:grid-cols-3">
                  {FEATURES.map((f) => (
                    <div
                      key={f.title}
                      className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-white/10 dark:bg-slate-900/60"
                    >
                      <h3 className="text-sm font-semibold text-slate-900 dark:text-white">
                        {f.title}
                      </h3>
                      <p className="mt-2 text-sm leading-relaxed text-slate-600 dark:text-slate-400">
                        {f.copy}
                      </p>
                    </div>
                  ))}
                </div>
              )}

              {section.id === "pricing" && (
                <div className="mt-8 grid max-w-2xl gap-4 sm:grid-cols-2">
                  <div className="rounded-2xl border border-slate-200 bg-white p-6 dark:border-white/10 dark:bg-slate-900/60">
                    <p className="text-sm font-medium text-slate-500 dark:text-slate-400">
                      Starter
                    </p>
                    <p className="mt-2 text-3xl font-semibold text-slate-900 dark:text-white">
                      $0
                      <span className="text-base font-normal text-slate-500 dark:text-slate-400">
                        {" "}
                        / forever
                      </span>
                    </p>
                    <p className="mt-3 text-sm text-slate-600 dark:text-slate-400">
                      Up to 3 developers and one environment. Everything you need to feel the difference.
                    </p>
                  </div>
                  <div className="rounded-2xl border-2 border-indigo-500 bg-white p-6 dark:bg-slate-900/60">
                    <p className="text-sm font-medium text-indigo-600 dark:text-indigo-400">
                      Team
                    </p>
                    <p className="mt-2 text-3xl font-semibold text-slate-900 dark:text-white">
                      $18
                      <span className="text-base font-normal text-slate-500 dark:text-slate-400">
                        {" "}
                        / active dev
                      </span>
                    </p>
                    <p className="mt-3 text-sm text-slate-600 dark:text-slate-400">
                      Unlimited environments, 30-day history, and priority on-call routing. Billed monthly.
                    </p>
                  </div>
                </div>
              )}

              {section.id === "customers" && (
                <figure className="mt-8 max-w-2xl">
                  <blockquote className="border-l-2 border-indigo-500 pl-4 text-lg font-medium leading-relaxed text-slate-800 dark:text-slate-100">
                    “We cut our mean-time-to-rollback from eleven minutes to under
                    forty seconds. The first Friday nobody got paged, I almost
                    didn&apos;t believe it.”
                  </blockquote>
                  <figcaption className="mt-3 text-sm text-slate-500 dark:text-slate-400">
                    Priya Ramanathan — Staff Platform Engineer, Meridian Freight
                  </figcaption>
                  <p className="mt-6 text-xs font-semibold uppercase tracking-[0.18em] text-slate-400 dark:text-slate-500">
                    Trusted by teams at
                  </p>
                  <div className="mt-3 flex flex-wrap gap-x-6 gap-y-2 text-sm font-medium text-slate-500 dark:text-slate-400">
                    <span>Meridian</span>
                    <span>Northwind Labs</span>
                    <span>Fathom</span>
                    <span>Quill</span>
                    <span>Volta</span>
                  </div>
                </figure>
              )}

              {section.id === "faq" && (
                <div className="mt-8 max-w-2xl divide-y divide-slate-200 rounded-2xl border border-slate-200 bg-white dark:divide-white/10 dark:border-white/10 dark:bg-slate-900/60">
                  {FAQS.map((item) => (
                    <details key={item.q} className="group px-5 py-4">
                      <summary
                        className={`flex cursor-pointer list-none items-center justify-between rounded-md text-sm font-medium text-slate-900 marker:content-none dark:text-white ${focusRing}`}
                      >
                        {item.q}
                        <svg
                          viewBox="0 0 24 24"
                          className="h-4 w-4 shrink-0 text-slate-400 transition-transform group-open:rotate-180"
                          fill="none"
                          stroke="currentColor"
                          strokeWidth="2"
                          strokeLinecap="round"
                          strokeLinejoin="round"
                          aria-hidden="true"
                        >
                          <path d="m6 9 6 6 6-6" />
                        </svg>
                      </summary>
                      <p className="mt-3 text-sm leading-relaxed text-slate-600 dark:text-slate-400">
                        {item.a}
                      </p>
                    </details>
                  ))}
                </div>
              )}
            </section>
          ))}

          <p className="pt-4 text-center text-xs text-slate-400 dark:text-slate-600">
            Press <kbd className="rounded border border-slate-300 px-1 font-mono dark:border-white/20">/</kbd> to search · <kbd className="rounded border border-slate-300 px-1 font-mono dark:border-white/20">Esc</kbd> to close
          </p>
        </div>
      </div>
    </section>
  );
}

Dependencies

motion

Licence

Built by Web Innoventix. Free for personal and commercial use, no attribution required.

Built by Web Innoventix

Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.

Get a free quote

Similar components

Browse all →
Centred Logo Navbar

Centred Logo Navbar

Original

A three-column navbar with the brand logo centred between the primary links and the account actions, wrapped in a soft rounded card.

Left Logo Navbar With CTA

Left Logo Navbar With CTA

Original

A classic left-aligned logo navbar with centred navigation links and a prominent pill call-to-action button on the right.

Navbar With Dropdown Menu

Navbar With Dropdown Menu

Original

A navbar featuring a JS-free product dropdown built with native details and summary, revealing a two-item mega-menu panel with icons.

Glass Transparent Navbar

Glass Transparent Navbar

Original

A frosted glass navbar floating over a colourful gradient hero backdrop, using backdrop blur and translucent borders for a modern overlay header.

Responsive Navbar with Mobile Menu

Responsive Navbar with Mobile Menu

MIT

A marketing site header with a logo, inline nav links, login/register buttons, and a working hamburger toggle that reveals a stacked mobile menu below the md breakpoint. Fully keyboard/ARIA accessible with light and dark variants.

Underline Tabs

Underline Tabs

MIT

An accessible underline-style tab switcher with a bottom-border active indicator and a live content panel. State-driven with proper role=tablist/tab/tabpanel and aria-selected wiring, styled for light and dark modes.

Split Button Dropdown Menu

Split Button Dropdown Menu

MIT

A split-button control with a chevron trigger that opens a divided action menu, including a destructive delete item. Closes on outside-click and Escape, with a rotating chevron and full ARIA menu roles for both light and dark themes.

Glass Navbar

Glass Navbar

Original

glassmorphic sticky navbar

Mega Navbar

Mega Navbar

Original

navbar with a mega dropdown

Centered Logo Navbar

Centered Logo Navbar

Original

navbar with centred logo and split links

Sidebar Toggle Navbar

Sidebar Toggle Navbar

Original

navbar with a sidebar toggle

Search Navbar

Search Navbar

Original

navbar with an expanding search