A popup that opens on top of the entire page.
Installation
npx shadcn@latest add @caprice/dialogpnpm dlx shadcn@latest add @caprice/dialogyarn dlx shadcn@latest add @caprice/dialogbunx shadcn@latest add @caprice/dialognpm install @caprice-ui/reactpnpm add @caprice-ui/reactyarn add @caprice-ui/reactbun add @caprice-ui/reactInstall the following dependencies:
npm install @base-ui/react lucide-reactpnpm add @base-ui/react lucide-reactyarn add @base-ui/react lucide-reactbun add @base-ui/react lucide-reactAdd a cn helper
import { cn as merge } from 'cn';
import type { VariantProps } from 'cva';
import { defineConfig } from 'cva/config';
import { getSchema } from 'cva/tools';
export const { cva, cx: cn } = defineConfig({
cx: merge,
});
export type { VariantProps };
export { getSchema };
Copy and paste the following code into your project.
'use client';
import { mergeProps, useRender } from '@base-ui/react';
import { Dialog as DialogPrimitive } from '@base-ui/react/dialog';
import { XIcon } from 'lucide-react';
import type * as React from 'react';
import { cn } from '@/lib/utils';
/**
* Groups all parts of the dialog.
* Doesn't render its own HTML element.
*
* Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog)
*
* API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#root)
*/
export namespace Root {
export type Props = React.ComponentProps<typeof DialogPrimitive.Root>;
export type Actions = DialogPrimitive.Root.Actions;
export type ChangeEventDetails = DialogPrimitive.Root.ChangeEventDetails;
export type ChangeEventReason = DialogPrimitive.Root.ChangeEventReason;
}
export function Root({ ...props }: Root.Props) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
}
/**
* A button that opens the dialog.
* Renders a `<button>` element.
*
* Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog)
*
* API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#trigger)
*/
export namespace Trigger {
export type State = DialogPrimitive.Trigger.State;
export type Props = React.ComponentProps<typeof DialogPrimitive.Trigger>;
}
export function Trigger({ ...props }: Trigger.Props) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
}
/**
* A positioning container for the dialog popup that can be made scrollable.
* Renders a `<div>` element.
*
* Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog)
*
* API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#viewport)
*/
export namespace Viewport {
export type State = DialogPrimitive.Viewport.State;
export type Props = React.ComponentProps<typeof DialogPrimitive.Viewport>;
}
export function Viewport({ ...props }: Viewport.Props) {
return <DialogPrimitive.Viewport data-slot="dialog-viewport" {...props} />;
}
/**
* A portal element that moves the popup to a different part of the DOM.
* By default, the portal element is appended to `<body>`.
* Renders a `<div>` element.
*
* Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog)
*
* API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#portal)
*/
export namespace Portal {
export interface State extends DialogPrimitive.Portal.State {}
export type Props = React.ComponentProps<typeof DialogPrimitive.Portal>;
}
export function Portal({ ...props }: Portal.Props) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
}
/**
* A button that closes the dialog.
* Renders a `<button>` element.
*
* Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog)
*
* API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#close)
*/
export namespace Close {
export type State = DialogPrimitive.Close.State;
export type Props = React.ComponentProps<typeof DialogPrimitive.Close>;
}
export function Close({ ...props }: Close.Props) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
}
/**
* An overlay displayed beneath the popup.
* Renders a `<div>` element.
*
* Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog)
*
* API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#backdrop)
*/
export namespace Backdrop {
export type State = DialogPrimitive.Backdrop.State;
export type Props = React.ComponentProps<typeof DialogPrimitive.Backdrop>;
}
export function Backdrop({ className, ...props }: Backdrop.Props) {
return (
<DialogPrimitive.Backdrop
className={cn(
'fixed inset-0 z-50 bg-black/40 backdrop-blur-[10px] transition-all duration-200 ease-in-out [[data-starting-style],[data-ending-style]]:opacity-0',
className
)}
data-slot="dialog-backdrop"
{...props}
/>
);
}
/**
* A container for the dialog contents.
* Renders a `<div>` element.
*
* Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog)
*
* API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#popup)
*/
export namespace Popup {
export type State = DialogPrimitive.Popup.State;
export type Props = React.ComponentProps<typeof DialogPrimitive.Popup> & {
/**
* Whether to show the close button
* @default true
*/
showCloseButton?: boolean;
};
}
export function Popup({ className, showCloseButton = true, children, ...props }: Popup.Props) {
return (
<Portal>
<Backdrop />
<DialogPrimitive.Popup
className={cn(
// Positioning
'fixed start-1/2 z-50 -translate-x-1/2 max-sm:bottom-2 sm:-translate-y-1/2',
// Sizing
'max-h-[calc(100vh-1rem)] w-full max-w-[calc(100%-4rem)] overflow-y-auto sm:max-w-lg',
// Display
'grid gap-4 rounded-2xl border bg-popover p-6 text-popover-foreground shadow-lg',
// Animation
'origin-top transition-all duration-200 ease-in-out will-change-transform motion-reduce:transition-none [[data-starting-style],[data-ending-style]]:scale-98 [[data-starting-style],[data-ending-style]]:opacity-0 max-sm:[[data-starting-style],[data-ending-style]]:translate-y-4',
// Nested dialogs
'data-nested-dialog-open:after:absolute data-nested-dialog-open:after:inset-0 data-nested-dialog-open:after:backdrop-blur-xs sm:top-[calc(50%+1.25rem*var(--nested-dialogs))] sm:scale-[calc(1-0.1*var(--nested-dialogs))] sm:data-nested:[[data-starting-style],[data-ending-style]]:translate-y-8',
className
)}
data-slot="dialog-popup"
{...props}
>
{children}
{showCloseButton && (
<Close
className="pointer-coarse:touch-hitbox absolute end-4 top-4 rounded-md p-1.5 opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0"
data-slot="dialog-close"
>
<XIcon />
<span className="sr-only">Close</span>
</Close>
)}
</DialogPrimitive.Popup>
</Portal>
);
}
/**
* @deprecated Use {@link Popup} instead
* @see {@link https://caprice-ui.com/docs/components/dialog}
*/
export const Content = Popup;
/**
* A container for the dialog header.
* Renders a `<div>` element.
*
* Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog)
*/
export namespace Header {
export type Props = useRender.ComponentProps<'div'>;
}
export function Header({ className, render, ...props }: Header.Props) {
const defaultProps: useRender.ElementProps<'div'> & { 'data-slot'?: string } = {
'data-slot': 'dialog-header',
className: cn('flex flex-col gap-2 text-center sm:text-start', className),
};
return useRender({
defaultTagName: 'div',
render,
props: mergeProps<'div'>(defaultProps, props),
});
}
/**
* A heading that labels the dialog.
* Renders an `<h2>` element.
*
* Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog)
*
* API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#title)
*/
export namespace Title {
export type State = DialogPrimitive.Title.State;
export type Props = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>;
}
export function Title({ className, ...props }: Title.Props) {
return (
<DialogPrimitive.Title
className={cn('font-semibold text-lg leading-none', className)}
data-slot="dialog-title"
{...props}
/>
);
}
/**
* A paragraph with additional information about the dialog.
* Renders a `<p>` element.
*
* Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog)
*
* API Reference: [Base UI Dialog](https://base-ui.com/react/components/dialog#description)
*/
export namespace Description {
export type State = DialogPrimitive.Description.State;
export type Props = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>;
}
export function Description({ className, ...props }: Description.Props) {
return (
<DialogPrimitive.Description
className={cn('text-muted-foreground text-sm', className)}
data-slot="dialog-description"
{...props}
/>
);
}
/**
* A container for the dialog footer.
* Renders a `<div>` element.
*
* Documentation: [Caprice UI Dialog](https://caprice-ui.com/docs/components/dialog)
*/
export namespace Footer {
export type Props = useRender.ComponentProps<'div'>;
}
export function Footer({ className, render, ...props }: Footer.Props) {
const defaultProps: useRender.ElementProps<'div'> & { 'data-slot'?: string } = {
'data-slot': 'dialog-footer',
className: cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className),
};
return useRender({
defaultTagName: 'div',
render,
props: mergeProps<'div'>(defaultProps, props),
});
}
Update the import paths to match your project setup.
Usage
import * as Dialog from "@/components/caprice-ui/dialog"import * as Dialog from "@caprice-ui/react/dialog"Examples
API Reference
Last updated on