next.jsz-indexlightboxreact-leafletchakra-ui

How to override Chakra UI Modal Component z-index value to place Modal component over React Leaflet Map


I'm trying to create a custom lightbox effect on images in a React Leaflet Circle Marker Popup component based on this article and usine the Chakra Modal component. The implementation works, but I'm running into z-index and placement issues with Chakra UI. My goal is to place the lightbox on top of all the rest of my content, at the moment the Modal is placed below parts of the Map container due to relative positioning.

As I understand it, the Chakra Modal component should be placed over all of the content, but the component's defautl z-index is placed relative to the Leaflet layers and thus gets rendered below part of the map container. Using the code in the article with the modal produces results close to what I want, except the Modal Lightbox is placed under part of the Leaflet map div appearing unaesthetic and the close button being obscured.

I'd like to have the lightbox div be placed on top of the whole document, but I'm not sure how to override the Modal component's default value of 1400 to 2400 so it's placed over the whole map. Overriding a multipart component is a bit tricky, and it doesn't look like the Modal z-index value is specified in the Modal theme definition. My lightbox using the Modal component is below:

import React, {useState} from 'react'
import { Modal, ModalContent, ModalBody, ModalCloseButton, Flex, ModalOverlay, ModalHeader, useDisclosure } from '@chakra-ui/react'

const LightBox = ({children, src, alt, Wrapper = 'div'}, zIndex=800) => {
    const { isOpen, onOpen, onClose } = useDisclosure()
    //const [ isOpen, setisOpen ] = useState(false)
    
    //const toggleIsOpen = () =>{
    //    setisOpen(!isOpen)
    //}

    return (
    <Wrapper
    onClick={onOpen}
    >

        {children}
        {isOpen ?
            <Modal isOpen={isOpen} onClose={onClose}>
                <ModalOverlay />
                <ModalContent zIndex={zIndex}>
                    <ModalHeader>
                        <ModalCloseButton />
                    </ModalHeader>
            <ModalBody>
            <Flex
             onClick={onClose}           
            
            >
                <img src={src}
                        alt={alt}
                        style={{
                            height: 'auto',
                            width: '100%'
                        }}
                    />
               </Flex> 
               </ModalBody>
               </ModalContent>
            </Modal>
        :null}
    </Wrapper>
    )
} 
export default LightBox

The working code can be seen at this link and the sandbox

UPDATE: I've tried the same approach using the Chakra Modal component which suffers from the same z-index issues. The Modal is placed within the map container and not on top of it in terms of content ordering. Additionally, the Modal can't be closed on click with the close button, which is partially obscured. My CSS isn't great, but I assume this is an issue with relative positioning with respect to the Leaflet map elements. Updated code with the Modal implementation can be found here, but I'm not linking to a demo as the implementation and results are similar to my own attempt.

UPDATE 2: So, I've realized that what I need to do is override the Chakra Modal's z-index via extending the theme. It's a bit tricky, as the Modal is a multi-part component, but it's not clear where the Modal z-index value is specified, or how to override it. I'm looking for answers that will help me place the value at 2400 or above.


Solution

  • Apparently, there is some confusion around z-index in general and the z-index values used in chakra-ui/react-leaflet.

    Quick fix: the right z-index for navbar

    It's the navbar (with your z-index 2000) overlaying the modal (default z-index 1400). If you use zIndex="sticky" for your navbar instead, it will work.

    Using z-index in chakra-ui

    chakra-ui provides you a minimal set of z-indices (documentation) that match your case. Instead of setting StickyNav's z-index to 2000, you'd chose the token "sticky" from the list. Once you start using Modals with Overlay it should just work as expected.

    const zIndices = {
      zIndices: {
        hide: -1,
        auto: 'auto',
        base: 0,
        docked: 10,
        dropdown: 1000,
        sticky: 1100,
        banner: 1200,
        overlay: 1300,
        modal: 1400,
        popover: 1500,
        skipLink: 1600,
        toast: 1700,
        tooltip: 1800,
      },
    }
    
    <StickyNav
      zIndex="sticky"
    

    Using react-leaflet with the right z-index

    react-leaflet (or the leaflet library) uses its own set of z-index values (200 to 700) (documentation) to lay out the map and ui elements (markers etc.) above it. But, apparently, it also uses z-index 1000 for the zoom-elements on the left. While it's not a problem with the chakra-ui default values, it could be if sticky was 1000 instead of 1100.

    Creating a new z-index stacking context

    In that case you could create a new stacking context (mdn documentation). mdn also tells you which conditions need to apply.

    A stacking context is formed, anywhere in the document, by any element in the following scenarios:

    • Root element of the document ().
    • Element with a position value absolute or relative and z-index value other than auto.
    • Element with a position value fixed or sticky (sticky for all mobile browsers, but not older desktop browsers).
    • Element that is a child of a flex container, with z-index value other than auto.

    You're using <main> with display:flex (4th example) and could just set the child elements z-index to 0 or "base" from chakra-ui. Otherwise you could set the child element position to relative and the z-index to 0 or "base" (2nd example).