Styled Components 101 ๐Ÿ’… Lecture 2: Creating a theme + Light/Dark theme toggler example โ˜€๏ธ๐ŸŒ™

Styled Components 101 ๐Ÿ’… Lecture 2: Creating a theme + Light/Dark theme toggler example โ˜€๏ธ๐ŸŒ™

ยท

9 min read

Hello, everybody! ๐Ÿ‘‹ And welcome to the 2nd lecture of the Styled Components 101 series ๐Ÿ™Œ

In this lecture, we'll be covering the creation of a custom theme with Styled Components.

If you're new to Styled Components and this lecture is the first one you run into, I suggest taking a look at the previous lecture first, where we covered some basic concepts and examples of Styled Components.

With all this said, let's move on to today's topic ๐Ÿ‘‡

Creating the theme file ๐Ÿ”ฎ

First things first. Before start generating components and other component-related stuff, what we need to do is create the file that will host our app's theme.

This theme file will be just a regular JavaScript file with js extension that will contain an object called theme with properties and values that we'll be using to style our components.

As easy as that ๐Ÿ’โ€โ™‚๏ธ

Let's have a look at how this file looks like:

theme.js

const theme = {
   colors: {
      plum: "#52314b",
      rose: "#c3537d",
      dryrose: "#dd7f9a",
      primrose: "#e8babf",
      white: "#fff"
   }
}

export default theme;

For now, we'll be working only with colors, but every property/value you can imagine that is used for styling will also be set in this file: values regarding fonts, padding, margin, etc. In short, every value that you'd set up in a CSS file.

Don't forget to export your theme object. Otherwise, there's no way for it to be found by the app ๐Ÿ‘

And that's all for the theme file. We're now set to head on to the next step ๐Ÿ˜ผ

Making the theme file reachable by the app ๐Ÿ•ต๏ธ

In order for the theme file to be able to be accessed, we need to provide it to the app. To do this, we'll import the component ThemeProvider from styled-components and will wrap our app with it.

App.js

import { ThemeProvider } from "styled-components";
import theme from "./theme";

const App = () => {
   return(
      <ThemeProvider theme={theme}>
         //your code here  
      </ThemeProvider>
   )
}
export default App;

Now, our theme is recognized by the app and can be accessed and used from anywhere throughout it.

Using the theme inside a styled component ๐Ÿ’…

Retrieving theme values from within a styled component is a really easy process, since the theme is implicit to the component props.

Let's see how to style a styled component making use of a couple values from our custom theme:

BasicThemedButton.js

import styled from "styled-components";

export default styled.button`
   background-color: ${props => props.theme.colors.primrose};
   color: ${props => props.theme.colors.white};
   font-size: 1.25rem;
   border: none;
   border-radius: 5px;
   padding: 10px;
`

Our custom theme is implicitly passed-in as a prop and it's easily accessed because the component is also wrapped by the provider:

App.js

import { ThemeProvider } from "styled-components";
import theme from "./theme";
import BasicThemedButton from "./components/BasicThemedButton";

const App = () => {
   return(
      <ThemeProvider theme={theme}>
         <BasicThemedButton>I am a basic themed button! ๐Ÿค—</BasicThemedButton>  
      </ThemeProvider>
   )
}
export default App;

b1.png

Using the theme inside a custom React component โš›๏ธ

To use our theme inside a custom React component, we need to write a little bit more code, but not a big deal at all.

ThemedButton.js โ‰ก React component

import React, { useContext } from "react";
import StyledButton from "./StyledButton";
import { ThemeContext } from "styled-components";

const ThemedButton = () => {
   const theme = useContext(ThemeContext);
   return(
      <StyledButton bgColor={theme.colors.plum} color={theme.colors.white}>I am a themed button ๐Ÿ˜œ</StyledButton>
   )
}

export default ThemedButton;

StyledButton.js โ‰ก Styled component

import styled from "styled-components";

export default styled.button`
   background-color: ${props => props.bgColor};
   color: ${props => props.color};
   padding: 10px;
   border: none;
   border-radius: 5px;
   font-size: 1.25rem;
`

