Tips & Tricks

Designing and Implementing Modal Dialogs dengan Claude Code

Learn about designing and implementing modal dialogs using Claude Code. Includes practical code examples.

設計原則 modal・dialog

modal pengguna 注意 特定 operasi 集中させるUIpola.Namun、フォーカスmanajemenやキーボードoperasi、スクリーンリーダーdukungan 怠る dan 、アクセシビリティ masalah 引き起こ.Claude Code 使えば、WAI-ARIA準拠 modal 正しくimplementasi bisa dilakukan.

HTML dialogelemenを使ったimplementasi

> HTML標準 dialogelemen 使ったmodalkomponen buatkan.
> アクセシビリティ dan animasi support.
import { useRef, useEffect } from 'react';

interface DialogProps {
  open: boolean;
  onClose: () => void;
  title: string;
  children: React.ReactNode;
}

function Dialog({ open, onClose, title, children }: DialogProps) {
  const dialogRef = useRef<HTMLDialogElement>(null);

  useEffect(() => {
    const dialog = dialogRef.current;
    if (!dialog) return;

    if (open) {
      dialog.showModal();
    } else {
      dialog.close();
    }
  }, [open]);

  useEffect(() => {
    const dialog = dialogRef.current;
    if (!dialog) return;

    const handleClose = () => onClose();
    dialog.addEventListener('close', handleClose);

    const handleClick = (e: MouseEvent) => {
      if (e.target === dialog) onClose();
    };
    dialog.addEventListener('click', handleClick);

    return () => {
      dialog.removeEventListener('close', handleClose);
      dialog.removeEventListener('click', handleClick);
    };
  }, [onClose]);

  return (
    <dialog
      ref={dialogRef}
      aria-labelledby="dialog-title"
      className="rounded-xl shadow-2xl p-0 backdrop:bg-black/50 backdrop:backdrop-blur-sm
        max-w-lg w-full open:animate-fadeIn"
    >
      <div className="p-6">
        <div className="flex items-center justify-between mb-4">
          <h2 id="dialog-title" className="text-xl font-bold">{title}</h2>
          <button onClick={onClose} aria-label="Close"
            className="rounded-full p-1 hover:bg-gray-100">✕</button>
        </div>
        {children}
      </div>
    </dialog>
  );
}

konfirmasidialog

> 「Deleteしてよろしい dengan すか?」 seperti konfirmasidialog Promiseベース dengan 使えるよう.
import { createRoot } from 'react-dom/client';

interface ConfirmOptions {
  title: string;
  message: string;
  confirmLabel?: string;
  cancelLabel?: string;
  variant?: 'danger' | 'default';
}

function confirm(options: ConfirmOptions): Promise<boolean> {
  return new Promise((resolve) => {
    const container = document.createElement('div');
    document.body.appendChild(container);
    const root = createRoot(container);

    const handleClose = (result: boolean) => {
      root.unmount();
      container.remove();
      resolve(result);
    };

    root.render(
      <Dialog open={true} onClose={() => handleClose(false)} title={options.title}>
        <p className="text-gray-600 mb-6">{options.message}</p>
        <div className="flex justify-end gap-3">
          <button onClick={() => handleClose(false)}
            className="px-4 py-2 border rounded-lg hover:bg-gray-50">
            {options.cancelLabel ?? 'キャンセル'}
          </button>
          <button onClick={() => handleClose(true)}
            className={`px-4 py-2 rounded-lg text-white ${
              options.variant === 'danger' ? 'bg-red-600 hover:bg-red-700' : 'bg-blue-600 hover:bg-blue-700'
            }`}>
            {options.confirmLabel ?? 'konfirmasi'}
          </button>
        </div>
      </Dialog>
    );
  });
}

// Usage example
async function handleDelete(id: string) {
  const ok = await confirm({
    title: 'Delete konfirmasi',
    message: 'こ 項目 Delete よろしい すか?こ operasi 取り消せません。',
    confirmLabel: 'Delete',
    variant: 'danger',
  });
  if (ok) await deleteItem(id);
}

commandパレット

function CommandPalette({ commands, onClose }: { commands: Command[]; onClose: () => void }) {
  const [query, setQuery] = useState('');
  const [activeIndex, setActiveIndex] = useState(0);
  const filtered = commands.filter((c) =>
    c.label.toLowerCase().includes(query.toLowerCase())
  );

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      setActiveIndex((prev) => Math.min(prev + 1, filtered.length - 1));
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      setActiveIndex((prev) => Math.max(prev - 1, 0));
    } else if (e.key === 'Enter' && filtered[activeIndex]) {
      filtered[activeIndex].action();
      onClose();
    }
  };

  return (
    <Dialog open={true} onClose={onClose} title="commandパレット">
      <input
        autoFocus
        value={query}
        onChange={(e) => { setQuery(e.target.value); setActiveIndex(0); }}
        onKeyDown={handleKeyDown}
        placeholder="command pencarian..."
        className="w-full px-3 py-2 border rounded-lg mb-3"
        role="combobox"
        aria-expanded={true}
      />
      <ul role="listbox" className="max-h-64 overflow-y-auto">
        {filtered.map((cmd, i) => (
          <li key={cmd.id} role="option" aria-selected={i === activeIndex}
            onClick={() => { cmd.action(); onClose(); }}
            className={`px-3 py-2 rounded cursor-pointer ${i === activeIndex ? 'bg-blue-100' : 'hover:bg-gray-50'}`}>
            {cmd.label}
          </li>
        ))}
      </ul>
    </Dialog>
  );
}

Summary

Untuk Claude Codeを使えば、HTML dialogelemenをpemanfaatanしたアクセシブルなmodalから、konfirmasidialog、commandパレットまでefisienにpembangunanできます。toastnotifikasiとの使い分けはtoastnotifikasiimplementasiを、アクセシビリティのdasar, lihat アクセシビリティガイド.

dialogelemen 仕様 MDN Web Docs - dialog silakan lihat.

#Claude Code #modal #dialog #React #accessibility