In this post I'll be going over an interesting problem I had to solve for while building out the redesign for Synapse Studios' marketing page. We needed a way to store the colors or the 'theme' for the site and use those colors anywhere, in any component so that they can be edited from one source of truth. If we decide to change the theme, those changes should reflect everywhere the colors are being used on the site.
Before diving into the specifics, I wanted to briefly explain the tools this site is built with.
Taken from their site:
Gatsby is a free and open source framework based on React that helps developers build blazing fast websites and apps
Gatsby allows you to build static sites while writing React. It also allows you to pull data from anywhere and then you use graphQL queries to access that data. The data for the Synapse site is coming from contentful.
Contentul is an "API-based cms" where:
Editors collaborate on content interactively in an easy-to-use editing interface, while developers use the content with the programming language and templating frameworks of their choice.
*This isn't a tutorial on how to link the 2 together, but here's a great starter package that will get you up and running fast.
So how can do we use Gatsby with Contentful to create this hero for example:
Notice we have a headline, a tagline and button text, that's the data being displayed. The data is coming from contentful. In contentful, our corresponding data looks like this:
And this is how we access that data via graphQL queries:
const IndexPage = ({ data }) => {const { headline, tagline, headlineButton } = data.home.edges[0].nodereturn (<div><h1>{headline}</h1><div>{tagline}</div><Button>{headlineButton}</Button></div>)}export const indexPageQuery = graphql`query indexPageQuery {home: allContentfulHomePage {edges {node {idheadlinetaglineheadlineButton}}}}`
In a pretty straightforward way, we grab the data we need in our graphQL query, that data becomes available via the data prop and we simply display it in our JSX.
An important thing to note is how this query is tied to this specific component. As in, if for some reason we needed to re-use this data in a separate component, we'd have to re-write this graphQL query. That isn't so much of an issue in the case of the hero section since it's only going to be displayed on the home page. But what if we need to access data in many separate places? Which brings us around to our theming issue.
Remember we want to use our theme colors anywhere in the site:
We need a way to store the colors or the "theme" for the site in 1 single source of truth and then use those colors anywhere, in any component.
Not only do we want to use them here on the home page:
We also want to use our theme colors in things like callouts and headers and buttons throughout the site:
Keep in mind we totally could grab those colors in a query and then just re-write that query in every component we need them. But that's no way to live our lives.
So how can we write 1 single query to grab our colors and then access that data anywhere?
drum roll please...π₯π₯π₯
import { graphql, useStaticQuery } from 'gatsby'useStaticQuery(graphql`query {allContentfulThemeColors {edges {node {primarysecondarytertiary}}}}`)
Our solution is one that comes baked in with Gatsby π. This useStaticQuery react hook, one that Gatsby provides, takes a GraphQL query and returns your data. Thatβs it.
So running that useStaticQuery function would just return that query object:
allContentfulThemeColors {edges {node {primarysecondarytertiary}}}
To access the contents of this query in a tidy way we can create a custom hook. We'll call it useThemeColors
:
import { graphql, useStaticQuery } from 'gatsby'const useThemeColors = () => {const { allContentfulThemeColors } = useStaticQuery(graphql`query {allContentfulThemeColors {edges {node {primarysecondarytertiary}}}}`)return allContentfulThemeColors.edges[0].node}export default useThemeColors
So here we have a custom hook useThemeColors
which composes that useStatic
query hook.
We're de-structuring allContentfulThemeColors
(which comes from the contentful API) from the query object.
We're sort of grabbing that value off of the query object and then we're just returning the data we need
by drilling down further into the object. The result of this query is just going to be the contents of that node object,
which are the theme colors themselves.
Now every time we need our colors, we can just use this custom hook:
const IndexPage = ({ data }) => {const { headline, tagline, headlineButton } = data.home.edges[0].node// our useThemeColors Hookconst colors = useThemeColors()return (<div><h1>{headline}</h1><div>{tagline}</div><Button color={colors.primary}>{headlineButton}</Button></div>)}export const indexPageQuery = graphql`query indexPageQuery {home: allContentfulHomePage {edges {node {idheadlineheadlineButtontagline}}}}`
Here we are assigning the constant colors
to the return value of our useThemeColors
hook then passing it to our button component's color prop.
So now that Button's color is reading from the value stored in contentful.
We can also use the colors in our callout component as the backgroundColor
.
This time we're de-structuring the secondary color off of that node object and just passing it to the style prop of our surrounding div
.
import React from 'react'import useThemeColors from '../../hooks/useThemeColors'const Callout = ({ heading, buttonText, description }) => {// our useThemeColors Hookconst { secondary } = useThemeColors()return (<divstyle={{backgroundColor: secondary,}}><h1>{heading}</h1><div>{description}</div><Button>{buttonText}</Button></div>)}export default Callout
And there you have it, the problem we had of editing our theme colors from one source of truth is solved!
And if we look at contentful, we just have this theme colors content model where our colors are stored, this is our source of truth.
All of the colors we are using in the site are derived from this single source. If we change the primary color for instance from green to blue, that will be reflected everywhere that primary color is being used on the site, which is many places.
Thanks for reading!