Skip to content

Commit ea3b7ee

Browse files
authored
Merge pull request #320 from velopert/feature/dark-theme
Implements dark theme
2 parents 8fbbb37 + 3bcbcc1 commit ea3b7ee

File tree

166 files changed

+2083
-961
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

166 files changed

+2083
-961
lines changed

package.json

+7-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"@loadable/babel-plugin": "^5.13.2",
1313
"@loadable/component": "^5.15.0",
1414
"@loadable/server": "^5.15.1",
15+
"@react-spring/web": "^9.4.2",
1516
"@reduxjs/toolkit": "^1.2.2",
1617
"@sentry/browser": "^5.11.1",
1718
"@svgr/webpack": "4.3.3",
@@ -46,10 +47,10 @@
4647
"@types/react-toastify": "^4.1.0",
4748
"@types/react-virtualized": "^9.21.8",
4849
"@types/sanitize-html": "^1.20.2",
49-
"@types/styled-components": "^4.4.1",
50+
"@types/styled-components": "^5.1.21",
5051
"@types/throttle-debounce": "^2.1.0",
51-
"@typescript-eslint/eslint-plugin": "^2.8.0",
52-
"@typescript-eslint/parser": "^2.8.0",
52+
"@typescript-eslint/eslint-plugin": "^4.1.1",
53+
"@typescript-eslint/parser": "^4.1.1",
5354
"apollo-boost": "^0.4.7",
5455
"apollo-link": "^1.2.13",
5556
"aws-lambda": "^1.0.4",
@@ -119,7 +120,7 @@
119120
"react-outside-click-handler": "^1.3.0",
120121
"react-redux": "^7.1.3",
121122
"react-router-dom": "^5.1.2",
122-
"react-spring": "^8.0.27",
123+
"react-spring": "^9.4.2",
123124
"react-textarea-autosize": "^7.1.2",
124125
"react-toastify": "^5.5.0",
125126
"react-use": "^13.12.2",
@@ -145,12 +146,12 @@
145146
"snakecase-keys": "^3.1.0",
146147
"strip-markdown": "^3.1.1",
147148
"style-loader": "1.0.0",
148-
"styled-components": "^4.4.1",
149+
"styled-components": "^5.3.3",
149150
"terser-webpack-plugin": "2.2.1",
150151
"throttle-debounce": "^2.1.0",
151152
"ts-pnp": "1.1.5",
152153
"typesafe-actions": "^5.1.0",
153-
"typescript": "~3.7.2",
154+
"typescript": "^4.5.5",
154155
"unist-util-visit": "^2.0.1",
155156
"url-loader": "2.3.0",
156157
"webpack": "4.41.2",

public/index.html

+13
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@
3838

3939
gtag('config', 'UA-125599395-1');
4040
</script>
41+
<script>
42+
function loadTheme() {
43+
try {
44+
const theme = localStorage.getItem('theme')
45+
if (!theme)
46+
return
47+
document.body.dataset.theme = theme
48+
} catch (e) {
49+
50+
}
51+
}
52+
loadTheme()
53+
</script>
4154

4255
<title>React App</title>
4356
</head>

src/App.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import NotFoundPage from './pages/NotFoundPage';
1313
import { Helmet } from 'react-helmet-async';
1414
import HomePage from './pages/home/HomePage';
1515
import MainPageTemplate from './components/main/MainPageTemplate';
16+
import { useThemeEffect } from './components/base/hooks/useThemeEffect';
1617

1718
const loadableConfig = {
1819
fallback: <PageTemplate />,

src/GlobalStyles.ts

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createGlobalStyle } from 'styled-components';
2+
import { themedPalette, themes } from './lib/styles/themes';
23

