Search code examples
htmlcsstailwind-css

Everything stops working after add opacity (in Tailwind)


This is my HTML document. I have an image tag beneath my tags and everything looks good until adding an opacity-50 class to my img tag. Now the image covers my other tags. (I tried to add opacity to other tags and even make my header my img tag sibling but still the problem exists).

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./fonts/fontawesome-free-6.4.2-web/css/all.css">

    <title>Document</title>
    <style>
        @font-face {
            font-family: "Ubunto";
            src: url("./fonts/Ubuntu-Medium.ttf");
        }

        .fo-color {
            color: white;
        }
    </style>
</head>

<body>
    <div>
        <header class="overflow-hidden text-white fixed">
            <div class="grid grid-cols-2 w-screen h-14 bg-slate-600">
                <div class="mt-2 ml-6 text-2xl" style="font-family: Ubunto;">Logo</div>
                <nav class="flex justify-end ">
                    <a href="#" class="menu-open-btn absolute mt-3 mr-7">
                        <i class="fa-solid fa-bars fo-color fa-xl"></i>
                    </a>
                    <div
                        class="w-1/3 main-menu duration-300 h-screen bg-slate-600 fixed flex items-center top-0 right-0">
                        <a href="#" class="menu-close-btn absolute top-4 left-5">
                            <i class="fa-solid fa-x fa-xl fo-color"></i>
                        </a>
                        <ul class="">
                            <li class="hover:text-lime-600 duration-200 ml-5">Home</li>
                            <li class="hover:text-lime-600 duration-200 ml-5">About Us</li>
                            <li class="hover:text-lime-600 duration-200 ml-5">Contact</li>
                            <li class="hover:text-lime-600 duration-200 ml-5">Team</li>
                        </ul>
                    </div>
                </nav>
            </div>
        </header>
        <div class="overflow-hidden">
            <div class="">
                <img src="./img/businesspeople.avif" alt="photo" class="h-auto w-screen bg-indigo-500 opacity-50">
            </div>
        </div>
    </div>


    <script>
        let main_menu = document.querySelector(".main-menu")
        let menu_open_btn = document.querySelector(".menu-open-btn")
        let menu_close_btn = document.querySelector(".menu-close-btn")

        menu_open_btn.addEventListener("click", () => {
            main_menu.classList.remove("translate-x-full")
            main_menu.classList.add("translate-x-0")
        })

        menu_close_btn.addEventListener("click", () => {
            main_menu.classList.remove("translate-x-0")
            main_menu.classList.add("translate-x-full")
        })
    </script>
    <script src="https://cdn.tailwindcss.com"></script>
</body>

</html>

Solution

  • This is because using opacity with a value less than 1 changes its painting order in the stacking context. One way to fix it is to adjust the z-index by adding a negative z index such as -z-10 relative on the image. (Side note: The overflow-hidden on the image container is not hiding any part of the image if that is what it's meant to do.)

    <img src="./img/businesspeople.avif" 
      alt="photo" class="h-auto w-screen bg-indigo-500 opacity-50 
      -z-10 relative">
    

    Detailed explanation

    The reason why the image is behind everything originally is because within the same stacking context (and the only one in your case), non-positioned elements will appear behind z-index:auto positioned elements (anything that is not position:static.)

    Since the image and its container div are both position:static which are non-positioned, while the whole header is set to position:fixed, the image will be drawn behind the header.

    However, if you set opacity to less than 1 on a non-positioned element, it will be painted on the same layer, within its parent stacking context, as positioned elements with stack level 0. Now, since the header and the image are both seen as positioned element without positive stack level in the same stacking context, the later appeared in the DOM (which is the image) will be drawn in the front.

    In W3C Spec 3.2. Transparency: the ‘opacity’ property:

    If an element with opacity less than 1 is not positioned, then it is painted on the same layer, within its parent stacking context, as positioned elements with stack level 0.