Craft Stunning Landing Pages with the Power of Next.js and Tailwind CSS

Navbar

<div className=' w-full p-4 flex items-center justify-center fixed top-0 z-50 '>
  <div className='border bg-neutral-950/70 border-white/50 w-full  lg:w-[800px] mx-5 md:mx-24 rounded-[40px] md:rounded-full backdrop-blur'>

fixed: Positions the div fixed relative to the viewport, so it stays in place when scrolling

top-0: Anchors the div to the very top of the screen

z-50: Sets a high z-index to ensure the element appears above other content

backdrop-blur: Applies a blur effect to the background behind the div, creating a frosted glass effect

<div className='flex justify-end pr-4'>
  <svg xmlns="http://www.w3.org/2000/svg" 
  width="24" height="24" viewBox="0 0 24 24" 
  fill="none" stroke="currentColor" strokeWidth="2" 
  strokeLinecap="round" strokeLinejoin="round" 
  className="feather feather-menu lg:hidden"
  onClick={()=>isOpen(!open)}>
    <line x1="3" y1="6" x2="21" y2="6"
    className={twMerge("origin-left transition",open && 'rotate-45 -translate-y-1')}></line>
    <line x1="3" y1="12" x2="21" y2="12"
    className={twMerge("transition",open && 'hidden')}
    ></line>
    <line x1="3" y1="18" x2="21" y2="18"
    className={twMerge("origin-left transition",open && '-rotate-45 translate-y-1')}></line>
    </svg>
  </div>
  </div>
  <AnimatePresence>
  {open && 
  <motion.div 
  initial={{height:0}}
  animate={{height:'auto'}}
  exit={{height:0}}
  className=' overflow-hidden '>
    <div className='flex flex-col items-center py-4 gap-4'>
    {Links.map((link)=>(
      <p key={link.id}>{link.name}</p>
    ))}
      <button className='rounded-full px-2 py-1 border border-lime-300/50 w-20'>Login</button>
      <button className='rounded-full px-2 py-1 bg-lime-400 w-20 text-white'>Signup</button>
    </div>
  </motion.div>
  }
  </AnimatePresence>

To obtain the menu SVG logo use this link.

Logo animation (Framer-motion).

  <div className=' flex overflow-hidden [mask-image:linear-gradient(to_right,transparent,black_10%,black_90%, transparent)]'>
            <motion.div 
            animate={{
              x:'-50%',
            }}
            transition={{
              duration:20,
              ease:'linear',
              repeat:Infinity,
            }}
            className='gap-24 flex flex-none pr-24 items-center'>
              {Array.from({length:2}).map((_,i)=>(
                <Fragment key={i}>
                {logos.map((logo)=>(
                  <Image src={logo.image} key={logo.id} alt='' 
                  className='w-24'/>
               )
               )}
                </Fragment>

              ))}
            </motion.div>
            </div>
<div className='flex overflow-hidden [mask-image:linear-gradient(to_right,transparent,black_10%,black_90%, transparent)]'>
  • flex: Creates a flex container

  • overflow-hidden: Hides any content that exceeds the container's dimensions

  • [mask-image:linear-gradient(...)]: A custom mask that creates a fade effect on the sides of the container

    • The gradient goes from transparent on the left, to black at 10% and 90%, then back to transparent

    • This creates a smooth fade-out effect on the horizontal edges

<motion.div              
    animate={{               
        x:'-50%',             
    }}             
    transition={{               
        duration:20,               
        ease:'linear',               
        repeat:Infinity,             
    }}             
    className='gap-24 flex flex-none pr-24 items-center'
>
  • animate={{ x:'-50%' }}: Animates the div to move horizontally by -50% of its width

  • transition properties:

    • duration:20: Animation lasts 20 seconds

    • ease:'linear': Constant speed throughout the animation

    • repeat:Infinity: Repeats the animation indefinitely

Tailwind classes on the div:

  • gap-24: Adds 24 units of gap between child elements

  • flex: Makes it a flex container

flex-none: Prevents the div from growing or shrinking

The flex-none makes sure the elements dont change size when the screen dimensions are changed

  • pr-24: Adds 24 units of padding to the right

Note: Its important to add pr-24 to match the gap-24, so as to prevent the loop breaking and skipping after the last item of the array. It adds the smooth transition from the first array to the next

{Array.from({length:2}).map((_,i)=>(                 
    <Fragment key={i}>                 
    {logos.map((logo)=>(                   
        <Image src={logo.image} key={logo.id} alt=''                    
            className='w-24'/>                
    ))}                 
    </Fragment>                                 
))}
  • Array.from({length:2}): Creates an array with 2 elements

  • .map((_,i)=>()): Iterates twice, creating a Fragment for each iteration

    (_, i) is the parameter list of the callback function.

    • _ is a placeholder for the current element in the array.

    • The underscore _ is a convention used when you don’t care about the actual value of the element.

  • logos.map(): Maps through a logos array inside each Fragment

