Search code examples
cssreactjstailwind-csscss-transitions

the transition-color aren't working correctly


I have a button that changes the theme color on navBar and it's working. When I click the button, the background color changes faster then the text colors. The "Home", "Cadastros", "Dash", "Notas" changes first than the icons and whatever is rendered in Outlet inside the MainLayout Demonstration

Relevant codes

index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

* {
  @apply transition-all duration-300;
}

.lightTheme {
  --background: 250, 250, 250;
  --text: 0, 0, 0;
  --title: 0, 0, 0;
  --navBarShadow: 2, 6, 23, 0.1;
}

.darkTheme {
  --background: 15, 23, 42;
  --text: 250, 250, 250;
  --title: 250, 250, 250;
  --navBarShadow: 2, 6, 23, 0.2;
}

NavBar.jsx

import { Moon, Sun, User } from "lucide-react";




function NavBar({theme, onChangeThemeClick}) {


    const themeIcon = theme === 'lightTheme' ? <Sun  /> : <Moon  />;




    return (
        <nav className={` ${theme} w-full h-15 absolute z-10 top-0 bg-background text-title p-4 shadow-navBarShadow shadow-lg `}>
            <ul className="flex space-x-10 bg justify-end ">
                <li ><a href="#" >Home</a></li>
                <li><a href="#">Cadastros</a></li>
                <li><a href="#">Dash</a></li>
                <li><a href="#">Notas</a></li>

                <li onClick={() => onChangeThemeClick()}><a href="#">
                    {themeIcon}
                </a></li>
                
                <li ><a href="#">
                    <User />
                </a></li>


            </ul>
        </nav>
        );
}

export default NavBar;

MainContainer.jsx

function MainContainer({theme, children}){



    return(

        <div className={`${theme} pt-20 z-0 w-full h-screen bg-background`}>
            {children}
        </div>

    )
}

export default MainContainer;

RegistrationsTable.jsx



function RegistrationsTable() {


    return(

        <div className="w-1/2 h-max bg-background text-text border-text">
            
            <table>
                <tbody>
                    <tr>
                        <td>
                            <p>adad</p>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>

    )
}

export default RegistrationsTable;

RegistrationsPage.jsx


import RegistrationsTable from "../components/RegistrationsTable.jsx";


function RegistrationsPage() {  



    return (
        <>    
            <RegistrationsTable />
        </>
    );
}

export default RegistrationsPage;

MainLayout.jsx


import { useState } from 'react'


import NavBar from '../components/NavBar.jsx'
import { Outlet } from 'react-router-dom'
import MainContainer from "../components/MainContainer.jsx";


function MainLayout() {



  const [theme, setTheme] = useState('lightTheme')



  function onChangeThemeClick() {

    if (theme === 'lightTheme'){
      setTheme('darkTheme')
    } else if (theme === 'darkTheme'){
      setTheme('lightTheme')
    }
    
  }

  return (
    <>
      
        <NavBar  theme={theme} onChangeThemeClick={onChangeThemeClick} />
        <MainContainer theme={theme}>
          <Outlet />
        </MainContainer>

    </>

  )
}

export default MainLayout

tailwind.config.js

/** @type {import('tailwindcss').Config} */
export default {
  content: ["./index.html", "./src/**/*.{html,js,jsx,ts,tsx}"],
  theme: {
    extend: {
      colors: {
        //Theme colors
        background: "rgba(var(--background))",
        text: "rgba(var(--text))",
        title: "rgba(var(--title))",
        navBarShadow: "rgba(var(--navBarShadow))",
      },
    },
  },
  plugins: [],
};

I tried to apply the transition duration to dark and light theme but doesn't work. Like this:

@tailwind base;
@tailwind components;
@tailwind utilities;

.lightTheme {
  --background: 250, 250, 250;
  --text: 0, 0, 0;
  --title: 0, 0, 0;
  --navBarShadow: 2, 6, 23, 0.1;

 @apply transition-all duration-300;
}

.darkTheme {
  --background: 15, 23, 42;
  --text: 250, 250, 250;
  --title: 250, 250, 250;
  --navBarShadow: 2, 6, 23, 0.2;

 @apply transition-all duration-300;
}

Tried to pass theme variable trough the Outlet to use it in ClassName, but doesn't work too


