feat: app level theming

This commit is contained in:
Kyle Gill
2019-05-19 19:28:09 -06:00
parent a67744c31d
commit f3030d8091
11 changed files with 206 additions and 105 deletions

View File

@@ -1,15 +1,21 @@
import React from "react" import React from "react"
import { ThemeProvider } from "emotion-theming" import { ThemeProvider as EmotionThemeProvider } from "emotion-theming"
import Firebase, { FirebaseContext } from "./src/components/firebase" import Firebase, { FirebaseContext } from "components/firebase"
import theme from "./src/styles/theme" import theme from "styles/theme"
import ThemeTogglerContext, { ThemeToggler } from "components/context/theme"
const selectedTheme =
new Date().getHours() >= 7 && new Date().getHours() <= 21 ? "LIGHT" : "DARK"
export const wrapRootElement = ({ element }) => { export const wrapRootElement = ({ element }) => {
return ( return (
<FirebaseContext.Provider value={new Firebase()}> <FirebaseContext.Provider value={new Firebase()}>
<ThemeProvider theme={theme[selectedTheme]}>{element}</ThemeProvider> <ThemeToggler>
<ThemeTogglerContext.Consumer>
{({ themeName }) => (
<EmotionThemeProvider theme={theme[themeName]}>
{element}
</EmotionThemeProvider>
)}
</ThemeTogglerContext.Consumer>
</ThemeToggler>
</FirebaseContext.Provider> </FirebaseContext.Provider>
) )
} }

View File

