Popover

Material Design 3 popover component for displaying contextual content in an overlay

Popover

Popovers display rich contextual content in an overlay positioned relative to a trigger element. They’re useful for displaying additional information, actions, or interactive content without navigating away from the current context.

Recommended: Use the Native HTML Popover API for modern browsers. It provides built-in accessibility, light-dismiss behavior, and requires no JavaScript.

Basic Usage

Basic Popover

This is a basic popover with some informational content. It tracks the button on scroll!

CSS Anchor Positioning

Native HTML popovers render in the top layer and don’t move with their trigger element by default. CSS Anchor Positioning solves this by linking the popover to its trigger:

<!-- 1. Add anchor-name to the trigger -->
<button style="anchor-name: --my-anchor" popovertarget="my-popover">Open</button>

<!-- 2. Add position-anchor to the popover -->
<div class="popover" id="my-popover" popover style="position-anchor: --my-anchor">
  Content that tracks the button on scroll!
</div>

Anchored Positions

Use position classes with anchor positioning for different placements:

Anchored Positions

Anchored below
Anchored above
Anchored left
Anchored right

Browser Support

CSS Anchor Positioning is supported in Chrome 125+, Edge 125+. For older browsers, popovers will still work but will be centered on screen instead of anchored to the trigger.

Legacy Positions (Class-based)

The position classes below work with the class-based approach for legacy browser support:

Position Classes (Class-based approach)

Position Classes

Top position
Bottom position
Left position
Right position

Size Variants

Control the size of popovers with size modifier classes.

Small Popover

Small Popover

Compact popover for brief messages.

Default Popover

Default Popover

Standard popover with balanced size for most use cases.

Large Popover

Large Popover

Spacious popover for more detailed content or multiple elements.

Color Variants

Apply theme colors to popovers for different contexts. The color intensity can be customized using the --popover-color-intensity CSS variable:

/* Adjust globally */
:root {
  --popover-color-intensity: 40%; /* bolder colors (default: 30%) */
}

/* Or per-theme for dark mode */
[data-theme="moonlight"] {
  --popover-color-intensity: 35%;
}

Primary Popover

Primary Popover

Primary themed popover for emphasis.

Secondary Popover

Secondary Popover

Secondary themed popover.

Tertiary Popover

Tertiary Popover

Tertiary themed popover.

Surface Variant

Surface Variant

Popover with highest surface elevation.

Rich Content

Popovers can contain headers, body content, and footers for complex layouts.

With Header

Popover with Header

Popover Title

This popover includes a header section with a title.

Popover with Footer

Complete Example

Complete Popover Example

Notification Settings

Choose how you want to receive notifications about activity in your workspace.

Arrow Customization

No Arrow

Remove the arrow indicator for a cleaner appearance:

No Arrow

This popover has no arrow indicator.

Interactive Content

For popovers with interactive elements like forms or buttons, use popover="manual" to prevent auto-dismiss while interacting:

Interactive Popover

Quick Feedback

Class-based Approach (Legacy)

For older browsers that don’t support the native Popover API, use the class-based approach:

Visibility Control

Toggle the popover-show class to control visibility:

Visibility Control

This popover is hidden
This popover is visible

JavaScript Toggle

JavaScript Integration

Toggle me with JavaScript!

Native HTML Popover API

DuskMoonUI fully supports the modern HTML Popover API, which provides native browser support for showing and hiding popovers without custom JavaScript.

Basic Native Popover

Use the popover attribute on the popover element and popovertarget on the trigger button. Add anchor-name and position-anchor for scroll tracking:

Native Popover

This popover uses the native HTML Popover API. Click outside or press Escape to close.

Native Popover with Rich Content

Native Popover with Header and Footer

Native Popover

This uses the browser's built-in popover functionality with smooth animations and automatic light-dismiss behavior.

Native Popover Color Variants

Native Popover Colors

Primary themed native popover.
Secondary themed native popover.
Tertiary themed native popover.

Manual Mode Native Popover

Use popover="manual" to prevent automatic light-dismiss (click outside to close):

Manual Native Popover

Manual Popover

This popover won't close when clicking outside. You must use the button to close it.

Native Popover with Modal Backdrop

Add popover-modal class for a more prominent backdrop:

Modal-style Native Popover

Browser Support

The HTML Popover API is supported in all modern browsers (Chrome 114+, Firefox 125+, Safari 17+). For older browsers, use the class-based approach with JavaScript shown above.

Best Practices

Positioning

  • Choose positions that keep the popover on screen
  • Top and bottom positions work best for horizontal layouts
  • Left and right positions work best for vertical layouts
  • Ensure the popover doesn’t overflow the viewport

Content

  • Keep content concise and relevant
  • Use headers for clarity when showing multiple pieces of information
  • Include close buttons for persistent popovers
  • Provide clear calls-to-action in footers when needed

Accessibility

  • Ensure popovers are keyboard accessible
  • Use appropriate ARIA attributes
  • Provide a way to dismiss the popover
  • Don’t rely solely on color to convey meaning

Accessible Popover Example

Trigger Behavior

Common trigger patterns:

  • Click: Toggle on click, close on outside click
  • Hover: Show on hover, hide on mouse leave
  • Focus: Show on focus, hide on blur
  • Manual: Control visibility programmatically

