Skip to content

Dismissible

Feedback

Manual contextual notice dismissal with controlled state and optional focus return.

Controlled success noticeRestore action and focus returnInternal-state tipManual dismissal
Open preview

Responsive viewport — resize the window to explore. Direction and density apply only to this preview. Theme applies to the docs too.

Usage

Use for a notice or tip that people may dismiss when they have read it. Own open/onOpenChange or initialize internal state with defaultOpen. Dismissal has no timer, persistence, or backend effect. Supply a ref to a focusable control outside the notice when keyboard dismissal should return there; focus is scheduled after the removal commit. Restoration belongs to the caller, as shown by the preview button.

Import
import { Dismissible } from "@/components/ui/dismissible";

Shipped as editable source in src/components/ui/dismissible.tsx in both apps. Copy the implementation below when moving it to another project. Copy the complete example from Source code for all imports and state. Add "use client" when using hooks or event handlers. See installation and theming for project setup.

Component source

The complete implementation is included in both apps. It uses the React, Radix, Lucide, and shared-core dependencies already installed in Apex. Keep your project’s semantic theme tokens when reusing it.

src/components/ui/dismissible.tsx
"use client";
import { useState, type ReactNode, type RefObject } from "react";
import { X, CheckCircle2, Info } from "lucide-react";
import { cn } from "@dashboardpack/core/lib/utils";
export interface DismissibleProps {
  title: string;
  children: ReactNode;
  open?: boolean;
  defaultOpen?: boolean;
  onOpenChange?: (open: boolean) => void;
  focusReturnRef?: RefObject<HTMLElement | null>;
  tone?: "neutral" | "success";
  className?: string;
}
export function Dismissible({
  title,
  children,
  open,
  defaultOpen = true,
  onOpenChange,
  focusReturnRef,
  tone = "neutral",
  className,
}: DismissibleProps) {
  const [internal, setInternal] = useState(defaultOpen);
  if (!(open ?? internal)) return null;
  const Icon = tone === "success" ? CheckCircle2 : Info;
  return (
    <section
      aria-label={title}
      className={cn(
        "flex min-w-0 items-start gap-3 rounded-xl border bg-card p-4",
        tone === "success" && "border-success/30 bg-success/5",
        className,
      )}
    >
      <Icon
        aria-hidden
        className={cn(
          "mt-0.5 size-5 shrink-0",
          tone === "success" ? "text-success" : "text-muted-foreground",
        )}
      />
      <div className="min-w-0 flex-1 space-y-1">
        <h3 className="text-sm font-semibold">{title}</h3>
        <div className="text-sm leading-6 text-muted-foreground">
          {children}
        </div>
      </div>
      <button
        type="button"
        aria-label={`Dismiss ${title}`}
        onClick={() => {
          setInternal(false);
          onOpenChange?.(false);
          requestAnimationFrame(() => focusReturnRef?.current?.focus());
        }}
        className="flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring"
      >
        <X className="size-4" />
      </button>
    </section>
  );
}

API reference

Common props and defaults. Slot-specific props are named below; native elements also accept their standard HTML attributes. The component TypeScript definitions are the complete type reference.

Dismissible props
PropType / DefaultDescription
titlestring

Default: required

Visible heading, section name, and close-action context.
childrenReactNode

Default: required

Readable notice content.
open / defaultOpenboolean

Default: internal true

Controlled state or initial internal visibility.
onOpenChange(open: boolean) => void

Default: —

Observe dismissal and apply controlled state.
focusReturnRefRefObject<HTMLElement | null>

Default: —

Focusable control outside the notice to receive focus after dismissal.
toneneutral | success

Default: neutral

Notice treatment.
classNamestring

Default: —

Extend the container with semantic theme utilities.

Build with these too