nextui icon indicating copy to clipboard operation
nextui copied to clipboard

[BUG] - Any Modal/Popover is outside the provider by default. `Select` does not have `portalContainer` to fix this.

Open buschco opened this issue 2 years ago • 7 comments

NextUI Version

2.2.9

Describe the bug

Being outside of the Provider, causes the content to have the wrong theme. Providing the portalContainer-prop for Select would be a fix, but it is kind of anyoing to have to fix every Modal like component like than.

Your Example Website or App

https://stackblitz.com/edit/vitejs-vite-ih6tja?file=src%2Fmain.tsx

Steps to Reproduce the Bug or Issue

  1. Init a bare react + vite project
  2. Add a select
  3. set to dark mode
  4. open select and get flashbanged

Expected behavior

Select popover should be in dark mode too

Screenshots or Videos

image

Operating System Version

macOS

Browser

Firefox

buschco avatar Dec 03 '23 18:12 buschco

I'm running into same issue. It also seems that even when I'm adding portalContainer={document.body} to the Select component, the children components aren't accessing my theme. Any updates?

daveyfirstname avatar Dec 28 '23 19:12 daveyfirstname

I'm having the same problem in Autocomplete, when my header has a fixed position

marlonfrontend avatar Jan 07 '24 15:01 marlonfrontend

Is this fundamentally difficult to fix, on a technical level?

I’m considering both NextUI and NextUI Pro for an upcoming project and ran into this pretty early on (my Dropdowns are light even though the theme is dark), and it seems like it’s going to be a bit of a pain to need theming logic for every dropdown, select component etc., instead of just being able to set it once on the outermost container.

I could of course create themed versions of these components to avoid duplicating the theming logic, but still, it’s not optimal to need such a workaround. Curious if a fix is at least technically possible.

stiang avatar Feb 27 '24 14:02 stiang

I was able to solve this by setting the dark / light class on the body element instead of the app wrapper. In doing so the class also applies to the sibling element for the portal/popover, which fixes the styling.

It’s a little bit more involved than just setting the class on the app wrapper, since you need to manipulate the class of an element outside the app boundary (for example by using a useLayoutEffect hook to apply the updated body class), but not a whole lot. If the docs mentioned it I wouldn’t even consider it a problem.

stiang avatar Mar 06 '24 08:03 stiang

I had the same bug but I managed to fix it. I created a provider and in it I have the theme logic.

Root Layout image

Theme Provider image

I solved it by simply changing the <main> tag to <body> within my theme provider.

image

mathlouly avatar Mar 12 '24 02:03 mathlouly

I found a more suitable solution, within your theme provider, simply add the class directly to the body as shown in the image, and when changing the theme in real time you must replace the name of the old theme with the new one.

export default function ThemeProvider({ children }: { children: React.ReactNode }) {
    const [ theme, setTheme ] = useState(() => localStorage.getItem("theme") ?? "dark");

    const toggleTheme = () => {
        const newTheme = localStorage.getItem("theme") == "dark" ? "light" : "dark";
        document.querySelector("body")?.classList.replace(theme, newTheme);
        localStorage.setItem("theme", newTheme);
        setTheme(newTheme);
    }

    useEffect(() => {
        document.querySelector("body")?.classList.add(theme, "text-foreground", "bg-background");
    })

    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
            {children}
        </ThemeContext.Provider>
    )
}

mathlouly avatar Mar 12 '24 02:03 mathlouly

I found a more suitable solution, within your theme provider, simply add the class directly to the body as shown in the image, and when changing the theme in real time you must replace the name of the old theme with the new one.

export default function ThemeProvider({ children }: { children: React.ReactNode }) {
    const [ theme, setTheme ] = useState(() => localStorage.getItem("theme") ?? "dark");

    const toggleTheme = () => {
        const newTheme = localStorage.getItem("theme") == "dark" ? "light" : "dark";
        document.querySelector("body")?.classList.replace(theme, newTheme);
        localStorage.setItem("theme", newTheme);
        setTheme(newTheme);
    }

    useEffect(() => {
        document.querySelector("body")?.classList.add(theme, "text-foreground", "bg-background");
    })

    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
            {children}
        </ThemeContext.Provider>
    )
}

I found a more suitable solution, within your theme provider, simply add the class directly to the body as shown in the image, and when changing the theme in real time you must replace the name of the old theme with the new one.

export default function ThemeProvider({ children }: { children: React.ReactNode }) {
    const [ theme, setTheme ] = useState(() => localStorage.getItem("theme") ?? "dark");

    const toggleTheme = () => {
        const newTheme = localStorage.getItem("theme") == "dark" ? "light" : "dark";
        document.querySelector("body")?.classList.replace(theme, newTheme);
        localStorage.setItem("theme", newTheme);
        setTheme(newTheme);
    }

    useEffect(() => {
        document.querySelector("body")?.classList.add(theme, "text-foreground", "bg-background");
    })

    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
            {children}
        </ThemeContext.Provider>
    )
}

Hey. Thank you soo much for this. i was looking everywhere until i found this that help me a lot. Thanks ❤️

iSaulX avatar May 02 '24 05:05 iSaulX

I found a more suitable solution, within your theme provider, simply add the class directly to the body as shown in the image, and when changing the theme in real time you must replace the name of the old theme with the new one.

export default function ThemeProvider({ children }: { children: React.ReactNode }) {
    const [ theme, setTheme ] = useState(() => localStorage.getItem("theme") ?? "dark");

    const toggleTheme = () => {
        const newTheme = localStorage.getItem("theme") == "dark" ? "light" : "dark";
        document.querySelector("body")?.classList.replace(theme, newTheme);
        localStorage.setItem("theme", newTheme);
        setTheme(newTheme);
    }

    useEffect(() => {
        document.querySelector("body")?.classList.add(theme, "text-foreground", "bg-background");
    })

    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
            {children}
        </ThemeContext.Provider>
    )
}

Awesome. Thank you very much.

ahmetselimkaraca avatar Aug 05 '24 01:08 ahmetselimkaraca