|Design System

Core Component

Modal

Dialog overlay for confirmations, settings, and focused tasks that require attention before continuing.

Preview

Delete conversation?

This will permanently delete the conversation and all artifacts. This action cannot be undone.

Source

Full component implementation using the design system tokens.

tsx
"use client";

import { useEffect, useRef } from "react";
import { X } from "lucide-react";

export function DSModal({
  open = false,
  onClose,
  title,
  children,
  footer,
}: {
  open?: boolean;
  onClose?: () => void;
  title: string;
  children: React.ReactNode;
  footer?: React.ReactNode;
}) {
  const overlayRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === "Escape" && onClose) onClose();
    };
    if (open) {
      document.addEventListener("keydown", handleKeyDown);
      document.body.style.overflow = "hidden";
    }
    return () => {
      document.removeEventListener("keydown", handleKeyDown);
      document.body.style.overflow = "";
    };
  }, [open, onClose]);

  if (!open) return null;

  return (
    <div
      ref={overlayRef}
      className="fixed inset-0 z-50 flex items-center justify-center p-4"
      role="dialog"
      aria-modal="true"
      aria-labelledby="modal-title"
    >
      <div
        className="absolute inset-0 bg-overlay/40 backdrop-blur-sm"
        onClick={onClose}
      />
      <div className="relative w-full max-w-md bg-background border border-overlay/10 rounded-2xl shadow-xl">
        <div className="flex items-center justify-between px-6 py-4 border-b border-overlay/5">
          <h2 id="modal-title" className="text-lg font-semibold text-foreground">
            {title}
          </h2>
          {onClose && (
            <button
              onClick={onClose}
              className="p-1 text-tertiary hover:text-foreground transition-colors rounded-lg hover:bg-overlay/5"
              aria-label="Close dialog"
            >
              <X className="w-5 h-5" />
            </button>
          )}
        </div>
        <div className="px-6 py-5">{children}</div>
        {footer && (
          <div className="flex items-center justify-end gap-3 px-6 py-4 border-t border-overlay/5">
            {footer}
          </div>
        )}
      </div>
    </div>
  );
}

Props

All available props with types and defaults.

PropTypeDescription
title*stringDialog title
children*ReactNodeDialog body content
openbooleanControls visibility
onClose() => voidClose callback — enables backdrop click and Escape key
footerReactNodeFooter area — typically action buttons

Variants

Confirmation

Destructive action confirmation with cancel/confirm buttons.

Delete conversation?

This will permanently delete the conversation and all artifacts.

tsx
<DSModal
  open={true}
  onClose={close}
  title="Delete conversation?"
  footer={
    <>
      <DSButton variant="ghost" onClick={close}>Cancel</DSButton>
      <DSButton variant="destructive">Delete</DSButton>
    </>
  }
>
  <p>This will permanently delete the conversation and all artifacts.</p>
</DSModal>

Settings

Form-based dialog for model configuration.

Model Settings

tsx
<DSModal
  open={true}
  onClose={close}
  title="Model Settings"
  footer={<DSButton>Save changes</DSButton>}
>
  <DSInput label="Temperature" placeholder="0.7" />
  <DSInput label="Max tokens" placeholder="4096" />
</DSModal>

Prompt Guide

Prompt Guide — Modal

Use for

  • Destructive action confirmations (delete conversation, clear history)
  • Settings dialogs (model config, API keys)
  • Focused input tasks (rename, export options)
  • Onboarding flows and first-run setup

Don't use for

  • Simple confirmations — use Toast instead
  • Content display — use Artifact Card for AI-generated content
  • Frequent actions — modals interrupt flow; use inline UI

AI Context

Modals in AI products are for high-stakes moments: 'Delete this conversation?' (destructive), 'Configure API key' (setup), 'Export chat' (focused task). Always provide Escape key dismissal and backdrop click. Focus traps inside the modal. Return focus to the trigger when closed.