Easy light and dark mode with Catppuccin and TailwindCSS

5 min read

I was looking to spice up my site a little with a coordinated colour theme. I use cattppucin for my terminal theme and I wanted to use the same colours for my site. I found a tailwindcss plugin that pulled in catppuccin colours and made them available in tailwindcss via their common name: “pink”, “blue”, “green”, etc. This was great, but I didn’t want to have to manage two sets of colours for light and dark mode. I discovered a clever trick to make this work using a single className within the layout.tsx file in my NextJS setup, and I wanted to share it here on the off chance that someone else is also a fan of this colour scheme.

The problem

Before this change, there were two places where I had to manage colours. The first was in the tailwindcss.config.js file, in which I could define colours used both in light and dark mode for common elements like links or headers (especially important in the blog posts). These settings look something like this, and could be made separately for the light mode and dark mode (dark mode is defined within invert). Note that here I have to use the raw hex value for the colour.

typography: ({ theme }) => ({
        DEFAULT: {
          css: {
            a: {
              color: '#1e66f5',
              '&:hover': {
                color: `#179299`,
              },
            },
            'h1,h2,h3,h4,h5,h6': {
              color: '#1e66f5',
            },
            code: {
              color: '#fe640b',
            },
          },
        },
        invert: {
          css: {
            a: {
              color: '#a6e3a1',
              '&:hover': {
                color: '#94e2d5',
              },
            },
            'h1,h2,h3,h4,h5,h6': {
              color: '#89dceb',
            },
            code: {
              color: '#fab387',
            },
          },
        },
      }),
    },

However, for the css in various components, there were still several text-[colour] and dark:text-[colour] classes that needed to be managed. Over the entirety of the site, this would amount to a lot of duplication and a lot of places to change if I wanted to change the colour scheme. The tailwindcss plugin allows for the selection of one of the four cattppucin colour themes (latte, frappe, macchiato and mocha) however this would make both light and dark mode the same. Out of those four, latte is a light theme, the other three are dark themes.

The solution

I wanted to use the latte theme for light mode and the mocha theme for dark mode. I also wanted to use the same colour names for both light and dark mode. I discovered that after adding the tailwindcss plugin to the tailwind.config.js file, I could then simply define className = "latte" dark:"mocha" in layout.js and all of the colours would load appropriately when called from various components. Critically, even bg-base switches based on the theme, even though latte and mocha are not in parent elements as described in the documentation.

  // Setting up the cattpuccin tailwindcss plugin (after running npm install @catppuccin/tailwindcss)
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
    require('@catppuccin/tailwindcss'),
  ],
<!-- Defining the className in the body of the html returned by layout.tsx -->
<body className="dark:mocha latte bg-base text-text antialiased"></body>

Now with this setup in the layout.ts file I can define colours once within each component, and they will adapt to the proper colour for light or dark mode. For example, the following is used within the header.tsx component, which is used to define the navigation bar at the top of the page for desktop. Line 7 defines that the link should use the “text” colour when not hovered, and the “teal” colour when hovered. The hex value for “text” is defined by cattppucin and adapts based on whether the site is in latte (#4c4f69) or mocha (#cdd6f4) mode. The background of the site automatically switches to the “base” colour for the latte and mocha themes, ensuring the text contrasts appropriately.

{
  headerNavLinks
    .filter((link) => link.href !== "/")
    .map((link) => (
      <Link
        key={link.title}
        href={link.href}
        className="hidden sm:block font-medium text-text hover:text-teal"
      >
        {link.title}
      </Link>
    ));
}

Colours used in the site

The colours used in this site can be found in the cattpuccin repo README.md. The name that is used for each colour is the same name that is called in the className for the tailwindcss plugin. For example, className="text-pink" will use the pink colour in the latte theme if the site is in light mode, and the pink colour in the mocha theme if the site is in dark mode.

Conclusion

Using the tailwindcss plugin has been fantastic, and overall the colour transition for the entire site took about one evening. I am pretty happy with the result, however there may be some tweaking to do in the future. In particular, I would really like to define a guide to refer to in future development to maintain consistency throughout the site. I would also like to add cattppucin to the code highlighting in the blog posts, and so am hoping to submit a PR to add it to the prismjs repo. As a thanks to the team for such a great colour theme, I have made a small donation to the cattppuccin team and you can do the same if you enjoy the theme as well.