@@ -2,11 +2,11 @@ import React, { Component } from "react"
import { Router } from "@reach/router" import { Router } from "@reach/router"
import { compose } from "recompose" import { compose } from "recompose"
import styled from "@emotion/styled" import styled from "@emotion/styled"
import { ThemeProvider } from "emotion-theming" import { withTheme } from "emotion-theming"
import { SIZES } from "./styles/constants" import { SIZES } from "./styles/constants"
import theme from "./styles/theme" // import theme from "./styles/theme"
import Navbar from "./components/Navbar" import Navbar from "./components/Navbar"
import Day from "routes/Day" import Day from "routes/Day"
import Month from "routes/Month" import Month from "routes/Month"
@@ -19,6 +19,7 @@ import PrivateRoute from "./components/PrivateRoute"
import { OnlineContext } from "./components/context/online" import { OnlineContext } from "./components/context/online"
import { withAuthentication } from "./components/session" import { withAuthentication } from "./components/session"
import { withFirebase } from "./components/firebase" import { withFirebase } from "./components/firebase"
import ThemeTogglerContext from "components/context/theme"
const FullscreenLayout = styled.div` const FullscreenLayout = styled.div`
background-color: ${props => props.theme.colors.bodyBackground}; background-color: ${props => props.theme.colors.bodyBackground};
@@ -36,10 +37,7 @@ const RouteLayout = styled.div`
class App extends Component { class App extends Component {
state = { state = {
selectedTheme: selectedTheme: this.props.theme.name,
new Date().getHours() >= 7 && new Date().getHours() <= 21
? "LIGHT"
: "DARK",
} }
componentDidMount() { componentDidMount() {
@@ -57,28 +55,17 @@ class App extends Component {
}) })
} }
onChangeTheme = () => {
const { selectedTheme } = this.state
const body = document.body
const newTheme = selectedTheme === "LIGHT" ? "DARK" : "LIGHT"
body.style.setProperty(
"background-color",
theme[newTheme].colors.bodyBackground
)
this.setState({ selectedTheme: newTheme })
}
render() { render() {
const { selectedTheme, authUser, online } = this.state const { authUser, online } = this.state
const { authUser: propAuthUser } = this.props const { authUser: propAuthUser, theme } = this.props
const authed = !!propAuthUser || !!authUser const authed = !!propAuthUser || !!authUser
const currentTheme = theme[selectedTheme]
return ( return (
<ThemeProvider theme={currentTheme}> <ThemeTogglerContext.Consumer>
{({ toggle }) => (
<OnlineContext.Provider value={online}> <OnlineContext.Provider value={online}>
<FullscreenLayout> <FullscreenLayout>
<Navbar toggleTheme={this.onChangeTheme} /> <Navbar toggleTheme={toggle} />
<RouteLayout> <RouteLayout>
<Router> <Router>
<PrivateRoute <PrivateRoute
@@ -116,12 +103,14 @@ class App extends Component {
</RouteLayout> </RouteLayout>
</FullscreenLayout> </FullscreenLayout>
</OnlineContext.Provider> </OnlineContext.Provider>
</ThemeProvider> )}
</ThemeTogglerContext.Consumer>
) )
} }
} }
export default compose( export default compose(
withAuthentication, withAuthentication,
withFirebase withFirebase,
withTheme
)(App) )(App)

View File

@@ -13,6 +13,7 @@ import { todayUrl, yearUrl } from "utils/date"
import Logo from "components/Logo" import Logo from "components/Logo"
import Icon from "components/Icon" import Icon from "components/Icon"
import { withAuthentication } from "components/session" import { withAuthentication } from "components/session"
import ThemeTogglerContext from "components/context/theme"
const Header = styled.div` const Header = styled.div`
background-color: ${props => props.theme.colors.headerBackground}; background-color: ${props => props.theme.colors.headerBackground};
@@ -93,16 +94,24 @@ const Navbar = ({ authUser, theme, toggleTheme }) => (
/> />
</React.Fragment> </React.Fragment>
) : ( ) : (
<>
<Link to={"/login"} style={{ textDecoration: "none" }}> <Link to={"/login"} style={{ textDecoration: "none" }}>
<Icon name="ArrowRight" label="Login" size={20} /> <Icon name="ArrowRight" label="Login" size={20} />
</Link> </Link>
<Icon
tabindex={0}
onClick={() => toggleTheme()}
onKeyPress={() => toggleTheme()}
name={theme.name === "Dark" ? "Sun" : "Moon"}
/>
</>
)} )}
</NavIcons> </NavIcons>
</Nav> </Nav>
</Header> </Header>
) )
const SimpleNav = ({ theme, toggleTheme }) => ( const SimpleNav = ({ authUser, theme }) => (
<Header> <Header>
<Nav> <Nav>
<LogoSection onClick={() => navigate("/")}> <LogoSection onClick={() => navigate("/")}>
@@ -111,17 +120,34 @@ const SimpleNav = ({ theme, toggleTheme }) => (
<LogoText color={theme.colors.secondary}>JOURNAL</LogoText> <LogoText color={theme.colors.secondary}>JOURNAL</LogoText>
</LogoSection> </LogoSection>
<NavIcons> <NavIcons>
{/* <Tooltip title="Login"> */} {authUser ? (
<StyledLink to={"/"}>
<Icon name="Circle" />
</StyledLink>
) : (
<Link to={"/login"} style={{ textDecoration: "none" }}> <Link to={"/login"} style={{ textDecoration: "none" }}>
<Icon name="ArrowRight" label="Login" size={20} /> <Icon name="ArrowRight" label="Login" size={20} />
</Link> </Link>
{/* </Tooltip> */} )}
<ThemeTogglerContext.Consumer>
{({ toggle }) => (
<Icon
tabindex={0}
onClick={() => toggle()}
onKeyPress={() => toggle()}
name={theme.name === "Dark" ? "Sun" : "Moon"}
/>
)}
</ThemeTogglerContext.Consumer>
</NavIcons> </NavIcons>
</Nav> </Nav>
</Header> </Header>
) )
const SimpleNavbar = withTheme(SimpleNav) const SimpleNavbar = compose(
withAuthentication,
withTheme
)(SimpleNav)
export { SimpleNavbar } export { SimpleNavbar }

View File

@@ -0,0 +1,46 @@
import React from "react"
import theme from "styles/theme"
const ThemeTogglerContext = React.createContext({
themeName: "LIGHT",
toggle: () => {},
})
class ThemeToggler extends React.Component {
state = {
themeName:
new Date().getHours() >= 7 && new Date().getHours() <= 21
? "LIGHT"
: "DARK",
}
toggle = () => {
const { themeName } = this.state
const body = document.body
const newTheme = themeName === "LIGHT" ? "DARK" : "LIGHT"
body.style.setProperty(
"background-color",
theme[newTheme].colors.bodyBackground
)
this.setState({ themeName: newTheme })
}
render() {
const { children } = this.props
const { themeName } = this.state
return (
<ThemeTogglerContext.Provider
value={{
themeName,
toggle: this.toggle,
}}
>
{children}
</ThemeTogglerContext.Provider>
)
}
}
export default ThemeTogglerContext
export { ThemeToggler }

View File

@@ -11,6 +11,18 @@ export const H1 = styled.h1`
color: ${props => props.color}; color: ${props => props.color};
` `
export const SimpleH1 = styled.h1`
color: ${props => props.color};
`
export const SimpleH2 = styled.h1`
color: ${props => props.color};
`
export const Em = styled.em`
color: ${props => props.color};
`
export const Input = styled.input` export const Input = styled.input`
color: ${props => props.colors.primary}; color: ${props => props.colors.primary};
background-color: ${props => props.colors.headerBackground}; background-color: ${props => props.colors.headerBackground};

