バナー - Vibe Coding Showcase

バナー

ユーザーに重要な情報を伝えるための通知バナーコンポーネント。成功、エラー、警告、プロモーションなど様々な用途に対応

デザインプレビュー

基本的なバナー

プロモーションバナー

アクション付きバナー

カスタムアイコン

フルワイドバナー

スティッキーバナー

バナーの特徴

豊富なバリエーション

情報、成功、警告、エラー、プロモーションなど、用途に応じた多彩なスタイルを提供

柔軟な配置オプション

ページ内配置、フルワイド、スティッキー表示など、様々な配置方法に対応

インタラクティブ

閉じるボタン、アクションボタン、カスタムアイコンによる豊かな対話性

AI活用ガイド

Cookieバナー

プロンプト例:

GDPR準拠のCookie同意バナーを作成してください。必須Cookie、分析Cookie、マーケティングCookieの個別選択、詳細説明リンク、同意/拒否ボタンを含めてください。

緊急通知システム

プロンプト例:

災害や緊急事態用の通知バナーシステムを実装してください。優先度レベル、自動表示/非表示、音声読み上げ対応、多言語サポート、位置情報連動を含めてください。

A/Bテスト対応バナー

プロンプト例:

マーケティング用のA/Bテスト可能なプロモーションバナーを開発してください。複数バリエーション管理、表示条件設定、CTR測定、コンバージョン追跡、レポート機能を実装してください。

実装コード

ファイルサイズ: 4.8KB TypeScript

コンポーネント実装

import React, { useState } from 'react';

interface BannerProps {
  type?: 'info' | 'success' | 'warning' | 'error' | 'promotion';
  title?: string;
  message: string;
  closable?: boolean;
  onClose?: () => void;
  action?: {
    label: string;
    onClick: () => void;
  };
  icon?: React.ReactNode;
  className?: string;
}

export const Banner: React.FC<BannerProps> = ({
  type = 'info',
  title,
  message,
  closable = true,
  onClose,
  action,
  icon,
  className = ''
}) => {
  const [isVisible, setIsVisible] = useState(true);

  const handleClose = () => {
    setIsVisible(false);
    onClose?.();
  };

  if (!isVisible) return null;

  const typeStyles = {
    info: {
      bg: 'bg-blue-50 border-blue-200',
      text: 'text-blue-800',
      icon: 'text-blue-600',
      button: 'bg-blue-600 hover:bg-blue-700 text-white',
      closeButton: 'text-blue-600 hover:text-blue-800'
    },
    success: {
      bg: 'bg-green-50 border-green-200',
      text: 'text-green-800',
      icon: 'text-green-600',
      button: 'bg-green-600 hover:bg-green-700 text-white',
      closeButton: 'text-green-600 hover:text-green-800'
    },
    warning: {
      bg: 'bg-yellow-50 border-yellow-200',
      text: 'text-yellow-800',
      icon: 'text-yellow-600',
      button: 'bg-yellow-600 hover:bg-yellow-700 text-white',
      closeButton: 'text-yellow-600 hover:text-yellow-800'
    },
    error: {
      bg: 'bg-red-50 border-red-200',
      text: 'text-red-800',
      icon: 'text-red-600',
      button: 'bg-red-600 hover:bg-red-700 text-white',
      closeButton: 'text-red-600 hover:text-red-800'
    },
    promotion: {
      bg: 'bg-gradient-to-r from-purple-600 to-pink-600',
      text: 'text-white',
      icon: 'text-white',
      button: 'bg-white text-purple-600 hover:bg-gray-100',
      closeButton: 'text-white hover:text-gray-200'
    }
  };

  const defaultIcons = {
    info: (
      <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
      </svg>
    ),
    success: (
      <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
      </svg>
    ),
    warning: (
      <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
      </svg>
    ),
    error: (
      <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
      </svg>
    ),
    promotion: (
      <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
      </svg>
    )
  };

  const styles = typeStyles[type];
  const displayIcon = icon || defaultIcons[type];

  return (
    <div
      className={`
        relative px-4 py-3 border rounded-lg
        ${type === 'promotion' ? '' : 'border'}
        ${styles.bg}
        ${className}
      `}
      role="alert"
    >
      <div className="flex items-start">
        {/* アイコン */}
        <div className={`flex-shrink-0 ${styles.icon}`}>
          {displayIcon}
        </div>

        {/* コンテンツ */}
        <div className="ml-3 flex-1">
          {title && (
            <h3 className={`text-sm font-semibold ${styles.text} mb-1`}>
              {title}
            </h3>
          )}
          <p className={`text-sm ${styles.text}`}>
            {message}
          </p>
        </div>

        {/* アクションボタン */}
        {action && (
          <div className="ml-4 flex-shrink-0">
            <button
              onClick={action.onClick}
              className={`
                px-3 py-1 text-sm font-medium rounded-md
                transition-colors duration-200
                ${styles.button}
              `}
            >
              {action.label}
            </button>
          </div>
        )}

        {/* 閉じるボタン */}
        {closable && (
          <button
            onClick={handleClose}
            className={`
              ml-4 flex-shrink-0 inline-flex rounded-md p-1.5
              transition-colors duration-200
              hover:bg-white/20
              ${styles.closeButton}
            `}
            aria-label="閉じる"
          >
            <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
            </svg>
          </button>
        )}
      </div>
    </div>
  );
};

// フルワイドバナー
export const FullWidthBanner: React.FC<BannerProps> = (props) => {
  return (
    <div className="w-full">
      <Banner {...props} className="rounded-none border-x-0" />
    </div>
  );
};

// スティッキーバナー
export const StickyBanner: React.FC<BannerProps & { position?: 'top' | 'bottom' }> = ({
  position = 'top',
  ...props
}) => {
  const positionClasses = position === 'top' 
    ? 'top-0' 
    : 'bottom-0';

  return (
    <div className={`fixed ${positionClasses} left-0 right-0 z-50`}>
      <FullWidthBanner {...props} />
    </div>
  );
};

使用例

// 基本的な使用例
import { Banner, FullWidthBanner, StickyBanner } from './banner';

function App() {
  const [showBanner, setShowBanner] = useState(true);

  return (
    <div>
      {/* 基本的なバナー */}
      <Banner
        type="info"
        title="お知らせ"
        message="システムメンテナンスは本日午後10時から実施されます。"
        onClose={() => setShowBanner(false)}
      />

      {/* 成功バナー */}
      <Banner
        type="success"
        message="データが正常に保存されました。"
      />

      {/* アクション付き警告バナー */}
      <Banner
        type="warning"
        title="アカウントの確認が必要です"
        message="セキュリティ向上のため、メールアドレスの確認をお願いします。"
        action={{
          label: '確認する',
          onClick: handleVerify
        }}
      />

      {/* プロモーションバナー */}
      <Banner
        type="promotion"
        title="期間限定オファー!"
        message="今なら全商品20%OFF"
        action={{
          label: '今すぐ購入',
          onClick: handleShop
        }}
      />

      {/* フルワイドバナー */}
      <FullWidthBanner
        type="info"
        message="新しいプライバシーポリシーが適用されました。"
        action={{
          label: '詳細を確認',
          onClick: handlePolicy
        }}
      />

      {/* スティッキーバナー(Cookie同意) */}
      <StickyBanner
        position="bottom"
        type="info"
        message="このサイトはCookieを使用しています。"
        action={{
          label: '同意する',
          onClick: handleAcceptCookies
        }}
        closable={false}
      />

      {/* カスタムアイコン */}
      <Banner
        type="info"
        message="新機能がリリースされました!"
        icon={<CustomIcon />}
      />
    </div>
  );
}