Web InnoventixFreeCode

Search Navbar

Original · free

navbar with an expanding search

byWeb InnoventixReact + Tailwind
navxsearchnavbars
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-search.json
navx-search.tsx
"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import type { KeyboardEvent as ReactKeyboardEvent, ReactNode } from "react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";

type SearchItem = {
  id: string;
  title: string;
  category: "Docs" | "Reference" | "Guides" | "Account" | "Updates" | "Support";
  description: string;
};

const SEARCH_ITEMS: SearchItem[] = [
  {
    id: "quickstart",
    title: "Quickstart guide",
    category: "Docs",
    description: "Create a project and send your first request in under five minutes.",
  },
  {
    id: "auth-keys",
    title: "Authentication & API keys",
    category: "Docs",
    description: "Issue, scope, and rotate secret keys for every environment.",
  },
  {
    id: "rate-limits",
    title: "Rate limits & retries",
    category: "Reference",
    description: "Per-plan request ceilings and the recommended backoff strategy.",
  },
  {
    id: "webhooks",
    title: "Webhooks",
    category: "Guides",
    description: "Subscribe to events and verify signatures on incoming payloads.",
  },
  {
    id: "roles",
    title: "Team roles & permissions",
    category: "Account",
    description: "Owner, admin, and read-only seats across your workspace.",
  },
  {
    id: "deploy",
    title: "Deploy to production",
    category: "Guides",
    description: "Promote a build, run health checks, and roll back safely.",
  },
  {
    id: "cli",
    title: "CLI reference",
    category: "Reference",
    description: "Every command, flag, and environment variable for the Meridian CLI.",
  },
  {
    id: "billing",
    title: "Billing & invoices",
    category: "Account",
    description: "Update payment methods and download receipts as PDFs.",
  },
  {
    id: "changelog",
    title: "Changelog — v4.2 “Ridgeline”",
    category: "Updates",
    description: "Streaming responses, regional endpoints, and a faster dashboard.",
  },
  {
    id: "status",
    title: "Platform status & incidents",
    category: "Support",
    description: "Live uptime for the API, dashboard, and background workers.",
  },
];

const NAV_LINKS: { label: string; badge?: string }[] = [
  { label: "Platform" },
  { label: "Solutions" },
  { label: "Docs" },
  { label: "Pricing" },
  { label: "Changelog", badge: "New" },
];

function categoryStyle(category: SearchItem["category"]): string {
  switch (category) {
    case "Docs":
      return "bg-indigo-50 text-indigo-700 ring-indigo-200 dark:bg-indigo-500/10 dark:text-indigo-300 dark:ring-indigo-500/25";
    case "Reference":
      return "bg-violet-50 text-violet-700 ring-violet-200 dark:bg-violet-500/10 dark:text-violet-300 dark:ring-violet-500/25";
    case "Guides":
      return "bg-emerald-50 text-emerald-700 ring-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-300 dark:ring-emerald-500/25";
    case "Account":
      return "bg-amber-50 text-amber-700 ring-amber-200 dark:bg-amber-500/10 dark:text-amber-300 dark:ring-amber-500/25";
    case "Updates":
      return "bg-sky-50 text-sky-700 ring-sky-200 dark:bg-sky-500/10 dark:text-sky-300 dark:ring-sky-500/25";
    case "Support":
      return "bg-rose-50 text-rose-700 ring-rose-200 dark:bg-rose-500/10 dark:text-rose-300 dark:ring-rose-500/25";
    default:
      return "bg-slate-100 text-slate-700 ring-slate-200 dark:bg-slate-500/10 dark:text-slate-300 dark:ring-slate-500/25";
  }
}

function SearchIcon({ className }: { className?: string }) {
  return (
    <svg
      className={className}
      viewBox="0 0 24 24"
      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>
  );
}

function CloseIcon({ className }: { className?: string }) {
  return (
    <svg
      className={className}
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth={2}
      strokeLinecap="round"
      strokeLinejoin="round"
      aria-hidden="true"
    >
      <path d="M6 6l12 12M18 6L6 18" />
    </svg>
  );
}

