Package Exports
- gatsby-styled-components-dark-mode
- gatsby-styled-components-dark-mode/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (gatsby-styled-components-dark-mode) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Gatsby Styled-Components Dark Mode
About
A Gatsby plugin that handles injecting a dark and light theme using styled components.
- Exposes React hook for toggling between dark and light theme
- Allows for using the system's theme
- Automatically handles content flash when refreshing the page
- Persists the state of users' dark options in their browsers
Installation
Install the package
$ npm i gatsby-styled-components-dark-mode
or
$ yarn add gatsby-styled-components-dark-mode
Add the plugin to your gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-styled-components-dark-mode`,
options: {
light: { mainColor: "brandyRose" },
dark: { mainColor: "manatee" },
},
},
],
};
Requirements
You must have the following installed in your gatsby project:
- styled-components
- Use this plugin for gatsby integration
How to Use
The plugin expects two options in your gatsby-config.js
file:
light: object;
dark: object;
These objects are defined by you in your project, so you have full control over your styling. Everything else is handled for you.
in a presumed src/theme.js
export const darkTheme = {
mainColor: "topaz",
secondaryColor: "ruby",
};
export const lightTheme = {
mainColor: "pampas",
secondaryColor: "ruby",
buttonColor: "shadyLady",
// Note both themes don't necessary have to match (you'll probably want them to though)
};
in gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-styled-components-dark-mode`,
options: {
light: require(`${__dirname}/src/theme.js`).lightTheme,
dark: require(`${__dirname}/src/theme.js`).darkTheme,
},
},
],
};
Multiple files
You may want to keep your themes separate, thus you could do the following:
in src/darkTheme.js
const darkTheme = {
mainColor: "topaz",
secondaryColor: "ruby",
};
export default darkTheme;
in src/lightTheme.js
const lightTheme = {
mainColor: "pampas",
buttonColor: "shadyLady",
};
export default lightTheme;
in gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-styled-components-dark-mode`,
options: {
light: require(`${__dirname}/src/lightTheme.js`),
dark: require(`${__dirname}/src/darkTheme.js`),
},
},
],
};
Accessing the styles
We can now utilize the power of styled-components. Any component wrapped in a styled
or css
has access to your theme.
Inside of styled's template string props
const MyLightOrDarkStyledComponent = styled.div`
background-color: ${(props) => props.theme.mainColor};
color: ${(props) => props.theme.secondaryColor};
`;
Using styled's withTheme
higher-order component
const MyThemedUpComponent = withTheme((props) => (
<div styles={{ color: props.theme.mainColor }}>Hello world</div>
));
Using styled's ThemeContext
to use as a hook
import { ThemeContext } from "styled-components"
const MyThemedUpComponent = () => {
const theme = useContext(ThemeContext)
return <div styles={{ color: theme.mainColor }}>Hello world</div>
};
In theme
you'll also have access to isDark
So you could do conditionally styling inside your styled components if you wanted to
const MyLightOrDarkStyledComponent = styled.div`
color: ${(props) =>
props.theme.isDark ? props.theme.darkColor : props.theme.lightColor};
`;
Toggling the theme
Somewhere in your app, you may want to provide functionality to change the theme from one theme to the other, or to respect the current system's theme setting. Note that the plugin will use the user's system theme out of the box. If this is your use case, you don't need to write any more code.
useStyledDarkMode
hook
prop | type | default | description |
---|---|---|---|
isDark | boolean? | undefined |
State that describes if your app is in dark mode or not. Notice this can be undefined since we don't know isDark until the provider can parse the initial styles after the first paint. |
toggleDark | (value?: boolean) => void | n/a | Function that toggles dark/light mode. |
themeSetting | ThemeSetting | ThemeSetting.SYSTEM |
The current theme setting - either ThemeSetting.LIGHT , ThemeSetting.DARK or ThemeSetting.SYSTEM |
changeThemeSetting | (setting: ThemeSetting) => void | n/a | Function that allows setting dark mode, light mode or system-setting mode |
Example - light/dark mode toggle
in a presumed src/component/layout.tsx
import { useStyledDarkMode } from "gatsby-styled-components-dark-mode";
export const Layout = () => {
const { isDark, toggleDark } = useStyledDarkMode();
return (
<div>
<label>
<input
type="checkbox"
onChange={() => toggleDark()}
checked={!!isDark}
/>
Dark mode
</label>
</div>
);
};
Example - light/dark/system mode toggle
in a presumed src/component/layout.tsx
import {
useStyledDarkMode,
ThemeSetting,
} from "gatsby-styled-components-dark-mode";
export const Layout = () => {
const { changeThemeSetting, themeSetting } = useStyledDarkMode();
const onThemeSettingChanged = (ev) => changeThemeSetting(ev.target.value);
return (
<div>
<label>
<input
type="radio"
value={ThemeSetting.LIGHT}
onChange={onThemeSettingChanged}
checked={themeSetting == ThemeSetting.LIGHT}
/>
Light mode
</label>
<br />
<label>
<input
type="radio"
value={ThemeSetting.DARK}
onChange={onThemeSettingChanged}
checked={themeSetting == ThemeSetting.DARK}
/>
Dark mode
</label>
<br />
<label>
<input
type="radio"
value={ThemeSetting.SYSTEM}
onChange={onThemeSettingChanged}
checked={themeSetting == ThemeSetting.SYSTEM}
/>
Use system setting
</label>
</div>
);
};
Global Styling
Until further notice, global styling requires a tad bit more overhead due to what styled-components' api offers.
Keep in mind, this is not required, but is probably needed for theming.
Your theme already knows if it's dark/light so all you have to do is pass the theme object to a createGlobalStyle
function
Check the docs for more info
Usage
in a presumed src/theme.js
export const GlobalStyle = createGlobalStyle`
html, body {
height: 100%;
}
body {
background-color: rgb(${(props) => props.theme.global.bg});
color: rgb(${(props) => props.theme.global.color});
}
a {
color: rgb(${(props) => props.theme.global.link});
}
a:hover {
color: rgb(${(props) => props.theme.global.linkHover});
}
blockquote {
color: inherit;
border-left-color: inherit;
}
`;
You'll probably want to render this in your root - layout.jsx
is a good place to start
in src/layout.jsx
import { ThemeContext } from "styled-components";
import { GlobalStyle } from "./theme";
export const Layout = () => {
const theme = useContext(ThemeContext);
return (
<div>
<GlobalStyle theme={theme} />
// content
</div>
);
};
Full Example
gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-styled-components-dark-mode`,
options: {
light: require(`${__dirname}/src/theme.js`).lightTheme,
dark: require(`${__dirname}/src/theme.js`).darkTheme,
},
},
],
};
src/theme.js
import { createGlobalStyle } from "styled-components";
// I'm using rgb numbers here for easily using rgba styling throughout the app
// You can put hexcodes, names, or any other definitions that fits the context of your app
const colorPalette = {
lightShades: "242, 242, 241",
lightAccent: "139, 142, 149",
mainBrand: "140, 100, 88",
darkAccent: "133, 129, 137",
darkShades: "32, 30, 32",
success: "95, 153, 81",
warning: "221, 136, 25",
error: "244, 67, 54",
};
const baseTheme = {
actions: {
error: colorPalette.error,
info: colorPalette.darkShades,
primary: colorPalette.mainBrand,
success: colorPalette.success,
warning: colorPalette.warning,
},
palette: {
darkAccent: colorPalette.darkAccent,
darkShades: colorPalette.darkShades,
lightAccent: colorPalette.lightAccent,
lightShades: colorPalette.lightShades,
mainBrand: colorPalette.mainBrand,
},
};
export const darkTheme = {
...baseTheme,
global: {
bg: colorPalette.darkShades,
color: colorPalette.lightShades,
link: colorPalette.mainBrand,
linkHover: colorPalette.lightAccent,
},
};
export const lightTheme = {
...baseTheme,
global: {
bg: colorPalette.lightShades,
color: colorPalette.darkShades,
link: colorPalette.mainBrand,
linkHover: colorPalette.darkAccent,
},
};
export const GlobalStyle = createGlobalStyle`
html, body {
height: 100%;
}
body {
background-color: rgb(${(props) => props.theme.global.bg});
color: rgb(${(props) => props.theme.global.color});
transition: background 0.2s ease-out;
}
a {
color: rgb(${(props) => props.theme.global.link});
}
a:hover {
color: rgb(${(props) => props.theme.global.linkHover});
}
`;
src/layout.js
import { useStyledDarkMode } from "gatsby-styled-components-dark-mode";
import React, { useContext } from "react";
import styled, { ThemeContext } from "styled-components";
import { GlobalStyle } from "./theme";
const MainHeading = styled.h2`
color: rgb(${(props) => props.theme.palette.mainBrand});
`;
export const Layout = (props) => {
const theme = useContext(ThemeContext);
const { isDark, toggleDark } = useStyledDarkMode();
return (
<div>
<GlobalStyle theme={theme} />
<header>
<MainHeading>
<a href={"#"}>Gatsby Dark Theme</a>
</MainHeading>
<div>
<label>
<input
type="checkbox"
onChange={() => toggleDark()}
checked={!!isDark}
/>{" "}
Dark mode
</label>
</div>
</header>
<main>{props.children}</main>
</div>
);
};
Typescript Example
https://github.com/gperl27/website
Contributing
This project uses conventional commits and leverages semantic release for versioning and distribution. It uses these tools out of the box.
Distribution channels
@latest
- Reflects current code in the
master
branch
- Reflects current code in the
@next
- Reflects current code in the
next
branch
- Reflects current code in the
Roadmap
typescripthot-reload
Currently, when changing a theme, the app will recompile but will not hot reload. You have to do a full page-refresh to see your theming changes.
- improved global styling
Ideally, I would like to pass { global: "path/to/globalStyles }
where these styles can get interpolated/compiled into something that's friendly enough for styled-components' createGlobalStyle
api.
live demo
default theme setup or scaffolding if people want it