diff --git a/package.json b/package.json index 50357f0..7323da4 100644 --- a/package.json +++ b/package.json @@ -4,22 +4,21 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite --host", + "dev": "vite --host 0.0.0.0", "build": "tsc -b && vite build", "lint": "eslint .", "preview": "vite preview" }, "dependencies": { + "@egjs/react-infinitegrid": "^4.12.0", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@egjs/react-infinitegrid": "^4.12.0", "@mui/icons-material": "^6.4.4", "@mui/material": "^6.4.4", "@tailwindcss/vite": "^4.0.6", "@tanstack/react-query": "^5.66.0", "axios": "^1.7.9", "gsap": "^3.12.7", - "pnpm": "^10.4.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router": "^7.1.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3c350ec..c0758e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,15 +8,15 @@ importers: .: dependencies: + '@egjs/react-infinitegrid': + specifier: ^4.12.0 + version: 4.12.0 '@emotion/react': specifier: ^11.14.0 version: 11.14.0(@types/react@19.0.8)(react@18.3.1) '@emotion/styled': specifier: ^11.14.0 version: 11.14.0(@emotion/react@11.14.0(@types/react@19.0.8)(react@18.3.1))(@types/react@19.0.8)(react@18.3.1) - '@egjs/react-infinitegrid': - specifier: ^4.12.0 - version: 4.12.0 '@mui/icons-material': specifier: ^6.4.4 version: 6.4.4(@mui/material@6.4.4(@emotion/react@11.14.0(@types/react@19.0.8)(react@18.3.1))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.8)(react@18.3.1))(@types/react@19.0.8)(react@18.3.1))(@types/react@19.0.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@19.0.8)(react@18.3.1) @@ -35,9 +35,6 @@ importers: gsap: specifier: ^3.12.7 version: 3.12.7 - pnpm: - specifier: ^10.4.1 - version: 10.4.1 react: specifier: ^18.3.1 version: 18.3.1 @@ -188,10 +185,7 @@ packages: resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} engines: {node: '>=6.9.0'} - '@emotion/babel-plugin@11.13.5': - resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} - -'@cfcs/core@0.0.24': + '@cfcs/core@0.0.24': resolution: {integrity: sha512-feB38qu+eDk0Pggh/yR7gjaNmvUYA2uCxHP3Pz2MLE4LZ/9jPdtu8bzCSI47yTEhWyZCF5Pk698hdz8IN2mTjA==} '@cfcs/core@0.0.5': @@ -218,6 +212,9 @@ packages: '@egjs/react-infinitegrid@4.12.0': resolution: {integrity: sha512-V661KH1nka13wgy73N0IGBazZGe7LiECml1JDqB5DP6axTERUVCs8KRHadHu6oQihDeXwJIHe1YRhhMN9eAnUw==} + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + '@emotion/cache@11.14.0': resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} @@ -1924,11 +1921,6 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} - pnpm@10.4.1: - resolution: {integrity: sha512-x1O2w616+hOvOI+m2AgDWgCOMOqZk/WMZmPivF/yFnmqg02wlJhxKapNSIuG31f3tjSYGy+CfNysxpjMDPuIrw==} - engines: {node: '>=18.12'} - hasBin: true - possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -2025,13 +2017,6 @@ packages: react-is@19.0.0: resolution: {integrity: sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==} - react-router-dom@7.1.5: - resolution: {integrity: sha512-/4f9+up0Qv92D3bB8iN5P1s3oHAepSGa9h5k6tpTFlixTTskJZwKGhJ6vRJ277tLD1zuaZTt95hyGWV1Z37csQ==} - engines: {node: '>=20.0.0'} - peerDependencies: - react: '>=18' - react-dom: '>=18' - react-router@7.1.5: resolution: {integrity: sha512-8BUF+hZEU4/z/JD201yK6S+UYhsf58bzYIDq2NS1iGpwxSXDu7F+DeGSkIXMFBuHZB21FSiCzEcUb18cQNdRkA==} engines: {node: '>=20.0.0'} @@ -2496,21 +2481,6 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@emotion/babel-plugin@11.13.5': - dependencies: - '@babel/helper-module-imports': 7.25.9 - '@babel/runtime': 7.26.9 - '@emotion/hash': 0.9.2 - '@emotion/memoize': 0.9.0 - '@emotion/serialize': 1.3.3 - babel-plugin-macros: 3.1.0 - convert-source-map: 1.9.0 - escape-string-regexp: 4.0.0 - find-root: 1.1.0 - source-map: 0.5.7 - stylis: 4.2.0 - transitivePeerDependencies: - - supports-color '@cfcs/core@0.0.24': dependencies: '@egjs/component': 3.0.5 @@ -2550,6 +2520,22 @@ snapshots: dependencies: '@egjs/infinitegrid': 4.12.0 + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.25.9 + '@babel/runtime': 7.26.9 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + '@emotion/cache@11.14.0': dependencies: '@emotion/memoize': 0.9.0 @@ -4235,8 +4221,6 @@ snapshots: picomatch@4.0.2: {} - pnpm@10.4.1: {} - possible-typed-array-names@1.1.0: {} postcss@8.5.2: @@ -4275,12 +4259,6 @@ snapshots: react-is@19.0.0: {} - react-router-dom@7.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-router: 7.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-router@7.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@types/cookie': 0.6.0 diff --git a/src/App.tsx b/src/App.tsx index 34e6dfa..e6148da 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,6 +3,7 @@ import { Route, Routes } from 'react-router'; import useViewport from './hooks/useViewport'; import Layout from './layouts/Layout'; import Home from './pages/Home'; +import Landing from './pages/Landing'; import LetterBoardPage from './pages/LetterBoard'; import LetterBoardDetailPage from './pages/LetterBoardDetail'; import LetterBoxPage from './pages/LetterBox'; @@ -24,6 +25,7 @@ const App = () => { } /> } /> + } /> } /> }> diff --git a/src/assets/icons/google.svg b/src/assets/icons/google.svg new file mode 100644 index 0000000..454d2c2 --- /dev/null +++ b/src/assets/icons/google.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts index 7983ffa..56e7f98 100644 --- a/src/assets/icons/index.ts +++ b/src/assets/icons/index.ts @@ -2,22 +2,29 @@ import AlarmIcon from './alarm.svg?react'; import ArrowLeftIcon from './arrow-left.svg?react'; import BoardIcon from './board.svg?react'; import CloudIcon from './cloud.svg?react'; -import ColorSirenIcon from './color-siren.svg?react'; import DeleteIcon from './delete.svg?react'; import EnvelopeIcon from './envelope.svg?react'; -import InformationIcon from './infromation.svg?react'; +import GoogleIcon from './google.svg?react'; +import InformationIcon from './information.svg?react'; +import KakaoIcon from './kakao.svg?react'; import LikeFilledIcon from './like-filled.svg?react'; import LikeOutlinedIcon from './like-outlined.svg?react'; +import NaverIcon from './naver.svg?react'; import NoticeIcon from './notice.svg?react'; import PersonIcon from './person.svg?react'; import RestartIcon from './restart.svg'; import SirenFilledIcon from './siren-filled.svg?react'; import SirenOutlinedIcon from './siren-outlined.svg?react'; import SnowIcon from './snow.svg?react'; +import StampIcon from './stamp.svg?react'; import ThermostatIcon from './thermostat.svg?react'; import WarmIcon from './warm.svg?react'; export { + NaverIcon, + KakaoIcon, + GoogleIcon, + StampIcon, AlarmIcon, PersonIcon, ArrowLeftIcon, @@ -29,7 +36,6 @@ export { SnowIcon, ThermostatIcon, WarmIcon, - ColorSirenIcon, SirenFilledIcon, SirenOutlinedIcon, NoticeIcon, diff --git a/src/assets/icons/infromation.svg b/src/assets/icons/information.svg similarity index 100% rename from src/assets/icons/infromation.svg rename to src/assets/icons/information.svg diff --git a/src/assets/icons/kakao.svg b/src/assets/icons/kakao.svg new file mode 100644 index 0000000..37f35ef --- /dev/null +++ b/src/assets/icons/kakao.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/icons/naver.svg b/src/assets/icons/naver.svg new file mode 100644 index 0000000..1c90751 --- /dev/null +++ b/src/assets/icons/naver.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/stamp.svg b/src/assets/icons/stamp.svg new file mode 100644 index 0000000..d78ecf1 --- /dev/null +++ b/src/assets/icons/stamp.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/go-to-write.png b/src/assets/images/go-to-write.png deleted file mode 100644 index d91b072..0000000 Binary files a/src/assets/images/go-to-write.png and /dev/null differ diff --git a/src/assets/images/landing-blur.png b/src/assets/images/landing-blur.png new file mode 100644 index 0000000..b9f95c1 Binary files /dev/null and b/src/assets/images/landing-blur.png differ diff --git a/src/assets/images/landing.png b/src/assets/images/landing.png new file mode 100644 index 0000000..950916b Binary files /dev/null and b/src/assets/images/landing.png differ diff --git a/src/assets/images/postoffice-letter.png b/src/assets/images/postoffice-letter.png new file mode 100644 index 0000000..39d27c9 Binary files /dev/null and b/src/assets/images/postoffice-letter.png differ diff --git a/src/assets/images/postoffice.png b/src/assets/images/postoffice.png new file mode 100644 index 0000000..02dad3a Binary files /dev/null and b/src/assets/images/postoffice.png differ diff --git a/src/pages/Home/components/GoToWrite.tsx b/src/pages/Home/components/GoToWrite.tsx index 302cba7..9c4d589 100644 --- a/src/pages/Home/components/GoToWrite.tsx +++ b/src/pages/Home/components/GoToWrite.tsx @@ -1,5 +1,6 @@ import { Link } from 'react-router'; -import goToWrite from '@/assets/images/go-to-write.png'; + +import goToWrite from '@/assets/images/postoffice-letter.png'; const GoToWrite = () => { return ( diff --git a/src/pages/Landing/constants/index.ts b/src/pages/Landing/constants/index.ts new file mode 100644 index 0000000..da27eda --- /dev/null +++ b/src/pages/Landing/constants/index.ts @@ -0,0 +1,25 @@ +export const STYLE_CLASS = [ + { + imagePosition: 'left-[calc(50%-200px)]', + mask: 'bottom-26 left-[calc(50%+65px)]', + circle: 'h-41 w-41', + textPosition: '-top-20 left-[calc(50%-30px)] text-right', + description: + '따뜻한 편지 사례들을 모아 볼 수 있어요.\n응원이 필요한 사람들에게\n단체로 롤링페이퍼를 쓸 수도 있습니다.', + }, + { + imagePosition: 'left-[calc(50%-200px)]', + mask: 'bottom-5 left-[calc(50%-75px)]', + circle: 'h-42 w-42', + textPosition: '-top-14 left-[calc(50%+40px)] text-left', + description: '주고받은 편지 내역을 확인해 보세요.\n보는 것 만으로도 마음이 따뜻해집니다.', + }, + { + imagePosition: 'left-[calc(50%+130px)]', + mask: 'bottom-8 left-[calc(50%)]', + circle: 'h-65 w-65', + textPosition: '-top-14 left-1/2 text-center', + description: + '모르는 사람에게 고민을 털어 놓아 보세요.\n다른 사람에게도 위로, 응원, 축하를 보낼 수 있어요.', + }, +]; diff --git a/src/pages/Landing/index.tsx b/src/pages/Landing/index.tsx new file mode 100644 index 0000000..9db2fc7 --- /dev/null +++ b/src/pages/Landing/index.tsx @@ -0,0 +1,49 @@ +import { useState } from 'react'; +import { Navigate } from 'react-router'; +import { twMerge } from 'tailwind-merge'; + +import LandingImg from '@/assets/images/landing.png'; + +import { STYLE_CLASS } from './constants'; + +const Landing = () => { + const [step, setStep] = useState(0); + + if (step === 3) return ; + + return ( +
setStep((prev) => prev + 1)}> + 서비스 소개 이미지 +
+

