diff --git a/src/components/Footer/OriginFooter/style.ts b/src/components/Footer/OriginFooter/style.ts index 68447727..6ae09bc7 100644 --- a/src/components/Footer/OriginFooter/style.ts +++ b/src/components/Footer/OriginFooter/style.ts @@ -4,7 +4,7 @@ import { colors } from '@sopt-makers/colors'; export const Root = styled.footer` width: 100%; min-height: 162px; - background-color: #2a2a2a; + background-color: ${colors.gray800}; /* 태블릿 + 데스크탑 뷰 */ @media (min-width: 766px) { diff --git a/src/components/Header/Desktop/DesktopHeader.tsx b/src/components/Header/Desktop/DesktopHeader.tsx index f996103f..42e7c09a 100644 --- a/src/components/Header/Desktop/DesktopHeader.tsx +++ b/src/components/Header/Desktop/DesktopHeader.tsx @@ -1,16 +1,15 @@ import styled from '@emotion/styled'; +import { colors } from '@sopt-makers/colors'; import Link from 'next/link'; import { css } from '@emotion/react'; -import { useCallback, useEffect, useState } from 'react'; import { LOGO_IMAGE_URL } from '@src/assets/mainLogo/base64_logo'; import useHeader from '@src/hooks/useHeader'; import { GrowDown } from '@src/lib/styles/animation'; import { menuTapList } from '../menuTapList'; -import { BaseMenuTap, MenuTapType, ParentMenuTap } from '../types'; +import { MenuTapType, SingleMenuTap } from '../types'; function DesktopHeader() { const { handleClickLogo, handleIsSelected } = useHeader(); - const [isSubTapOpened, setIsSubTapOpened] = useState(false); return ( <> @@ -20,147 +19,44 @@ function DesktopHeader() { {menuTapList.map((menuTap) => ( - + ))} - {isSubTapOpened && } ); } type MenuTapProps = { - menuTap: BaseMenuTap; + menuTap: SingleMenuTap; handleIsSelected: (path: string | string[]) => boolean; - isSubTapOpened: boolean; - setIsSubTapOpened: (isOpened: boolean) => void; }; -function MenuTap({ menuTap, handleIsSelected, isSubTapOpened, setIsSubTapOpened }: MenuTapProps) { +function MenuTap({ menuTap, handleIsSelected }: MenuTapProps) { switch (menuTap.type) { case MenuTapType.Anchor: return ( - setIsSubTapOpened(false)} - onMouseEnter={() => setIsSubTapOpened(false)} - > + {menuTap.title} ); case MenuTapType.Router: return ( - setIsSubTapOpened(false)} - onMouseEnter={() => setIsSubTapOpened(false)} - > + {menuTap.title} ); - case MenuTapType.Parent: - return ( - - ); } } -type ParentMenuProps = { - menuTap: ParentMenuTap; - handleIsSelected: (path: string | string[]) => boolean; - isSubTapOpened: boolean; - setIsSubTapOpened: (isOpened: boolean) => void; -}; - -function ParentMenu({ - menuTap, - handleIsSelected, - isSubTapOpened, - setIsSubTapOpened, -}: ParentMenuProps) { - const [isOpened, setIsOpened] = useState(false); - - const closeSubTap = useCallback(() => { - setIsSubTapOpened(false); - setIsOpened(false); - }, [setIsSubTapOpened, setIsOpened]); - - const onMouseIn = () => { - setIsSubTapOpened(true); - setIsOpened(true); - }; - - useEffect(() => { - if (!isSubTapOpened) { - setIsOpened(false); - } - }, [isSubTapOpened]); - - useEffect(() => { - if (isSubTapOpened) { - document.addEventListener('click', closeSubTap); - } - return () => { - document.removeEventListener('click', closeSubTap); - }; - }, [closeSubTap, isSubTapOpened]); - - return ( - t.href))} - isOpened={isOpened} - onMouseEnter={onMouseIn} - onClick={(e) => e.stopPropagation()} - > - {menuTap.title} - {isOpened && ( - - {menuTap.children.map((c) => { - switch (c.type) { - case MenuTapType.Anchor: - return ( - - - {c.title} - - - ); - case MenuTapType.Router: - return ( - - {c.title} - - ); - } - })} - - )} - - ); -} - interface MenuTitleProps { isSelected?: boolean; isOpened?: boolean; - type?: 'main' | 'sub'; } export const Wrapper = styled.div` - max-width: 1280px; - width: 90%; + max-width: 1200px; + width: 100%; display: flex; justify-content: space-between; `; @@ -175,21 +71,6 @@ export const SubMenuWrapper = styled.div` animation: growdown 0.4s forwards; `; -const SubMenu = styled.div` - position: absolute; - z-index: 1; - display: flex; - align-items: center; - justify-content: center; - - width: 300px; - top: 80px; - left: 25%; /* it is bad practice */ - transform: translateX(-50%); - - cursor: default; -`; - export const CenterAligner = styled.div` display: flex; align-items: center; @@ -234,46 +115,35 @@ export const MenuTitleAnchor = styled(Link)` } `; -const menuTitleUnderline = css` - &::after { - content: ''; - position: absolute; - top: 3.5rem; /* this is bad practice */ - left: -20px; - width: 100%; - border-bottom: 2px solid white; - } - &:last-child { - &::after { - width: calc(100% + 40px); - } - } -`; - export const MenuTitle = styled.div` font-size: 18px; height: 100%; line-height: 36px; font-weight: ${({ isSelected }) => (isSelected ? '700' : '500')}; - color: ${({ type, isSelected }) => - type === 'sub' ? (isSelected ? '#fff' : 'rgba(255, 255, 255, 0.5)') : '#fff'}; + color: ${colors.white}; cursor: pointer; position: relative; - ${({ isOpened }) => isOpened && menuTitleUnderline}; &:not(:last-child) { padding-right: 40px; } &:hover { - color: #fff; - ${({ type }) => type !== 'sub' && menuTitleUnderline} + &::after { + content: ''; + position: absolute; + top: 3.5rem; /* this is bad practice */ + left: -20px; + width: 100%; + border-bottom: 2px solid ${colors.white}; + } + &:last-child { + &::after { + width: calc(100% + 40px); + } + } } `; -const ParentMenuTitle = styled(MenuTitle)` - position: relative; -`; - export default DesktopHeader; diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 3e2a3fe9..b6095b5a 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -1,18 +1,18 @@ import { useIsDesktop, useIsMobile, useIsTablet } from '@src/hooks/useDevice'; import DesktopHeader from './Desktop/DesktopHeader'; import MobileHeader from './Mobile/MobileHeader'; -import styles from './header.module.scss'; +import * as S from './style'; export function Header() { - const isDesktop = useIsDesktop('992px'); - const isTablet = useIsTablet('766px', '991.9px'); + const isDesktop = useIsDesktop('940px'); + const isTablet = useIsTablet('768px', '939px'); const isMobile = useIsMobile(); return ( -
+ {isDesktop && } {isTablet && } {isMobile && } -
+ ); } diff --git a/src/components/Header/Mobile/HeaderMenu.style.ts b/src/components/Header/Mobile/HeaderMenu.style.ts index 1a8798a9..4777fdcb 100644 --- a/src/components/Header/Mobile/HeaderMenu.style.ts +++ b/src/components/Header/Mobile/HeaderMenu.style.ts @@ -63,7 +63,7 @@ export const MenuWrap = styled.div` export const Background = styled.div` height: 100vh; - background: #181818; + background: ${colors.gray950}; opacity: 0.8; `; @@ -110,7 +110,7 @@ export const ContentsWrap = styled.div` display: flex; flex-direction: column; justify-content: space-between; - background: #181818; + background: ${colors.gray950}; padding-top: 50px; margin-bottom: 0px; diff --git a/src/components/Header/Mobile/HeaderMenu.tsx b/src/components/Header/Mobile/HeaderMenu.tsx index cff9138a..e5880e01 100644 --- a/src/components/Header/Mobile/HeaderMenu.tsx +++ b/src/components/Header/Mobile/HeaderMenu.tsx @@ -2,7 +2,7 @@ import Link from 'next/link'; import { useEffect } from 'react'; import useHeader from '@src/hooks/useHeader'; import { menuTapList } from '../menuTapList'; -import { BaseMenuTap, MenuState, MenuTapType } from '../types'; +import { MenuState, MenuTapType, SingleMenuTap } from '../types'; import * as S from './HeaderMenu.style'; function useNoScroll(isMenuShown: MenuState) { @@ -27,7 +27,7 @@ interface HeaderMenuProps { } type MenuTapProps = { - menuTap: BaseMenuTap; + menuTap: SingleMenuTap; handleIsSelected: (path: string) => boolean; }; @@ -47,18 +47,6 @@ function MenuTap({ menuTap, handleIsSelected }: MenuTapProps) { {menuTap.title} ); - case MenuTapType.Parent: - return ( - <> - {menuTap.children.map((childMenuTap) => ( - - ))} - - ); } } diff --git a/src/components/Header/header.module.scss b/src/components/Header/header.module.scss deleted file mode 100644 index a4a41259..00000000 --- a/src/components/Header/header.module.scss +++ /dev/null @@ -1,26 +0,0 @@ -@import '@src/lib/styles/mixins'; - -.wrapper { - width: 100%; - min-height: 80px; - margin: 0 auto; - display: flex; - justify-content: center; - align-items: center; - position: fixed; - background-color: rgba(22, 22, 28, 0.9); - backdrop-filter: blur(20px); - z-index: 100; - - @include tablet { - height: 48px; - padding: 0 48px; - } - - @include mobile { - height: 48px; - min-height: 48px; - padding: 0 20px; - justify-content: space-between; - } -} diff --git a/src/components/Header/menuTapList.ts b/src/components/Header/menuTapList.ts index 98ee20dd..a8bb1663 100644 --- a/src/components/Header/menuTapList.ts +++ b/src/components/Header/menuTapList.ts @@ -12,20 +12,9 @@ export const menuTapList: MenuTapList = [ href: '/project', }, { - type: MenuTapType.Parent, + type: MenuTapType.Router, title: '블로그', - children: [ - { - type: MenuTapType.Router, - title: '활동후기', - href: '/review', - }, - { - type: MenuTapType.Router, - title: '솝티클', - href: '/sopticle', - }, - ], + href: '/blog', }, { type: MenuTapType.Router, diff --git a/src/components/Header/style.ts b/src/components/Header/style.ts new file mode 100644 index 00000000..08ceef24 --- /dev/null +++ b/src/components/Header/style.ts @@ -0,0 +1,30 @@ +import styled from '@emotion/styled'; +import { colors } from '@sopt-makers/colors'; + +export const Wrapper = styled.header` + width: 100%; + min-height: 80px; + margin: 0 auto; + display: flex; + justify-content: center; + align-items: center; + position: fixed; + background-color: ${colors.gray950}; + backdrop-filter: blur(20px); + z-index: 100; + padding: 0 20px; + top: 0; + + /* 태블릿 + 데스크탑 뷰 */ + @media (min-width: 768px) { + height: 48px; + } + + /* 모바일 뷰 */ + @media (max-width: 767px) { + height: 48px; + min-height: 48px; + + justify-content: space-between; + } +`; diff --git a/src/components/Header/types.ts b/src/components/Header/types.ts index 55a813b8..b1783b91 100644 --- a/src/components/Header/types.ts +++ b/src/components/Header/types.ts @@ -6,18 +6,10 @@ export const enum MenuTapType { Parent = 'PARENT', } -export type ParentMenuTap = { - type: MenuTapType.Parent; - title: string; - children: SingleMenuTap[]; -}; - export type SingleMenuTap = { type: MenuTapType.Router | MenuTapType.Anchor; title: string; href: string; }; -export type BaseMenuTap = SingleMenuTap | ParentMenuTap; - -export type MenuTapList = BaseMenuTap[]; +export type MenuTapList = SingleMenuTap[]; diff --git a/src/lib/styles/colors.ts b/src/lib/styles/colors.ts new file mode 100644 index 00000000..64ce3eba --- /dev/null +++ b/src/lib/styles/colors.ts @@ -0,0 +1,79 @@ +export const colors = { + white: '#FFFFFF', + black: '#000000', + + gray10: '#FCFCFC', + gray30: '#F0F0F0', + gray50: '#E4E4E5', + gray100: '#C3C3C6', + gray200: '#9D9DA4', + gray300: '#808087', + gray400: '#66666D', + gray500: '#515159', + gray600: '#3F3F47', + gray700: '#2E2E35', + gray800: '#202025', + gray950: '#0F0F12', + + blue50: '#C8E1FF', + blue100: '#8FC0FF', + blue200: '#619EFF', + blue300: '#4485FF', + blue400: '#346FFA', + blue500: '#2C53DF', + blue600: '#2649B3', + blue700: '#253B8C', + blue800: '#23306A', + blue900: '#20274D', + + red50: '#FFD1D3', + red100: '#FFA8AD', + red200: '#FE818B', + red300: '#FA616D', + red400: '#F04251', + red500: '#CA2F3D', + red600: '#9E2733', + red700: '#7A242D', + red800: '#562025', + red900: '#3C2020', + + green50: '#CCFFEC', + green100: '#82F6CB', + green200: '#4EE4AD', + green300: '#26CF91', + green400: '#16BF81', + green500: '#13A06C', + green600: '#138A5E', + green700: '#136D4C', + green800: '#13533C', + green900: '#15372B', + + yellow50: '#FFF4D4', + yellow100: '#FFE9B2', + yellow200: '#FFDE8A', + yellow300: '#FFCD59', + yellow400: '#FFC234', + yellow500: '#FFB326', + yellow600: '#EBA01E', + yellow700: '#B57B1D', + yellow800: '#72531E', + yellow900: '#3D301A', + + orange50: '#FFECE5', + orange100: '#FFCEBD', + orange200: '#FFA480', + orange300: '#FF834A', + orange400: '#F77234', + orange500: '#D4591C', + orange600: '#AD4E17', + orange700: '#853D11', + orange800: '#5C2B0C', + orange900: '#422109', + + attention: '#FFC234', + error: '#F04251', + background: '#0F0F12', + secondary: '#F77234', + success: '#346FFA', + information: '#16BF81', +}; diff --git a/src/lib/styles/global.ts b/src/lib/styles/global.ts index f38426db..3a89d308 100644 --- a/src/lib/styles/global.ts +++ b/src/lib/styles/global.ts @@ -80,7 +80,7 @@ export const global = css` } body { - background-color: #16161c; + background-color: #0f0f12; line-height: 1; } diff --git a/src/pages/blog.tsx b/src/pages/blog.tsx new file mode 100644 index 00000000..29da1c6a --- /dev/null +++ b/src/pages/blog.tsx @@ -0,0 +1 @@ +export { default } from '@src/views/BlogPage'; diff --git a/src/views/BlogPage/BlogPage.tsx b/src/views/BlogPage/BlogPage.tsx new file mode 100644 index 00000000..285c20bf --- /dev/null +++ b/src/views/BlogPage/BlogPage.tsx @@ -0,0 +1,10 @@ +import PageLayout from '@src/components/common/PageLayout'; +import BlogTab from './components/blogTab'; + +export default function BlogPage() { + return ( + + + + ); +} diff --git a/src/views/BlogPage/components/blogTab/index.tsx b/src/views/BlogPage/components/blogTab/index.tsx new file mode 100644 index 00000000..c8cd633d --- /dev/null +++ b/src/views/BlogPage/components/blogTab/index.tsx @@ -0,0 +1,39 @@ +import { useState } from 'react'; +import * as S from './style'; +import { BlogTabList } from './types'; + +export default function BlogTab() { + const [selectedTab, setSelectedTab] = useState('REVIEW'); + const blogTabList: BlogTabList = { + REVIEW: { + title: '활동후기', + description: '회원들의 진솔한 후기를 통해 SOPT를 미리 만나보세요. ', + }, + ARTICLE: { + title: '아티클', + description: '회원들의 아티클을 통해 SOPT에서 얻은 인사이트를 확인해보세요.', + }, + }; + + return ( + + + + {Object.entries(blogTabList).map(([blogTab, tabInfo]) => { + return ( + setSelectedTab(blogTab as keyof BlogTabList)} + isSelected={selectedTab === blogTab} + > + {tabInfo.title} + + ); + })} + + {blogTabList[selectedTab]?.description} +

