Tips & Tricks

Como Implementar Notificacoes Toast com Claude Code

Aprenda a implement toast notifications usando o Claude Code. Inclui exemplos praticos de codigo e orientacao passo a passo.

トースト通知の設計

トースト通知は操作結果やシステムメッセージを一時的に表示するUIパターンです。モーダルと違いユーザーの操作を妨げないため、フィードバックに最適です。Claude Codeを使えば、アクセシブルで美しいトースト通知システムを構築できます。

トースト管理システムの実装

> グローバルに使えるトースト通知システムを作って。
> success, error, warning, infoの4種類に対応して。
type ToastType = 'success' | 'error' | 'warning' | 'info';

interface Toast {
  id: string;
  type: ToastType;
  message: string;
  duration: number;
  dismissible: boolean;
}

type ToastListener = (toasts: Toast[]) => void;

class ToastManager {
  private toasts: Toast[] = [];
  private listeners: Set<ToastListener> = new Set();
  private counter = 0;

  subscribe(listener: ToastListener) {
    this.listeners.add(listener);
    return () => this.listeners.delete(listener);
  }

  private notify() {
    this.listeners.forEach((fn) => fn([...this.toasts]));
  }

  add(type: ToastType, message: string, options?: Partial<Pick<Toast, 'duration' | 'dismissible'>>) {
    const id = `toast-${++this.counter}`;
    const toast: Toast = {
      id,
      type,
      message,
      duration: options?.duration ?? 5000,
      dismissible: options?.dismissible ?? true,
    };

    this.toasts = [...this.toasts, toast];
    this.notify();

    if (toast.duration > 0) {
      setTimeout(() => this.remove(id), toast.duration);
    }

    return id;
  }

  remove(id: string) {
    this.toasts = this.toasts.filter((t) => t.id !== id);
    this.notify();
  }

  success(message: string) { return this.add('success', message); }
  error(message: string) { return this.add('error', message, { duration: 8000 }); }
  warning(message: string) { return this.add('warning', message); }
  info(message: string) { return this.add('info', message); }
}

export const toast = new ToastManager();

Reactコンポーネント

import { useState, useEffect } from 'react';

const ICONS: Record<ToastType, string> = {
  success: '✓',
  error: '✕',
  warning: '⚠',
  info: 'ℹ',
};

const STYLES: Record<ToastType, string> = {
  success: 'bg-green-50 border-green-200 text-green-800',
  error: 'bg-red-50 border-red-200 text-red-800',
  warning: 'bg-yellow-50 border-yellow-200 text-yellow-800',
  info: 'bg-blue-50 border-blue-200 text-blue-800',
};

function ToastContainer() {
  const [toasts, setToasts] = useState<Toast[]>([]);

  useEffect(() => {
    return toast.subscribe(setToasts);
  }, []);

  return (
    <div
      aria-live="polite"
      aria-label="通知"
      className="fixed top-4 right-4 z-50 flex flex-col gap-2 max-w-sm w-full"
    >
      {toasts.map((t) => (
        <div
          key={t.id}
          role="status"
          className={`flex items-start gap-3 p-4 rounded-lg border shadow-lg
            animate-slideIn ${STYLES[t.type]}`}
        >
          <span className="text-lg font-bold flex-shrink-0">{ICONS[t.type]}</span>
          <p className="flex-1 text-sm">{t.message}</p>
          {t.dismissible && (
            <button
              onClick={() => toast.remove(t.id)}
              aria-label="通知をClose"
              className="text-gray-400 hover:text-gray-600"
            >✕</button>
          )}
        </div>
      ))}
    </div>
  );
}

アニメーション

@keyframes slideIn {
  from {
    transform: translateX(100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

@keyframes slideOut {
  from {
    transform: translateX(0);
    opacity: 1;
  }
  to {
    transform: translateX(100%);
    opacity: 0;
  }
}

.animate-slideIn {
  animation: slideIn 0.3s ease-out;
}

.animate-slideOut {
  animation: slideOut 0.3s ease-in forwards;
}

Reactフックでの活用

export function useToast() {
  return {
    success: (msg: string) => toast.success(msg),
    error: (msg: string) => toast.error(msg),
    warning: (msg: string) => toast.warning(msg),
    info: (msg: string) => toast.info(msg),
  };
}

// Usage example
function SaveButton() {
  const { success, error } = useToast();

  const handleSave = async () => {
    try {
      await saveData();
      success('保存しました');
    } catch (e) {
      error('保存に失敗しました。もう一度お試しください。');
    }
  };

  return <button onClick={handleSave}>保存</button>;
}

Summary

Claude Codeを使えば、型安全でアクセシブルなトースト通知システムをアニメーション付きで素早く構築できます。モーダルとの使い分けはモーダル・ダイアログ設計を、アニメーション全般はアニメーション実装を参照してください。

WAI-ARIAのライブリージョンについてはMDN Web Docs - aria-liveをご覧ください。

#Claude Code #toast #通知 #React #UI