Tailwind + Headless UI: Close Mobile Menu after click or clickaway - tailwind-css

I am using Tailwind + Headless UI to create a hamburger Menu bar on mobile to show the menu on click. But when I click on the menu it does not close automatically and creates a bad UX.
<Disclosure
as="nav"
className="fixed top-0 left-0 right-0 z-10 w-full bg-white shadow"
>
{({ open }) => (
<>
<div className="px-2 mx-auto max-w-7xl sm:px-4 lg:px-8">
<div className="flex justify-between h-16">
<div className="flex px-2 lg:px-0">
<div className="flex items-center flex-shrink-0">
<Link href="/">
<a className="relative block w-12 h-12">
<Image
src="/img/logo.png"
alt="NFT Volt Logo"
layout="fill"
className="w-auto h-6 lg:block"
/>
</a>
</Link>
</div>
<div className="hidden lg:ml-6 lg:flex lg:space-x-1">
{navLinks.map((link) => (
<NavLink key={link.id} href={link.href}>
{link.name}
</NavLink>
))}
</div>
</div>
<div className="flex items-center justify-center flex-1 px-2 lg:ml-6 lg:justify-end">
<div className="w-full max-w-lg lg:max-w-xs">
<label htmlFor="search" className="sr-only">
Search
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<SearchIcon
className="w-5 h-5 text-gray-400"
aria-hidden="true"
/>
</div>
<input
id="search"
name="search"
className="block w-full py-2 pl-10 pr-3 leading-5 placeholder-gray-500 bg-white border border-gray-300 rounded-md focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
placeholder="Search NFT projects..."
type="search"
/>
</div>
</div>
<Link href="/list-project" passHref>
<a
href="#"
className="items-center hidden px-4 py-2 ml-6 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md shadow-sm lg:inline-flex hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 whitespace-nowrap"
>
List your Project
</a>
</Link>
</div>
<div className="flex items-center lg:hidden">
{/* Mobile menu button */}
<Disclosure.Button className="inline-flex items-center justify-center p-2 text-gray-400 rounded-md hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue-500">
<span className="sr-only">
Open main menu
</span>
{open ? (
<XIcon
className="block w-6 h-6"
aria-hidden="true"
/>
) : (
<MenuIcon
className="block w-6 h-6"
aria-hidden="true"
/>
)}
</Disclosure.Button>
</div>
</div>
</div>
<Disclosure.Panel className="lg:hidden">
<div className="pt-2 pb-3 space-y-1">
{/* {navLinks.map((link) => (
<NavLinkMobile
key={link.id}
href={link.href}
onClick={() => {
console.log('click');
close();
}}
>
{link.name}
</NavLinkMobile>
))} */}
{navLinks.map((link) => (
<Disclosure.Button
as={NavLinkMobile}
key={link.id}
href={link.href}
>
{link.name}
</Disclosure.Button>
))}
<NavLinkMobile href="/list-project">
List your Project
</NavLinkMobile>
</div>
</Disclosure.Panel>
</>
)}
</Disclosure>
Tried to add manually close on click but doesn't seem to work.

The trick is to wrap the links with <Disclosure.Button> </Disclosure.Button> and it will close automatically the panel.
See: https://headlessui.dev/react/disclosure#closing-disclosures-manually

Use can use the close prop from Disclose itself.
import it this way ({ open, close }) and use it
onClick={() => {
close();
}}

Try using Next.js Router's push function to navigate the user when the Disclosure.Button is clicked.
At the top of your component, call the useRouter hook:
const router = useRouter();
With that, you can modify your JSX by adding an onClick property where you then call router.push({path}) , like this:
{
navLinks.map((link) => (
<Disclosure.Button
as="a"
key={link.id}
onClick={() => {
router.push(`${link.href}`);
}}
>
{link.name}
</Disclosure.Button>
));
}
Using the Next.js router will navigate the user to the desired href while still allowing the Disclosure render prop to toggle from open to closed.
For more infomation, check out - https://nextjs.org/docs/api-reference/next/router#routerpush

Related

Element with higher z-index is still on the background of another elements