Introduction (scroll-driven text reveal animation).

"use client";
import { useScroll, useTransform } from "framer-motion"
import { span } from "framer-motion/client";
import { useEffect, useRef, useState } from "react";
import { twMerge } from "tailwind-merge";

const text = "responsive websites with speed optimization with next js frameworks. We aim to not only meet but exceed our customer's expectations."
const words = text.split(' ');

const Intro = () => {

  const scrollRef = useRef(null);
  const {scrollYProgress} = useScroll({
    target:scrollRef, offset:['start end','end end']
  })

  const [currentWord,setCurrentWord] = useState(0);
  const wordIndex = useTransform(scrollYProgress,[0,1],[0,words.length]);

  useEffect(()=>{
    wordIndex.on('change',(latest)=>{
      setCurrentWord(latest);
    })
  },[wordIndex])

  return (
    <div className="py-24 "> 
    <div className="sticky top-20 md:top-32 lg:top-36">
    <div className=" flex justify-center ">
        <h1 className=" border border-lime-400 text-lime-400 rounded-full p-2 uppercase text-2xl"> &#10038; Introduction</h1>
      </div>
        <div className="mt-10 text-4xl md:text-5xl lg:text-6xl text-center px-4 md:px-10 lg:px-20 justify-center">
            <p className=" text-white font-medium" >
               <span className="">Layers is a modern application web service provide with </span> 
               <span>
               {words.map((word,wordIndex)=>(
                <span 
                className={twMerge("transition duration-500 text-white/15",wordIndex<currentWord && 'text-white')}
                key={wordIndex}>{`${word} `}</span>
               ))}
               </span>
            </p>
            <p className=" text-lime-400">Let&apos;s connect and work on projects</p>
        </div>
    </div>
    <div className="h-[150vh]" ref={scrollRef}></div>
    </div>
  )
}

export default Intro
const {scrollYProgress} = useScroll({
    target:scrollRef, offset:['start end','end end']
  })

The useScroll hook from Framer Motion tracks the scroll progress of an element, providing a normalized value between 0 and 1 that represents how far the user has scrolled through a specific element.

target: scrollRef

  • Specifies the DOM element being tracked

  • scrollRef is a React reference pointing to the scroll container

In this case, it's the <div className="h-[150vh]" ref={scrollRef}></div> element

offset: ['start end', 'end end'] It defines when the scroll progress calculation begins and ends.

  • First value 'start end':

    • When the top of the target element reaches the bottom of the viewport

    • Marks the starting point of scroll progress (0%)

  • Second value 'end end':

    • When the bottom of the target element reaches the bottom of the viewport

    • Marks the ending point of scroll progress (100%)

const wordIndex = useTransform(scrollYProgress,[0,1],[0,words.length]);

  useEffect(()=>{
    wordIndex.on('change',(latest)=>{
      setCurrentWord(latest);
    })
  },[wordIndex])

useTransform is a powerful Framer Motion hook that maps values from one range to another. It's like a translation device for numeric values.

How It Works Here:

  • Input Range [0, 1]: Represents scroll progress from 0% to 100%

  • Output Range [0, words.length]: Converts scroll progress to word indices

  • As you scroll, it smoothly converts scroll percentage to a word index

This useEffect() listens for changes in wordIndex and updates the currentWord state accordingly.

Breakdown:

  • wordIndex.on('change', callback): Listens for value changes

  • (latest) => setCurrentWord(latest): Updates state with new word index

  • [wordIndex]: Ensures effect runs when wordIndex changes

The key insight is that useTransform doesn't just return integer values. It provides a floating-point number that smoothly transitions between 0 and words.length.

Instead of strictly comparing integers, the condition wordIndex < currentWord works because:

  • currentWord captures the latest transformed value

  • The comparison allows partial word reveals

  • Floating-point precision enables smooth animations

