First of all.
The following idea can be applied to all kinds of sites while those use React, you need to know that Gatsby is a React-based open-source framework for creating websites and apps.
The main Idea 💡
We need to know that themes are CSS properties that change when we select a specific theme. eg.
<button type="button" onClick={handleToggle} style={styles(theme).toggle}>
{ theme === themes.light ? '☽' : '☼' }
</button>
We will create a dark ☽ and light ☼ theme:
import { createContext, useState } from 'react'
export const themes = {
light: {
foreground: 'rebeccapurple',
background: 'white'
},
dark: {
foreground: 'white',
background: 'rebeccapurple'
}
};
export const ThemeContext = createContext();
We need to create also the hook to manage the state of the current theme:
export const useThemes = () => {
const [mode, setMode] = useState(themes.light);
const toggleMode = () => {
if (mode === themes.light) {
setMode(themes.dark);
} else {
setMode(themes.light);
}
};
return [mode, toggleMode]
};
Please create a context to wrap your app :
import React from "react"
import PropTypes from "prop-types"
import { useStaticQuery, graphql } from "gatsby"
import { ThemeContext, themes, useThemes } from './Context/Theme'
import Header from "./Header"
import "./layout.css"
const Layout = ({ children }) => {
Import and create only one instance of our theme, this is a kind of singleton pattern.
const [theme, handleToggle ] = useThemes();
const data = useStaticQuery(graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
}
}
}
`)
console.log(theme.foreground);
return (
<ThemeContext.Provider value={themes}>
<Header siteTitle={data.site.siteMetadata.title} handleToggle={handleToggle} theme={theme}/>
<div
style={{
color: theme.foreground,
background: theme.background,
margin: `0 auto`,
maxWidth: 960,
padding: `10rem 1.0875rem 1.45rem`,
}}
>
<main>{children}</main>
<footer>
© {new Date().getFullYear()}, Built with
{` `}
<a href="https://www.gatsbyjs.org">Gatsby</a>
</footer>
</div>
</ThemeContext.Provider>
)
}
Layout.propTypes = {
children: PropTypes.node.isRequired,
}
export default Layout
The Header component will look like:
import { Link } from "gatsby"
import PropTypes from "prop-types"
import React, { useContext } from "react"
import {styles} from './styles'
import { ThemeContext } from "../Context/Theme";
I will use Context to compare what is the current theme and
handleToggle
function to change between themes.
function Header({ siteTitle, handleToggle, theme }) {
const themes = useContext(ThemeContext);
return (
<header
style={styles(theme).header}
>
<div
style={styles(theme).navbar}
>
<h1 style={styles(theme).title}>
<Link
to="/"
style={styles(theme).link}
>
{siteTitle}
</Link>
</h1>
<div style={styles(theme).buttonContainer}>
<button type="button" onClick={handleToggle} style={styles(theme).toggle}>
{ theme === themes.light ? '☽' : '☼' }
</button>
</div>
</div>
</header>
);
}
Header.propTypes = {
siteTitle: PropTypes.string,
}
Header.defaultProps = {
siteTitle: ``,
}
export default Header
Wrapping up
The main idea is to know that you can apply this to any site ;)
Top comments (4)
Hi, I used the same way to build my theme, however, the children component inside couldn't get ThemeContext... Do you know how to solve it?
Also look that I only themed the Header component, Main and Footer components need the prop theme or reach themes via useContext
Hi! Could you please show me a snippet of code?
Maybe you have to assign some css classes to the children
Hi, I found the solution. Before, I wrapped ThemeContext inside layout
Then in my page Blog, I coudln't get themeContext
The solution is to create a wrapRootElement in gatsby-browser.js. Then all pages could get ThemeContext's value.