admin管理员组

文章数量:1404924

I need to implement a Popover primitive that always appear on top of all other elements in the page, I tried to see if some well known solutions have this implemented but it seems in all cases the Popover is added to the bottom of the Page but still z-index is still in use:

For example:

  • shadcn popover
  • mui popover
  • radix popover

Implementing a simple Popover is not that difficult but doing it with plain CSS might have the same issues as the ones above

  • It might need z-index to be set in most cases
  • It might not be correctly positioned if its position is fixed and it is inside a container that have a css transform, since a new stacking context might have been created.
  • It might be shown inside a container with overflow auto or hidden.

Despite those issues a CSS only popover offers a very simple API and it is very easy to create.

Here is an example of the simple API I'm looking for

// the Overlay is a controlled component so the 
// fact that is shown or not is controlled by the 
// open property
<CSSOverlay open={isOpen}>
  <div>Content here</div>
</CSSOverlay>

But most of the examples I've found go for something like

<SomeOverlay open={isOpen} style={{ zIndex: 100000 /* some arbitrary hight number no other popover will use, or at least we hope it won't */ }}>
  <div>Content here</div>
</SomeOverlay>

or appending it to the body, which might still have z-index issues, and now might also have issues accessing content inside React Providers as context might not be shared with it

<SomeOverlay open={isOpen} appendToBody={true}>
  <div>Content here</div>
</SomeOverlay>

Here is an example of a the CSS Popover with z-index issues

Full disclosure: This is a re-write of a now closed question posted here: What are the downsides or potential drawbacks of using `popover` attribute to create overlays, like tooltips, popovers and dialogs I've modified

I need to implement a Popover primitive that always appear on top of all other elements in the page, I tried to see if some well known solutions have this implemented but it seems in all cases the Popover is added to the bottom of the Page but still z-index is still in use:

For example:

  • shadcn popover
  • mui popover
  • radix popover

Implementing a simple Popover is not that difficult but doing it with plain CSS might have the same issues as the ones above

  • It might need z-index to be set in most cases
  • It might not be correctly positioned if its position is fixed and it is inside a container that have a css transform, since a new stacking context might have been created.
  • It might be shown inside a container with overflow auto or hidden.

Despite those issues a CSS only popover offers a very simple API and it is very easy to create.

Here is an example of the simple API I'm looking for

// the Overlay is a controlled component so the 
// fact that is shown or not is controlled by the 
// open property
<CSSOverlay open={isOpen}>
  <div>Content here</div>
</CSSOverlay>

But most of the examples I've found go for something like

<SomeOverlay open={isOpen} style={{ zIndex: 100000 /* some arbitrary hight number no other popover will use, or at least we hope it won't */ }}>
  <div>Content here</div>
</SomeOverlay>

or appending it to the body, which might still have z-index issues, and now might also have issues accessing content inside React Providers as context might not be shared with it

<SomeOverlay open={isOpen} appendToBody={true}>
  <div>Content here</div>
</SomeOverlay>

Here is an example of a the CSS Popover with z-index issues

Full disclosure: This is a re-write of a now closed question posted here: What are the downsides or potential drawbacks of using `popover` attribute to create overlays, like tooltips, popovers and dialogs I've modified

Share Improve this question asked Mar 24 at 1:25 roy riojasroy riojas 2,4963 gold badges28 silver badges30 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

A light at the end of the tunnel. The popover attribute

I think I've found a solution that doesn't suffer from any of the z-index issues or wrong positioning on new stacking contexts :

https://developer.mozilla./en-US/docs/Web/HTML/Global_attributes/popover

Apparently the popover attribute is widely available now so I went ahead and created a super simple Overlay like this:

import { ComponentProps, FC, useEffect, useRef } from "react";

export type OverlayProps = ComponentProps<"div"> & {
  open: boolean;
};

export const Overlay: FC<OverlayProps> = ({ children, open, ...rest }) => {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (open) {
      ref.current?.showPopover?.();
    } else {
      ref.current?.hidePopover?.();
    }
  }, [open, ref]);

  return (
    <div
      {...rest}
      ref={ref}
      // @ts-expect-error React types complain but the property works
      popover="manual"
    >
      {children}
    </div>
  );
};

The way to use it is now super simple


const useOverlayState = () => {
  const [open, setOpen] = useState(false);

  return {
    isOpen: open,
    open: () => setOpen(true),
    close: () => setOpen(false),
    toggle: () => setOpen((o) => !o),
  };
};

export default function App() {
  const { isOpen, toggle } = useOverlayState();
  
  return (
    <div className="App">
      <div id="bad-element"></div>
      <h1>Overlay with popover attribute example</h1>
      <button onClick={toggle}>
        {isOpen ? `Close overlay` : `Open overlay`}
      </button>
  
      <Overlay className="overlay-popover" open={isOpen}>
        I'm an overlay using popover
      </Overlay>
    </div>
  );
}

Here is a working example of this super simple component

Since the Overlay is moved by the browser to the top-layer this solution does not suffer from any of the issues mentioned in the question.

This Popover can be used as a foundation for other more complex/useful components like

  • PositionableOverlay (for tooltips and popovers)
  • Modals (The Dialog element might also be useful for this one)
  • SlidingPanels

Basically anything that need to behave like an overlay.

本文标签: