Tooltip

Small pop-up element that provides additional information when users hover or focus on an element.

  • Default

    Tooltips are tricky because of all the accessibility concerns. We recommend using a 3rd party solution like Tippy.js because it takes care of the anchoring, a11y, and keeps the tooltip inside the viewport.

    <div class="gap-4 grid items-center grid-cols-2 [&_[data-tippy-root]]:inline-block md:grid-cols-4"> <!-- <- this is just for demo purposes -->
      <div>
        <div data-tippy-root>
          <div class="tippy-box" data-placement="top">
            <div class="tippy-backdrop"></div>
            <div class="tippy-arrow"></div>
            <div class="tippy-content">Top</div>
          </div>
        </div>
        <div class="mt-1 pl-4 text-sm">Top</div> <!-- this is for demo purposes -->
      </div>
      <div>
        <div class="mb-1 pl-4 text-sm">Bottom</div> <!-- this is for demo purposes -->
        <div data-tippy-root>
          <div class="tippy-box" data-placement="bottom">
            <div class="tippy-backdrop"></div>
            <div class="tippy-arrow"></div>
            <div class="tippy-content">Bottom</div>
          </div>
        </div>
      </div>
      <div>
        <div data-tippy-root>
          <div class="tippy-box" data-placement="left">
            <div class="tippy-backdrop"></div>
            <div class="tippy-arrow"></div>
            <div class="tippy-content">Left</div>
          </div>
        </div>
        <span class="ml-2 text-sm">Left</span> <!-- this is for demo purposes -->
      </div>
      <div>
        <span class="mr-2 text-sm">Right</span> <!-- this is for demo purposes -->
        <div data-tippy-root>
          <div class="tippy-box" data-placement="right">
            <div class="tippy-backdrop"></div>
            <div class="tippy-arrow"></div>
            <div class="tippy-content">Right</div>
          </div>
        </div>
      </div>
    </div>
    
  • Example

    The following examples are all using Alpine.js with Tippy.js used as a directive with optional $magic.

    <button class="prs-btn prs-btn-primary" x-tooltip="'Tooltip'">Hover/Focus for Tooltip</button>
    <button class="prs-btn prs-btn-primary" @mouseenter="$tooltip('Tooltip')">Hover Only</button>
    <button class="prs-btn prs-btn-primary" @focus="$tooltip('Tooltip')">Focus Only</button>
    
    // alpine
    import Alpine from 'alpinejs';
    import tippy from 'tippy.js';
    
    document.addEventListener('alpine:init', () => {
      // tooltip
      // magic: @focus="$tooltip('some tooltip')"
      Alpine.magic('tooltip', el => message => {
        let instance = tippy(el, { content: message, trigger: 'manual' });
        instance.show();
        setTimeout(() => {
          instance.hide()
          setTimeout(() => instance.destroy(), 150)
        }, 2000);
      });
      // directive: x-tooltip="'some tooltip'"
      Alpine.directive('tooltip', (el, { expression }, { evaluate }) => {
        tippy(el, { content: evaluate(expression) })
      });
    });
    

CSS

Full Library

Component Only

/* tippy.js tooltips */
.tippy-box {
  background-color: var(--prs-c-navy-600);
  color: var(--prs-c-white);
  white-space: normal;
  outline: 0;
  position: relative;
  opacity: 1;
  box-shadow: 0 0 0 1px rgb(255 255 255), 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -1px rgb(0 0 0 / 0.06);
  border-radius: 0.25rem;
  transform: translateY(0);
  transition-property: var(--prs-transition-property);
  transition-timing-function: var(--prs-transition-timing);
  transition-duration: var(--prs-transition-duration);

  &[data-state="hidden"] {
    opacity: 0;
    transform: translateY(0.25rem);
  }

  &[data-placement^="top"],&[data-placement^="bottom"],&[data-placement^="left"],&[data-placement^="right"] {
    > .tippy-arrow {
      width: 1rem;
      height: 1rem;
      color: var(--prs-c-navy-600);
      position: absolute;

      &::before {
        content: '';
        position: absolute;
        border-style: solid;
        border-color: transparent;
      }
    }
  }

  &[data-placement^="top"] > .tippy-arrow {
    filter: drop-shadow(0 1px 0 rgb(255 255 255));
    bottom: 0;
    transform: translateX(-50%);
    left: 50%;
    transform-origin: top;

    &::before {
      bottom: -7px;
      left: 0;
      border-top: 8px solid currentColor;
      border-right: 8px solid transparent;
      border-bottom: 0;
      border-left: 8px solid transparent;
    }
  }

  &[data-placement^="bottom"] > .tippy-arrow {
    filter: drop-shadow(0 -1px 0 rgb(255 255 255));
    top: 0;
    transform: translateX(-50%);
    left: 50%;
    transform-origin: bottom;

    &::before {
      top: -7px;
      left: 0;
      border-top: 0;
      border-right: 8px solid transparent;
      border-bottom: 8px solid currentColor;
      border-left: 8px solid transparent;
    }
  }

  &[data-placement^="left"] > .tippy-arrow {
    right: 0;
    filter: drop-shadow(1px 0 0 rgb(255 255 255));
    transform: translateY(-50%);
    top: 50%;
    transform-origin: left;

    &::before {
      right: -7px;
      border-top: 8px solid transparent;
      border-right: 0;
      border-bottom: 8px solid transparent;
      border-left: 8px solid currentColor;
    }
  }

  &[data-placement^="right"] > .tippy-arrow {
    left: 0;
    filter: drop-shadow(-1px 0 0 rgb(255 255 255));
    transform: translateY(-50%);
    top: 50%;
    transform-origin: right;

    &::before {
      left: -7px;
      border-top: 8px solid transparent;
      border-right: 8px solid currentColor;
      border-bottom: 8px solid transparent;
      border-left: 0;
    }
  }
}

[data-tippy-root] {
  max-width: calc(100vw - 10px);
}

.tippy-content {
  padding: 0.5rem 1rem;
  font-size: 16px;
  line-height: 24px;
  position: relative;
  z-index: 1;

  @media (min-width: 768px) {
    padding: 0.25rem 1rem;
    font-size: 14px;
    line-height: 22px;
  }
}

Figma

Coming soon...