Here is me trying to hover over 'delete' popup button positioned absolute relatively to three dots to the left.
As you can see it ignores the delete button completely, even though I even explicitly added a higher z-index to it. It has the same effect with any other elements on the background, so I estimate that problem is somewhere in code relevant to 'delete' button and it's popover. There are no elements with sticky or fixed positioning in this project. Here is my JSX with TailwindCSS and Headless UI's Popover component:
Relative part of parent component.
<Disclosure.Panel className="w-full text-gray-900 flex flex-col" >
{projects?.map(project => Object.keys(project).map(key => (
<div className={`w-4/5 py-2 hover:font-medium cursor-pointer self-end pl-2 flex items-center justify-between ${selectedProject === key ? 'bg-gray-900 text-gray-50 font-medium' : ''}`} onClick={() => handleSelect(key)} key={key}>
<p>{key}</p>
<Popover className='relative z-100'>
<Popover.Button className='flex items-center'><EllipsisHorizontalIcon className='w-8 h-6 pr-2 hover:text-zinc-400'/></Popover.Button>
<ProjectSettingsPopover selectedProject={selectedProject} />
</Popover>
</div>
)))}
<div className="flex gap-2 h-7 mt-2 w-4/5 ml-auto">
<input type='text' className='w-4/5 bg-background rounded-md px-2 py-1 text-sm border-2 border-grayDark' placeholder='New Project' value={input} onChange={(e) => setInput(e.target.value)}></input>
<button className='bg-black p-1 rounded-full flex items-center justify-center hover:bg-zinc-700' onClick={() => handleNewProject()}><PlusCircleIcon className='text-primary h-5 w-5 group-hover:text-primaryDark'/></button>
</div>
</Disclosure.Panel>
ProjectSettingsPopover.js
return (
<Transition
enter="transition ease-out duration-200"
enterFrom="opacity-0 -translate-x-1"
enterTo="opacity-100 -translate-x-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 -translate-x-0"
leaveTo="opacity-0 -translate-x-1"
appear
>
<Popover.Panel className="left-9 -top-8 w-24 text-sm z-50 rounded-sm flex flex-col absolute bg-background shadow-lg ring-1 border border-black ring-black ring-opacity-5">
<Button btnStyle='default' btnSize='edgy' onClick={deleteProject}>delete</Button>
</Popover.Panel>
</Transition>
)
What is the reason of such a weird behavior?

Smooth scrolling not working while <a> works?

I have been building an UI for twitch useing Next JS but when I use the Link next provides it does not smooth scroll to the section I want it to. However, when I use an tag it surprisingly works. So, how can i use Link from Next to smooth scroll ?
This is my code
`
import Image from "next/image";
import React from "react";
import logo from "../public/assets/logo.png";
import { GoSearch } from "react-icons/go";
import { FaUserAlt } from "react-icons/fa";
import { Menu, Transition } from "#headlessui/react";
import { MdOutlineMoreVert } from "react-icons/Md";
import Link from "next/link";
function classNames(...classes: string[]) {
return classes.filter(Boolean).join(" ");
}
export const Navbar = () => {
return (
<nav className="fixed top-0 left-0 z-50 flex h-12 w-full items-center justify-between bg-[#18181b] p-5 shadow-md shadow-black">
{/* Right Side */}
<div className="flex items-center">
<Image
className="cursor-pointer duration-500 hover:scale-125"
src={logo}
height="30"
width="30"
alt="/twitch"
/>
<p className="cursor-pointer pl-2 text-sm font-bold text-white hover:text-purple-600 sm:text-xl">
Browse
</p>
<div>
<Menu as="div" className="relative text-left">
<div className="flex">
<Menu.Button>
<MdOutlineMoreVert
size={20}
className="hover:text-purple-600"
/>
</Menu.Button>
</div>
<Transition
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute left-0 mt-2 w-56 origin-top-right rounded-md bg-[#0e0e10] shadow-lg ring-1 ring-white ring-opacity-5 focus:outline-none">
<div className="py-1">
<Menu.Item>
{({ active }) => (
<Link
href="/#"
className={classNames(
active
? " bg-purple-600 text-gray-100"
: "text-gray-200",
"block px-4 py-2 text-sm"
)}
>
Home
</Link>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<Link
href="/#streams"
className={classNames(
active
? "bg-purple-600 text-gray-100"
: "text-gray-200",
"block px-4 py-2 text-sm"
)}
>
Streams
</Link>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<Link
href="/#categories"
className={classNames(
active
? "bg-purple-600 text-gray-100"
: "text-gray-200",
"block px-4 py-2 text-sm"
)}
>
Categories
</Link>
)}
</Menu.Item>
</div>
</Menu.Items>
</Transition>
</Menu>
</div>
</div>
{/* middle */}
<div className="hidden max-w-[30rem] flex-grow items-center rounded-lg bg-[#3a3a3d] p-1 hover:border-2 hover:border-purple-500 hover:bg-black sm:flex">
<input
type="text"
placeholder="Search"
className=" w-full rounded-lg bg-transparent text-gray-300 focus:outline-none "
/>
<GoSearch className=" pr-1 text-white" size={30} />
</div>
{/* left side */}
<div className="flex items-center">
<button className="m-3 w-20 rounded-lg bg-[#3a3a3d] p-1 font-bold hover:bg-gray-500">
Log In
</button>
<button className="w-20 rounded-lg bg-[#9147ff] p-1 font-bold hover:bg-purple-700">
Sign Up
</button>
<div className="ml-1 flex items-center rounded-lg hover:bg-gray-600">
<Menu as="div" className="relative text-left">
<div className="flex">
<Menu.Button className="p-1 text-center">
<FaUserAlt
className="m-3 cursor-pointer text-white"
size={20}
/>
</Menu.Button>
</div>
<Transition
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute right-0 mt-2 w-56 origin-top-right rounded-md bg-[#0e0e10] shadow-lg ring-1 ring-white ring-opacity-5 focus:outline-none">
<div className="py-1">
<Menu.Item>
{({ active }) => (
<Link
href="/#signup"
className={classNames(
active
? " bg-purple-600 text-gray-100"
: "text-gray-200",
"block px-4 py-2 text-sm"
)}
>
Sign Up
</Link>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<Link
href="/#logout"
className={classNames(
active
? "bg-purple-600 text-gray-100"
: "text-gray-200",
"block px-4 py-2 text-sm"
)}
>
Log Out
</Link>
)}
</Menu.Item>
</div>
</Menu.Items>
</Transition>
</Menu>
</div>
</div>
</nav>
);
};
This is my global css
#tailwind base;
#tailwind components;
#tailwind utilities;
html {
scroll-behavior: smooth;
}
body {
background-color: #0e0e10;
color: white;
}
#layer base {
button {
#apply p-5 text-white;
}
}
`
I tried replacing link from next with just a tag but then i am unable to deploy it in vercel