Services(translate,transition,video).

 <div className='mt-12 grid md:grid-cols-2 lg:grid-cols-3 grid-cols-1 gap-8 '>
                <div className='bg-neutral-900 p-6 rounded-3xl border border-white/20 group'>
                    <div className='aspect-video flex items-center justify-center overflow-hidden'>
                        <div className='z-40 overflow-hidden size-20 border-4 border-lime-400 rounded-full p-1 bg-neutral-900'><Image src={avatar1} alt=''
                        className='rounded-full'/></div>
                        <div 
                        className='z-30 -ml-6 overflow-hidden size-20 border-4 border-sky-400 p-1 rounded-full bg-neutral-900'><Image src={avatar2} alt=''
                         className=' rounded-full '/></div>
                        <div className='z-20 overflow-hidden -ml-6 size-20 border-4 border-fuchsia-400 rounded-full p-1 bg-neutral-900'>
                        <Image src={avatar3} alt=''
                        className='rounded-full '/></div>
                        <div className='-ml-6 size-20 border-transparent group-hover:border-orange-500 transition relative overflow-hidden rounded-full border-4'>
                            <Image src={avatar4} alt="" 
                            className='size-full opacity-0 group-hover:opacity-100 absolute rounded-full transition duration-500'
                            />
                            <div className='size-full rounded-full bg-neutral-700 inline-flex justify-center items-center gap-2'>
                                {Array.from({length:3}).map((_,i)=>(
                                    <span className='size-1.5 rounded-full bg-white inline-flex ' key={i}></span>
                                ))}
                            </div>
                        </div>
                    </div>
                    <div className=''>
                        <h3 className='text-2xl font-medium mt-2'> Project management</h3>
                        <p className='mt-3 text-white/50'>We optimize your webpage for quick and efficient performance</p>
                    </div>
                    </div>
                <div className='bg-neutral-900 p-6 rounded-3xl border border-white/20'>
                    <div className='aspect-video flex items-center p-2 text-center relative group'>
                        <p className='xl:text-6xl text-4xl font-extrabold text-white/20 group-hover:text-white/10 transition duration-500'>
                            Contributed to <span 
                            className='bg-gradient-to-r from-purple-500 to-pink-500 bg-clip-text text-transparent'>over 100</span>
                            <video src="/assets/run.mp4"  
                            autoPlay
                            muted
                            loop
                            className='absolute bottom-32 size-36 left-1/2 -translate-x-1/2 rounded-2xl opacity-0 group-hover:opacity-100 transition duration-500 shadow-xl pointer-events-none'
                            />
                             projects
                        </p>
                    </div>
                    <div className=''>
                        <h3 className='text-2xl font-medium mt-6'> Speed optimization</h3>
                        <p className='mt-3 text-white/50'>We optimize your webpage for quick and efficient performance</p>
                    </div>
                </div>
                <div className='bg-neutral-900 p-6 rounded-3xl border border-white/20 group'>
                    <div className='aspect-video text-2xl text-black font-medium flex gap-4 items-center justify-center '>
                        <div 
                        className='w-28 bg-slate-200 border p-2 rounded-2xl outline outline-offset-4 outline-transparent group-hover:outline-lime-400 transition-all duration-500 group-hover:translate-y-2 '>Shift</div>
                        <div className='w-12 bg-slate-200 border p-2 rounded-2xl outline outline-offset-4 outline-transparent group-hover:outline-lime-400 transition-all duration-500 group-hover:translate-y-2 delay-150 '>C</div>
                        <div className='w-16 bg-slate-200 border p-2 rounded-2xl outline outline-offset-4 outline-transparent group-hover:outline-lime-400 transition-all duration-500 group-hover:translate-y-2 delay-300'>Crtl</div>
                    </div>
                    <div className=''>
                        <h3 className='text-2xl font-medium mt-6'> Speed optimization</h3>
                        <p className='mt-3 text-white/50'>We optimize your webpage for quick and efficient performance</p>
                    </div>
                </div>
            </div>
 <div className='z-40 overflow-hidden size-20 border-4 border-lime-400 rounded-full p-1 bg-neutral-900'><Image src={avatar1} alt=''
                        className='rounded-full'/></div>
     <div 
      className='z-30 -ml-6 overflow-hidden size-20 border-4 border-sky-400 p-1 rounded-full bg-neutral-900'><Image src={avatar2} alt=''
      className=' rounded-full '/></div>
     <div className='z-20 overflow-hidden -ml-6 size-20 border-4 border-fuchsia-400 rounded-full p-1 bg-neutral-900'>
      <Image src={avatar3} alt=''
      className='rounded-full '/></div>
      <div className='-ml-6 size-20 border-transparent group-hover:border-orange-500 transition relative overflow-hidden rounded-full border-4'>
      <Image src={avatar4} alt="" 
       className='size-full opacity-0 group-hover:opacity-100 absolute rounded-full transition duration-500'
                            />
      <div className='size-full rounded-full bg-neutral-700 inline-flex justify-center items-center gap-2'>
        {Array.from({length:3}).map((_,i)=>(
      <span className='size-1.5 rounded-full bg-white inline-flex ' key={i}></span>
                       ))}
       </div>
       </div>

z-40: Highest z-index, ensuring this avatar appears in front