Performance

  • Hide popovers when not needed to reduce DOM complexity
  • Use event delegation for multiple popovers
  • Consider lazy-loading popover content for complex data
  • Avoid nesting multiple popovers

Framework Examples

React

import { useState, useRef, useEffect } from 'react';

interface PopoverProps {
  trigger: React.ReactNode;
  content: React.ReactNode;
  position?: 'top' | 'bottom' | 'left' | 'right';
  size?: 'sm' | 'md' | 'lg';
}

export function Popover({
  trigger,
  content,
  position = 'bottom',
  size = 'md'
}: PopoverProps) {
  const [isOpen, setIsOpen] = useState(false);
  const popoverRef = useRef<HTMLDivElement>(null);
  const triggerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (
        popoverRef.current &&
        triggerRef.current &&
        !popoverRef.current.contains(event.target as Node) &&
        !triggerRef.current.contains(event.target as Node)
      ) {
        setIsOpen(false);
      }
    };

    document.addEventListener('click', handleClickOutside);
    return () => document.removeEventListener('click', handleClickOutside);
  }, []);

  return (
    <div style={{ position: 'relative', display: 'inline-block' }}>
      <div ref={triggerRef} onClick={() => setIsOpen(!isOpen)}>
        {trigger}
      </div>
      <div
        ref={popoverRef}
        className={`popover popover-${position} ${size !== 'md' ? `popover-${size}` : ''} ${isOpen ? 'popover-show' : ''}`}
      >
        <div className="popover-arrow"></div>
        <div className="popover-body">
          {content}
        </div>
      </div>
    </div>
  );
}

// Usage
<Popover
  trigger={<button className="btn btn-primary">Show Popover</button>}
  content="This is the popover content"
  position="bottom"
/>

Vue

<template>
  <div style="position: relative; display: inline-block">
    <div ref="triggerRef" @click="toggle">
      <slot name="trigger" />
    </div>
    <div
      ref="popoverRef"
      :class="popoverClasses"
    >
      <div class="popover-arrow"></div>
      <div class="popover-body">
        <slot />
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue';

const props = defineProps({
  position: {
    type: String,
    default: 'bottom',
    validator: (value) => ['top', 'bottom', 'left', 'right'].includes(value)
  },
  size: {
    type: String,
    default: 'md',
    validator: (value) => ['sm', 'md', 'lg'].includes(value)
  }
});

const isOpen = ref(false);
const popoverRef = ref(null);
const triggerRef = ref(null);

const popoverClasses = computed(() => [
  'popover',
  `popover-${props.position}`,
  props.size !== 'md' ? `popover-${props.size}` : '',
  isOpen.value ? 'popover-show' : ''
]);

const toggle = () => {
  isOpen.value = !isOpen.value;
};

const handleClickOutside = (event) => {
  if (
    popoverRef.value &&
    triggerRef.value &&
    !popoverRef.value.contains(event.target) &&
    !triggerRef.value.contains(event.target)
  ) {
    isOpen.value = false;
  }
};

onMounted(() => {
  document.addEventListener('click', handleClickOutside);
});

onUnmounted(() => {
  document.removeEventListener('click', handleClickOutside);
});
</script>

<!-- Usage -->
<Popover position="bottom" size="md">
  <template #trigger>
    <button class="btn btn-primary">Show Popover</button>
  </template>
  This is the popover content
</Popover>

API Reference

CSS Variables

VariableDefaultDescription
--popover-color-intensity30%Controls how strongly theme colors appear in color variants (20% = subtle, 40% = bold)

Class Names

ClassDescription
.popoverBase popover styles (required)
.popover-showMakes the popover visible (class-based approach)
.popover-topPosition popover above trigger
.popover-bottomPosition popover below trigger
.popover-leftPosition popover to the left of trigger
.popover-rightPosition popover to the right of trigger
.popover-smSmall size variant
.popover-lgLarge size variant
.popover-darkDark color theme
.popover-primaryPrimary color theme
.popover-secondarySecondary color theme
.popover-tertiaryTertiary color theme
.popover-surface-highestHighest surface elevation
.popover-no-arrowHide the arrow indicator
.popover-interactiveFor popovers with interactive content
.popover-modalAdds a prominent backdrop (native API only)

Native Popover API Attributes

AttributeDescription
popoverMakes element a popover with auto light-dismiss
popover="manual"Popover without light-dismiss (requires explicit close)
popovertarget="id"Links trigger button to popover by ID
popovertargetaction="show"Button shows the popover
popovertargetaction="hide"Button hides the popover
popovertargetaction="toggle"Button toggles popover (default)

CSS Anchor Positioning (Inline Styles)

PropertyExampleDescription
anchor-namestyle="anchor-name: --my-anchor"Names the trigger as an anchor point
position-anchorstyle="position-anchor: --my-anchor"Links popover to its anchor for scroll tracking

Note: CSS Anchor Positioning requires Chrome 125+ or Edge 125+. In unsupported browsers, popovers will fall back to centered positioning.

Structure Classes

ClassDescription
.popover-arrowArrow indicator pointing to trigger
.popover-headerHeader section container
.popover-titleTitle text in header
.popover-bodyMain content container
.popover-footerFooter section for actions
.popover-closeClose button

Combinations

Class Combinations

Combined modifiers
Interactive content

See Also