DEV Community

Cover image for Multiple themes with one object
shrey vijayvargiya
shrey vijayvargiya

Posted on

Multiple themes with one object

Why Toggle among multiple themes from one source of truth

Under the Hood

I am sure most of you have seen multiple themes like dark and white on the website.

Ever wonder how these themes are added, well nowadays, React.js supported by various third-party UI libraries provides dark/white theme toggle methods.

For example, Tailwind CSS checks the dark theme enabled in the configuration and if Yes, it opens the classname with the prefix dark to apply the dark theme to the corresponding element.

<div className="text-white dark:text-gray-900" />

For the above code, all we need is to enable the Tailwind config with a dark theme, and this is done by setting the key dark as true because it accepts a boolean value.

// tailwind.config.js file in the root directory
module.exports = () => {
  darkMode: true,
};
Enter fullscreen mode Exit fullscreen mode

If you are using Material UI, Mantine, or Chakra UI, the process is almost the same; we directly add the theme colour, and it will take care of under the hood.

But that’s not what we are here for. In this case, how can we add multiple themes?

Adding Multiple Themes

Well, I am a fan of scalable code and reusable code. I prefer writing automated code instead of manual if-else loops.

So I create a single source of truth called themeMap object that contains all the themes I want to add to the React.js application.

const themeMap = {
  delhi: "bg-gray-50",
  london: "bg-blue-50",
  sydney: "bg-indigo-50",
  // ....
};
Enter fullscreen mode Exit fullscreen mode

This will become my theme object; you can extend this object for handling other UI components styling.

For example, if you want to add a grey shade of value 200 as the text colour, how you will do this using the themeMap object?

This can be the DSA-related question, of how to extend this theme object to cover multiple shades of each theme.

Why I am concerned about adding other shades of each theme because we do need shades of grey, sky, indigo, or blue if the active theme is the respective one.

Extending Themes

One can use a direct theme name or colour name instead of the background colour class.

const themeMap = {
  delhi: "gray",
  london: "blue",
  sydney: "indigo",
  // .....
}
Enter fullscreen mode Exit fullscreen mode

In this way, we can simply use other shades of each theme.

Another way is to extend this theme object itself.

const themeMap = {
 delhi: {
   current: "bg-gray-50",
   next: "bg-gray-100"
 },
 london: {
  current: "bg-blue-50",
  next: "bg-blue-100"
 }
}
Enter fullscreen mode Exit fullscreen mode

Can you guess which Data structure is used in the above code?

Hint: Something related to a list.

The themeMap object uses a kind of Linked List, where the node has the current value and pointer pointing to the next node.

Another good way is to use primary and secondary values just like Tailwind CSS did.

const themeMap = {
 delhi: { ...colors.gray },
 london: { ...colors.blue },
 sydney: { ...colors.indigo }
}
Enter fullscreen mode Exit fullscreen mode

This also looks good providing all kinds of colors and shades for each theme.

Toggling Theme

It’s pure Math; we will store the active theme in the useState and keep on updating the theme and setting it to the state.

const [active, setActive] = useState("jaipur");

let activeIndex = 0;

const toggleActiveTheme = (active) => {
  return Object.keys(themeMap)[activeIndex + 1];
}

// haven't handled the edge case such as activeIndex <=0 and so on
Enter fullscreen mode Exit fullscreen mode

In this above code, we are simply increasing the active index value by 1 from the previous value and setting the theme in the state object.

Now one can simply use the active value and map it to the themeMap object to grab the shades of colors.

const activeShades = themeMap(active);

const activeShade = themeMap(active).current;

const activeShades = themeMap(active)[50] || themeMap(active)[100];
Enter fullscreen mode Exit fullscreen mode

In whatever fashion the themeMap object is designed, we can grab the active color shades from the same using the above methods.

Now the last part is to pass the object to the UI components and use the color shades.

Wow, this looks good and is easy to manage because our entire theme comes from a single source of truth which is themeMap an object.

We can further store the active theme in the cookies or local storage to avoid falling back to the default theme in the initial rendering.

Redux can also be used to store the active theme over here in the app.

Extending Color Values

This is also not tough; instead of using Tailwind CSS colour pallets, one can easily replace the colour shade with his/her values.

Simply go to the themeMap object and add different shades or values for any theme you want, for example, you can add off blue for the jaipur theme and similar can be done for other themes.

const themeMap = {
  jaipur: "#f1f1f1",
  london: colors.blue,
  sydney: colors.indigo
};
Enter fullscreen mode Exit fullscreen mode

I prefer saying this themeMap object is highly customizable and extensible. Since it’s an object it will find all the theme shades in O(1) time saving algorithm time in calculation.

Backend in Frontend

I love this part, have you ever thought about how the admin pages or webflow, framer, and wordpress support multiple themes?

They simply store and pass the themeMap object from the backend, stored in the database.

This is not a rock-solid solution but it gives the power to change the theme automatically from a single dashboard.

I’ve talked about this a lot, how the admin page changes the entire website UI and layout and theme and that can be done using one themeMap object stored in the database.

// imagine `themeMap` object coming from the backend store in the database

// frontend fetch the themes and map them accordingly

// backend provide API to return `themeMap` object

// Admin page can use the same `themeMap` and change the active theme to any new values
Enter fullscreen mode Exit fullscreen mode

Theme Toggling in Production

How Amazon and Myntra change their website theme during New Year, Diwali, and other festivals.

Most of the companies simply use the single source of truth that traverses to the bottom-most children of the DOM tree.

In this way, manually handling the theme for each child of the DOM tree in the front end is avoided.


            themeMap
          |          |
First Parent   Second Parent
 |       |          |       |
child1   child2    child3  child4

Enter fullscreen mode Exit fullscreen mode

The themeMap object value is accessible by each of the children of the DOM tree.

Can you guess another data structure we are using in the above code?

It’s Tree, easy to guess but more can be learned such as Binary Tree etc.

Conclusion

While adding themes, we have covered a little bit of data structure such as

First, we structure the theme object into a kind of hash data structure

Then we use Linked List also for each theme

Lastly, we store the themeMap object in the database so another kind of data structure is used here

In the end, we simply pass and provide access to the themeMap object to each of the DOM tree children

Within one single example, we have and can learn so many things about DSA, objects, maps, and so on. It looks like a small project but teaches you a lot of programming stuff.

A few other concepts to learn over here are as follows

  • Intersection Observer API provided by the window object
  • Scroll into the view method provided by useRef of react to scroll the element in the view (I was using this in the above demo project)
  • CSS animations such as :has operator, transition and so on.

Project Demo 👇

https://tailwind-css-theme-toggle-demo.vercel.app/

That’s it for today, see you in the next one.

Feel free to subscribe to my FREE newsletter for more such stories. I’ll write directly to your INBOX once a week.

Subscribe to my FREE newsletter

Shrey

iHateReading

Top comments (0)