Navbar dropdown not overflowing out of the navbar container

I have created a navbar using tailwind. I needed to add a dropdown in the navbar. The dropdown works fine but it does not escape the navbar container and kind of hides under it.
I want the dropdown to exceed the container and overflow.
Here is my Navbar code:
<header className="bg-[#182038]">
<nav className="max-w-wt 2xl:max-w-ft mx-auto">
<Container className="relative z-50 sm:z-0 flex justify-between py-4 sm:py-2 drop-shadow-2xl ">
<div className="relative z-10 sm:z-0 flex items-center gap-16 px-0">
<Link to="/" aria-label="Home">
<img
className="object-contain w-36 md:w-56"
src="https://i.stack.imgur.com/iGBaP.png"
alt="Workflow"
/>
</Link>
<div className="hidden lg:flex lg:gap-10">
{checkAuth && <NavLinks />}
{!checkAuth && <ProNavLinks />}
</div>
</div>
<div className="flex items-center gap-2">
<Popover className="lg:hidden">
{({ open }) => (
<>
<Popover.Button
className="relative z-10 inline-flex items-center rounded-lg "
aria-label="Toggle site navigation"
>
{({ open }) =>
open ? (
<XIcon
className="block h-6 w-6 text-white"
aria-hidden="true"
/>
) : (
<MenuIcon
className="block h-6 w-6 text-white"
aria-hidden="true"
/>
)
}
</Popover.Button>
<AnimatePresence initial={false}>
{open && (
<>
<Popover.Overlay
static
as={motion.div}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 z-0 bg-gray-600/60 backdrop-blur"
/>
<Popover.Panel
static
as={motion.div}
initial={{ opacity: 0, y: -32 }}
animate={{ opacity: 1, y: 0 }}
exit={{
opacity: 0,
y: -32,
transition: { duration: 0.2 },
}}
className="absolute inset-x-0 top-0 z-0 origin-top rounded-b-2xl bg-[#182038] px-6 pb-6 pt-32 shadow-2xl shadow-gray-900/20"
>
<div className="space-y-4 text-white">
<MobileNavLink to="/">Home</MobileNavLink>
<MobileNavLink to="/cex-activity">
CEX Activity
</MobileNavLink>
<MobileNavLink to="/dex-activity">
DEX Activity
</MobileNavLink>
<MobileNavLink to="/trending">
Trending
</MobileNavLink>
<MobileNavLink to="/ranking">Ranking</MobileNavLink>
<MobileNavLink to="/pricing">Pricing</MobileNavLink>
</div>
<div className="mt-8 flex flex-col gap-4">
<button
type="button"
className=" p-1 rounded-md text-gray-400 hover:text-white border px-2 border-[#B0EEFD]"
onClick={props.handleLoginClick}
>
Login
</button>
<button
type="button"
className="bg-[#B0EEFD] px-3 py-1 rounded text-white hover:text-white"
onClick={props.handleRegisterClick}
>
Register
</button>
</div>
</Popover.Panel>
</>
)}
</AnimatePresence>
</>
)}
</Popover>
{auth ? (
<>
{user?.fullname && (
// <p className="text-white text-xs">Hello {user?.fullname}!</p>
<Menu as="div" className="ml-3 relative">
<div>
<Menu.Button className="bg-gray-800 flex text-sm rounded-full focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white">
<span className="sr-only">Open user menu</span>
<img
className="h-8 w-8 rounded-full"
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
alt=""
/>
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none ">
<Menu.Item>
{({ active }) => (
<a
href="/"
className={classNames(
active ? "bg-gray-100" : "",
"block px-4 py-2 text-sm text-gray-700"
)}
>
Your Profile
</a>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<a
href="/"
className={classNames(
active ? "bg-gray-100" : "",
"block px-4 py-2 text-sm text-gray-700"
)}
>
Settings
</a>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<a
href="/"
className={classNames(
active ? "bg-gray-100" : "",
"block px-4 py-2 text-sm text-gray-700"
)}
>
Sign out
</a>
)}
</Menu.Item>
</Menu.Items>
</Transition>
</Menu>
)}
<button
type="button"
className="p-1 rounded-full text-gray-400 hover:text-white"
onClick={props.handleLogout}
>
Logout
</button>
</>
) : (
<>
<button
type="button"
className="hidden sm:block p-1 rounded-md text-[#B0EEFD] border px-2 border-[#B0EEFD]"
onClick={props.handleLoginClick}
>
Login
</button>
<button
type="button"
className="bg-[#B0EEFD] px-3 py-1 rounded text-black hover:text-black hidden sm:block "
onClick={props.handleRegisterClick}
>
Register
</button>
</>
)}
</div>
</Container>
</nav>
</header>
How can I fix this issue? Is there an issue with overflow? There is no overflow property in the navbar which would make me believe it is that issue.
You can add z-10 to the root Menu tag. This will lift it up to the top.

