M. Layton
M. Layton

Reputation: 613

Synchronize TailwindCSS theme with NextUI color tokens - NextJS

I'm creating a NextJS application that uses TailwindCSS for theming and styles but I'm also using NextUI for a component library.

In my tailwind.config.ts file, I have defined some custom colors.

import type { Config } from "tailwindcss";
import { nextui } from "@nextui-org/react";

const config: Config = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    colors: {
      transparent: 'transparent',
      red: {
        25: '#fcf7f8',
        50: '#f9eff1',
        100: '#f6dee2',
        200: '#f1b9c1',
        // ...
      },
      orange: {
        25: '#fcf9f7',
        50: '#f9f1ed',
        100: '#f5e3d9',
        200: '#efc5af',
        // ...
      },
      yellow: {
        25: '#fcfbf7',
        50: '#f8f5ed',
        100: '#f3edd9',
        200: '#ecddae',
        // ...
      },
      green: {
        25: '#f7fafa',
        50: '#eaf1f0',
        100: '#d4e5e2',
        200: '#a7cdc6',
        // ...
      },
      blue: {
        25: '#f7fafc',
        50: '#ebf1f6',
        100: '#d6e3ef',
        200: '#a9c7e3',
        // ...
        500: '#0a73d7'
        // ...
      },
      purple: {
        25: '#f8f7fa',
        50: '#eeedf2',
        100: '#e5dced',
        200: '#cbb5de',
        // ...
      },
      gray: {
        25: '#f8f8f9',
        50: '#f1f2f2',
        100: '#e3e5e7',
        200: '#c7cbcf',
        // ...
      },
      black: '#000000',
      white: '#ffffff',
    },
  },
  darkMode: 'class',
  plugins: [nextui()],
};

export default config;

However, when I try to use a color token such as primary in a NextUI component, it's the color that appears on the page is NextUI's default theming color values. For example:

import { Tabs } from "@nextui-org/react";

export default function TabsMenu() {
    return (
        <Tabs variant="solid" color="primary" fullWidth={true}>
            // ...
        </Tabs>
    )
}

In the browser, the active tab shows up as rgb(0, 111, 238)/#006fee, which is NextUI's color setting for 'primary', but I want it to be rgb(10, 115, 215)/#0a73d7 from my Tailwind theme.

According to NextUI's documentation on creating a theme I could manually duplicate all my color objects into the NextUI plugin, but that's rather ineffective since each color definition has 12 individual shades.

There should be a way for me to programmatically tell NextUI that it should use custom colors set in the Tailwind theme. How can I accomplish this?

Upvotes: 0

Views: 175

Answers (1)

M. Layton
M. Layton

Reputation: 613

The best way I found to do this required a bit of digging between the NextUI and Tailwind docs.

Note: step numbers are referenced in comments in the tailwind.config.ts file below

  1. Import resolveConfig function from Tailwind.
  2. Remove the plugins key from the config object.
  3. Use the resolveConfig function you imported to get the full Tailwind config. Pass the config object to it.
  4. Get a reference to the custom Tailwind colors as a variable. This would work for custom properties you've set on your Tailwind theme, not just colors.
  5. Add the plugins key back to the config object, and set NextUI's color tokens to specific custom colors using the colors variable you set in #4.
    • The reason I removed plugins from the config object and added it back later is to prevent possible circular dependencies between the resolveConfig function and the nextui() plugin function.
import type { Config } from "tailwindcss";
import { nextui } from "@nextui-org/react";
import resolveConfig from 'tailwindcss/resolveConfig'; // (1)

const config: Config = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    colors: {
      transparent: 'transparent',
      red: { ... },
      orange: { ... },
      yellow: { ... },
      green: { ... },
      blue: { ... },
      purple: { ... },
      gray: { ... },
      black: '#000000',
      white: '#ffffff',
    },
  },
  darkMode: 'class',
  // (2)
};

const fullConfig = resolveConfig(config); // (3)

const { colors } = fullConfig.theme; // (4)

// (5)
config.plugins = [
  nextui({
    themes: {
      'my-theme': {
        extend: 'light',
        colors: {
          primary: colors.blue[500],
          secondary: colors.purple[500],
          success: colors.green[500],
          warning: colors.orange[500],
          danger: colors.red[500],
          default: colors.gray[500],
        }
      }
    }
  })
];

export default config;
  1. Set your custom theme name (here, my-theme) as a class on wrapping element of your app. Which element this is depends on your project structure; mine looks like this:

_app.tsx:

import type { AppProps } from "next/app";
import Layout from '@/components/layout';
import "@/styles/globals.css";

export default function App({ Component, pageProps }: AppProps) {
  return (
      <Layout>
          <Component {...pageProps} />
      </Layout>
  );
}

layout.tsx:

import {Providers} from "@/components/providers";

export default function Layout({children}) {
    return (
        <>
            <Providers>
                <main className="my-theme"> // (6)
                    {children}
                </main>
            </Providers>
        </>
    )
}

Now, as long as a NextUI component is used inside the element that has the my-theme class, any color tokens that have been set to custom Tailwind colors will function properly.

In my original example,

import { Tabs } from "@nextui-org/react";

export default function TabsMenu() {
    return (
        <Tabs variant="solid" color="primary" fullWidth={true}>
            // ...
        </Tabs>
    )
}

the active tab is now rendered with my custom blue[500] color as I would expect: rgb(10, 115, 215)/#0a73d7.

Upvotes: 1

Related Questions