diff --git a/package.json b/package.json index ba787ee..af558fe 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@tailwindcss/vite": "^4.1.7", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "framer-motion": "^12.12.1", "jotai": "^2.12.4", "react": "^19.1.0", "react-dom": "^19.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c8be69f..6058ffc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + framer-motion: + specifier: ^12.12.1 + version: 12.12.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) jotai: specifier: ^2.12.4 version: 2.12.4(@types/react@19.1.5)(react@19.1.0) @@ -944,6 +947,20 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + framer-motion@12.12.1: + resolution: {integrity: sha512-PFw4/GCREHI2suK/NlPSUxd+x6Rkp80uQsfCRFSOQNrm5pZif7eGtmG1VaD/UF1fW9tRBy5AaS77StatB3OJDg==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1153,6 +1170,12 @@ packages: modern-ahocorasick@1.1.0: resolution: {integrity: sha512-sEKPVl2rM+MNVkGQt3ChdmD8YsigmXdn5NifZn6jiwn9LRJpWm8F3guhaqrJT/JOat6pwpbXEk6kv+b9DMIjsQ==} + motion-dom@12.12.1: + resolution: {integrity: sha512-GXq/uUbZBEiFFE+K1Z/sxdPdadMdfJ/jmBALDfIuHGi0NmtealLOfH9FqT+6aNPgVx8ilq0DtYmyQlo6Uj9LKQ==} + + motion-utils@12.12.1: + resolution: {integrity: sha512-f9qiqUHm7hWSLlNW8gS9pisnsN7CRFRD58vNjptKdsqFLpkVnX00TNeD6Q0d27V9KzT7ySFyK1TZ/DShfVOv6w==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1364,6 +1387,9 @@ packages: peerDependencies: typescript: '>=4.8.4' + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -2201,6 +2227,15 @@ snapshots: flatted@3.3.3: {} + framer-motion@12.12.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + motion-dom: 12.12.1 + motion-utils: 12.12.1 + tslib: 2.8.1 + optionalDependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + fsevents@2.3.3: optional: true @@ -2355,6 +2390,12 @@ snapshots: modern-ahocorasick@1.1.0: {} + motion-dom@12.12.1: + dependencies: + motion-utils: 12.12.1 + + motion-utils@12.12.1: {} + ms@2.1.3: {} nanoid@3.3.11: {} @@ -2503,6 +2544,8 @@ snapshots: dependencies: typescript: 5.8.3 + tslib@2.8.1: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 diff --git a/src/app/global.css b/src/app/global.css index 4590063..8602d86 100644 --- a/src/app/global.css +++ b/src/app/global.css @@ -10,6 +10,7 @@ --color-s: #767676; --color-sd: #333; --color-sl: #dadada; + --color-sxl: #f5f5f5; --color-grad1: #5ba5f8; --color-grad2: #7c81ff; @@ -20,7 +21,7 @@ --text-xl: 20px; --text-lg: 18px; - --text-md: 16px; + --text-mid: 16px; --text-sm: 14px; --spacing-normal: 20px; @@ -38,8 +39,8 @@ :root { font-family: - Pretendard, 'Pretendard Variable', + 'Pretendard', -apple-system, BlinkMacSystemFont, system-ui, @@ -55,6 +56,7 @@ sans-serif; font-size: 16px; color: #111; + letter-spacing: -0.48px; } /* Chrome, Safari, Edge, Opera */ @@ -91,4 +93,12 @@ input[type='number'] { .shadow-login { box-shadow: 0px 4px 20px 0px rgba(55, 127, 248, 0.04); } + + .shadow-card { + box-shadow: 8px 8px 30px rgba(55, 127, 248, 0.1); + } + + .shadow-button { + box-shadow: 4px 4px 20px 0px rgba(55, 127, 248, 0.1); + } } diff --git a/src/app/stackflow/Stack.tsx b/src/app/stackflow/Stack.tsx index be5e13f..1717a06 100644 --- a/src/app/stackflow/Stack.tsx +++ b/src/app/stackflow/Stack.tsx @@ -1,11 +1,13 @@ import { basicUIPlugin } from '@stackflow/plugin-basic-ui'; import { basicRendererPlugin } from '@stackflow/plugin-renderer-basic'; import { stackflow } from '@stackflow/react'; + import { LoginScreen } from '@/screen/login/ui'; +import { HomeScreen } from '@/screen/home/ui'; export const { Stack, useFlow } = stackflow({ transitionDuration: 350, - activities: { LoginScreen }, + activities: { LoginScreen, HomeScreen }, plugins: [ basicRendererPlugin(), basicUIPlugin({ diff --git a/src/assets/icon/icon-notice.svg b/src/assets/icon/icon-notice.svg new file mode 100644 index 0000000..827c5fc --- /dev/null +++ b/src/assets/icon/icon-notice.svg @@ -0,0 +1,25 @@ + diff --git a/src/assets/icon/icon-result.svg b/src/assets/icon/icon-result.svg new file mode 100644 index 0000000..8737208 --- /dev/null +++ b/src/assets/icon/icon-result.svg @@ -0,0 +1,28 @@ + diff --git a/src/assets/icon/icon-user.png b/src/assets/icon/icon-user.png new file mode 100644 index 0000000..6cfbad0 Binary files /dev/null and b/src/assets/icon/icon-user.png differ diff --git a/src/assets/icon/index.ts b/src/assets/icon/index.ts new file mode 100644 index 0000000..89cbc40 --- /dev/null +++ b/src/assets/icon/index.ts @@ -0,0 +1,5 @@ +import UserIcon from './icon-user.png'; +import ResultIcon from './icon-result.svg'; +import NoticeIcon from './icon-notice.svg'; + +export { UserIcon, ResultIcon, NoticeIcon }; diff --git a/src/assets/image/bg-home.png b/src/assets/image/bg-home.png new file mode 100644 index 0000000..0dccd4a Binary files /dev/null and b/src/assets/image/bg-home.png differ diff --git a/src/assets/image/index.ts b/src/assets/image/index.ts index 5de9c45..bcdd47a 100644 --- a/src/assets/image/index.ts +++ b/src/assets/image/index.ts @@ -1,3 +1,4 @@ import LoginBg from './bg-login.png'; +import HomeBg from './bg-home.png'; -export { LoginBg }; +export { LoginBg, HomeBg }; diff --git a/src/features/home/mock/card.ts b/src/features/home/mock/card.ts new file mode 100644 index 0000000..51cf3bc --- /dev/null +++ b/src/features/home/mock/card.ts @@ -0,0 +1,32 @@ +import type { CardProps } from '@/features/home/types'; + +export const CARD_MOCK: CardProps[] = [ + { + id: 1, + title: '2025 총학생회 선거', + campus: '수원캠', + status: '진행중', + date: '2024.04.15 - 2024.04.16', + }, + { + id: 2, + title: '제 2회 총무 선거', + campus: '수원캠', + status: '진행중', + date: '2024.04.15 - 2024.04.16', + }, + { + id: 3, + title: '제 3회 기획 선거', + campus: '수원캠', + status: '진행중', + date: '2024.04.15 - 2024.04.16', + }, + { + id: 4, + title: '제 3회 기획 선거', + campus: '수원캠', + status: '진행중', + date: '2024.04.15 - 2024.04.16', + }, +]; diff --git a/src/features/home/mock/index.ts b/src/features/home/mock/index.ts new file mode 100644 index 0000000..cb5809f --- /dev/null +++ b/src/features/home/mock/index.ts @@ -0,0 +1 @@ +export * from './card'; diff --git a/src/features/home/types/card.ts b/src/features/home/types/card.ts new file mode 100644 index 0000000..d4271cc --- /dev/null +++ b/src/features/home/types/card.ts @@ -0,0 +1,7 @@ +export type CardProps = { + id: number; + title: string; + campus: string; + status: string; + date: string; +}; diff --git a/src/features/home/types/index.ts b/src/features/home/types/index.ts new file mode 100644 index 0000000..cb5809f --- /dev/null +++ b/src/features/home/types/index.ts @@ -0,0 +1 @@ +export * from './card'; diff --git a/src/features/home/ui/Card.tsx b/src/features/home/ui/Card.tsx new file mode 100644 index 0000000..0ecbb94 --- /dev/null +++ b/src/features/home/ui/Card.tsx @@ -0,0 +1,33 @@ +import { Button } from '@/shared/ui'; + +interface CardProps { + campus: string; + status: string; + title: string; + date: string; +} + +export default function Card({ campus, status, title, date }: CardProps) { + return ( + <> +
{title}
+{date}
+ + > + ); +} diff --git a/src/features/home/ui/CardStack.tsx b/src/features/home/ui/CardStack.tsx new file mode 100644 index 0000000..6a2d089 --- /dev/null +++ b/src/features/home/ui/CardStack.tsx @@ -0,0 +1,117 @@ +import { useRef, useState } from 'react'; +import { animate, motion } from 'framer-motion'; + +import { Card } from '@/features/home/ui'; +import type { CardProps } from '@/features/home/types'; +import { CARD_MOCK } from '@/features/home/mock'; + +export default function CardStack() { + const [stack, setStack] = useState