34
const GlobalStyles = createGlobalStyle`
45
body {
@@ -7,8 +8,9 @@ body {
78
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Apple SD Gothic Neo", "Malgun Gothic", "맑은 고딕", 나눔고딕, "Nanum Gothic", "Noto Sans KR", "Noto Sans CJK KR", arial, 돋움, Dotum, Tahoma, Geneva, sans-serif;
89
-webkit-font-smoothing: antialiased;
910
-moz-osx-font-smoothing: grayscale;
10-
color: #212529;
11+
color: ${themedPalette.text1};
1112
box-sizing: border-box;
13+
background: ${themedPalette.bg_page2};
1214
}
1315
1416
* {
@@ -27,6 +29,26 @@ input, button, textarea {
2729
html, body, #root {
2830
height: 100%;
2931
}
32+
33+
body {
34+
background: ${themedPalette.bg_page2};
35+
${themes.light}
36+
}
37+
38+
@media (prefers-color-scheme: dark) {
39+
body {
40+
${themes.dark}
41+
}
42+
}
43+
44+
body[data-theme='light'] {
45+
${themes.light};
46+
}
47+
48+
body[data-theme='dark'] {
49+
${themes.dark};
50+
}
51+
3052
`;
3153

3254
export default GlobalStyles;

src/components/auth/AuthEmailForm.tsx

+16-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as React from 'react';
22
import styled from 'styled-components';
3+
import { themedPalette } from '../../lib/styles/themes';
34
import palette from '../../lib/styles/palette';
45

