Component

Popover

Anchored panel for contextual details or controls.

Text referenceSource and examples as JSON

npx -y github:anwarlemu/opencommerce-brand-cli#release add popover --surface site

Run from the target React 19.2+ app root with Tailwind CSS 4 and the @/* source alias.

Open alone
popover.preview
Preview
Open alone
popover.glass
Glass
Open alone
popover.open
Open

Usage

use-it-for.md

Use for explanations, filters or small controls. Use variant="glass" over imagery or the header. Keep longer tasks in Dialog or Sheet.

never.md
  • Forms longer than a few fields (use a dialog)
  • Popovers that open on hover (use a tooltip)
  • Gray panels

Exports: Popover, PopoverAnchor, PopoverContent, PopoverTrigger. This component includes a client boundary. Keep event handlers and state in a client component.

Dependencies: surface, @radix-ui/react-popover.

Example

examples/popover.tsx
import { Button } from "@/components/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Surface } from "@/lib/surface";

function Limits({ defaultOpen }: { defaultOpen?: boolean }) {
  return (
    <Popover defaultOpen={defaultOpen}>
      <PopoverTrigger asChild>
        <Button variant="secondary">What are limits?</Button>
      </PopoverTrigger>
      <PopoverContent className="grid gap-2">
        <p className="font-display text-body-sm font-medium text-fg">Spending limits</p>
        <p className="text-body-sm leading-relaxed text-fg-muted">Each card stops at its per purchase and monthly limits, whatever the agent tries to buy.</p>
      </PopoverContent>
    </Popover>
  );
}

export function Site() {
  return <Limits />;
}

export function Open() {
  return (
    <div className="h-48">
      <Limits defaultOpen />
    </div>
  );
}

export function Glass() {
  return (
    <Surface ground="mist" className="h-60 rounded-panel p-8">
      <Popover defaultOpen>
        <PopoverTrigger asChild>
          <Button variant="secondary">About monitoring</Button>
        </PopoverTrigger>
        <PopoverContent variant="glass" className="grid gap-1.5">
          <p className="font-display text-body-sm font-medium">Every route, around the clock</p>
          <p className="text-body-sm leading-relaxed text-fg-muted">Each store your agents buy from is exercised every few minutes, so a broken checkout is caught first.</p>
        </PopoverContent>
      </Popover>
    </Surface>
  );
}

Source

components/ui/popover.tsx
"use client";

import * as React from "react";
import * as PopoverPrimitive from "@radix-ui/react-popover";

import { usePortalContainer } from "@/lib/surface";
import { cn } from "@/lib/utils";

// The brand's floating panel: white, a 9 percent ink edge, 8px corners and a soft drop, 6px off its trigger, growing out
// of the side nearest the trigger (scale 0.97 and a fade, 160ms ease-out). `glass` is the header's tray as a panel: white
// at 72 percent under a light sheen and a blur, a 10 percent ink edge. Reduced motion keeps only the fade.
const VARIANTS = {
  default: "border-line bg-surface text-fg shadow-floating",
  glass: "border-line-glass bg-glass bg-(image:--ds-glass-sheen) text-fg shadow-floating [backdrop-filter:var(--ds-glass-blur)]",
} as const;

function Popover(props: React.ComponentProps<typeof PopoverPrimitive.Root>) {
  return <PopoverPrimitive.Root data-slot="popover" {...props} />;
}

function PopoverTrigger(props: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
  return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
}

function PopoverContent({ className, align = "start", sideOffset = 6, collisionPadding = 8, variant = "default", ...props }: React.ComponentProps<typeof PopoverPrimitive.Content> & { variant?: keyof typeof VARIANTS }) {
  return (
    <PopoverPrimitive.Portal container={usePortalContainer()}>
      <PopoverPrimitive.Content
        data-slot="popover-content"
        data-variant={variant}
        align={align}
        sideOffset={sideOffset}
        collisionPadding={collisionPadding}
        className={cn("z-50 w-72 max-w-(--radix-popover-content-available-width) max-h-(--radix-popover-content-available-height) overflow-y-auto overscroll-contain origin-(--radix-popover-content-transform-origin) animate-pop-in motion-reduce:animate-none rounded-control border p-4 text-body-sm outline-none", VARIANTS[variant], className)}
        {...props}
      />
    </PopoverPrimitive.Portal>
  );
}

function PopoverAnchor(props: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
  return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
}

export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };

Reference: Extension from opencommerce-agentcard-web src/app/globals.css (.nav-tray, .win).