Search code examples
reactjsnext.jstailwind-csstoastdarkmode

Dark theme on toast doesn't work without this line, why?


I recently implemented a dark theme on my website and noticed that the toast messages were not being displayed properly in dark mode. After some investigation, I found a line of code that needed to be added for the dark theme to work correctly with toast messages.I think the problem must be coming from TailwindCSS.

Here's the line of code that solved the issue:

  <ToastContext.Provider value={value}>
            {children}
            {/* why without this line dark theme on toast won't work */}
            <span className='hidden error_dark warning_dark info_dark success_dark' />
            {toastQueue.map(({ id, props }) => {
                const { severity, ...rest } = props;
                const style = `${severity}${theme === 'dark' ? '_dark' : ''}`;
                return (
                    <div key={id} className={`${style}`}>
                        <Toast {...rest} />
                    </div>
                );
            })}
        </ToastContext.Provider>

full source of this file:ToastContext.tsx

Tailwind config file: tailwind.config.js

demo of toast: demo

I have found a solution to an issue where toast messages were not displaying properly in dark mode after implementing a dark theme on my website. I added a line of code that includes a hidden span element with specific class names, which allowed the dark theme to work correctly with toast messages. It is possible that the problem was related to TailwindCSS and its configuration. I would appreciate any better solutions if anyone can find them."


Solution

  • As per the documentation:

    The most important implication of how Tailwind extracts class names is that it will only find classes that exist as complete unbroken strings in your source files.

    If you use string interpolation or concatenate partial class names together, Tailwind will not find them and therefore will not generate the corresponding CSS:

    Don’t construct class names dynamically

    <div class="text-{{ error ? 'red' : 'green' }}-600"></div>
    

    In the example above, the strings text-red-600 and text-green-600 do not exist, so Tailwind will not generate those classes. Instead, make sure any class names you’re using exist in full:

    Always use complete class names

    <div class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>
    

    You could:

    • Use some sort of if statements with full strings:

      {toastQueue.map(({ id, props }) => {
        const { severity, ...rest } = props;
        let style;
        if (severity == 'success') {
          style = theme === 'dark' ? 'success_dark' : 'success';
        } elseif (severity == 'info') {
          style = theme === 'dark' ? 'info_dark' : 'info';
        } elseif (severity == 'warning') {
          style = theme === 'dark' ? 'warning_dark' : 'warning';
        } elseif (severity == 'error') {
          style = theme === 'dark' ? 'error_dark' : 'error';
        }
        return (
          <div key={id} className={style}>
            <Toast {...rest} />
          </div>
        );
      })}
      
    • Have a dictionary that maps severity and theme to the appropriate class name:

      const DICTIONARY = {
        light: {
          success: 'success',
          // …
        },
        dark: {
          success: 'success_dark',
          // …
        },
      },
      
      // …
      
      {toastQueue.map(({ id, props }) => {
        const { severity, ...rest } = props;
        return (
          <div key={id} className={DICTIONARY[theme ?? 'light'][severity] ?? ''}>
            <Toast {...rest} />
          </div>
        );
      })}
      
    • Write the CSS rules in src/styles/globals.css outside of any @layer.

    • safelist the classes:

      module.exports = {
        safelist: [
          'success',
          'success_dark',
          // …
        ],
        // …
      ];