56
const AuthEmailFormBlock = styled.form`
@@ -12,18 +13,24 @@ const AuthEmailFormBlock = styled.form`
1213
border-bottom-left-radius: 2px;
1314
padding: 1rem;
1415
font-size: 1rem;
15-
border: 1px solid ${palette.gray3};
16+
background: ${themedPalette.bg_element1};
17+
border: 1px solid ${themedPalette.border3};
18+
color: ${themedPalette.text1};
1619
border-right: none;
20+
outline: none;
21+
&:focus {
22+
border: 1px solid ${themedPalette.primary1};
23+
}
1724
&::placeholder {
18-
color: ${palette.gray6};
25+
color: ${themedPalette.text3};
1926
}
2027
&:disabled {
21-
background: ${palette.gray1};
28+
background: ${themedPalette.bg_element2};
2229
}
2330
}
2431
button {
25-
background: ${palette.teal6};
26-
color: white;
32+
background: ${themedPalette.primary1};
33+
color: ${themedPalette.button_text};
2734
font-size: 1rem;
2835
font-weight: bold;
2936
outline: none;
@@ -35,11 +42,11 @@ const AuthEmailFormBlock = styled.form`
3542
cursor: pointer;
3643
&:hover,
3744
&:focus {
38-
background: ${palette.teal5};
45+
background: ${themedPalette.primary2};
3946
}
4047
&:disabled {
41-
background: ${palette.gray5};
42-
color: ${palette.gray3};
48+
background: ${themedPalette.border4};
49+
color: ${themedPalette.text4};
4350
cursor: default;
4451
}
4552
}
@@ -62,7 +69,7 @@ const AuthEmailForm: React.FC<AuthEmailFormProps> = ({
6269
}) => {
6370
return (
6471
<AuthEmailFormBlock
65-
onSubmit={e => {
72+
onSubmit={(e) => {
6673
e.preventDefault();
6774
onSubmit(value);
6875
}}

src/components/auth/AuthEmailSuccess.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as React from 'react';
22
import styled from 'styled-components';
33
import { MdCheck } from 'react-icons/md';
4+
import { themedPalette } from '../../lib/styles/themes';
45
import palette from '../../lib/styles/palette';
56

67
const AuthEmailSuccessBlock = styled.div`
@@ -11,7 +12,7 @@ const AuthEmailSuccessBlock = styled.div`
1112
padding-left: 0.75rem;
1213
padding-right: 0.75rem;
1314
height: 3rem;
14-
color: ${palette.teal7};
15+
color: ${palette.teal9};
1516
.icon {
1617
font-size: 1.5rem;
1718
}

src/components/auth/AuthForm.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import styled from 'styled-components';
33
import useInput from '../../lib/hooks/useInput';
44
import AuthEmailForm from './AuthEmailForm';
55
import { AuthMode } from '../../modules/core';
6+
import { themedPalette } from '../../lib/styles/themes';
67
import palette from '../../lib/styles/palette';
78
import AuthSocialButtonGroup from './AuthSocialButtonGroup';
89
import AuthEmailSuccess from './AuthEmailSuccess';
@@ -25,12 +26,12 @@ const AuthFormBlock = styled.div`
2526
}
2627
h2 {
2728
font-size: 1.3125rem;
28-
color: ${palette.gray8};
29+
color: ${themedPalette.text1};
2930
}
3031
h4 {
3132
margin-top: 1rem;
3233
margin-bottom: 1rem;
33-
color: ${palette.gray6};
34+
color: ${themedPalette.text3};
3435
}
3536
section + section {
3637
margin-top: 2.5rem;
@@ -47,7 +48,7 @@ const AuthFormBlock = styled.div`
4748
.link {
4849
display: inline-block;
4950
font-weight: bold;
50-
color: ${palette.teal6};
51+
color: ${themedPalette.primary1};
5152
cursor: pointer;
5253
&:hover {
5354
text-decoration: underline;

src/components/auth/AuthModal.tsx

+7-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as React from 'react';
22
import styled, { css } from 'styled-components';
33
import { MdClose } from 'react-icons/md';
44
import zIndexes from '../../lib/styles/zIndexes';
5+
import { themedPalette } from '../../lib/styles/themes';
56
import palette from '../../lib/styles/palette';
67
import { undrawJoyride } from '../../static/images';
78
import transitions from '../../lib/styles/transitions';
@@ -28,7 +29,7 @@ const AuthModalBlock = styled.div<{ visible: boolean }>`
2829
height: 100%;
2930
}
3031
31-
${props =>
32+
${(props) =>
3233
props.visible
3334
? css`
3435
animation: ${transitions.popInFromBottom} 0.4s forwards ease-in-out;
@@ -44,7 +45,7 @@ const AuthModalBlock = styled.div<{ visible: boolean }>`
4445
display: none;
4546
}
4647
width: 216px;
47-
background: ${palette.gray1};
48+
background: ${themedPalette.bg_element2};
4849
padding: 1.5rem;
4950
display: flex;
5051
flex-direction: column;
@@ -58,14 +59,14 @@ const AuthModalBlock = styled.div<{ visible: boolean }>`
5859
.welcome {
5960
font-size: 1.75rem;
6061
margin-top: 1.5rem;
61-
color: ${palette.gray7};
62+
color: ${themedPalette.text2};
6263
text-align: center;
6364
font-weight: 600;
6465
}
6566
}
6667
.white-block {
6768
flex: 1;
68-
background: white;
69+
background: ${themedPalette.bg_page2};
6970
padding: 1.5rem;
7071
display: flex;
7172
flex-direction: column;
@@ -76,7 +77,7 @@ const AuthModalBlock = styled.div<{ visible: boolean }>`
7677
display: flex;
7778
justify-content: flex-end;
7879
font-size: 1.5rem;
79-
color: ${palette.gray6};
80+
color: ${themedPalette.text3};
8081
margin-bottom: 2.25rem;
8182
svg {
8283
cursor: pointer;
@@ -106,7 +107,7 @@ const AuthModal: React.FC<AuthModalProps> = ({
106107
}) => {
107108
const [closed, setClosed] = useState(true);
108109
useEffect(() => {
109-
let timeoutId: number | null = null;
110+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
110111
if (visible) {
111112
setClosed(false);
112113
} else {

src/components/auth/AuthSocialButton.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as React from 'react';
22
import styled, { css } from 'styled-components';
33
import { FacebookIcon, GoogleIcon, GithubIcon } from '../../static/svg';
4+
import { themedPalette } from '../../lib/styles/themes';
45
import palette from '../../lib/styles/palette';
56

67
const AuthSocialButtonBlock = styled.a<{ border: boolean }>`
@@ -13,10 +14,10 @@ const AuthSocialButtonBlock = styled.a<{ border: boolean }>`
1314
outline: none;
1415
transition: 0.125s all ease-in;
1516
color: white;
16-
${props =>
17+
${(props) =>
1718
props.border &&
1819
css`
19-
border: 1px solid ${palette.gray3};
20+
border: 1px solid ${themedPalette.border3};
2021
`}
2122
&:focus {
2223
box-shadow: 0px 2px 12px rgba(0, 0, 0, 0.35);

src/components/auth/__tests__/__snapshots__/AuthForm.test.tsx.snap

+6-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
exports[`AuthForm renders correctly 1`] = `
44
<div>
55
<div
6-
class="sc-ifAKCX kIBOZW"
6+
class="sc-eCImPb fOWQkg"
77
>
88
<div
99
class="upper-wrapper"
@@ -19,7 +19,7 @@ exports[`AuthForm renders correctly 1`] = `
1919
회원가입
2020
</h4>
2121
<form
22-
class="sc-bdVaJa izhlxF"
22+
class="sc-bdvvtL knJYhw"
2323
>
2424
<input
2525
placeholder="이메일을 입력하세요."
@@ -39,10 +39,10 @@ exports[`AuthForm renders correctly 1`] = `
3939
회원가입
4040
</h4>
4141
<div
42-
class="sc-htpNat jVlyIb"
42+
class="sc-dkPtRN dHVNVs"
4343
>
4444
<a
45-
class="sc-bwzfXH dOnywA"
45+
class="sc-gsDKAQ cQxaoZ"
4646
href="http://localhost:5000/api/v2/auth/social/redirect/github?next=/"
4747
style="background: rgb(39, 46, 51);"
4848
tabindex="4"
@@ -52,7 +52,7 @@ exports[`AuthForm renders correctly 1`] = `
5252
</svg>
5353
</a>
5454
<a
55-
class="sc-bwzfXH fVlvjo"
55+
class="sc-gsDKAQ YbYbf"
5656
href="http://localhost:5000/api/v2/auth/social/redirect/google?next=/"
5757
style="background: white;"
5858
tabindex="5"
@@ -62,7 +62,7 @@ exports[`AuthForm renders correctly 1`] = `
6262
</svg>
6363
</a>
6464
<a
65-
class="sc-bwzfXH dOnywA"
65+
class="sc-gsDKAQ cQxaoZ"
6666
href="http://localhost:5000/api/v2/auth/social/redirect/facebook?next=/"
6767
style="background: rgb(59, 89, 152);"
6868
tabindex="6"
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React from 'react';
2+
import { useEffect, useState } from 'react';
3+
import { createGlobalStyle } from 'styled-components';
4+
5+
const Styles = createGlobalStyle`
6+
body {
7+
transition: background 0.125s ease-in;
8+
}
9+
`;
10+
11+
function BodyTransition() {
12+
const [enabled, setEnabled] = useState(false);
13+
useEffect(() => {
14+
setTimeout(() => {
15+
setEnabled(true);
16+
}, 500);
17+
}, []);
18+
19+
if (!enabled) return null;
20+
return <Styles />;
21+
}
22+
23+
export default BodyTransition;

src/components/base/FloatingHeader.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import MainResponsive from '../main/MainResponsive';
66
import { getScrollTop } from '../../lib/utils';
77
import { Route } from 'react-router-dom';
88
import ReadingListTab from '../readingList/ReadingListTab';
9+
import { themedPalette } from '../../lib/styles/themes';
910

1011
export type FloatingHeaderProps = {};
1112

@@ -107,7 +108,7 @@ function FloatingHeader(props: FloatingHeaderProps) {
107108
const Block = styled.div`
108109
position: fixed;
109110
top: 0;
110-
background: white;
111+
background: ${themedPalette.bg_element1};
111112
width: 100%;
112113
z-index: 10;
113114

0 commit comments

Comments
 (0)