Weird gray gradient background appearing on mobile

I have a simple login form on a sliding modal. On desktop and in responsive mode with dev tools it looks as it should. However on mobile Chrome and Safari -- on an iphone, there's a weird gray rounded box or shadow.
I'm using tialwind css. And the component was built with headless ui.
I've tried adding -webkit-appearance: none; to the form, but the box still persits.
Here's how it looks on desktop:
desktop view
and here is how it looks on my iphone:
mobile view
here is the component code:
return (
<Transition.Root show={showModal.open} as={Fragment}>
<Dialog
as="div"
className="fixed inset-0 overflow-hidden"
onClose={() => setShowModal({ open: false })}
>
<div className="absolute inset-0 overflow-hidden">
<Transition.Child
as={Fragment}
enter="ease-in-out duration-500"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in-out duration-500"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Dialog.Overlay className="absolute inset-0 bg-black bg-opacity-80 transition-opacity" />
</Transition.Child>
<div className="fixed inset-y-0 right-0 pl-10 max-w-full flex">
<Transition.Child
as={Fragment}
enter="transform transition ease-in-out duration-500 sm:duration-700"
enterFrom="translate-x-full"
enterTo="translate-x-0"
leave="transform transition ease-in-out duration-500 sm:duration-700"
leaveFrom="translate-x-0"
leaveTo="translate-x-full"
>
<div className="w-screen max-w-md">
<div className="h-full flex flex-col py-6 bg-white shadow-xl overflow-y-scroll">
<div className="px-4 sm:px-6">
<div className="flex items-start justify-between">
<Dialog.Title className="text-2xl font-light text-vhGreen">
Welcome!
</Dialog.Title>
<div className="ml-3 h-7 flex items-center">
<button
type="button"
className="bg-white rounded-md text-vhGreen hover:text-gray-500 focus:outline-none"
onClick={() => setShowModal({ open: false })}
>
<span className="sr-only">Close panel</span>
<XCircleIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
</div>
</div>
<div className="mt-6 relative flex-1 px-4 sm:px-6">
{/* Replace with your content */}
<div className="absolute inset-0 px-4 sm:px-6">
<div className="relative p-6 flex-auto bg-white">
<h4 className="text-vhGreen font-light">
Login or sign up to gain access to the gallery and to
receive periodic updates from Thea
</h4>
<form
type="submit"
onSubmit={(e) => handleSubmit(e)}
className="bg-white"
>
{isNewUser && (
<>
<div className="my-4 text-sm">
<label className="block text-vhGreen">
First Name
</label>
<input
value={authForm.firstName || ""}
onChange={(e) => handleChange(e)}
type="text"
name="firstName"
placeholder="First Name"
className="w-full px-4 py-3 rounded-md text-vhGreen font-light"
/>
</div>
<div className="my-4 text-sm">
<label className="block text-vhGreen">
Last Name
</label>
<input
value={authForm.lastName || ""}
onChange={(e) => handleChange(e)}
type="text"
name="lastName"
placeholder="Last Name"
className="w-full px-4 py-3 rounded-md"
/>
</div>
</>
)}
<div className="my-4 text-sm">
<label className="block text-vhGreen">Email</label>
<input
value={authForm.email || ""}
onChange={(e) => handleChange(e)}
type="text"
name="email"
placeholder="Email"
className="w-full px-4 py-3 rounded-md"
/>
</div>
<div className="my-4 text-sm">
<label className="block text-vhGreen">
Password
</label>
<input
value={authForm.password || ""}
onChange={(e) => handleChange(e)}
type="password"
name="password"
placeholder="Password"
className="w-full px-4 py-3 rounded-md"
onKeyDown={(e) => onKeyDown(e)}
/>
<div className="flex justify-end text-vhGreen text-sm font-light">
<button
type="button"
onClick={() => handleNewSwitch()}
>
{!isNewUser
? "Need an account? Sign Up!"
: "Already have an account? Login!"}
</button>
</div>
</div>
</form>
</div>
{/*footer*/}
<div className="flex items-center justify-center p-6 border-t border-solid border-blueGray-200 rounded-b">
<button
className="text-vhGreen background-transparent font-light uppercase px-6 py-2 text-xl outline-none focus:outline-none mr-1 mb-1 ease-linear transition-all duration-150"
type="button"
onClick={(e) => handleSubmit(e)}
>
{isNewUser ? "Sign Up" : "Login"}
</button>
</div>
</div>
{/* /End replace */}
</div>
</div>
</div>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
faced same thing.
The problem is in:
type="button"
Try to remove that, works for me

Display Block not moving to next line

I'm trying to make a responsive nav bar with tailwind. I want to list my nav links with a single item in each row when I open the navigation menu. Right now, they are all on one row. I'm using tailwind:
function Navbar() {
const [isOpen, setIsOpen] = useState(false);
return (
<header>
<div className="flex flex-col max-w-screen-xl px-4 mx-auto md:items-center md:justify-between md:flex-row md:px-6 lg:px-8">
<div className="p-4 flex flex-row items-center justify-between">
<Link href="/">
<a className="text-lg font-semibold tracking-widest text-gray-900 uppercase rounded-lg dark-mode:text-white focus:outline-none focus:shadow-outline">
Header
</a>
</Link>
<button
className="md:hidden rounded-lg focus:outline-none focus:shadow-outline"
onClick={() => {
setIsOpen(!isOpen);
}}
>
Menu
</button>
</div>
<nav className={isOpen ? "flex" : "md:flex hidden"}>
<NavLink pathTo="/" displayText="Home" />
<NavLink pathTo="/" displayText="Page 1" />
<NavLink pathTo="/" displayText="Page 2" />
</nav>
</div>
</header>
);
}
export default Navbar;
My NavLink component is as follows:
import React from "react";
import Link from "next/link";
interface NavItem {
pathTo: string;
displayText: string;
}
function NavLink(item: NavItem) {
return (
<Link href={item.pathTo}>
<a className="block lg:p-4 px-4 py-2 mt-2 border-b-2 border-white text-sm bg-transparent dark-mode:bg-transparent dark-mode:hover:border-red-400 dark-mode:focus:border-red-400 dark-mode:focus:text-white dark-mode:text-gray-200 md:mt-0 md:ml-4 hover:text-gray-900 focus:text-gray-900 hover:border-red-400 focus:border-red-400">
{item.displayText}
</a>
</Link>
);
}
export default NavLink;
What am I missing? I want the NavLinks to have one item on each row when opened from the menu. I thought display:block would do it, but it seems not to. Why's that?
You need to add a flex direction to your nav links. Adding flex-col should do the trick.

Resources