Skip to content
This repository has been archived by the owner on Jul 16, 2023. It is now read-only.

Commit

Permalink
feat: ✨ add dark mode support
Browse files Browse the repository at this point in the history
Features:
- header dark/light mode toggle
- configurable theming
- configuration: enabled/disabled, setting dark mode as default
- setting up user-preferred theme based on browser / system config
  • Loading branch information
filipowm committed Jun 6, 2020
1 parent abd0474 commit ba1a983
Show file tree
Hide file tree
Showing 12 changed files with 407 additions and 253 deletions.
4 changes: 4 additions & 0 deletions config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,7 @@ features:
matchRegex: "^/"
outputPath: "/rss.xml"
generator: "gidocs"
darkMode:
enabled: true
default: false

4 changes: 4 additions & 0 deletions config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,9 @@ module.exports = {
outputPath: '/rss.xml',
generator: 'gidocs',
},
darkMode: {
enabled: true,
default: false,
}
},
};
36 changes: 36 additions & 0 deletions src/components/darkModeSwitch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import styled from '@emotion/styled';
import { Sun as DayImage, Moon as NightImage } from 'react-feather';

const StyledSwitch = styled('div')`
display: flex;
justify-content: flex-end;
width: 100%;
margin: 0 20px 0 5px;
padding: 6px;
background-color: ${(props) => props.theme.darkModeSwitch.background};
border-radius: 50%;
cursor: pointer;
&:hover {
svg {
fill: ${(props) => props.theme.darkModeSwitch.hover};
stroke: ${(props) => props.theme.darkModeSwitch.hover};
}
}
svg {
transition: ${(props) => props.theme.transitions.hover};
fill: ${(props) => props.theme.darkModeSwitch.fill};
stroke: ${(props) => props.theme.darkModeSwitch.stroke};
}
`;

export const DarkModeSwitch = ({ isDarkThemeActive, toggleActiveTheme, ...props }) => {
const img = isDarkThemeActive ? NightImage : DayImage;
return (
<StyledSwitch {...props} role={'button'} tabIndex={0} onClick={toggleActiveTheme}>
{/* not defining color as a workaround to use css styling instead */}
{img.render({ color: '' })}
</StyledSwitch>
);
};
23 changes: 21 additions & 2 deletions src/components/header/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import { StaticQuery, graphql } from 'gatsby';
import 'css';
import config from 'config';
Expand All @@ -8,6 +8,7 @@ import Navigation from './navigation';
import Sidebar from '../sidebar';
import styled from '@emotion/styled';
import SearchBox from '../search/input';
import { DarkModeSwitch } from '../darkModeSwitch';

const isSearchEnabled = config.features.search && config.features.search.enabled;

Expand Down Expand Up @@ -85,7 +86,7 @@ const TopNavigation = styled.div`
}
`;

const Index = ({ setShowSearch, location }) => (
const Index = ({ setShowSearch, location, themeProvider }) => (
<StaticQuery
query={graphql`
query headerTitleQuery {
Expand Down Expand Up @@ -114,6 +115,12 @@ const Index = ({ setShowSearch, location }) => (
} = data;
const logoLink = logo.link !== '' ? logo.link : '/';
const logoImg = require('images/logo.svg');
const [darkMode, setDarkMode] = useState(false);

useEffect(() => {
setDarkMode(themeProvider.current.retrieveActiveTheme());
});

return (
<Header>
<Logo link={logoLink} img={logoImg} title={headerTitle} />
Expand Down Expand Up @@ -142,6 +149,18 @@ const Index = ({ setShowSearch, location }) => (
/>
</SearchWrapper>
) : null}

{config.features.darkMode.enabled ? (
<DarkModeSwitch
css={{ flex: '0' }}
isDarkThemeActive={darkMode}
toggleActiveTheme={() => {
setDarkMode(themeProvider.current.toggleActiveTheme());
}}
/>
) : (
''
)}
</Header>
);
}}
Expand Down
10 changes: 7 additions & 3 deletions src/components/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@ const Content = styled('main')`

const Layout = ({ children, location }) => {
const [showSearch, setShowSearch] = useState(false);

const themeProviderRef = React.createRef();
return (
<ThemeProvider>
<ThemeProvider ref={themeProviderRef} darkModeConfig={config.features.darkMode}>
{config.header.enabled === true ? (
<>
<SearchSidebar show={showSearch} setShow={setShowSearch} />
<Header location={location} setShowSearch={setShowSearch} />
<Header
location={location}
setShowSearch={setShowSearch}
themeProvider={themeProviderRef}
/>
</>
) : (
''
Expand Down
51 changes: 0 additions & 51 deletions src/components/theme/themeProvider.js

This file was deleted.

64 changes: 54 additions & 10 deletions src/components/themeProvider.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,56 @@
import * as React from 'react';
import React from 'react';
import { ThemeProvider as EmotionThemeProvider } from 'emotion-theming';
import { default as defaultTheme } from '../theme';
import 'css';

export default function ThemeProvider({ children, theme = {} }) {
return (
<div>
<EmotionThemeProvider theme={{ ...defaultTheme, ...theme }}>{children}</EmotionThemeProvider>
</div>
);
import { light, dark } from '../theme';

class ThemeProvider extends React.Component {
state = {
isDarkThemeActive: false,
};

constructor({ darkModeConfig }) {
super();
this.darkModeConfig = darkModeConfig;
}

componentDidMount() {
this.retrieveActiveTheme();
}

retrieveActiveTheme = () => {
if (!this.darkModeConfig.enabled) {
return false;
}
let isDarkThemeActive = JSON.parse(window.localStorage.getItem('isDarkThemeActive'));
if (isDarkThemeActive == null) {
isDarkThemeActive =
window.matchMedia('(prefers-color-scheme: dark)').matches || this.darkModeConfig.default;
}
this.setState({ isDarkThemeActive });
return isDarkThemeActive;
};

toggleActiveTheme = () => {
if (!this.darkModeConfig.enabled) {
console.warn('Dark mode is disabled, but trying to activate it.');
return false;
}
this.setState((prevState) => ({ isDarkThemeActive: !prevState.isDarkThemeActive }));

window.localStorage.setItem('isDarkThemeActive', JSON.stringify(!this.state.isDarkThemeActive));
return !this.state.isDarkThemeActive;
};

render() {
const { children } = this.props;
const { isDarkThemeActive } = this.state;
const currentActiveTheme = isDarkThemeActive ? dark : light;

return (
<div>
<EmotionThemeProvider theme={currentActiveTheme}>{children}</EmotionThemeProvider>
</div>
);
}
}

export default ThemeProvider;
Loading

0 comments on commit ba1a983

Please sign in to comment.