Skip to content

Commit 0ff0e73

Browse files
committed
feat: 다크모드 추가
1 parent 1dc655a commit 0ff0e73

File tree

14 files changed

+458
-138
lines changed

14 files changed

+458
-138
lines changed

src/components/PostList.js

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import React from 'react';
22
import { Link } from 'gatsby';
33
import styled from 'styled-components';
4+
import {
5+
cardBackgroundColor,
6+
cardBorderColor,
7+
textColor,
8+
} from '../utils/theme';
49

510
const PostListContainer = styled.div`
611
display: flex;
@@ -9,14 +14,18 @@ const PostListContainer = styled.div`
914
`;
1015

1116
const PostItem = styled.article`
12-
border: 1px solid #eee;
17+
border: 1px solid ${cardBorderColor};
1318
border-radius: 8px;
1419
padding: 2rem;
1520
transition: all 0.2s ease;
1621
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.03);
22+
background-color: ${cardBackgroundColor};
1723
1824
&:hover {
19-
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.07);
25+
box-shadow: ${(props) =>
26+
props.theme.mode === 'light'
27+
? '0 5px 15px rgba(0, 0, 0, 0.07)'
28+
: '0 5px 15px rgba(255, 255, 255, 0.07)'};
2029
transform: translateY(-3px);
2130
}
2231
`;
@@ -27,12 +36,13 @@ const PostTitle = styled.h2`
2736
line-height: 1.4;
2837
2938
a {
30-
color: #333;
39+
color: ${(props) => (props.theme.mode === 'light' ? '#333' : '#f0f0f0')};
3140
text-decoration: none;
3241
transition: color 0.2s ease;
3342
3443
&:hover {
35-
color: #3498db;
44+
color: ${(props) =>
45+
props.theme.mode === 'light' ? '#3498db' : '#64b5f6'};
3646
}
3747
}
3848
`;
@@ -42,7 +52,7 @@ const PostMeta = styled.div`
4252
align-items: center;
4353
margin-bottom: 1.5rem;
4454
font-size: 0.95rem;
45-
color: #777;
55+
color: ${(props) => (props.theme.mode === 'light' ? '#777' : '#d0d0d0')};
4656
`;
4757

4858
const PostDate = styled.time`
@@ -66,36 +76,38 @@ const PostTags = styled.div`
6676
const PostTag = styled(Link)`
6777
font-size: 0.85rem;
6878
padding: 0.2rem 0.6rem;
69-
background-color: #f5f5f5;
70-
color: #666;
79+
background-color: ${(props) =>
80+
props.theme.mode === 'light' ? '#f5f5f5' : '#444'};
81+
color: ${(props) => (props.theme.mode === 'light' ? '#666' : '#e0e0e0')};
7182
border-radius: 3px;
7283
text-decoration: none;
7384
transition: all 0.2s ease;
7485
7586
&:hover {
76-
background-color: #3498db;
87+
background-color: ${(props) =>
88+
props.theme.mode === 'light' ? '#3498db' : '#64b5f6'};
7789
color: white;
7890
}
7991
`;
8092

8193
const PostExcerpt = styled.p`
8294
margin: 0;
83-
color: #555;
95+
color: ${(props) => (props.theme.mode === 'light' ? '#555' : '#d0d0d0')};
8496
line-height: 1.6;
8597
font-size: 1.1rem;
8698
`;
8799

88100
const ReadMore = styled(Link)`
89101
display: inline-block;
90102
margin-top: 1.2rem;
91-
color: #3498db;
103+
color: ${(props) => (props.theme.mode === 'light' ? '#3498db' : '#64b5f6')};
92104
text-decoration: none;
93105
font-weight: 500;
94106
font-size: 0.95rem;
95107
transition: all 0.2s ease;
96108
97109
&:hover {
98-
color: #2980b9;
110+
color: ${(props) => (props.theme.mode === 'light' ? '#2980b9' : '#90caf9')};
99111
text-decoration: underline;
100112
}
101113

src/components/ThemeToggle.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import React, { useContext } from 'react';
2+
import styled from 'styled-components';
3+
import { ThemeContext } from '../context/ThemeContext';
4+
5+
const ToggleContainer = styled.button`
6+
position: relative;
7+
display: flex;
8+
align-items: center;
9+
justify-content: center;
10+
background: transparent;
11+
border: none;
12+
cursor: pointer;
13+
padding: 8px;
14+
width: 40px;
15+
height: 40px;
16+
border-radius: 50%;
17+
transition: all 0.3s ease;
18+
19+
&:hover {
20+
background: ${(props) =>
21+
props.theme.mode === 'light'
22+
? 'rgba(0, 0, 0, 0.05)'
23+
: 'rgba(255, 255, 255, 0.1)'};
24+
}
25+
26+
&:focus {
27+
outline: none;
28+
}
29+
`;
30+
31+
const IconSVG = styled.svg`
32+
width: 20px;
33+
height: 20px;
34+
fill: none;
35+
stroke: ${(props) => (props.theme.mode === 'light' ? '#555' : '#eee')};
36+
stroke-width: 2;
37+
stroke-linecap: round;
38+
stroke-linejoin: round;
39+
transition: all 0.3s ease;
40+
`;
41+
42+
const ThemeToggle = () => {
43+
const { mode, toggleTheme } = useContext(ThemeContext);
44+
45+
return (
46+
<ToggleContainer
47+
onClick={toggleTheme}
48+
aria-label={`${mode === 'light' ? '다크모드로 전환' : '라이트모드로 전환'}`}
49+
title={`${mode === 'light' ? '다크모드로 전환' : '라이트모드로 전환'}`}
50+
>
51+
{mode === 'light' ? (
52+
<IconSVG viewBox="0 0 24 24">
53+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
54+
</IconSVG>
55+
) : (
56+
<IconSVG viewBox="0 0 24 24">
57+
<circle cx="12" cy="12" r="5" />
58+
<line x1="12" y1="1" x2="12" y2="3" />
59+
<line x1="12" y1="21" x2="12" y2="23" />
60+
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
61+
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
62+
<line x1="1" y1="12" x2="3" y2="12" />
63+
<line x1="21" y1="12" x2="23" y2="12" />
64+
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
65+
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
66+
</IconSVG>
67+
)}
68+
</ToggleContainer>
69+
);
70+
};
71+
72+
export default ThemeToggle;

