Search code examples
reactjstooltip

How can I add a shadow to a tooltip arrow that goes under the tooltip content?


I am currently in the process of making a tooltip for an input box in a react project. The arrow goes above the tooltip content, pointing upwards. I would like for the arrow and the contents of the tooltip to have a shadow behind them. However, I can't seem to get the shadow to work with the arrow part of the tooltip.

The tooltip content has a box shadow around it using css. The box shadow approach unfortunately won't work for the arrow because the arrow is triangular, not box shaped. I tried using a drop shadow filter as well, but I can't get the shadow to go under the content: Shadow covering tooltip content

I could always move the drop shadow up, but then the shadow looks really awkward: Shadow awkwardly positioned up

I am beginning to run out of ideas. I've also tried using z-index to force the tooltip arrow to go under the content, but that doesn't seem to work either. Is there any other good way to do this? I would also like to point out that the arrow needs to be the size and color as I've shown in the pictures, because the website has other tooltips with the same arrow at the bottom instead of the top, so they need to match.

Below I'm including some code to hopefully reproduce the issue.

CSS:

.toolTip-container {
  position: absolute;
  top: 10%;
  left: 50%;
  transform: translateX(-80%);
  padding: 10px;
  z-index: 1;
  width: 300px;
  box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.25);
  background-color: white;
  border-radius: 4px;
  font-family: "Roboto Flex";
  font-style: normal;
  font-weight: 400;
  font-size: 16px;
  line-height: 19px;
  color: #333333;
  visibility: visible;
  opacity: 1;
  white-space: normal;
  word-wrap: break-word;
  text-align: center;
  vertical-align: middle;
}

.toolTip-container::after {
  content: " ";
  position: absolute;
  bottom: 100%;
  left: 75%;
  transform: translateX(50%);
  border-width: 29px;
  border-style: solid;
  border-left: 19px solid;
  border-right: 19px solid;
  border-color: transparent transparent white transparent;
  filter: drop-shadow(5px 8px 5px rgba(0, 0, 0, 0.25));
}

React:

function Example() {
  return <ToolTip text="Example text"></ToolTip>;
}

function ToolTip({ text }: ToolTipProps) {
  return (
    <>
      <div className="toolTip-container">{text}</div>
    </>
  );
}

Solution

  • You'll need to move the filter to a container element (along with absolute positioning) to avoid the stacking context created by transform:

    function ToolTip({ text }) {
      return (
        <div className="container">
          <div className="toolTip-container">{text}</div>
        </div>
      );
    }
    
    .toolTip-container {
      padding: 10px;
      z-index: 1;
      width: 300px;
      box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.25);
      background-color: white;
      border-radius: 4px;
      font-family: 'Roboto Flex';
      font-style: normal;
      font-weight: 400;
      font-size: 16px;
      line-height: 19px;
      color: #333333;
      visibility: visible;
      opacity: 1;
      white-space: normal;
      word-wrap: break-word;
      text-align: center;
      vertical-align: middle;
    }
    
    .toolTip-container::after {
      content: ' ';
      position: absolute;
      bottom: 100%;
      left: 75%;
      transform: translateX(50%);
      border-width: 29px;
      border-style: solid;
      border-left: 19px solid;
      border-right: 19px solid;
      border-color: transparent transparent white transparent;
    }
    
    .container {
      position: absolute;
      top: 10%;
      left: 50%;
      transform: translateX(-80%);
      filter: drop-shadow(5px 8px 5px rgba(0, 0, 0, 0.25));
    }
    

    function Example() {
      return <ToolTip text="Example text"></ToolTip>;
    }
    
    function ToolTip({ text }) {
      return (
        <div className="container">
          <div className="toolTip-container">{text}</div>
        </div>
      );
    }
    
    ReactDOM.createRoot(document.getElementById("root")).render(
        <Example />
    );
    .toolTip-container {
      padding: 10px;
      z-index: 1;
      width: 300px;
      box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.25);
      background-color: white;
      border-radius: 4px;
      font-family: 'Roboto Flex';
      font-style: normal;
      font-weight: 400;
      font-size: 16px;
      line-height: 19px;
      color: #333333;
      visibility: visible;
      opacity: 1;
      white-space: normal;
      word-wrap: break-word;
      text-align: center;
      vertical-align: middle;
    }
    
    .toolTip-container::after {
      content: ' ';
      position: absolute;
      bottom: 100%;
      left: 75%;
      transform: translateX(50%);
      border-width: 29px;
      border-style: solid;
      border-left: 19px solid;
      border-right: 19px solid;
      border-color: transparent transparent white transparent;
    }
    
    .container {
      position: absolute;
      top: 10%;
      left: 50%;
      transform: translateX(-80%);
      filter: drop-shadow(5px 8px 5px rgba(0, 0, 0, 0.25));
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js"></script>
    <div id="root"></div>