diff --git a/package-lock.json b/package-lock.json index 8f93bf6a..f0a98a52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@heroui/theme": "^2.4.12", "@stomp/stompjs": "^7.0.0", "axios": "^1.8.3", + "framer-motion": "^12.23.22", "lucide-react": "^0.508.0", "next": "^15.1.4", "react": "^18", @@ -6349,14 +6350,13 @@ } }, "node_modules/framer-motion": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.0.3.tgz", - "integrity": "sha512-5yy1sAqOjBUo+8O+kOa6/rIJ1AQLME8vEpUwwMf9Gv6YktaeeTd4bf0sb89AWwDVX7GwwuLebH94bakGOqWQ/g==", + "version": "12.23.22", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.22.tgz", + "integrity": "sha512-ZgGvdxXCw55ZYvhoZChTlG6pUuehecgvEAJz0BHoC5pQKW1EC5xf1Mul1ej5+ai+pVY0pylyFfdl45qnM1/GsA==", "license": "MIT", - "peer": true, "dependencies": { - "motion-dom": "^12.0.0", - "motion-utils": "^12.0.0", + "motion-dom": "^12.23.21", + "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { @@ -7482,21 +7482,19 @@ } }, "node_modules/motion-dom": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.0.0.tgz", - "integrity": "sha512-CvYd15OeIR6kHgMdonCc1ihsaUG4MYh/wrkz8gZ3hBX/uamyZCXN9S9qJoYF03GqfTt7thTV/dxnHYX4+55vDg==", + "version": "12.23.21", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.21.tgz", + "integrity": "sha512-5xDXx/AbhrfgsQmSE7YESMn4Dpo6x5/DTZ4Iyy4xqDvVHWvFVoV+V2Ri2S/ksx+D40wrZ7gPYiMWshkdoqNgNQ==", "license": "MIT", - "peer": true, "dependencies": { - "motion-utils": "^12.0.0" + "motion-utils": "^12.23.6" } }, "node_modules/motion-utils": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.0.0.tgz", - "integrity": "sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA==", - "license": "MIT", - "peer": true + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "license": "MIT" }, "node_modules/ms": { "version": "2.1.3", diff --git a/package.json b/package.json index 0561316e..6b3a05e5 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@heroui/theme": "^2.4.12", "@stomp/stompjs": "^7.0.0", "axios": "^1.8.3", + "framer-motion": "^12.23.22", "lucide-react": "^0.508.0", "next": "^15.1.4", "react": "^18", diff --git a/src/app/layouts/service.tsx b/src/app/layouts/service.tsx index 70c756e3..b05a0c79 100644 --- a/src/app/layouts/service.tsx +++ b/src/app/layouts/service.tsx @@ -1,4 +1,5 @@ import type { FC, ReactNode } from 'react'; +import { tv } from 'tailwind-variants'; import Menu from '@/features/service-menu'; import Header from '@/widgets/header'; @@ -9,11 +10,19 @@ interface Props { children: ReactNode; } +const style = tv({ + base: [ + 'relative overflow-y-hidden', + 'h-0 grow', // mobile + 'md:grid md:grid-cols-[auto,1fr] md:bg-default-100', // desktop + ], +}); + const ServiceLayout: FC = ({ children }) => { return (
-
+
{children}
diff --git a/src/features/drawer/ui/styles.ts b/src/features/drawer/ui/styles.ts index 18441fb5..49b1e8f6 100644 --- a/src/features/drawer/ui/styles.ts +++ b/src/features/drawer/ui/styles.ts @@ -1,7 +1,7 @@ import { tv } from 'tailwind-variants'; export const drawer = tv({ - base: 'absolute right-0 top-0 z-50 flex h-full w-[300px] flex-col bg-white transition-transform', + base: 'absolute right-0 top-0 z-50 flex h-screen w-[300px] flex-col bg-white transition-transform', variants: { open: { true: 'translate-x-0', @@ -11,7 +11,7 @@ export const drawer = tv({ }); export const backdrop = tv({ - base: 'fixed left-0 top-0 z-40 size-full bg-black bg-opacity-50 md:hidden', + base: 'fixed left-0 top-0 z-40 h-screen w-screen bg-black bg-opacity-50 md:hidden', }); export const button = tv({ diff --git a/src/pages/landing/ui/page.tsx b/src/pages/landing/ui/page.tsx index 786cf1c4..d5a97590 100644 --- a/src/pages/landing/ui/page.tsx +++ b/src/pages/landing/ui/page.tsx @@ -8,21 +8,24 @@ import SupportForm from './support/form'; import TutorialChatting from './tutorial-chatting/chatting'; import TutorialChattingDescription from './tutorial-chatting/description'; +const NAV_ID = 'nav-menu'; + const LandingPage = () => { return ( <>
-
- + - + diff --git a/src/pages/landing/ui/support/description.tsx b/src/pages/landing/ui/support/description.tsx index 18ac1ec8..1dc250cd 100644 --- a/src/pages/landing/ui/support/description.tsx +++ b/src/pages/landing/ui/support/description.tsx @@ -1,3 +1,7 @@ +'use client'; + +import { motion } from 'framer-motion'; + import { description } from '../styles'; import { CheckCircle } from './check-circle'; @@ -5,7 +9,20 @@ const SupportDescription = () => { const styles = description(); return ( -
+

{'지금 영업팀에 '}
@@ -30,7 +47,7 @@ const SupportDescription = () => { ), )} -

+ ); }; diff --git a/src/pages/landing/ui/tutorial-chatting/chatting.tsx b/src/pages/landing/ui/tutorial-chatting/chatting.tsx index c03a3c3a..898167b1 100644 --- a/src/pages/landing/ui/tutorial-chatting/chatting.tsx +++ b/src/pages/landing/ui/tutorial-chatting/chatting.tsx @@ -1,4 +1,7 @@ +'use client'; + import { Card } from '@heroui/react'; +import { motion } from 'framer-motion'; import { ChattingRoomProvider } from '@/entities/chatting-room'; import { TUTORIAL_CHAT_MEMBER_ID } from '@/shared/config'; @@ -17,24 +20,35 @@ const createMember = ( const TutorialChatting = () => { return ( - - - - - - - - - - + + + + + + + + + + + + ); }; diff --git a/src/pages/landing/ui/tutorial-chatting/description.tsx b/src/pages/landing/ui/tutorial-chatting/description.tsx index 690b639b..7858441c 100644 --- a/src/pages/landing/ui/tutorial-chatting/description.tsx +++ b/src/pages/landing/ui/tutorial-chatting/description.tsx @@ -1,3 +1,6 @@ +'use client'; + +import { motion } from 'framer-motion'; import { tv } from 'tailwind-variants'; import { description } from '../styles'; @@ -18,7 +21,19 @@ const TutorialChattingDescription = () => { const styles = createStyle(); return ( -
+

{'문맥까지 이해하는 '}
@@ -29,7 +44,7 @@ const TutorialChattingDescription = () => {
누구나 안심하고 대화할 수 있는 커뮤니케이션 공간을 만듭니다.

-

+ ); }; diff --git a/src/shared/ui/component/hero.tsx b/src/shared/ui/component/hero.tsx index 35bb0f2f..01faeb38 100644 --- a/src/shared/ui/component/hero.tsx +++ b/src/shared/ui/component/hero.tsx @@ -1,3 +1,6 @@ +'use client'; + +import { type MotionProps, motion, stagger } from 'framer-motion'; import { tv } from 'tailwind-variants'; const createStyle = tv({ @@ -18,19 +21,34 @@ const createStyle = tv({ }, }); -interface Props { +type Props = { children: ReactNode; className?: string; isFirst?: boolean; -} +} & Pick; -const Hero = ({ children, className, isFirst }: Props) => { +const Hero = ({ children, className, isFirst, viewport }: Props) => { const styles = createStyle({ isFirst }); return ( -
+
{children}
-
+ ); }; diff --git a/src/widgets/header/ui/index.tsx b/src/widgets/header/ui/index.tsx index 352f915b..f08d6ca1 100644 --- a/src/widgets/header/ui/index.tsx +++ b/src/widgets/header/ui/index.tsx @@ -9,6 +9,7 @@ import { NAVIGATIONS } from '../config/navigation'; import DrawerLink from './drawer-item/link'; import NavigationBelt from './nav-belt'; import Login from './nav-login'; +import HeaderWrapper from './wrapper'; interface Props { business?: boolean; @@ -25,7 +26,7 @@ const createStyle = tv({ variants: { position: { fixed: { base: 'fixed' }, - sticky: { base: 'sticky top-0' }, + sticky: { base: 'sticky top-0 bg-transparent backdrop-blur-lg' }, }, }, }); @@ -39,7 +40,7 @@ const Header = async ({ business, className, position, children }: Props) => { const styles = createStyle({ position }); return ( -
+
@@ -60,7 +61,7 @@ const Header = async ({ business, className, position, children }: Props) => {
회원가입 -
+ ); }; diff --git a/src/widgets/header/ui/wrapper.tsx b/src/widgets/header/ui/wrapper.tsx new file mode 100644 index 00000000..74958eb0 --- /dev/null +++ b/src/widgets/header/ui/wrapper.tsx @@ -0,0 +1,29 @@ +'use client'; + +import { motion } from 'framer-motion'; + +interface Props { + children: React.ReactNode; + className?: string; + animated?: boolean; +} + +const HeaderWrapper = ({ animated, ...props }: Props) => { + if (!animated) { + return
; + } + + return ( + + ); +}; + +export default HeaderWrapper;