Skip to content

Commit b531588

Browse files
Merge pull request #66 from SOPT-36-NINEDOT/feat/#36/layoutHeaderFooter
[Feat]: 헤더, 푸터 구현
2 parents 8a92eae + e8066ae commit b531588

File tree

8 files changed

+178
-52
lines changed

8 files changed

+178
-52
lines changed

src/route/path.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ export const PATH = {
66
MANDAL: '/mandal',
77
EDIT: '/edit',
88
HISTORY: '/history',
9+
TERMS: '/terms',
10+
PRIVACY: '/privacy',
911
} as const;
1012

1113
export type PathType = (typeof PATH)[keyof typeof PATH];

src/shared/component/Layout/Header/Header.tsx

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import { style } from '@vanilla-extract/css';
22

33
export const layoutContainer = style({
4+
height: '100vh',
45
display: 'flex',
56
flexDirection: 'column',
6-
height: '100vh',
7-
overflow: 'hidden',
7+
overflow: 'auto',
8+
scrollbarWidth: 'none',
9+
msOverflowStyle: 'none',
10+
selectors: {
11+
'&::-webkit-scrollbar': {
12+
display: 'none',
13+
},
14+
},
815
});
916

1017
export const layoutMain = style({
1118
flex: 1,
12-
overflow: 'hidden',
1319
});

src/shared/component/Layout/Layout.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { Outlet } from 'react-router-dom';
22

3-
import Header from './Header/Header';
43
import * as styles from './Layout.css';
4+
import Header from './header/Header';
5+
import Footer from './footer/Footer';
56