overflow-hidden: Prevents content from spilling outside the div

Positioning Mechanics:

  • Negative margin (-ml-6) creates a stacked, overlapping effect

  • Decreasing z-index creates a layered appearance

  • Each avatar slightly peeks out from behind the previous one

<Image 
  className='size-full opacity-0 group-hover:opacity-100 absolute rounded-full transition duration-500'
/>
  • opacity-0: Image hidden by default

  • group-hover:opacity-100: Image appears on hover

  • absolute: Positioned relative to parent

  • transition duration-500: Smooth fade-in effect

The group attribute in Tailwind CSS is a utility class that allows you to target and style child elements based on the state of a parent element

This is particularly useful when building interactive components like dropdowns, modals, or buttons with hover or focus effects.

Target Child Elements with group- Modifiers: You can style child elements based on the state of the parent by using the group- prefix. For example:

  • group-hover: Styles the child when the parent is hovered.

  • group-focus: Styles the child when the parent is focused

 <video src="/assets/run.mp4"  
      autoPlay
       muted
      loop
  className='absolute bottom-32 size-36 left-1/2 -translate-x-1/2 rounded-2xl opacity-0 group-hover:opacity-100 transition duration-500 shadow-xl pointer-events-none'
                            />

autoPlay: Video starts playing automatically when page loads. No user interaction required to begin playback

muted: Prevents video audio from playing. Critical for autoplay in most browsers

loop:Video restarts from the beginning once it reaches the end. Creates a continuous, repeating animation effect

left-1/2: Moves the video's left edge to the horizontal center of its container. Positions it at 50% of the container's width

-translate-x-1/2: Negative horizontal translation by 50% .Shifts the video back by half its own width. Ensures perfect horizontal centering. Corrects the positioning from left-1/2

 <div className='bg-neutral-900 p-6 rounded-3xl border border-white/20 group'>
      <div className='aspect-video text-2xl text-black font-medium flex gap-4 items-center justify-center '>
       <div 
        className='w-28 bg-slate-200 border p-2 rounded-2xl outline outline-offset-4 outline-transparent group-hover:outline-lime-400 transition-all duration-500 group-hover:translate-y-2 '>Shift</div>
       <div className='w-12 bg-slate-200 border p-2 rounded-2xl outline outline-offset-4 outline-transparent group-hover:outline-lime-400 transition-all duration-500 group-hover:translate-y-2 delay-150 '>C</div>
       <div className='w-16 bg-slate-200 border p-2 rounded-2xl outline outline-offset-4 outline-transparent group-hover:outline-lime-400 transition-all duration-500 group-hover:translate-y-2 delay-300'>Crtl</div>
        </div>
        <div className=''>
        <h3 className='text-2xl font-medium mt-6'> Speed optimization</h3>
        <p className='mt-3 text-white/50'>We optimize your webpage for quick and efficient performance</p>
        </div>
   </div>

outline-offset-4. Specifies the distance between the outline and the edge of the element (the space between the border and the outline).

Call to action (useAnimate)

 const [isHovered,setIsHovered] = useState(false);
  const animation = useRef<AnimationPlaybackControls>();
  const [scope,animate] = useAnimate();

  useEffect(()=>{
    animation.current=animate(scope.current,{x:"-50%"},
      { duration: 20,
      ease:'linear',
      repeat:Infinity});
  },[]);

  useEffect(()=>{
    if(animation.current){
      if(isHovered){
        animation.current.speed = 0.25;
      } else{
        animation.current.speed=1;
      }
    }
  },[isHovered]);

animation Ref: Stores a reference to the animation controls. Allows direct manipulation of animation properties. Uses TypeScript's AnimationPlaybackControls type for type safety

useAnimate() Hook: Creates an animation scope

const [scope,animate] = useAnimate();

Returns a tuple with:

  • scope:A React ref pointing to the target animation element. Used to define the specific DOM element to animate. Allows precise targeting of animations

    •    <motion.div 
                ref={scope}
                className='flex flex-none gap-16 pr-16 text-8xl'
                onMouseEnter={()=>setIsHovered(true)}
                onMouseLeave={()=>setIsHovered(false)}
                >
      
    • animate: is used to define animation states and apply them to DOM elements.It simplifies animating elements by managing their states and lifecycle in React, making animations declarative and easier to implement.

      Signature: animate(target, definition, options)

      • target: The element to animate (uses scope.current)

      • definition: Animation properties. Like the scale, opacity, rotate and etc. Any simultaneous transformations.

      • options: Animation configuration. The transition properties for the animation

 useEffect(()=>{
    animation.current=animate(scope.current,{x:"-50%"},
      { duration: 20,
      ease:'linear',
      repeat:Infinity});
  },[]);