For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to documentation URLs. Homepage markdown is available at /llms.md/content.md. An agent skill is available at /.well-known/agent-skills/site-skill.md.

Popover

The Popover component from this design system, built on Base UI.

Installation

$ pnpm dlx shadcn@latest add @cobalt/popover

Options

  • <PopoverContent size="flush"> removes padding and gap, for lists, calendars and other content that draws its own edges.
  • size="sm" is a compact panel (12px padding, 8px gap). size="xs" is a menu-like list (6px padding, 2px gap).
  • radius sets the corner radius in px (12, 16, 20, 24). The default is 20.
  • variant="ghost" draws no border, fill, shadow or ring, for a Card placed inside so only one frame shows.

Source

"use client";

import { Popover as PopoverPrimitive } from "@base-ui/react/popover";
import { cn } from "cn";
import * as React from "react";

const POPOVER_RADIUS = {
  12: "rounded-[12px]",
  16: "rounded-[16px]",
  20: "rounded-[20px]",
  24: "rounded-[24px]",
} as const;

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

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

function PopoverContent({
  className,
  align = "center",
  alignOffset = 0,
  side = "bottom",
  sideOffset,
  collisionAvoidance,
  collisionPadding = 8,
  positionMethod,
  showArrow = false,
  size = "default",
  radius = 20,
  variant = "default",
  ...props
}: PopoverPrimitive.Popup.Props &
  Pick<
    PopoverPrimitive.Positioner.Props,
    | "align"
    | "alignOffset"
    | "side"
    | "sideOffset"
    | "collisionAvoidance"
    | "collisionPadding"
    | "positionMethod"
  > & {
    showArrow?: boolean;
    /**
     * `sm` is a compact panel (12px padding). `xs` is a menu-like list
     * (6px padding). `flush` has no padding.
     */
    size?: "default" | "sm" | "xs" | "flush";
    /** Corner radius in px. */
    radius?: 12 | 16 | 20 | 24;
    /** `ghost` has no border, fill, shadow or ring, for a Card placed inside. */
    variant?: "default" | "ghost";
  }) {
  return (
    <PopoverPrimitive.Portal>
      <PopoverPrimitive.Positioner
        align={align}
        alignOffset={alignOffset}
        side={side}
        sideOffset={sideOffset ?? (showArrow ? 10 : 8)}
        collisionAvoidance={collisionAvoidance}
        collisionPadding={collisionPadding}
        positionMethod={positionMethod}
        className="isolate z-50"
      >
        <PopoverPrimitive.Popup
          data-slot="popover-content"
          data-size={size}
          className={cn(
            "flex max-h-[var(--available-height)] w-80 max-w-[calc(100vw-32px)] origin-(--transform-origin) flex-col gap-3 rounded-[20px] border border-[var(--border-subtle,var(--border))] bg-popover p-4 text-sm text-popover-foreground shadow-[shadow:var(--shadow-dropdown,0_1px_1px_0_rgb(0_0_0/0.04),0_4px_4px_0_rgb(0_0_0/0.02))] outline-none transition-[opacity,scale,filter] duration-150 ease-out data-starting-style:opacity-0 data-ending-style:opacity-0 motion-safe:data-starting-style:scale-95 motion-safe:data-starting-style:blur-[2px] motion-safe:data-ending-style:scale-95 motion-safe:data-ending-style:blur-[2px] motion-reduce:transition-opacity",
            POPOVER_RADIUS[radius],
            size === "sm" && "gap-2 p-3",
            size === "xs" && "gap-0.5 p-1.5",
            size === "flush" && "gap-0 p-0",
            variant === "ghost" &&
              "border-0 bg-transparent p-0 shadow-none ring-0",
            className
          )}
          {...props}
        >
          {showArrow && (
            <PopoverPrimitive.Arrow className="data-[side=bottom]:-top-[6px] data-[side=top]:-bottom-[6px] data-[side=left]:-right-[6px] data-[side=right]:-left-[6px]">
              <svg
                aria-hidden="true"
                width="12"
                height="7"
                viewBox="0 0 12 7"
                className="hidden overflow-visible fill-popover stroke-[var(--border-subtle,var(--border))] [filter:drop-shadow(0_-1.5px_1px_rgb(0_0_0/0.05))] in-data-[side=bottom]:block"
              >
                <path d="M0 7 L6 1 L12 7" />
              </svg>
              <svg
                aria-hidden="true"
                width="12"
                height="7"
                viewBox="0 0 12 7"
                className="hidden overflow-visible fill-popover stroke-[var(--border-subtle,var(--border))] [filter:drop-shadow(0_1.5px_1px_rgb(0_0_0/0.05))] in-data-[side=top]:block"
              >
                <path d="M0 0 L6 6 L12 0" />
              </svg>
              <svg
                aria-hidden="true"
                width="7"
                height="12"
                viewBox="0 0 7 12"
                className="hidden overflow-visible fill-popover stroke-[var(--border-subtle,var(--border))] [filter:drop-shadow(0_1.5px_1px_rgb(0_0_0/0.05))] in-data-[side=left]:block"
              >
                <path d="M0 0 L6 6 L0 12" />
              </svg>
              <svg
                aria-hidden="true"
                width="7"
                height="12"
                viewBox="0 0 7 12"
                className="hidden overflow-visible fill-popover stroke-[var(--border-subtle,var(--border))] [filter:drop-shadow(0_1.5px_1px_rgb(0_0_0/0.05))] in-data-[side=right]:block"
              >
                <path d="M7 0 L1 6 L7 12" />
              </svg>
            </PopoverPrimitive.Arrow>
          )}
          {props.children}
        </PopoverPrimitive.Popup>
      </PopoverPrimitive.Positioner>
    </PopoverPrimitive.Portal>
  );
}

function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="popover-header"
      className={cn("flex flex-col gap-0.5 text-sm", className)}
      {...props}
    />
  );
}

function PopoverFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="popover-footer"
      className={cn("flex items-center justify-end gap-2", className)}
      {...props}
    />
  );
}

function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) {
  return (
    <PopoverPrimitive.Title
      data-slot="popover-title"
      className={cn("font-medium", className)}
      {...props}
    />
  );
}

function PopoverDescription({
  className,
  ...props
}: PopoverPrimitive.Description.Props) {
  return (
    <PopoverPrimitive.Description
      data-slot="popover-description"
      className={cn("text-muted-foreground", className)}
      {...props}
    />
  );
}

export {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverHeader,
  PopoverFooter,
  PopoverTitle,
  PopoverTrigger,
};