View File

@@ -30,7 +30,10 @@ const LoginPage = ({ theme }) => (
{firebase => <LoginForm firebase={firebase} />} {firebase => <LoginForm firebase={firebase} />}
</FirebaseContext.Consumer> </FirebaseContext.Consumer>
<P colors={theme.colors} style={{ fontStyle: "italic" }}> <P colors={theme.colors} style={{ fontStyle: "italic" }}>
Don't have an account? <Link to={"/register"}>Sign Up</Link> Don't have an account?{" "}
<Link style={{ color: theme.colors.primary }} to={"/register"}>
Sign Up
</Link>
</P> </P>
</LoginLayout> </LoginLayout>
</Layout> </Layout>
@@ -91,7 +94,7 @@ class LoginFormBase extends Component {
Login Login
</Button> </Button>
</LoginGrid> </LoginGrid>
{error && <p>{error.message}</p>} {error && <P colors={theme.colors}>{error.message}</P>}
</form> </form>
) )
} }

View File

@@ -1,19 +1,22 @@
import React from "react" import React from "react"
import { P } from "components/elements" import { SimpleH1, SimpleH2, P, Em } from "components/elements"
import Layout from "components/Layout" import Layout from "components/Layout"
import Container from "components/container" import Container from "components/container"
import { SimpleNavbar } from "components/Navbar"
import { withTheme } from "emotion-theming"
const Terms = () => ( const Terms = ({ theme }) => (
<Layout> <Layout>
<SimpleNavbar />
<Container> <Container>
<h1>Privacy Policy</h1> <SimpleH1 color={theme.colors.primary}>Privacy Policy</SimpleH1>
<em>Last update: April 30, 2019</em> <Em color={theme.colors.secondary}>Last update: April 30, 2019</Em>{" "}
<P> <P>
Sol Journal supports the following browsers: Chrome (latest), Safari Sol Journal supports the following browsers: Chrome (latest), Safari
(latest), Firefox (50+) (latest), Firefox (50+)
</P> </P>
<h2>Rights</h2> <SimpleH2 color={theme.colors.secondary}>Rights</SimpleH2>
<P> <P>
You don't have to provide your real name when you register to an You don't have to provide your real name when you register to an
account, but you need to use a valid/verifiable email address. account, but you need to use a valid/verifiable email address.
@@ -43,7 +46,7 @@ const Terms = () => (
<br /> <br />
Any new features that affect privacy will be strictly opt-in. Any new features that affect privacy will be strictly opt-in.
</P> </P>
<h2>Responsibilites</h2> <SimpleH2 color={theme.colors.secondary}>Responsibilites</SimpleH2>
<P> <P>
You will not use the site to store illegal information or data under You will not use the site to store illegal information or data under
United States law (or any law). United States law (or any law).
@@ -68,7 +71,7 @@ const Terms = () => (
(millions of entries or overloading services with requests) or use it in (millions of entries or overloading services with requests) or use it in
an unreasonable manner. an unreasonable manner.
</P> </P>
<h2>Other</h2> <SimpleH2 color={theme.colors.secondary}>Other</SimpleH2>
<P> <P>
Other important legal stuff Though I want to provide a great service, Other important legal stuff Though I want to provide a great service,
there are certain things about the service I cannot promise. For there are certain things about the service I cannot promise. For
@@ -88,4 +91,4 @@ const Terms = () => (
</Layout> </Layout>
) )
export default Terms export default withTheme(Terms)

View File

@@ -29,7 +29,10 @@ const RegisterPage = ({ theme }) => (
{firebase => <RegisterForm firebase={firebase} />} {firebase => <RegisterForm firebase={firebase} />}
</FirebaseContext.Consumer> </FirebaseContext.Consumer>
<P colors={theme.colors} style={{ fontStyle: "italic" }}> <P colors={theme.colors} style={{ fontStyle: "italic" }}>
Already have an account? <Link to={"/login"}>Login</Link> Already have an account?{" "}
<Link style={{ color: theme.colors.primary }} to={"/login"}>
Login
</Link>
</P> </P>
</RegisterLayout> </RegisterLayout>
</Layout> </Layout>
@@ -133,7 +136,7 @@ class RegisterFormBase extends Component {
</Button> </Button>
</RegisterGrid> </RegisterGrid>
{error && <p>{error.message}</p>} {error && <P colors={theme.colors}>{error.message}</P>}
</form> </form>
) )
} }

View File

@@ -1,20 +1,23 @@
import React from "react" import React from "react"
import { P } from "components/elements" import { SimpleH1, SimpleH2, P, Em } from "components/elements"
import Layout from "components/Layout" import Layout from "components/Layout"
import Container from "components/container" import Container from "components/container"
import { SimpleNavbar } from "components/Navbar"
import { withTheme } from "emotion-theming"
const Terms = () => ( const Terms = ({ theme }) => (
<Layout> <Layout>
<SimpleNavbar />
<Container> <Container>
<h1>Terms of Service</h1> <SimpleH1 color={theme.colors.primary}>Terms of Service</SimpleH1>
<em>Last update: April 30, 2019</em> <Em color={theme.colors.secondary}>Last update: April 30, 2019</Em>
<h2>Scope of Service</h2> <SimpleH2 color={theme.colors.secondary}>Scope of Service</SimpleH2>
<P> <P>
Sol Journal supports the following browsers: Chrome (latest), Safari Sol Journal supports the following browsers: Chrome (latest), Safari
(latest), Firefox (50+) (latest), Firefox (50+)
</P> </P>
<h2>Rights</h2> <SimpleH2 color={theme.colors.secondary}>Rights</SimpleH2>
<P> <P>
You don't have to provide your real name when you register to an You don't have to provide your real name when you register to an
account, but you need to use a valid/verifiable email address. account, but you need to use a valid/verifiable email address.
@@ -37,7 +40,7 @@ const Terms = () => (
<br /> <br />
Any new features that affect privacy will be strictly opt-in. Any new features that affect privacy will be strictly opt-in.
</P> </P>
<h2>Responsibilites</h2> <SimpleH2 color={theme.colors.secondary}>Responsibilites</SimpleH2>
<P> <P>
You will not use the site to store illegal information or data under You will not use the site to store illegal information or data under
United States law (or any law). United States law (or any law).
@@ -62,7 +65,7 @@ const Terms = () => (
(millions of entries or overloading services with requests) or use it in (millions of entries or overloading services with requests) or use it in
an unreasonable manner. an unreasonable manner.
</P> </P>
<h2>Other</h2> <SimpleH2 color={theme.colors.secondary}>Other</SimpleH2>
<P> <P>
Other important legal stuff Though I want to provide a great service, Other important legal stuff Though I want to provide a great service,
there are certain things about the service I cannot promise. For there are certain things about the service I cannot promise. For
@@ -82,4 +85,4 @@ const Terms = () => (
</Layout> </Layout>
) )
export default Terms export default withTheme(Terms)

View File

@@ -120,7 +120,7 @@ class Start extends Component {
} }
`} `}
render={data => { render={data => {
return theme.name === "Light" ? ( return theme.name === "LIGHT" ? (
<Img <Img
style={{ style={{
maxWidth: 320, maxWidth: 320,
@@ -131,7 +131,15 @@ class Start extends Component {
fluid={data.landingGraphicLight.childImageSharp.fluid} fluid={data.landingGraphicLight.childImageSharp.fluid}
/> />
) : ( ) : (
<Img fluid={data.LandingGraphicDark.childImageSharp.fluid} /> <Img
style={{
maxWidth: 320,
width: "100%",
maxHeight: 350,
height: "100%",
}}
fluid={data.landingGraphicDark.childImageSharp.fluid}
/>
) )
}} }}
/> />

View File

@@ -1,6 +1,6 @@
const theme = { const theme = {
LIGHT: { LIGHT: {
name: "Light", name: "LIGHT",
colors: { colors: {
logo: "#344157", logo: "#344157",
primary: "#2E3136", primary: "#2E3136",
@@ -11,10 +11,11 @@ const theme = {
bodyBackground: "#FFF", bodyBackground: "#FFF",
inputBackground: "#FAFBFC", inputBackground: "#FAFBFC",
hover: "hsla(233, 5%, 31%, 0.12)", hover: "hsla(233, 5%, 31%, 0.12)",
button: "#f2f3f5",
}, },
}, },
DARK: { DARK: {
name: "Dark", name: "DARK",
colors: { colors: {
logo: "#EAEAEA", logo: "#EAEAEA",
primary: "#F3F6F8", primary: "#F3F6F8",
@@ -25,6 +26,7 @@ const theme = {
bodyBackground: "#262B34", bodyBackground: "#262B34",
inputBackground: "#272f3d", inputBackground: "#272f3d",
hover: "hsla(233, 100%, 96%, 0.12)", hover: "hsla(233, 100%, 96%, 0.12)",
button: "#464d5d",
}, },
}, },
} }