드롭다운 들어가는 부분

+
+
+ ); +} diff --git a/src/views/BlogPage/components/blogTab/style.ts b/src/views/BlogPage/components/blogTab/style.ts new file mode 100644 index 00000000..4ba9d40b --- /dev/null +++ b/src/views/BlogPage/components/blogTab/style.ts @@ -0,0 +1,102 @@ +import styled from '@emotion/styled'; +import { colors } from '@sopt-makers/colors'; + +export const Wrapper = styled.section` + width: 100%; + margin-top: 188px; + display: flex; + align-items: center; + justify-content: center; + + /* 태블릿 뷰 */ + @media (max-width: 939px) and (min-width: 768px) { + margin-top: 188px; + } + /* 모바일 뷰 */ + @media (max-width: 767px) { + margin-top: 76px; + } +`; + +export const Container = styled.main` + width: 900px; + display: flex; + align-items: flex-start; + flex-direction: column; + + /* 태블릿, 모바일 뷰 */ + @media (max-width: 939px) { + margin-left: 20px; + margin-right: 20px; + width: 100%; + } +`; + +export const TabContainer = styled.section` + display: flex; + align-items: center; +`; + +interface MenuTitleProps { + isSelected: boolean; +} + +export const TabTitle = styled.article` + font-size: 24px; + height: 100%; + line-height: 36px; + font-weight: 700; + letter-spacing: -0.48px; + + color: ${({ isSelected }) => (isSelected ? `${colors.gray30}` : `${colors.gray300}`)}; + + cursor: pointer; + position: relative; + border-bottom: ${({ isSelected }) => isSelected && `2px solid ${colors.gray200}`}; + + padding-bottom: 16px; + margin-right: 20px; + + /* 모바일 뷰 */ + @media (max-width: 767px) { + border-bottom: ${({ isSelected }) => isSelected && `1px solid ${colors.gray200}`}; + margin-right: 12px; + } + + &:hover { + color: ${colors.gray100}; + } +`; + +export const TabDescription = styled.h1` + margin-top: 24px; + margin-bottom: 48px; + word-break: keep-all; + + font-size: 20px; + font-weight: 600; + line-height: 30px; + letter-spacing: -0.4px; + color: ${colors.gray100}; + width: 100%; + + /* 모바일 뷰 */ + @media (max-width: 767px) { + margin-top: 14px; + margin-bottom: 16px; + display: flex; + + padding: 16px; + flex-direction: column; + align-items: flex-start; + gap: 10px; + border-radius: 12px; + background: ${colors.gray800}; + + font-size: 14px; + font-weight: 500; + line-height: 23.1px; + letter-spacing: -0.21px; + color: ${colors.gray30}; + } +`; diff --git a/src/views/BlogPage/components/blogTab/types.ts b/src/views/BlogPage/components/blogTab/types.ts new file mode 100644 index 00000000..b14bb9ac --- /dev/null +++ b/src/views/BlogPage/components/blogTab/types.ts @@ -0,0 +1,10 @@ +export interface BlogTabList { + REVIEW: { + title: string; + description: string; + }; + ARTICLE: { + title: string; + description: string; + }; +} diff --git a/src/views/BlogPage/index.tsx b/src/views/BlogPage/index.tsx new file mode 100644 index 00000000..75531ca0 --- /dev/null +++ b/src/views/BlogPage/index.tsx @@ -0,0 +1 @@ +export { default } from './BlogPage';