Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix dark mode FOUC #39

Merged
merged 3 commits into from
Feb 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion gatsby-ssr.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@
*
* See: https://www.gatsbyjs.org/docs/ssr-apis/
*/
import React from 'react';
import HydrateTheme from './src/components/hydrate-theme';

// You can delete this file if you're not using it
export const onRenderBody = ({ setPreBodyComponents }) => {
setPreBodyComponents([<HydrateTheme key="hydrate-theme" />]);
};
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
"@wkovacs64/normalize.css": "8.0.1",
"axios": "0.18.0",
"dotenv": "6.2.0",
"emotion-theming": "10.0.7",
"gatsby": "2.0.118",
"gatsby-plugin-emotion": "4.0.3",
"gatsby-plugin-manifest": "2.0.17",
Expand All @@ -56,9 +55,9 @@
"react-helmet": "6.0.0-beta",
"react-icons": "3.3.0",
"react-key-handler": "1.2.0-beta.3",
"react-use": "5.3.0",
"typeface-nunito": "0.0.54",
"typeface-source-sans-pro": "0.0.54"
"typeface-source-sans-pro": "0.0.54",
"use-dark-mode": "2.2.2"
},
"devDependencies": {
"@babel/core": "7.2.2",
Expand Down
1 change: 0 additions & 1 deletion src/@types/react-wait.d.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/@types/use-callbag.d.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/@types/use-onclickoutside.d.ts

This file was deleted.

32 changes: 23 additions & 9 deletions src/components/alert-on-update.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import React, { useState } from 'react';
import { StaticQuery, graphql } from 'gatsby';
import axios from 'axios';
import ms from 'ms';
import styled from '../utils/styled';
import styled from '@emotion/styled';
import isMobile from '../utils/is-mobile';
import { light, dark } from '../theme';
import UpdatePoller from './update-poller';
import UpdateAlert from './update-alert';

Expand All @@ -12,15 +13,28 @@ const UpdateAlertContainer = styled.div`
justify-content: center;
border-style: solid;
border-width: 0 0 1px;
border-color: ${({ theme }) => theme.colors.alertBorder};
box-shadow: 4px 4px 8px 0px ${({ theme }) => theme.colors.alertShadow};
color: ${({ theme }) => theme.colors.alertText};
background-color: ${({ theme }) => theme.colors.alertBackground};
transition: color 0.3s ease, background-color 0.3s ease;
&:hover,
&:focus-within {
color: ${({ theme }) => theme.colors.alertBackground};
background-color: ${({ theme }) => theme.colors.alertText};
body.light-mode & {
border-color: ${light.colors.alertBorder};
box-shadow: 4px 4px 8px 0px ${light.colors.alertShadow};
color: ${light.colors.alertText};
background-color: ${light.colors.alertBackground};
&:hover,
&:focus-within {
color: ${light.colors.alertBackground};
background-color: ${light.colors.alertText};
}
}
body.dark-mode & {
border-color: ${dark.colors.alertBorder};
box-shadow: 4px 4px 8px 0px ${dark.colors.alertShadow};
color: ${dark.colors.alertText};
background-color: ${dark.colors.alertBackground};
&:hover,
&:focus-within {
color: ${dark.colors.alertBackground};
background-color: ${dark.colors.alertText};
}
}
`;

Expand Down
10 changes: 8 additions & 2 deletions src/components/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import React from 'react';
import { css } from '@emotion/core';
import styled from '@emotion/styled';
import { FaGithub } from 'react-icons/fa';
import styled from '../utils/styled';
import { light, dark } from '../theme';

const SourceLink = styled.a`
color: ${({ theme }) => theme.colors.pageText};
text-decoration: none;
padding: 0.5rem;
body.light-mode & {
color: ${light.colors.pageText};
}
body.dark-mode & {
color: ${dark.colors.pageText};
}
`;

const SourceLinkIcon = styled(FaGithub)`
Expand Down
20 changes: 16 additions & 4 deletions src/components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import { StaticQuery, graphql } from 'gatsby';
import { FiSun } from 'react-icons/fi';
import styled from '../utils/styled';
import styled from '@emotion/styled';
import mq from '../utils/mq';
import { light, dark } from '../theme';
import AlertOnUpdate from './alert-on-update';

const HeaderContent = styled.section`
Expand All @@ -20,10 +21,15 @@ const ThemeToggleButton = styled.button`
top: 1rem;
right: 1rem;
cursor: pointer;
color: ${({ theme }) => theme.colors.pageText};
background-color: transparent;
border: none;
padding: 0.5rem;
body.light-mode & {
color: ${light.colors.pageText};
}
body.dark-mode & {
color: ${dark.colors.pageText};
}
`;

const ThemeToggleButtonIcon = styled(FiSun)`
Expand All @@ -37,8 +43,6 @@ const H1 = styled.h1`
font-family: 'Nunito', sans-serif;
font-size: 2.25rem;
font-variant: small-caps;
color: ${({ theme }) => theme.colors.headline};
text-shadow: 1px 1px 1px ${({ theme }) => theme.colors.headlineShadow};
margin: 0;
${mq.md} {
font-size: 3rem;
Expand All @@ -47,6 +51,14 @@ const H1 = styled.h1`
${mq.lg} {
font-size: 5rem;
}
body.light-mode & {
color: ${light.colors.headline};
text-shadow: 1px 1px 1px ${light.colors.headlineShadow};
}
body.dark-mode & {
color: ${dark.colors.headline};
text-shadow: 1px 1px 1px ${dark.colors.headlineShadow};
}
`;

interface HeaderProps {
Expand Down
49 changes: 49 additions & 0 deletions src/components/hydrate-theme.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';

// https://raw.githubusercontent.com/donavon/use-dark-mode/develop/noflash.js.txt
const hydrateThemeScript = `
(function() {
// Change these if you use something different in your hook.
var storageKey = 'darkMode';
var classNameDark = 'dark-mode';
var classNameLight = 'light-mode';

function setClassOnDocumentBody(darkMode) {
document.body.classList.add(darkMode ? classNameDark : classNameLight);
document.body.classList.remove(darkMode ? classNameLight : classNameDark);
}

var preferDarkQuery = '(prefers-color-scheme: dark)';
var mql = window.matchMedia(preferDarkQuery);
var supportsColorSchemeQuery = mql.media === preferDarkQuery;
var localStorageTheme = null;
try {
localStorageTheme = localStorage.getItem(storageKey);
} catch (err) {}
var localStorageExists = localStorageTheme !== null;
if (localStorageExists) {
localStorageTheme = JSON.parse(localStorageTheme);
}

// Determine the source of truth
if (localStorageExists) {
// source of truth from localStorage
setClassOnDocumentBody(localStorageTheme);
} else if (supportsColorSchemeQuery) {
// source of truth from system
setClassOnDocumentBody(mql.matches);
localStorage.setItem(storageKey, mql.matches);
} else {
// source of truth from document.body
var isDarkMode = document.body.classList.contains(classNameDark);
localStorage.setItem(storageKey, JSON.stringify(isDarkMode));
}
})();
`;

const HydrateTheme: React.FunctionComponent = () => (
// eslint-disable-next-line react/no-danger
<script dangerouslySetInnerHTML={{ __html: hydrateThemeScript }} />
);

export default HydrateTheme;
29 changes: 16 additions & 13 deletions src/components/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,30 @@ import 'typeface-source-sans-pro';
import React from 'react';
import { Helmet } from 'react-helmet';
import { css, Global, ClassNames } from '@emotion/core';
import { ThemeProvider } from 'emotion-theming';
import styled from '@emotion/styled';
import { IconContext } from 'react-icons';
import { StaticQuery, graphql } from 'gatsby';
import useLocalStorageState from '../utils/use-local-storage-state';
import styled from '../utils/styled';
import useDarkMode from 'use-dark-mode';
import { light, dark } from '../theme';
import Header from './header';
import Main from './main';
import Footer from './footer';

const FullHeightThemedContainer = styled.div`
color: ${({ theme }) => theme.colors.pageText};
background-color: ${({ theme }) => theme.colors.pageBackground};
min-height: 100vh;
padding-bottom: 2rem;
body.light-mode & {
color: ${light.colors.pageText};
background-color: ${light.colors.pageBackground};
}
body.dark-mode & {
color: ${dark.colors.pageText};
background-color: ${dark.colors.pageBackground};
}
`;

const Layout: React.FunctionComponent = ({ children }) => {
const [darkMode, setDarkMode] = useLocalStorageState('pwl:darkMode', false);
const darkMode = useDarkMode(false);

return (
<StaticQuery
Expand Down Expand Up @@ -107,13 +112,11 @@ const Layout: React.FunctionComponent = ({ children }) => {
data-version={siteMetadata.buildInfo.version}
/>
</Helmet>
<ThemeProvider theme={darkMode ? dark : light}>
<FullHeightThemedContainer>
<Header onThemeToggle={() => setDarkMode(!darkMode)} />
<Main>{children}</Main>
<Footer />
</FullHeightThemedContainer>
</ThemeProvider>
<FullHeightThemedContainer>
<Header onThemeToggle={darkMode.toggle} />
<Main>{children}</Main>
<Footer />
</FullHeightThemedContainer>
</IconContext.Provider>
)}
</ClassNames>
Expand Down
2 changes: 1 addition & 1 deletion src/components/legend-item.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import styled from '../utils/styled';
import styled from '@emotion/styled';

const LegendRow = styled.div`
display: flex;
Expand Down
23 changes: 17 additions & 6 deletions src/components/password-input.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import React from 'react';
import styled from '../utils/styled';
import styled from '@emotion/styled';
import mq from '../utils/mq';
import { light, dark } from '../theme';

const Input = styled.input`
font-family: 'Courier New', Courier, monospace;
padding: 1rem;
text-align: center;
letter-spacing: 0.25rem;
white-space: pre;
color: ${({ theme }) => theme.colors.pageText};
background-color: ${({ theme }) => theme.colors.inputBackground};
border: 2px solid ${({ theme }) => theme.colors.inputBorder};
width: 100%;
font-size: 1.25rem;
${mq.md} {
Expand All @@ -22,8 +20,21 @@ const Input = styled.input`
&::-ms-clear {
display: none;
}
&::placeholder {
color: ${({ theme }) => theme.colors.dullText};
body.light-mode & {
color: ${light.colors.pageText};
background-color: ${light.colors.inputBackground};
border: 2px solid ${light.colors.inputBorder};
&::placeholder {
color: ${light.colors.dullText};
}
}
body.dark-mode & {
color: ${dark.colors.pageText};
background-color: ${dark.colors.inputBackground};
border: 2px solid ${dark.colors.inputBorder};
&::placeholder {
color: ${dark.colors.dullText};
}
}
`;

Expand Down
58 changes: 42 additions & 16 deletions src/components/password-through-lense.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,17 @@
import React from 'react';
import { css } from '@emotion/core';
import styled from '../utils/styled';
import styled from '@emotion/styled';
import { ColorMap } from '../legend/colors';
import { LabelMap } from '../legend/labels';
import classifyCharacters, {
ClassifiedCharacter,
} from '../utils/classify-characters';
import mq from '../utils/mq';
import { light, dark } from '../theme';

const Lense = styled.div`
background-color: ${({ theme }) => theme.colors.lenseBackground};
border: 2px solid ${({ theme }) => theme.colors.lenseBorder};
scrollbar-color: ${({ theme }) =>
`${theme.colors.lenseScrollThumb} ${theme.colors.lenseScrollTrack}}`};
/* TODO: remove -webkit-scrollbar once Chrome supports scrollbar-color */
&::-webkit-scrollbar {
width: 1rem;
}
&::-webkit-scrollbar-thumb {
background-color: ${({ theme }) => theme.colors.lenseScrollThumb};
}
&::-webkit-scrollbar-track {
background-color: ${({ theme }) => theme.colors.lenseScrollTrack};
}
border-width: 2px;
border-style: solid;
overflow-x: scroll;
overflow-y: hidden;
white-space: nowrap;
Expand All @@ -35,11 +24,48 @@ const Lense = styled.div`
${mq.lg} {
font-size: 2.25rem;
}
/* TODO: remove -webkit-scrollbar once Chrome supports scrollbar-color */
&::-webkit-scrollbar {
width: 1rem;
}
body.light-mode & {
background-color: ${light.colors.lenseBackground};
border-color: ${light.colors.lenseBorder};
scrollbar-color: ${`${light.colors.lenseScrollThumb} ${
light.colors.lenseScrollTrack
}}`};
&::-webkit-scrollbar-thumb {
background-color: ${light.colors.lenseScrollThumb};
}
&::-webkit-scrollbar-track {
background-color: ${light.colors.lenseScrollTrack};
}
}
body.dark-mode & {
background-color: ${dark.colors.lenseBackground};
border-color: ${dark.colors.lenseBorder};
scrollbar-color: ${`${dark.colors.lenseScrollThumb} ${
dark.colors.lenseScrollTrack
}}`};
&::-webkit-scrollbar-thumb {
background-color: ${dark.colors.lenseScrollThumb};
}
&::-webkit-scrollbar-track {
background-color: ${dark.colors.lenseScrollTrack};
}
}
`;

const Character = styled.span`
border-bottom: 1px dotted ${({ theme }) => theme.colors.lenseUnderline};
border-bottom-width: 1px;
border-bottom-style: dotted;
white-space: pre;
body.light-mode & {
border-bottom-color: ${light.colors.lenseUnderline};
}
body.dark-mode & {
border-bottom-color: ${dark.colors.lenseUnderline};
}
`;

const PasswordThroughLense: React.FunctionComponent<
Expand Down