67
const Layout = () => {
78
return (
@@ -11,11 +12,7 @@ const Layout = () => {
1112
<Outlet />
1213
</main>
1314

14-
{/* TODO: Footer 컴포넌트 추가 */}
15-
<footer>
16-
{/* 임시 푸터 */}
17-
<p>© 2024 NINEDOT</p>
18-
</footer>
15+
<Footer />
1916
</div>
2017
);
2118
};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { style } from '@vanilla-extract/css';
2+
3+
import { colors } from '@/style/token';
4+
import { fonts } from '@/style/token/typography.css';
5+
6+
export const footerContainer = style({
7+
display: 'flex',
8+
flexDirection: 'column',
9+
height: '15.1rem',
10+
padding: '4rem 0',
11+
justifyContent: 'center',
12+
alignItems: 'center',
13+
borderTop: `2px solid ${colors.grey1}`,
14+
background: colors.black01,
15+
});
16+
17+
export const linkWrapper = style({
18+
display: 'flex',
19+
gap: '2.7rem',
20+
marginBottom: '2rem',
21+
});
22+
23+
export const linkText = style({
24+
color: colors.grey11,
25+
...fonts.caption01,
26+
cursor: 'pointer',
27+
textDecoration: 'none',
28+
});
29+
30+
export const infoWrapper = style({
31+
textAlign: 'center',
32+
});
33+
34+
export const infoText = style({
35+
color: colors.grey11,
36+
...fonts.caption02,
37+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Link } from 'react-router-dom';
2+
3+
import * as styles from './Footer.css';
4+
5+
import { PATH } from '@/route';
6+
7+
const Footer = () => {
8+
return (
9+
<footer className={styles.footerContainer}>
10+
<nav className={styles.linkWrapper}>
11+
<Link to={PATH.TERMS} className={styles.linkText}>
12+
이용약관
13+
</Link>
14+
<Link to={PATH.PRIVACY} className={styles.linkText}>
15+
개인정보처리방침
16+
</Link>
17+
</nav>
18+
<address className={styles.infoWrapper}>
19+
<p className={styles.infoText}>대표: 이현준</p>
20+
<p className={styles.infoText}>이메일: 999inedot@gmail.com</p>
21+
</address>
22+
</footer>
23+
);
24+
};
25+
26+
export default Footer;

src/shared/component/Layout/Header/Header.css.ts renamed to src/shared/component/Layout/header/Header.css.ts

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ import { style } from '@vanilla-extract/css';
22

33
import { colors } from '@/style/token/color.css';
44
import { fonts } from '@/style/token/typography.css';
5+
import { zIndex } from '@/style/token/zIndex.css';
56

67
export const header = style({
8+
position: 'sticky',
9+
top: 0,
10+
zIndex: zIndex.header,
711
width: '100%',
812
backgroundColor: colors.bg_black01,
913
display: 'flex',
@@ -12,8 +16,8 @@ export const header = style({
1216

1317
export const headerInner = style({
1418
width: '100%',
15-
maxWidth: '90rem',
16-
padding: '0.94rem 5rem',
19+
maxWidth: '128rem',
20+
padding: '1.5rem 8rem',
1721
display: 'flex',
1822
justifyContent: 'space-between',
1923
alignItems: 'center',
@@ -26,33 +30,62 @@ export const logo = style({
2630

2731
export const navWrapper = style({
2832
display: 'flex',
29-
gap: '1.25rem',
33+
gap: '2rem',
3034
});
3135

3236
export const navItem = style({
3337
display: 'flex',
34-
padding: '0.75rem 1.25rem',
38+
padding: '1.2rem 2rem',
3539
justifyContent: 'center',
3640
alignItems: 'center',
3741
flexShrink: 0,
38-
color: '#5A5E66', // 추후 토큰 색상으로 변경
42+
color: colors.grey6,
3943
textAlign: 'center',
4044
...fonts.subtitle05,
4145
background: 'transparent',
4246
border: 'none',
4347
cursor: 'pointer',
4448
whiteSpace: 'nowrap',
4549
transition: 'color 0.2s',
50+
selectors: {
51+
'&:hover': {
52+
color: colors.grey7,
53+
},
54+
},
55+
});
56+
57+
export const navItemActive = style({
58+
color: `${colors.grey11}`,
59+
selectors: {
60+
'&:hover': {
61+
color: `${colors.grey11}`,
62+
},
63+
},
4664
});
4765

4866
export const active = style({
49-
color: '#FDFDFD',
67+
color: colors.grey11,
5068
});
5169

5270
export const profilePlaceholder = style({
53-
width: '3.125rem',
54-
height: '3.125rem',
71+
width: '5rem',
72+
height: '5rem',
5573
borderRadius: '50%',
56-
backgroundColor: '#5A5E66', // 추후 토큰 색상으로 변경
74+
backgroundColor: colors.grey6,
5775
flexShrink: 0,
5876
});
77+
78+
export const loginButton = style({
79+
color: colors.grey11,
80+
textAlign: 'center',
81+
...fonts.subtitle05,
82+
background: 'transparent',
83+
border: 'none',
84+
cursor: 'pointer',
85+
transition: 'color 0.2s',
86+
selectors: {
87+
'&:hover': {
88+
color: colors.grey7,
89+
},
90+
},
91+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { useState } from 'react';
2+
import { useNavigate } from 'react-router-dom';
3+
4+
import * as styles from './Header.css';
5+
6+
import { PATH } from '@/route/path';
7+
8+
const MENUS = [
9+
{ label: '나의 할 일', path: PATH.TODO },
10+
{ label: '나의 만다르트', path: PATH.MANDAL },
11+
{ label: '나의 히스토리', path: PATH.HISTORY },
12+
];
13+
14+
const Header = () => {
15+
const [activeMenu, setActiveMenu] = useState<string>(MENUS[0].label);
16+
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
17+
const navigate = useNavigate();
18+
19+
const handleLogin = () => {
20+
// 로그인 로직 추가하기
21+
setIsLoggedIn(true);
22+
};
23+
24+
const handleMenuClick = (menuLabel: string, path: string) => {
25+
setActiveMenu(menuLabel);
26+
navigate(path);
27+
};
28+
29+
return (
30+
<header className={styles.header}>
31+
<div className={styles.headerInner}>
32+
<h1 className={styles.logo}>NINEDOT</h1>
33+
34+
<nav className={styles.navWrapper}>
35+
{MENUS.map((menu) => (
36+
<button
37+
key={menu.label}
38+
className={`${styles.navItem} ${activeMenu === menu.label ? styles.navItemActive : ''}`}
39+
onClick={() => handleMenuClick(menu.label, menu.path)}
40+
aria-current={activeMenu === menu.label ? 'page' : undefined}
41+
>
42+
{menu.label}
43+
</button>
44+
))}
45+
</nav>
46+
47+
{isLoggedIn ? (
48+
<div className={styles.profilePlaceholder} />
49+
) : (
50+
<button className={styles.loginButton} onClick={handleLogin}>
51+
로그인
52+
</button>
53+
)}
54+
</div>
55+
</header>
56+
);
57+
};
58+
59+
export default Header;

0 commit comments

Comments
 (0)