This time, the values from the theme are passed to the styled component via props, which have been previously retrieved in the React component by making use of the useContext hook and the ThemeContext component from styled-components.

Note that we'll be calling our React component as we regularly do:

App.js

import { ThemeProvider } from "styled-components";
import theme from "./theme";
import ThemedButton from "./components/ThemedButton";

const App = () => {
   return(
      <ThemeProvider theme={theme}>
         <ThemedButton />
      </ThemeProvider>
   )
}
export default App;

b2.png

The theme prop ๐Ÿค–

The theme prop is used to pass in an inline object that contains the specification for the theme to a styled component.

App.js

import { ThemeProvider } from "styled-components";
import StyledButton from "./components/StyledButton"; 
import theme from "./theme";

const App = () => {
   return(
      <ThemeProvider theme={theme}>
         <StyledButton theme={{bg: theme.colors.primrose, color: theme.colors.white}}>I am a button with a theme prop! ๐Ÿ˜Ž</StyledButton>
      </ThemeProvider>
   )
}
export default App;

StyledButton.js

import styled from "styled-components";

export default styled.button`
   background-color: ${props => props.theme.bg};
   color: ${props => props.theme.color};
   padding: 10px;
   border: none;
   border-radius: 5px;
`

b3.png

This method can be useful if we don't have many properties, but, when we have a robust application with several properties and values for styling, this method becomes difficult to maintain and tricky to deal with โŒ

Using JavaScript objects to write our styles rather than CSS ๐Ÿคนโ€โ™€๏ธ

Did you know that is also possible to write custom styles using JS instead of CSS in Styled Components? And it's easier than you may think.

Let's have a look at the following snippet:

App.js

import { ThemeProvider } from "styled-components";
import JSStyledButton from "./components/JSStyledButton";
import theme from "./theme";

const App = () => {
   return(
      <ThemeProvider theme={theme}>
         <JSStyledButton bgColor={theme.colors.plum} color={theme.colors.white}>I am a JS Styled Button ๐Ÿค </JSStyledButton>
      </ThemeProvider>
   )
}
export default App;

JSStyledButton.js

import styled from "styled-components";

export default styled.button(props => ({
   backgroundColor: props.bgColor,
   fontSize: "1.5rem",
   color: props.color
}));

b4.png

Notice that property names are written in camelcase notation, as we're using a JavaScript object, but it's also accepted to write them within double quotes, like this:

import styled from "styled-components";

export default styled.button(props => ({
   "background-color": props.bgColor,
   "font-size": "1.5rem",
   color: props.color
}));

Also note that the used method to specify the styles is similar to the one we use when extending styles from a supercomponent (Lecture 1 - Inheriting styles from another component section):

...
export default styled.button(...);
...

And now... ๐Ÿ‘‘ The jewel in the crown: Let's create a Light/Dark theme toggler with Styled Components

Theory is cool but, but let's get our hands dirty and let's create something interesting using themes ๐Ÿ‘

Excuse me, but... what exactly is a theme toggler? ๐Ÿค”

We can say that a theme toggler is a system that allows to switch between a light and a dark theme with just one click by using a button or an emoji. Cool, right?

In this tutorial, we'll implement a styled button that will perform this task really fast and efficiently.

And now, let's get it started ๐Ÿ‘‰

The components and files ๐ŸŽฌ

These are the components and files that will make up our app:

Button.js: the button that will switch between themes.

GlobalStyles.js: a component that will inject global styles into the app.

ThemeToggler.js: the component that will receive the theme as a prop, which, in turn, will be passed in to the Button component.

useDarkTheme.js: a custom hook that will contain the business logic.

App.js: the main app. It will wrap everything up with the theme provider and will include the global styles.

themes.js: the file that will host our themes (we already know how to do this at this point ๐Ÿ˜บ).

Let's first create our themes file ๐Ÿ“

themes.js

export const lightTheme = {
    background: '#fff',
    color: '#1d1f28',
    buttonBg: '#c5718d'
}

export const darkTheme = {
    background: '#1d1f28',
    color: '#fafafa',
    buttonBg: '#515d90'
}