Solution

  • Elements that don't have an explicit color set have inherit as their effective value. This means that the element resolves its value from its parent:

    <div style="color: blue">
      I am blue.
      <p>I am blue also because I inherit from parent.</p>
    </div>
    

    In Chromium-based browsers (and as per your screencast, Opera is based on Chromium), when a parent's color value is switched, it seems like child elements with inherit value get the new value after transitions happen, and then switch their own resolved value, playing their own transition, thus causing the cascading delay.

    Thus, you could consider only applying the color transition to element(s) that don't have inherit as a value for color. So:

    • Change your * rule to only transition color properties that are not color:
      * {
        transition: theme(transitionDuration.DEFAULT) theme(transitionTimingFunction.DEFAULT);
        transition-property: var(--transition-properties);
        --transition-properties: background-color, border-color;
      }
      
    • Transition color for the top-level only:
      .lightTheme, .darkTheme {
        transition-property: color, var(--transition-properties);
      }
      
    • And transition color for elements that have their color property explicitly set:
      <div className="… text-text … transition-color">
      

    tailwind.config = {
      theme: {
        extend: {
          colors: {
            //Theme colors
            background: "rgba(var(--background))",
            text: "rgba(var(--text))",
            title: "rgba(var(--title))",
            navBarShadow: "rgba(var(--navBarShadow))",
          },
        },
      },
    };
    <script src="https://cdn.tailwindcss.com/3.4.15"></script>
    <style type="text/tailwindcss">
    * {
      transition: theme(transitionDuration.DEFAULT) theme(transitionTimingFunction.DEFAULT);
      transition-property: var(--transition-properties);
      --transition-properties: background-color, border-color;
    }
    
    .lightTheme {
      --background: 250, 250, 250;
      --text: 0, 0, 0;
      --title: 0, 0, 0;
      --navBarShadow: 2, 6, 23, 0.1;
    }
    
    .darkTheme {
      --background: 15, 23, 42;
      --text: 250, 250, 250;
      --title: 250, 250, 250;
      --navBarShadow: 2, 6, 23, 0.2;
    }
    
    .lightTheme, .darkTheme {
      transition-property: color, var(--transition-properties);
    }
    </style>
    
    <div id="app"></div>
    
    <script src="https://unpkg.com/@babel/[email protected]"></script>
    <script type="text/babel" data-type="module">
      import React from "https://esm.sh/[email protected]";
      import client from "https://esm.sh/[email protected]/client";
      import { Moon, Sun, User } from "https://esm.sh/[email protected]";
      import { BrowserRouter, Routes, Route, Outlet } from "https://esm.sh/[email protected]";
    
      function NavBar({ theme, onChangeThemeClick }) {
        const themeIcon = theme === "lightTheme" ? <Sun /> : <Moon />;
    
        return (
          <nav
            className={` ${theme} w-full h-15 absolute z-10 top-0 bg-background text-title p-4 shadow-navBarShadow shadow-lg `}
          >
            <ul className="flex space-x-10 bg justify-end ">
              <li>
                <a href="#">Home</a>
              </li>
              <li>
                <a href="#">Cadastros</a>
              </li>
              <li>
                <a href="#">Dash</a>
              </li>
              <li>
                <a href="#">Notas</a>
              </li>
    
              <li onClick={() => onChangeThemeClick()}>
                <a href="#">{themeIcon}</a>
              </li>
    
              <li>
                <a href="#">
                  <User />
                </a>
              </li>
            </ul>
          </nav>
        );
      }
    
      function MainContainer({ theme, children }) {
        return (
          <div className={`${theme} pt-20 z-0 w-full h-screen bg-background`}>
            {children}
          </div>
        );
      }
    
      function RegistrationsTable() {
        return (
          <div className="w-1/2 h-max bg-background text-text border-text transition-color">
            <table>
              <tbody>
                <tr>
                  <td>
                    <p>adad</p>
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
        );
      }
    
      function RegistrationsPage() {
        return (
          <>
            <RegistrationsTable />
          </>
        );
      }
      
      const { useState } = React;
    
      function MainLayout() {
        const [theme, setTheme] = useState("lightTheme");
    
        function onChangeThemeClick() {
          if (theme === "lightTheme") {
            setTheme("darkTheme");
          } else if (theme === "darkTheme") {
            setTheme("lightTheme");
          }
        }
    
        return (
          <>
            <NavBar theme={theme} onChangeThemeClick={onChangeThemeClick} />
            <MainContainer theme={theme}>
              <Outlet />
            </MainContainer>
          </>
        );
      }
    
      
      client.createRoot(document.getElementById('app')).render(
        <BrowserRouter>
          <Routes>
            <Route path="/" element={<MainLayout />}>
              <Route path="js" element={<RegistrationsPage />} />
            </Route>
          </Routes>
        </BrowserRouter>
      );
    </script>