function MenuIcon({ className }: { className?: string }) {
  return (
    <svg
      className={className}
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth={2}
      strokeLinecap="round"
      strokeLinejoin="round"
      aria-hidden="true"
    >
      <path d="M4 7h16M4 12h16M4 17h16" />
    </svg>
  );
}

function ReturnIcon({ className }: { className?: string }) {
  return (
    <svg
      className={className}
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth={2}
      strokeLinecap="round"
      strokeLinejoin="round"
      aria-hidden="true"
    >
      <path d="M9 10 4 15l5 5" />
      <path d="M20 4v7a4 4 0 0 1-4 4H4" />
    </svg>
  );
}

function BrandMark({ className }: { className?: string }) {
  return (
    <svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <path
        d="M4 18V7.5L12 13l8-5.5V18"
        stroke="currentColor"
        strokeWidth={2.2}
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

function Kbd({ children }: { children: ReactNode }) {
  return (
    <kbd className="inline-flex min-w-[1.4rem] items-center justify-center rounded-md border border-slate-200 bg-slate-50 px-1.5 py-0.5 font-sans text-[11px] font-medium text-slate-500 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-400">
      {children}
    </kbd>
  );
}

export default function NavxSearch() {
  const prefersReduced = useReducedMotion();

  const [open, setOpen] = useState(false);
  const [query, setQuery] = useState("");
  const [activeIndex, setActiveIndex] = useState(0);
  const [menuOpen, setMenuOpen] = useState(false);
  const [selected, setSelected] = useState<SearchItem | null>(null);
  const [isMac, setIsMac] = useState(true);

  const inputRef = useRef<HTMLInputElement>(null);
  const toggleRef = useRef<HTMLButtonElement>(null);
  const searchRef = useRef<HTMLDivElement>(null);

  const results = useMemo(() => {
    const q = query.trim().toLowerCase();
    if (!q) return SEARCH_ITEMS;
    return SEARCH_ITEMS.filter((item) =>
      [item.title, item.description, item.category].some((field) =>
        field.toLowerCase().includes(q)
      )
    );
  }, [query]);

  const hasResults = results.length > 0;
  const safeIndex = hasResults ? Math.min(activeIndex, results.length - 1) : -1;
  const shortcut = isMac ? "⌘" : "Ctrl";

  useEffect(() => {
    setIsMac(/mac|iphone|ipad|ipod/i.test(navigator.platform || navigator.userAgent));
  }, []);

  useEffect(() => {
    setActiveIndex(0);
  }, [query]);

  // Global shortcut: Cmd/Ctrl + K opens the search.
  useEffect(() => {
    function onKey(e: KeyboardEvent) {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
        e.preventDefault();
        setOpen(true);
      }
    }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  // Focus the field once it has expanded open.
  useEffect(() => {
    if (!open) return;
    const id = window.setTimeout(() => inputRef.current?.focus(), prefersReduced ? 0 : 70);
    return () => window.clearTimeout(id);
  }, [open, prefersReduced]);

  // Close when a click lands outside the search.
  useEffect(() => {
    if (!open) return;
    function onDown(e: MouseEvent) {
      if (searchRef.current && !searchRef.current.contains(e.target as Node)) {
        setOpen(false);
      }
    }
    document.addEventListener("mousedown", onDown);
    return () => document.removeEventListener("mousedown", onDown);
  }, [open]);

  function closeSearch(focusToggle: boolean) {
    setOpen(false);
    setActiveIndex(0);
    if (focusToggle) {
      window.setTimeout(() => toggleRef.current?.focus(), prefersReduced ? 0 : 20);
    }
  }

  function selectItem(item: SearchItem) {
    setSelected(item);
    setQuery("");
    setOpen(false);
    setActiveIndex(0);
    window.setTimeout(() => toggleRef.current?.focus(), prefersReduced ? 0 : 20);
  }

  function onInputKeyDown(e: ReactKeyboardEvent<HTMLInputElement>) {
    switch (e.key) {
      case "ArrowDown":
        if (hasResults) {
          e.preventDefault();
          setActiveIndex((i) => (i + 1) % results.length);
        }
        break;
      case "ArrowUp":
        if (hasResults) {
          e.preventDefault();
          setActiveIndex((i) => (i - 1 + results.length) % results.length);
        }
        break;
      case "Home":
        if (hasResults) {
          e.preventDefault();
          setActiveIndex(0);
        }
        break;
      case "End":
        if (hasResults) {
          e.preventDefault();
          setActiveIndex(results.length - 1);
        }
        break;
      case "Enter":
        if (hasResults && safeIndex >= 0) {
          e.preventDefault();
          selectItem(results[safeIndex]);
        }
        break;
      case "Escape":
        e.preventDefault();
        if (query) setQuery("");
        else closeSearch(true);
        break;
      default:
        break;
    }
  }

  const expandSpring = prefersReduced
    ? { duration: 0 }
    : { type: "spring" as const, stiffness: 420, damping: 36, mass: 0.9 };

  return (
    <section className="relative w-full overflow-hidden bg-gradient-to-b from-slate-50 via-white to-slate-100 px-4 py-16 sm:px-6 sm:py-24 dark:from-slate-950 dark:via-slate-950 dark:to-slate-900">
      <style>{`
        @keyframes navxsearch-pulse {
          0%, 100% { opacity: 1; transform: scale(1); }
          50% { opacity: 0.35; transform: scale(0.62); }
        }
        @keyframes navxsearch-sheen {
          0% { background-position: -140% 0; }
          100% { background-position: 240% 0; }
        }
        .navxsearch-dot { animation: navxsearch-pulse 2.4s ease-in-out infinite; }
        .navxsearch-sheen {
          background-size: 220% 100%;
          animation: navxsearch-sheen 6.5s linear infinite;
        }
        @media (prefers-reduced-motion: reduce) {
          .navxsearch-dot, .navxsearch-sheen { animation: none !important; }
        }
      `}</style>

      {/* Decorative atmosphere */}
      <div
        aria-hidden="true"
        className="pointer-events-none absolute inset-x-0 -top-32 mx-auto h-64 max-w-4xl rounded-full bg-gradient-to-r from-indigo-300/40 via-violet-300/30 to-sky-300/40 blur-3xl dark:from-indigo-600/20 dark:via-violet-600/15 dark:to-sky-600/20"
      />

      <div className="relative mx-auto w-full max-w-6xl">
        <nav
          aria-label="Primary"
          className="relative z-20 flex items-center gap-3 rounded-2xl border border-slate-200/80 bg-white/75 px-3 py-2.5 shadow-lg shadow-slate-900/[0.04] backdrop-blur-xl sm:px-4 dark:border-slate-800 dark:bg-slate-900/70 dark:shadow-black/20"
        >
          {/* Brand */}
          <a
            href="#"
            className="group flex shrink-0 items-center gap-2.5 rounded-xl px-1.5 py-1 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-offset-slate-900"
          >
            <span className="grid h-9 w-9 place-items-center rounded-xl bg-gradient-to-br from-indigo-500 via-violet-500 to-sky-500 text-white shadow-sm shadow-indigo-500/30">
              <BrandMark className="h-5 w-5" />
            </span>
            <span className="text-[15px] font-semibold tracking-tight text-slate-900 dark:text-slate-50">
              Meridian
            </span>
          </a>

          {/* Desktop links */}
          <ul className="ml-2 hidden items-center gap-1 md:flex">
            {NAV_LINKS.map((link) => (
              <li key={link.label}>
                <a
                  href="#"
                  className="relative inline-flex items-center gap-1.5 rounded-lg px-3 py-2 text-sm font-medium text-slate-600 outline-none transition-colors hover:bg-slate-100 hover:text-slate-900 focus-visible:ring-2 focus-visible:ring-indigo-500 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white"
                >
                  {link.label}
                  {link.badge ? (
                    <span className="inline-flex items-center gap-1 rounded-full bg-emerald-50 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-emerald-700 ring-1 ring-inset ring-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-300 dark:ring-emerald-500/25">
                      <span className="navxsearch-dot h-1.5 w-1.5 rounded-full bg-emerald-500" />
                      {link.badge}
                    </span>
                  ) : null}
                </a>
              </li>
            ))}
          </ul>

          <div className="ml-auto flex items-center gap-2">
            {/* Expanding search */}
            <div ref={searchRef} role="search" className="relative">
              <motion.div
                initial={false}
                animate={{ width: open ? 320 : 44 }}
                transition={expandSpring}
                className="relative flex h-11 max-w-[calc(100vw-6.5rem)] items-center overflow-hidden rounded-full border border-slate-200 bg-white/80 shadow-sm transition-colors focus-within:border-indigo-400 focus-within:ring-2 focus-within:ring-indigo-500/30 dark:border-slate-700 dark:bg-slate-800/70 dark:focus-within:border-indigo-500"
              >
                {!open ? (
                  <button
                    ref={toggleRef}
                    type="button"
                    onClick={() => setOpen(true)}
                    aria-label={`Open search (${shortcut} K)`}
                    aria-expanded={false}
                    aria-controls="navx-search-listbox"
                    className="grid h-11 w-11 place-items-center rounded-full text-slate-500 outline-none transition-colors hover:text-slate-900 focus-visible:ring-2 focus-visible:ring-indigo-500 dark:text-slate-400 dark:hover:text-white"
                  >
                    <SearchIcon className="h-[18px] w-[18px]" />
                  </button>
                ) : (
                  <>
                    <span className="grid h-11 w-11 shrink-0 place-items-center text-indigo-500 dark:text-indigo-400">
                      <SearchIcon className="h-[18px] w-[18px]" />
                    </span>
                    <input
                      ref={inputRef}
                      type="text"
                      value={query}
                      onChange={(e) => setQuery(e.target.value)}
                      onKeyDown={onInputKeyDown}
                      role="combobox"
                      aria-expanded={hasResults}
                      aria-controls="navx-search-listbox"
                      aria-autocomplete="list"
                      aria-label="Search Meridian docs and features"
                      aria-activedescendant={safeIndex >= 0 ? `navx-opt-${safeIndex}` : undefined}
                      placeholder="Search docs, guides, API…"
                      className="h-11 min-w-0 flex-1 bg-transparent pr-1 text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none dark:text-slate-100 dark:placeholder:text-slate-500"
                    />
                    <button
                      type="button"
                      onClick={() => (query ? setQuery("") : closeSearch(true))}
                      aria-label={query ? "Clear search" : "Close search"}
                      className="mr-1.5 grid h-8 w-8 shrink-0 place-items-center rounded-full text-slate-400 outline-none transition-colors hover:bg-slate-100 hover:text-slate-700 focus-visible:ring-2 focus-visible:ring-indigo-500 dark:hover:bg-slate-700 dark:hover:text-slate-100"
                    >
                      <CloseIcon className="h-4 w-4" />
                    </button>
                  </>
                )}
              </motion.div>

              <AnimatePresence>
                {open ? (
                  <motion.div
                    key="panel"
                    initial={prefersReduced ? false : { opacity: 0, y: -8, scale: 0.98 }}
                    animate={{ opacity: 1, y: 0, scale: 1 }}
                    exit={prefersReduced ? { opacity: 0 } : { opacity: 0, y: -8, scale: 0.98 }}
                    transition={{ duration: prefersReduced ? 0 : 0.16, ease: "easeOut" }}
                    className="absolute right-0 top-[calc(100%+0.6rem)] z-30 w-[min(24rem,calc(100vw-2rem))] origin-top-right overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-2xl shadow-slate-900/10 dark:border-slate-700 dark:bg-slate-900 dark:shadow-black/40"
                  >
                    <div className="flex items-center justify-between border-b border-slate-100 px-4 py-2.5 dark:border-slate-800">
                      <span className="text-xs font-semibold uppercase tracking-wide text-slate-400 dark:text-slate-500">
                        {query.trim()
                          ? `${results.length} result${results.length === 1 ? "" : "s"}`
                          : "Popular searches"}
                      </span>
                      <span className="flex items-center gap-1.5 text-xs text-slate-400 dark:text-slate-500">
                        <Kbd>{shortcut}</Kbd>
                        <Kbd>K</Kbd>
                      </span>
                    </div>

                    {hasResults ? (
                      <ul
                        id="navx-search-listbox"
                        role="listbox"
                        aria-label="Search results"
                        className="max-h-[19rem] overflow-y-auto p-1.5"
                      >
                        {results.map((item, idx) => {
                          const active = idx === safeIndex;
                          return (
                            <li
                              key={item.id}
                              id={`navx-opt-${idx}`}
                              role="option"
                              aria-selected={active}
                              onMouseDown={(e) => e.preventDefault()}
                              onMouseEnter={() => setActiveIndex(idx)}
                              onClick={() => selectItem(item)}
                              className={`flex cursor-pointer items-start gap-3 rounded-xl px-3 py-2.5 transition-colors ${
                                active
                                  ? "bg-indigo-50 dark:bg-indigo-500/10"
                                  : "hover:bg-slate-50 dark:hover:bg-slate-800/60"
                              }`}
                            >
                              <span
                                className={`mt-0.5 inline-flex shrink-0 items-center rounded-md px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide ring-1 ring-inset ${categoryStyle(
                                  item.category
                                )}`}
                              >
                                {item.category}
                              </span>
                              <span className="min-w-0 flex-1">
                                <span className="block truncate text-sm font-medium text-slate-900 dark:text-slate-100">
                                  {item.title}
                                </span>
                                <span className="mt-0.5 block truncate text-xs text-slate-500 dark:text-slate-400">
                                  {item.description}
                                </span>
                              </span>
                              <ReturnIcon
                                className={`mt-1 h-4 w-4 shrink-0 transition-opacity ${
                                  active
                                    ? "text-indigo-500 opacity-100 dark:text-indigo-400"
                                    : "text-slate-300 opacity-0 dark:text-slate-600"
                                }`}
                              />
                            </li>
                          );
                        })}
                      </ul>
                    ) : (
                      <div className="px-4 py-8 text-center">
                        <span className="mx-auto grid h-10 w-10 place-items-center rounded-full bg-slate-100 text-slate-400 dark:bg-slate-800 dark:text-slate-500">
                          <SearchIcon className="h-5 w-5" />
                        </span>
                        <p className="mt-3 text-sm font-medium text-slate-700 dark:text-slate-200">
                          No matches for “{query.trim()}”
                        </p>
                        <p className="mt-1 text-xs text-slate-500 dark:text-slate-400">
                          Try “webhooks”, “billing”, or “deploy”.
                        </p>
                      </div>
                    )}

                    <div className="flex items-center gap-4 border-t border-slate-100 px-4 py-2.5 text-[11px] text-slate-400 dark:border-slate-800 dark:text-slate-500">
                      <span className="flex items-center gap-1.5">
                        <Kbd>↑</Kbd>
                        <Kbd>↓</Kbd>
                        navigate
                      </span>
                      <span className="flex items-center gap-1.5">
                        <Kbd>↵</Kbd>
                        open
                      </span>
                      <span className="ml-auto flex items-center gap-1.5">
                        <Kbd>esc</Kbd>
                        close
                      </span>
                    </div>
                  </motion.div>
                ) : null}
              </AnimatePresence>
            </div>

            {/* Primary CTA */}
            <a
              href="#"
              className="hidden shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-indigo-600 to-violet-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm shadow-indigo-500/30 outline-none transition-transform hover:-translate-y-0.5 focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white sm:inline-flex dark:focus-visible:ring-offset-slate-900"
            >
              Start free
            </a>

            {/* Mobile menu toggle */}
            <button
              type="button"
              onClick={() => setMenuOpen((v) => !v)}
              aria-label={menuOpen ? "Close menu" : "Open menu"}
              aria-expanded={menuOpen}
              aria-controls="navx-mobile-menu"
              className="grid h-11 w-11 shrink-0 place-items-center rounded-full border border-slate-200 bg-white/80 text-slate-600 outline-none transition-colors hover:text-slate-900 focus-visible:ring-2 focus-visible:ring-indigo-500 md:hidden dark:border-slate-700 dark:bg-slate-800/70 dark:text-slate-300 dark:hover:text-white"
            >
              {menuOpen ? <CloseIcon className="h-5 w-5" /> : <MenuIcon className="h-5 w-5" />}
            </button>
          </div>
        </nav>

        {/* Mobile menu */}
        <AnimatePresence>
          {menuOpen ? (
            <motion.div
              id="navx-mobile-menu"
              initial={prefersReduced ? false : { opacity: 0, height: 0 }}
              animate={{ opacity: 1, height: "auto" }}
              exit={prefersReduced ? { opacity: 0 } : { opacity: 0, height: 0 }}
              transition={{ duration: prefersReduced ? 0 : 0.2, ease: "easeOut" }}
              className="relative z-10 mt-2 overflow-hidden rounded-2xl border border-slate-200/80 bg-white/85 shadow-lg backdrop-blur-xl md:hidden dark:border-slate-800 dark:bg-slate-900/80"
            >
              <ul className="flex flex-col p-2">
                {NAV_LINKS.map((link) => (
                  <li key={link.label}>
                    <a
                      href="#"
                      onClick={() => setMenuOpen(false)}
                      className="flex items-center justify-between rounded-xl px-3 py-3 text-sm font-medium text-slate-700 outline-none transition-colors hover:bg-slate-100 focus-visible:ring-2 focus-visible:ring-indigo-500 dark:text-slate-200 dark:hover:bg-slate-800"
                    >
                      {link.label}
                      {link.badge ? (
                        <span className="inline-flex items-center gap-1 rounded-full bg-emerald-50 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-emerald-700 ring-1 ring-inset ring-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-300 dark:ring-emerald-500/25">
                          <span className="navxsearch-dot h-1.5 w-1.5 rounded-full bg-emerald-500" />
                          {link.badge}
                        </span>
                      ) : null}
                    </a>
                  </li>
                ))}
                <li className="p-1.5">
                  <a
                    href="#"
                    onClick={() => setMenuOpen(false)}
                    className="flex items-center justify-center rounded-full bg-gradient-to-br from-indigo-600 to-violet-600 px-4 py-3 text-sm font-semibold text-white outline-none focus-visible:ring-2 focus-visible:ring-indigo-500"
                  >
                    Start free
                  </a>
                </li>
              </ul>
            </motion.div>
          ) : null}
        </AnimatePresence>

        {/* In-context hero so the navbar reads as a real product page */}
        <div className="mt-16 max-w-2xl sm:mt-24">
          <span className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white/70 px-3 py-1 text-xs font-medium text-slate-600 dark:border-slate-800 dark:bg-slate-900/60 dark:text-slate-300">
            <span className="navxsearch-dot h-1.5 w-1.5 rounded-full bg-indigo-500" />
            Meridian Platform · v4.2
          </span>
          <h1 className="mt-5 text-4xl font-semibold tracking-tight text-slate-900 sm:text-5xl dark:text-slate-50">
            Everything you ship,{" "}
            <span className="navxsearch-sheen bg-gradient-to-r from-indigo-500 via-violet-500 to-sky-500 bg-clip-text text-transparent">
              behind one API.
            </span>
          </h1>
          <p className="mt-4 text-base leading-relaxed text-slate-600 sm:text-lg dark:text-slate-400">
            Press{" "}
            <span className="inline-flex items-center gap-1 align-middle">
              <Kbd>{shortcut}</Kbd>
              <Kbd>K</Kbd>
            </span>{" "}
            or tap the search icon to jump straight to any doc, guide, or reference.
          </p>

          <div aria-live="polite" className="mt-6 min-h-[2rem]">
            {selected ? (
              <span className="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-indigo-50 px-3 py-1.5 text-sm font-medium text-indigo-700 dark:border-indigo-500/30 dark:bg-indigo-500/10 dark:text-indigo-300">
                <ReturnIcon className="h-4 w-4" />
                Opening “{selected.title}”
              </span>
            ) : (
              <span className="sr-only">No page selected yet.</span>
            )}
          </div>
        </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

Gradient Navbar

Gradient Navbar

Original

gradient navbar