codefast/ui

Command Palette

Search for a command to run...

Source
Overlay

Popover

Non-modal floating panel anchored to a trigger. Use for settings panels and pickers.

Examples

Align

Use the align prop on PopoverContent to control the horizontal alignment.

39 lines
import { Button } from "@codefast/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@codefast/ui/popover";

export function PopoverAlignments() {
  return (
    <div className="flex gap-6">
      <Popover>
        <PopoverTrigger asChild>
          <Button variant="outline" size="sm">
            Start
          </Button>
        </PopoverTrigger>
        <PopoverContent align="start" className="w-40">
          Aligned to start
        </PopoverContent>
      </Popover>
      <Popover>
        <PopoverTrigger asChild>
          <Button variant="outline" size="sm">
            Center
          </Button>
        </PopoverTrigger>
        <PopoverContent align="center" className="w-40">
          Aligned to center
        </PopoverContent>
      </Popover>
      <Popover>
        <PopoverTrigger asChild>
          <Button variant="outline" size="sm">
            End
          </Button>
        </PopoverTrigger>
        <PopoverContent align="end" className="w-40">
          Aligned to end
        </PopoverContent>
      </Popover>
    </div>
  );
}

Basic

A simple popover with a header, title, and description.

25 lines
import { Button } from "@codefast/ui/button";
import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverHeader,
  PopoverTitle,
  PopoverTrigger,
} from "@codefast/ui/popover";

export function PopoverBasic() {
  return (
    <Popover>
      <PopoverTrigger asChild>
        <Button variant="outline">Open Popover</Button>
      </PopoverTrigger>
      <PopoverContent align="start">
        <PopoverHeader>
          <PopoverTitle>Dimensions</PopoverTitle>
          <PopoverDescription>Set the dimensions for the layer.</PopoverDescription>
        </PopoverHeader>
      </PopoverContent>
    </Popover>
  );
}

With Form

A popover with form fields inside.

41 lines
import { Button } from "@codefast/ui/button";
import { Field, FieldGroup, FieldLabel } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverHeader,
  PopoverTitle,
  PopoverTrigger,
} from "@codefast/ui/popover";

export function PopoverForm() {
  return (
    <Popover>
      <PopoverTrigger asChild>
        <Button variant="outline">Open Popover</Button>
      </PopoverTrigger>
      <PopoverContent className="w-64" align="start">
        <PopoverHeader>
          <PopoverTitle>Dimensions</PopoverTitle>
          <PopoverDescription>Set the dimensions for the layer.</PopoverDescription>
        </PopoverHeader>
        <FieldGroup className="gap-4">
          <Field orientation="horizontal">
            <FieldLabel htmlFor="width" className="w-1/2">
              Width
            </FieldLabel>
            <Input id="width" defaultValue="100%" />
          </Field>
          <Field orientation="horizontal">
            <FieldLabel htmlFor="height" className="w-1/2">
              Height
            </FieldLabel>
            <Input id="height" defaultValue="25px" />
          </Field>
        </FieldGroup>
      </PopoverContent>
    </Popover>
  );
}

RTL

Right-to-left layout support for languages such as Arabic and Hebrew.

Translations are AI-generated for demonstration and may be imperfect.

72 lines
import { Button } from "@codefast/ui/button";
import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverHeader,
  PopoverTitle,
  PopoverTrigger,
} from "@codefast/ui/popover";

import type { Translations } from "#/features/components-catalog/components/detail/language";
import { useTranslation } from "#/features/components-catalog/components/detail/language-context";

const translations: Translations = {
  en: {
    dir: "ltr",
    values: {
      title: "Dimensions",
      description: "Set the dimensions for the layer.",
      left: "Left",
      top: "Top",
      bottom: "Bottom",
      right: "Right",
    },
  },
  ar: {
    dir: "rtl",
    values: {
      title: "الأبعاد",
      description: "تعيين الأبعاد للطبقة.",
      left: "يسار",
      top: "أعلى",
      bottom: "أسفل",
      right: "يمين",
    },
  },
  he: {
    dir: "rtl",
    values: {
      title: "מימדים",
      description: "הגדר את המימדים לשכבה.",
      left: "שמאל",
      top: "למעלה",
      bottom: "למטה",
      right: "ימין",
    },
  },
};

const physicalSides = ["left", "top", "bottom", "right"] as const;

export function PopoverRtl() {
  const { dir, t } = useTranslation(translations, "ar");

  return (
    <div className="flex flex-wrap justify-center gap-2">
      {physicalSides.map((side) => (
        <Popover key={side}>
          <PopoverTrigger asChild>
            <Button variant="outline">{t[side]}</Button>
          </PopoverTrigger>
          <PopoverContent side={side} dir={dir}>
            <PopoverHeader>
              <PopoverTitle>{t.title}</PopoverTitle>
              <PopoverDescription>{t.description}</PopoverDescription>
            </PopoverHeader>
          </PopoverContent>
        </Popover>
      ))}
    </div>
  );
}

Anatomy

How the parts nest — every slot the component exposes, in composition order.

Popover
├── PopoverTrigger
└── PopoverContent
└── PopoverHeader
└── PopoverTitle

Features

  • Non-modal by default — the rest of the page stays interactive while it's open; set modal to trap focus like a Dialog.
  • PopoverAnchor lets the panel point at a different element than the one that opens it.
  • side/align/sideOffset on PopoverContent control placement and the gap from the trigger.

API reference

Props for each part of the component. All native element props are also forwarded.

Popover

Root. Manages open state. Non-modal by default.

openboolean

The controlled open state.

defaultOpenboolean

The open state when initially rendered (uncontrolled).

Defaultfalse

onOpenChange(open: boolean) => void

Called when the open state changes.

modalboolean

When true, traps focus and blocks outside interaction.

Defaultfalse

PopoverContent

side"top" | "right" | "bottom" | "left"

The preferred edge of the trigger to render against.

Default"bottom"

align"start" | "center" | "end"

The preferred alignment against the trigger.

Default"center"

sideOffsetnumber

Gap in px between trigger and content.

Default4

Accessibility

Built to be keyboard-navigable and screen-reader friendly out of the box.

KeyFunction
SpaceOpens the popover when the trigger is focused.
TabMoves through focusable elements inside the content.
EscCloses the popover and returns focus to the trigger.
  • Focus moves into the content on open and returns to the trigger on close.
  • Unlike a Dialog it is non-modal — the rest of the page stays interactive unless modal is set.
  • Use a Tooltip for hover hints; reach for a Popover when the panel holds controls.

Guidelines

Conventions that keep usage consistent across an app.

Do

  • Anchor short, focused tasks to the control they relate to.
  • Give the panel a title with PopoverTitle when it contains a form.

Don’t

  • Don’t put long, primary content in a popover — use a Dialog or a page.
  • Don’t use a Popover for passive hints — that’s a Tooltip.

Explore further

Ready to integrate?

Follow the Getting Started guide to install @codefast/ui, or browse the full component gallery.