4단계로 React 및 Tailwind Css를 사용하여 처음부터 daily.dev 확장과 같은 사이드바를 만드는 방법
72241 단어 reacttutorialjavascripttailwindcss
두 가지를 결합하면 놀라운 결과를 얻을 수 있고 훨씬 더 나은 성과를 낼 수 있습니다.
이 게시물에서는 nextjs와 tailwind CSS를 설정하고 멋진 사이드바를 만들기 위해 알아야 할 모든 것을 자세히 설명합니다.
비디오 버전이 마음에 들면
마음에 드셨다면 잊지 마세요 subscribe 🤓
전제 조건
이 튜토리얼을 완료하려면 다음이 필요합니다.
Node.js용 로컬 개발 환경입니다.
0 - 최종 결과의 데모
여기에서 최종 결과를 볼 수 있습니다.
1 - Nextjs 및 Tailwind CSS 설정
우리는 우리의 프로젝트 unsing npx를 시작할 수 있습니다
npx create-next-app sidebar
사이드바 디렉토리로 이동하자
cd sidebar
애니메이션의 프레이머 모션과 아이콘의 반응 아이콘과 같은 사이드바 코딩을 시작하려면 몇 개의 라이브러리가 필요합니다.
npm i react-icons framer-motion
이제 프로젝트에 필요한 패키지가 생겼습니다. 이제 tailwind를 설정하여 설정 프로세스를 완료할 수 있습니다.
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
tailwind.config.js 파일을 편집하기만 하면 tailwind를 활성화하고 프로젝트에서 작동하도록 만들 수 있습니다.
module.export 내부에 구성 요소 및 페이지 디렉토리의 경로를 추가하기만 하면 됩니다(여기서 Tailwind CSS를 사용함).
우리의 경우 한 페이지만 가질 것이기 때문에 페이지 폴더입니다. 우리는 한 페이지만 가질 것입니다.
module.exports = {
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
마지막 단계는 전역 CSS 파일에 tailwind 지시문을 추가하는 것입니다.
@tailwind base;
@tailwind components;
@tailwind utilities;
2 - 모든 것을 준비하자
첫 번째는 일부 아이콘을 가져오고 사이드바에 데이터를 추가하는 것입니다.
우리가 그림에서 볼 수 있듯이
당신이 보면 우리는 3 부분
import Head from 'next/head'
import {
BsPlus,
BsSearch,
BsEyeFill,
BsBookmarkFill,
BsFillArrowLeftSquareFill,
BsPeopleFill,
BsTerminalFill,
BsFillArrowRightSquareFill
} from 'react-icons/bs'
import { AiFillFire, AiFillMessage, } from 'react-icons/ai'
import { IoMdArrowRoundUp } from 'react-icons/io'
import { MdNightlightRound, MdFeedback } from 'react-icons/md'
import { FaCog } from 'react-icons/fa'
const data = [
{
name: 'Discover',
items: [
{
title: 'Popular',
icon: AiFillFire,
},
{
title: 'Most Upvoted',
icon: IoMdArrowRoundUp,
},
{
title: 'Best Discussions',
icon: AiFillMessage,
},
{
title: 'Search',
icon: BsSearch,
},
]
},
{
name: 'Manage',
items: [
{
title: 'Bookmarks',
icon: BsBookmarkFill,
},
{
title: 'Reading history',
icon: BsEyeFill,
},
{
title: 'Focus Mode',
icon: MdNightlightRound,
},
{
title: 'Customize',
icon: FaCog,
},
]
},
]
const datafooter = [
{
name: '',
items: [
{
title: 'Docs',
icon: BsBookmarkFill,
},
{
title: 'Changelog',
icon: BsTerminalFill,
},
{
title: 'Feedback',
icon: MdFeedback,
},
{
title: 'Invite people',
icon: BsPeopleFill,
},
]
},
]
//.... the body of the component
...
//the end
이제 모든 데이터와 아이콘이 생겼습니다. 이제 코딩을 시작할 수 있습니다.
3 - 기본 컨테이너 및 카드 요소 만들기
첫 번째 작업은 사이드바에 왼쪽 테두리를 추가하고 내부에 카드 요소를 만드는 것입니다.
두 번째 단계는 축소 아이콘을 추가하고 볼 수 있는 것처럼 마우스로 컨테이너를 호버링할 때만 표시되도록 하는 것입니다.
이것이 코드임을 생성하기 위해
//imports
...
//end imports
export default function Home() {
return (
<div className='min-h-screen bg-black' >
<div className='max-w-[250px] animate duration-300 border-r border-gray-700 relative flex flex-col py-10 min-h-screen group' >
<BsFillArrowLeftSquareFill className='absolute hidden text-2xl text-white cursor-pointer -right-4 top-10 group-hover:block ' />
<div className={` border-green-400 border shadow-green-400/60 shadow-lg rounded-lg px-4 max-w-[220px] h-[120px] flex justify-center mx-2 flex-col mb-4`} >
<p className='font-thin text-white text-md' >
Get the content you need by creating a personal feed
</p>
<button className='flex items-center justify-center w-full py-2 my-2 font-bold text-black bg-green-400 rounded-lg' >
<BsPlus className='text-2xl' />
<p >
Create me feed
</p>
</button>
</div>
</div>
</div>
)
}
//imports
...
//end imports
export default function Home() {
return (
<div className='min-h-screen bg-black' >
<div className='max-w-[250px] animate duration-300 border-r border-gray-700 relative flex flex-col py-10 min-h-screen group' >
<BsFillArrowLeftSquareFill className='absolute hidden text-2xl text-white cursor-pointer -right-4 top-10 group-hover:block ' />
<div className={` border-green-400 border shadow-green-400/60 shadow-lg rounded-lg px-4 max-w-[220px] h-[120px] flex justify-center mx-2 flex-col mb-4`} >
<p className='font-thin text-white text-md' >
Get the content you need by creating a personal feed
</p>
<button className='flex items-center justify-center w-full py-2 my-2 font-bold text-black bg-green-400 rounded-lg' >
<BsPlus className='text-2xl' />
<p >
Create me feed
</p>
</button>
</div>
<div className='grow'>
{data.map((group, index) => (
<div key={index} className='my-2' >
<p className='mb-2 ml-4 text-sm font-bold text-gray-500' >{group.name}</p>
{group.items.map((item, index2) => (
<div key={index2} className='flex px-4 py-1 cursor-pointer' >
<item.icon className='text-lg text-gray-500' />
<p className='ml-4 text-sm font-bold text-gray-400' > {item.title}</p>
</div>
))}
</div>
))}
</div>
<div>
{datafooter.map((group, index) => (
<div key={index} className='my-2' >
<p className='mb-2 ml-4 text-sm font-bold text-gray-500' >{group.name}</p>
{group.items.map((item, index2) => (
<div key={index2} className='flex px-4 py-1 cursor-pointer' >
<item.icon className='text-lg text-gray-500' />
<p className='ml-4 text-sm font-bold text-gray-400' > {item.title}</p>
</div>
))}
</div>
))}
</div>
</div>
</div>
)
}
4 - 애니메이션
이제 프레이머 모션을 사용하여 프로젝트에 약간의 애니메이션을 추가할 수 있습니다.
첫 번째는 모션을 가져오고 코드에 애니메이션을 사용하는 것입니다.
//...import
import { useState, useEffect } from 'react'
import { motion, useAnimation } from 'framer-motion'
//
...
//
여기에 세 가지 종류의 애니메이션이 있습니다.
첫 번째는 사이드바 컨테이너입니다. 사이드바가 열려 있는지 여부에 따라 너비를 애니메이션해야 합니다.
두 번째는 텍스트를 보이거나 보이지 않게 만든 다음 display 속성을 사용하여 DOM에서 제거하거나 표시하는 것입니다.
마지막 애니메이션은 항목 제목의 불투명도를 간단하게 애니메이션하는 것입니다. 사이드바를 열고 닫을 때마다 디자인이 점프하지 않도록 하고 싶습니다.
우리는 더 유명한 모션 컨트롤러를 정의하고 위의 모든 세부 사항을 애니메이션으로 변환해야 한다는 것을 알고 있습니다.
export default function Home() {
const [active, setActive] = useState(false)
const controls = useAnimation()
const controlText = useAnimation()
const controlTitleText = useAnimation()
const showMore = () => {
controls.start({
width: '250px',
transition: { duration: 0.001 }
})
controlText.start({
opacity: 1,
display: 'block',
transition: {delay:0.3}
})
controlTitleText.start({
opacity: 1,
transition: {delay:0.3}
})
setActive(true)
}
const showLess = () => {
controls.start({
width: '55px',
transition: { duration: 0.001 }
})
controlText.start({
opacity: 0,
display: 'none',
})
controlTitleText.start({
opacity: 0,
})
setActive(false)
}
//main part
...
//end main part
}
애니메이션을 선언하면 페이지에서 사용할 수 있습니다.
export default function Home() {
const [active, setActive] = useState(false)
const controls = useAnimation()
const controlText = useAnimation()
const controlTitleText = useAnimation()
const showMore = () => {
controls.start({
width: '250px',
transition: { duration: 0.001 }
})
controlText.start({
opacity: 1,
display: 'block',
transition: {delay:0.3}
})
controlTitleText.start({
opacity: 1,
transition: {delay:0.3}
})
setActive(true)
}
const showLess = () => {
controls.start({
width: '55px',
transition: { duration: 0.001 }
})
controlText.start({
opacity: 0,
display: 'none',
})
controlTitleText.start({
opacity: 0,
})
setActive(false)
}
useEffect(() => {
showMore()
},[])
return (
<div className='min-h-screen bg-black' >
<Head>
<title>Sidebar</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<motion.div animate={controls} className='max-w-[250px] animate duration-300 border-r border-gray-700 relative flex flex-col py-10 min-h-screen group' >
{active && <BsFillArrowLeftSquareFill onClick={showLess} className='absolute hidden text-2xl text-white cursor-pointer -right-4 top-10 group-hover:block ' />}
{!active && <BsFillArrowRightSquareFill onClick={showMore} className='absolute text-2xl text-white cursor-pointer -right-4 top-10' />}
<div className={`${active && 'border-green-400 border shadow-green-400/60 shadow-lg rounded-lg px-4'} max-w-[220px] h-[120px] flex justify-center mx-2 flex-col mb-4`} >
<motion.p animate={controlText} className='font-thin text-white text-md' >
Get the content you need by creating a personal feed
</motion.p>
<button className='flex items-center justify-center w-full py-2 my-2 font-bold text-black bg-green-400 rounded-lg' >
<BsPlus className='text-2xl' />
<motion.p animate={controlText} >
Create me feed
</motion.p>
</button>
</div>
<div className='grow'>
{data.map((group, index) => (
<div key={index} className='my-2' >
<motion.p animate={controlTitleText} className='mb-2 ml-4 text-sm font-bold text-gray-500' >{group.name}</motion.p>
{group.items.map((item, index2) => (
<div key={index2} className='flex px-4 py-1 cursor-pointer' >
<item.icon className='text-lg text-gray-500' />
<motion.p animate={controlText} className='ml-4 text-sm font-bold text-gray-400' > {item.title}</motion.p>
</div>
))}
</div>
))}
</div>
<div>
{datafooter.map((group, index) => (
<div key={index} className='my-2' >
<motion.p animate={controlTitleText} className='mb-2 ml-4 text-sm font-bold text-gray-500' >{group.name}</motion.p>
{group.items.map((item, index2) => (
<div key={index2} className='flex px-4 py-1 cursor-pointer' >
<item.icon className='text-lg text-gray-500' />
<motion.p animate={controlText} className='ml-4 text-sm font-bold text-gray-400' > {item.title}</motion.p>
</div>
))}
</div>
))}
</div>
</motion.div>
</div>
)
}
우리 페이지의 최종 코드
import Head from 'next/head'
import { useState, useEffect } from 'react'
import {
BsPlus,
BsSearch,
BsEyeFill,
BsBookmarkFill,
BsFillArrowLeftSquareFill,
BsPeopleFill,
BsTerminalFill,
BsFillArrowRightSquareFill
} from 'react-icons/bs'
import { AiFillFire, AiFillMessage, } from 'react-icons/ai'
import { IoMdArrowRoundUp } from 'react-icons/io'
import { MdNightlightRound, MdFeedback } from 'react-icons/md'
import { FaCog } from 'react-icons/fa'
import { motion, useAnimation } from 'framer-motion'
const data = [
{
name: 'Discover',
items: [
{
title: 'Popular',
icon: AiFillFire,
},
{
title: 'Most Upvoted',
icon: IoMdArrowRoundUp,
},
{
title: 'Best Discussions',
icon: AiFillMessage,
},
{
title: 'Search',
icon: BsSearch,
},
]
},
{
name: 'Manage',
items: [
{
title: 'Bookmarks',
icon: BsBookmarkFill,
},
{
title: 'Reading history',
icon: BsEyeFill,
},
{
title: 'Focus Mode',
icon: MdNightlightRound,
},
{
title: 'Customize',
icon: FaCog,
},
]
},
]
const datafooter = [
{
name: '',
items: [
{
title: 'Docs',
icon: BsBookmarkFill,
},
{
title: 'Changelog',
icon: BsTerminalFill,
},
{
title: 'Feedback',
icon: MdFeedback,
},
{
title: 'Invite people',
icon: BsPeopleFill,
},
]
},
]
export default function Home() {
const [active, setActive] = useState(false)
const controls = useAnimation()
const controlText = useAnimation()
const controlTitleText = useAnimation()
const showMore = () => {
controls.start({
width: '250px',
transition: { duration: 0.001 }
})
controlText.start({
opacity: 1,
display: 'block',
transition: {delay:0.3}
})
controlTitleText.start({
opacity: 1,
transition: {delay:0.3}
})
setActive(true)
}
const showLess = () => {
controls.start({
width: '55px',
transition: { duration: 0.001 }
})
controlText.start({
opacity: 0,
display: 'none',
})
controlTitleText.start({
opacity: 0,
})
setActive(false)
}
useEffect(() => {
showMore()
},[])
return (
<div className='min-h-screen bg-black' >
<Head>
<title>Sidebar</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<motion.div animate={controls} className='max-w-[250px] animate duration-300 border-r border-gray-700 relative flex flex-col py-10 min-h-screen group' >
{active && <BsFillArrowLeftSquareFill onClick={showLess} className='absolute hidden text-2xl text-white cursor-pointer -right-4 top-10 group-hover:block ' />}
{!active && <BsFillArrowRightSquareFill onClick={showMore} className='absolute text-2xl text-white cursor-pointer -right-4 top-10' />}
<div className={`${active && 'border-green-400 border shadow-green-400/60 shadow-lg rounded-lg px-4'} max-w-[220px] h-[120px] flex justify-center mx-2 flex-col mb-4`} >
<motion.p animate={controlText} className='font-thin text-white text-md' >
Get the content you need by creating a personal feed
</motion.p>
<button className='flex items-center justify-center w-full py-2 my-2 font-bold text-black bg-green-400 rounded-lg' >
<BsPlus className='text-2xl' />
<motion.p animate={controlText} >
Create me feed
</motion.p>
</button>
</div>
<div className='grow'>
{data.map((group, index) => (
<div key={index} className='my-2' >
<motion.p animate={controlTitleText} className='mb-2 ml-4 text-sm font-bold text-gray-500' >{group.name}</motion.p>
{group.items.map((item, index2) => (
<div key={index2} className='flex px-4 py-1 cursor-pointer' >
<item.icon className='text-lg text-gray-500' />
<motion.p animate={controlText} className='ml-4 text-sm font-bold text-gray-400' > {item.title}</motion.p>
</div>
))}
</div>
))}
</div>
<div>
{datafooter.map((group, index) => (
<div key={index} className='my-2' >
<motion.p animate={controlTitleText} className='mb-2 ml-4 text-sm font-bold text-gray-500' >{group.name}</motion.p>
{group.items.map((item, index2) => (
<div key={index2} className='flex px-4 py-1 cursor-pointer' >
<item.icon className='text-lg text-gray-500' />
<motion.p animate={controlText} className='ml-4 text-sm font-bold text-gray-400' > {item.title}</motion.p>
</div>
))}
</div>
))}
</div>
</motion.div>
</div>
)
}
복제하려는 결과와 비교하여 최종 결과를 볼 수 있습니다.
우리는 해냈다.
읽어주셔서 감사합니다.
my github 에서 코드를 찾을 수 있습니다.
Reference
이 문제에 관하여(4단계로 React 및 Tailwind Css를 사용하여 처음부터 daily.dev 확장과 같은 사이드바를 만드는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/unshift/how-you-can-create-a-sidebar-like-the-dailydev-extension-fron-scratch-using-react-and-tailwindcss-in-5-steps-nk7텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)