Components / Layout & Overlay

Modal

Overlay dialogs for focused tasks and confirmations.

Default

Source ModalDemo.tsx

The exact code behind the live demo above. Fetch it raw at /components/source/modal.txt.

import React, { useState, useCallback } from "react";

function Section({ title, children }: { title: string; children: React.ReactNode }) {
  return (
    <div className="mb-8">
      <h3 className="text-sm font-bold text-[var(--flux-heading)] mb-3">{title}</h3>
      <div className="p-6 rounded border border-[var(--flux-grey-100)] bg-[var(--flux-surface)]">
        {children}
      </div>
    </div>
  );
}

export default function ModalDemo() {
  const [open, setOpen] = useState(false);

  const handleBackdropClick = useCallback(
    (e: React.MouseEvent<HTMLDivElement>) => {
      if (e.target === e.currentTarget) {
        setOpen(false);
      }
    },
    [],
  );

  return (
    <div>
      <Section title="Default">
        <button
          onClick={() => setOpen(true)}
          className="px-4 py-2 text-sm font-bold rounded bg-[var(--flux-primary-400)] text-white hover:opacity-90 transition-opacity"
        >
          Open Modal
        </button>
      </Section>

      {open && (
        <div
          className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center"
          onClick={handleBackdropClick}
        >
          <div className="bg-[var(--flux-surface)] rounded shadow-xl max-w-md w-full mx-4">
            <div className="p-5 border-b border-[var(--flux-grey-100)] flex items-center justify-between">
              <h4 className="text-base font-bold text-[var(--flux-heading)]">
                Confirm Action
              </h4>
              <button
                onClick={() => setOpen(false)}
                className="text-[var(--flux-grey-300)] hover:text-[var(--flux-black)] transition-colors"
              >
                <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
                  <path
                    d="M15 5L5 15M5 5l10 10"
                    stroke="currentColor"
                    strokeWidth="1.5"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                  />
                </svg>
              </button>
            </div>

            <div className="p-5">
              <p className="text-sm text-[var(--flux-black)] leading-relaxed">
                Are you sure you want to proceed with this action? This operation
                cannot be undone and will apply changes immediately.
              </p>
            </div>

            <div className="p-5 border-t border-[var(--flux-grey-100)] flex justify-end gap-3">
              <button
                onClick={() => setOpen(false)}
                className="px-4 py-2 text-sm font-bold rounded text-[var(--flux-black)] hover:bg-[var(--flux-grey-100)] transition-colors"
              >
                Cancel
              </button>
              <button
                onClick={() => setOpen(false)}
                className="px-4 py-2 text-sm font-bold rounded bg-[var(--flux-primary-400)] text-white hover:opacity-90 transition-opacity"
              >
                Confirm
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}