Demo
The text expands in width with the sidebar, when ideally, the transition is much smoother. Maybe delayed so it tricks the eye to thinking it appears only when fully expanded.
How should I think about achieving this?
<div className="flex flex-col items-center space-y-4 w-full mt-8">
<Logo open={open} />
{open && <div className="h-auto w-3/4 text-xs text-slate-400">This looks weird when the line is super long</div>}
....
Should I attach a delay? transition-delay does nothing on the inner div.
I made an over simplified example, hopefully can help finding a good solution.
This one uses a cross delay approach to make the transition smooth when opening and closing the drawer.
There is a state open controls the drawer. On the drawer container:
open ? "w-52 delay-0" : "w-12 delay-300"
And the inside element uses a reversed delay value, something like:
open ? "opacity-100 delay-300" : "opacity-0 delay-0"
This way, during both opening and closing transition, parent and child elements will have properly ordered animation.
Live demo is here: stackblitz
import React, { useState } from "react";
import "./App.css";
const list = ["Option 1", "Option 2", "Option 3", "Option 4", "Option 5"];
const ListItem = ({ open, text, index }) => {
return (
<li className={`bg-teal-100 h-12 flex justify-end`}>
<div
className={`${
open ? "w-24" : "w-12"
} flex justify-center align-center p-3 transition-all duration-300 ease-in-out`}
>
{index + 1}
</div>
<div
className={`${
open ? "opacity-100 delay-300" : "opacity-0 delay-0"
} flex justify-center align-center p-3 flex-1 transition-all duration-300 ease-in-out`}
>
{text}
</div>
</li>
);
};
const SlideDrawer = ({ open }) => {
return (
<div
className={`${
open ? "w-52 delay-0" : "w-12 delay-300"
} absolute left-0 top-0 bottom-0 transition-all duration-300 ease-in-out overflow-hidden flex justify-start bg-pink-200`}
>
<div className={`w-52 flex flex-col justify-start align-end`}>
<div
className={`${
open ? "w-52 p-6 delay-300" : "w-12 p-3 delay-0"
} h-72 flex flex-col justify-start align-center transition-all duration-300 ease-in-out `}
>
<figure
className={`${
open ? "w-40 h-40 delay-300" : "w-6 h-6 delay-0"
} transition-all bg-teal-100 rounded-full duration-300 ease-in-out`}
></figure>
</div>
<ul className="w-full flex flex-col list-none">
{list.map((item, index) => (
<ListItem key={item} open={open} index={index} text={item} />
))}
</ul>
</div>
</div>
);
};
function App() {
const [drawerOpen, setDrawerOpen] = useState(false);
return (
<>
<div className="w-full flex justify-end">
<button
className="m-6 p-3 rounded-lg bg-slate-300"
onClick={() => setDrawerOpen((prev) => !prev)}
>
Open drawer
</button>
</div>
<SlideDrawer open={drawerOpen} />
</>
);
}
export default App;
I have created a badge component using Next.js and TailwindCSS.
After completing I can see there's a text alignment issue on the badge. When I add any alphabet with a tail I got the alignment issue. I am attaching my component as well as the web view of the badge. Any kind of help will be highly appreciated.
Thanks.
import clsx from 'clsx';
import { ReactNode } from 'react';
const classes = {
base: 'flex items-center justify-center font-medium leading-none',
size: {
sm: 'px-1.5 py-[3px] text-[10px]',
DEFAULT: 'px-2 py-1 text-xs',
lg: 'px-2.5 py-1 text-sm',
xl: 'px-3 py-1.5 text-base',
},
dot: {
size: {
sm: 'w-2.5 h-2.5 text-xs',
DEFAULT: 'w-4 h-4 text-sm',
lg: 'w-[18px] h-[18px] text-base',
xl: 'w-5 h-5 text-lg',
},
},
rounded: {
none: 'rounded-none',
sm: 'rounded-sm',
DEFAULT: 'rounded-md',
lg: 'rounded-lg',
full: 'rounded-full',
},
variant: {
solid: {
base: 'text-white',
color: {
DEFAULT: 'bg-gray-900 hover:enabled:bg-gray-1000',
primary: 'bg-primary-light',
secondary: 'bg-secondary-light',
success: 'bg-green-light',
warning: 'bg-orange-light',
danger: 'bg-red-light',
info: 'bg-blue-light',
},
},
flat: {
base: 'font-medium',
color: {
DEFAULT: 'bg-gray-300 text-gray-800',
primary: 'bg-primary-lighter text-primary-dark',
secondary: 'bg-secondary-lighter text-secondary-dark',
success: 'bg-green-lighter text-green-dark',
warning: 'bg-orange-lighter text-orange-dark',
danger: 'bg-red-lighter text-red-dark',
info: 'bg-blue-lighter text-info-blue-dark',
},
},
outlined: {
base: 'bg-gray-0 border font-medium',
color: {
DEFAULT: 'border-gray-900 text-gray-900',
primary: 'border-primary-dark text-primary-dark',
secondary: 'border-secondary-dark text-secondary-dark',
success: 'border-green-dark text-green-dark',
warning: 'border-orange-dark text-orange-dark',
danger: 'border-red-dark text-red-dark',
info: 'border-blue-dark text-blue-dark',
},
},
},
};
interface BadgeProps extends React.HTMLAttributes<HTMLDivElement> {
content?: ReactNode;
color?: keyof typeof classes.variant['solid']['color'];
variant?: keyof typeof classes.variant;
size?: keyof typeof classes.size | keyof typeof classes.dot.size;
dotSize?: keyof typeof classes.dot.size;
rounded?: keyof typeof classes.rounded;
dot?: boolean;
}
/**
* Bedge is a small overlapped UI item which indicates a status, notification, or event that appears in relativity with the underlying object.
* You can use props like `id`, `title` etc.
*/
const Badge = ({
content,
color = 'DEFAULT',
variant = 'solid',
children,
className,
size = 'DEFAULT',
dot = false,
rounded = 'full',
...props
}: BadgeProps) => {
const styles = classes.variant[variant];
return (
<span
className={clsx(
classes.base,
classes.variant[variant],
dot ? classes.dot.size[size] : classes.size[size],
styles.color[color],
styles.base,
classes.rounded[rounded],
className,
{ ...props }
)}
>
{content}
{children}
</span>
);
};
Badge.displayName = 'Badge';
export default Badge;
<div class="my-4" style="align-items: center; display: flex">
<span
class="base bg-gray-900 color font-medium hover:enabled:bg-gray-1000 inline-flex items-center justify-center leading-none mr-4 px-2.5 py-1 rounded rounded-full text-sm text-white"
style="display: inline-block"
>Default</span
><span
class="flex items-center justify-center font-medium leading-none base color px-2.5 py-1 text-sm bg-primary-light text-white rounded-full mr-4 flex inline-flex rounded"
>Primary</span
><span
class="flex items-center justify-center font-medium leading-none base color px-2.5 py-1 text-sm bg-secondary-light text-white rounded-full mr-4 flex inline-flex rounded"
>Secondary</span
><span
class="flex items-center justify-center font-medium leading-none base color px-2.5 py-1 text-sm bg-green-light text-white rounded-full mr-4 flex inline-flex rounded"
>Success</span
><span
class="flex items-center justify-center font-medium leading-none base color px-2.5 py-1 text-sm bg-red-light text-white rounded-full mr-4 flex inline-flex rounded"
>Danger</span
><span
class="flex items-center justify-center font-medium leading-none base color px-2.5 py-1 text-sm bg-orange-light text-white rounded-full mr-4 flex inline-flex rounded"
>Warning</span
><span
class="flex items-center justify-center font-medium leading-none base color px-2.5 py-1 text-sm bg-blue-light text-white rounded-full mr-4 flex inline-flex rounded"
>Info</span
>
</div>
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
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
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.