+ {STYLE_CLASS[step].description} +

+
+
+
+ ); +}; + +export default Landing; diff --git a/src/pages/LetterDetail/index.tsx b/src/pages/LetterDetail/index.tsx index ac0f42c..0dd48a4 100644 --- a/src/pages/LetterDetail/index.tsx +++ b/src/pages/LetterDetail/index.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'react'; import { twMerge } from 'tailwind-merge'; -import { CloudIcon, ColorSirenIcon, SnowIcon, ThermostatIcon, WarmIcon } from '@/assets/icons'; +import { CloudIcon, SirenOutlinedIcon, SnowIcon, ThermostatIcon, WarmIcon } from '@/assets/icons'; import ReportModal from '@/components/ReportModal'; const LetterDetailPage = () => { @@ -54,7 +54,7 @@ const LetterDetailPage = () => { setReportModalOpen(true); }} > - + {degreeModalOpen && (
diff --git a/src/pages/Login/components/Background.tsx b/src/pages/Login/components/Background.tsx new file mode 100644 index 0000000..0c6caf0 --- /dev/null +++ b/src/pages/Login/components/Background.tsx @@ -0,0 +1,46 @@ +import { Link } from 'react-router'; + +import FieldImg from '@/assets/images/home-left-mountain.png'; +import BlurImg from '@/assets/images/landing-blur.png'; +import EnvelopeImg from '@/assets/images/postoffice-letter.png'; +import PostofficeImg from '@/assets/images/postoffice.png'; +import BackgroundImageWrapper from '@/components/BackgroundImageWrapper'; + +const Background = () => { + return ( + <> +
+
+ +
+

+ 36.5 설명 보기 +

+ 편지 이미지 +
+ + 우체국 이미지 + 언덕 이미지 +
+
+ + + ); +}; + +export default Background; diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx index 85f0fa4..aa17b83 100644 --- a/src/pages/Login/index.tsx +++ b/src/pages/Login/index.tsx @@ -1,5 +1,48 @@ +import { GoogleIcon, KakaoIcon, NaverIcon, StampIcon } from '@/assets/icons'; + +import Background from './components/Background'; + const LoginPage = () => { - return
LoginPage
; + return ( + <> +
+
+ +

마음이 맞닿는 온도

+

36.5

+

+ 모르는 사람과 편지를 주고 받으며 +
+ 마음의 위안을 얻어보세요. +

+
+ +
+ + + +
+
+ + ); }; export default LoginPage; diff --git a/src/styles/animations.css b/src/styles/animations.css index a3e366a..3c69ab1 100644 --- a/src/styles/animations.css +++ b/src/styles/animations.css @@ -5,6 +5,8 @@ --animate-show-text: showing 0.5s ease-in-out 6s forwards; --animate-rotate-show: rotate-show 1s ease-in-out forwards; --animate-blink: showing 0.3s forwards; + --animate-login-move-up-down: move-up-down 3s ease-in-out infinite; + --animate-marquee: marquee 10s linear infinite; @keyframes down { 0% { @@ -54,9 +56,18 @@ } } - /* SpecialLetterBanner 애니메이션 */ - --animate-marquee: marquee 10s linear infinite; + /* login */ + @keyframes move-up-down { + 0%, + 100% { + transform: translateY(10px); + } + 50% { + transform: translateY(-10px); + } + } + /* SpecialLetterBanner 애니메이션 */ @keyframes marquee { 0% { transform: translateX(10%); diff --git a/src/styles/fonts.css b/src/styles/fonts.css index 7f3e2b7..b176073 100644 --- a/src/styles/fonts.css +++ b/src/styles/fonts.css @@ -1,3 +1,32 @@ +@font-face { + font-family: 'KyoboHandwriting2020A'; + src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/noonfonts_2112@1.0/KyoboHandwriting2020A.woff') + format('woff'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'iceHimchan-Rg'; + src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/noonfonts_2307-2@1.0/iceHimchan-Rg.woff2') + format('woff2'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'Gyeonggi_Batang_Regular'; + src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/2410-3@1.0/Batang_Regular.woff') + format('woff'); + font-weight: 400; + font-style: normal; +} +@font-face { + font-family: 'HancomMalangMalang'; + src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/2406-1@1.0/HancomMalangMalang-Bold.woff2') + format('woff2'); + font-weight: 700; + font-style: normal; +} + @layer base { /* 폰트들 */ .pretendard { @@ -12,25 +41,4 @@ .batang { font-family: 'Gyeonggi_Batang_Regular'; } - @font-face { - font-family: 'KyoboHandwriting2020A'; - src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/noonfonts_2112@1.0/KyoboHandwriting2020A.woff') - format('woff'); - font-weight: normal; - font-style: normal; - } - @font-face { - font-family: 'iceHimchan-Rg'; - src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/noonfonts_2307-2@1.0/iceHimchan-Rg.woff2') - format('woff2'); - font-weight: normal; - font-style: normal; - } - @font-face { - font-family: 'Gyeonggi_Batang_Regular'; - src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/2410-3@1.0/Batang_Regular.woff') - format('woff'); - font-weight: 400; - font-style: normal; - } } diff --git a/src/styles/preflight.css b/src/styles/preflight.css index 230cfc6..d51f42e 100644 --- a/src/styles/preflight.css +++ b/src/styles/preflight.css @@ -2,7 +2,6 @@ @layer base { :root { - --body-bg-color: var(--color-white); --vh: 1vh; --vw: 1vw; } diff --git a/src/styles/theme.css b/src/styles/theme.css index 0f182ad..528014c 100644 --- a/src/styles/theme.css +++ b/src/styles/theme.css @@ -30,4 +30,6 @@ --color-primary-5: #fffbf8; --color-default-bg: #fffbf8; + + --font-malang: 'HancomMalangMalang'; }