src/components/bio.js

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,22 @@ const TagLine = styled.sub`
5050
display: block;
5151
`;
5252

53+
const SocialIconWrapper = styled.a`
54+
display: inline-block;
55+
transition: all 0.3s ease;
56+
57+
&:hover {
58+
transform: translateY(-3px);
59+
}
60+
`;
61+
5362
const SocialIcon = styled.img`
5463
height: 2.5rem;
5564
width: 2.5rem;
5665
padding: 1.5rem 1rem;
66+
filter: ${(props) =>
67+
props.theme.mode === 'dark' ? 'brightness(1.8)' : 'none'};
68+
transition: all 0.3s ease;
5769
`;
5870

5971
const Bio = () => (
@@ -66,27 +78,30 @@ const Bio = () => (
6678
<TextContainer>
6779
<Name>{author}</Name>
6880
<TagLine>{authorTagline}</TagLine>
69-
<a
81+
<SocialIconWrapper
7082
href={`https://github.com/${social.github}`}
7183
target="_blank"
7284
rel="noopener noreferrer"
85+
aria-label="GitHub 프로필"
7386
>
7487
<SocialIcon src={github} alt="github" />
75-
</a>
76-
<a
88+
</SocialIconWrapper>
89+
<SocialIconWrapper
7790
href={`https://www.linkedin.com/in/${social.linkedin}`}
7891
target="_blank"
7992
rel="noopener noreferrer"
93+
aria-label="LinkedIn 프로필"
8094
>
8195
<SocialIcon src={linkedin} alt="linkedin" />
82-
</a>
83-
<a
96+
</SocialIconWrapper>
97+
<SocialIconWrapper
8498
href={`mailto:${social.mail}`}
8599
target="_blank"
86100
rel="noopener noreferrer"
101+
aria-label="이메일 보내기"
87102
>
88103
<SocialIcon src={mail} alt="mail" />
89-
</a>
104+
</SocialIconWrapper>
90105
</TextContainer>
91106
<ImageContainer>
92107
<Image fixed={data.avatar.childImageSharp.fixed} alt={author} />

src/components/header.js

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import styled from 'styled-components';
44

55
import StyledLink from '../utils/styled-link';
66
import media from '../utils/media';
7+
import ThemeToggle from './ThemeToggle';
8+
import { headerBackgroundColor } from '../utils/theme';
79

810
const Container = styled.nav`
911
box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.05);
@@ -14,26 +16,44 @@ const Container = styled.nav`
1416
position: sticky;
1517
top: 0;
1618
z-index: 1000;
17-
background-color: white;
18-
transition: box-shadow 0.3s ease;
19+
background-color: ${headerBackgroundColor};
20+
transition: all 0.3s ease;
1921
2022
&.scrolled {
2123
box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.1);
2224
}
2325
`;
2426

27+
const HeaderContent = styled.div`
28+
display: flex;
29+
align-items: center;
30+
width: 100%;
31+
max-width: 1300px;
32+
padding: 0 2rem;
33+
position: relative;
34+
justify-content: center;
35+
`;
36+
2537
const Title = styled.h1`
2638
font-size: 1.6rem;
2739
font-weight: 800;
2840
letter-spacing: 0.1rem;
2941
text-transform: uppercase;
3042
margin: 0;
43+
text-align: center;
3144
3245
${media.phone`
33-
text-align: center;
46+
font-size: 1.4rem;
3447
`}
3548
`;
3649

50+
const ThemeToggleWrapper = styled.div`
51+
position: absolute;
52+
right: 2rem;
53+
top: 50%;
54+
transform: translateY(-50%);
55+
`;
56+
3757
const Header = ({ title }) => {
3858
const [scrolled, setScrolled] = React.useState(false);
3959

@@ -54,9 +74,14 @@ const Header = ({ title }) => {
5474

5575
return (
5676
<Container className={scrolled ? 'scrolled' : ''}>
57-
<StyledLink to={'/'}>
58-
<Title>{title}</Title>
59-
</StyledLink>
77+
<HeaderContent>
78+
<StyledLink to={'/'}>
79+
<Title>{title}</Title>
80+
</StyledLink>
81+
<ThemeToggleWrapper>
82+
<ThemeToggle />
83+
</ThemeToggleWrapper>
84+
</HeaderContent>
6085
</Container>
6186
);
6287
};

0 commit comments

Comments
 (0)