Not a lot to remark here: we have defined both themes with their respective style properties. Don't forget to export them both ๐Ÿ‘

Now let's go with the switch button ๐Ÿ”ต

Button.js

import styled from "styled-components";

export default styled.button`
   font-family: "Monaco", monospace;
   cursor: pointer;
   border: none;
   background-color: ${({ theme }) => theme.buttonBg};
   color: #fff;
   padding: 10px;
   border-radius: 5px;
   font-size: 1.5rem;
`

A regular styled button. You can style it as you like best.

Note that the value for background-color will be set depending on the selected theme. The rest is up to you ๐Ÿ˜‡

Time for global styles ๐Ÿ˜ผ

GlobalStyles.js

import { createGlobalStyle } from "styled-components";

export const GlobalStyles = createGlobalStyle`
   body {
     font-family: "Monaco", monospace;
     background: ${({ theme }) => theme.background};
     color: ${({ theme }) => theme.color};
     transition: all 0.50s linear; 
  }
`

GlobalStyles defines the global styles for our app. They will be injected into our app by in App.js.

Note that for this purpose, we'll import the createGlobalStyle helper from styled-components, which will create a new styled component that will handle global styles.

Regarding the properties, background and text colors will be retrieved from the theme. We'll also add a transition property to make the switch effect smoother.

Creating the theme toggler ๐Ÿ’

ThemeToggler.js

import React from 'react';
import Button from "./Button";

const ThemeToggler = (props) => {
   const { themeToggler } = props;
   return (
      <Button onClick={themeToggler}>Switch Theme โ˜€๏ธ ๐ŸŒ™</Button>
   );
};

export default ThemeToggler;

ThemeToggler renders the Button component and provides it with the style properties that correspond to the passed-in theme.

useDarkTheme.js

import { useState } from 'react';

const THEMES = {
   LIGHT: "light",
   DARK: "dark"
}

const useDarkTheme = () => {
    const [theme, setTheme] = useState(THEMES.LIGHT);
    const themeToggler = () => {
        theme === THEMES.LIGHT ? setTheme(THEMES.DARK) : setTheme(THEMES.LIGHT);
    };
    return [theme, themeToggler];
};

export default useDarkTheme;

useDarkTheme is a custom hook that contains the logic for our toggler. We're using a hook to abstract our app as much as possible.

The switching procedure is as easy as follows: if the passed-in theme is light, the dark theme will be set up, and vice versa.

Building the main app ๐Ÿ˜ˆ

App.js

import React from "react";
import { ThemeProvider } from "styled-components";
import { GlobalStyles } from "./components/GlobalStyles";
import { lightTheme, darkTheme } from "./themes";
import ThemeToggler from "./components/ThemeToggler";
import useDarkTheme from "./hooks/useDarkTheme";

const App = () => {
   const [theme, themeToggler] = 
   useDarkTheme();
   const selectedTheme = theme === 'light' ? lightTheme : darkTheme;

   return (
      <ThemeProvider theme={selectedTheme}>
         <>
            <GlobalStyles />
            <ThemeToggler themeToggler={themeToggler} />
            <p>Trying out light and dark themes! ๐Ÿ˜</p>
         </>
      </ThemeProvider>
   )
}
export default App;

First, we're calling the useDarkTheme hook, that will handle the switch logic.

Then, we also need to have a function selectedTheme, that decides which theme is going to be used.

And now, what's only left is to wrap up ThemeToggler with ThemeProvider and include GlobalStyles.

The result โ˜€๏ธ โžก๏ธ ๐ŸŒ™

switch.gif


And this is all for the second Styled Components 101 lecture!

Stay tuned to learn more about Styled Component in future episodes of the series.

A big thanks for reading ๐Ÿค— and don't hesitate to reach out to me if you any questions or doubts about today's lecture.

I hope you found this article useful and I see you all in the next ๐Ÿ‘‹


๐Ÿ‘‰ You can also check out the related slides for this article on Instagram ๐Ÿ‘ˆ


๐ŸŽ‰ Don't forget to follow @underscorecode on Instagram and Twitter for more daily webdev content ๐Ÿ–ฅ๐Ÿ–ค


ย