Modal Variant

Standard Modal Layout

The Modal variant provides a classic, versatile modal design that works in any context. It features a centered layout, clean typography, and simple controls. The most flexible and universally compatible option.

Live Preview

Installation

Generate the Modal component:

bash
npx devfund@latest modal

This creates components/devfund/ModalPaymentQR.tsx

Basic Usage

tsx
import ModalPaymentQR from '@/components/devfund/ModalPaymentQR';

export default function SupportPage() {
  return (
    <div className="flex items-center justify-center min-h-screen">
      <ModalPaymentQR username="yourname" />
    </div>
  );
}

Props

typescript
interface ModalPaymentQRProps {
  username: string;  // Required: The username to fetch payment data for
}

States

Default State

Shows a yellow button:

[šŸ’› Support {DisplayName}]

Loading State

Loading indicator:

[ā³ Loading...]

Success State

Standard modal showing:

  • User name as heading
  • "Scan with any UPI app" subtitle
  • QR code in gray background
  • UPI ID text
  • Close button

Error State

Error message:

[āš ļø Error]

When to Use Modal

Perfect for:

  • General-purpose implementations
  • When you're unsure which variant to use
  • Flexible design contexts
  • Teams with varying design preferences
  • Multiple platforms/contexts
  • Prototypes and MVPs

Customization

Change Button Color

tsx
className="bg-red-400 hover:bg-red-500"

Change Modal Size

tsx
// Larger modal
className="max-w-lg"  // from max-w-sm

// Smaller modal
className="max-w-xs"  // from max-w-sm

Change Button Text

tsx
// Custom text
<button>Send Support to {userData.displayName}</button>

Change QR Code Size

tsx
// Larger QR
<div className="bg-gray-50 rounded-xl p-8 mb-4">
  <img className="w-96 h-96" />
</div>

// Smaller QR
<div className="bg-gray-50 rounded-xl p-2 mb-4">
  <img className="w-40 h-40" />
</div>

Change Close Button Icon

tsx
// Text close
<button>Ɨ</button>

// Different icon
<button>āœ•</button>

Advanced Usage

In a Dashboard

tsx
import ModalPaymentQR from '@/components/devfund/ModalPaymentQR';

export default function Dashboard() {
  return (
    <div className="grid grid-cols-3 gap-8">
      <div className="col-span-2">
        <h1>Dashboard</h1>
        {/* Dashboard content */}
      </div>
      
      <aside className="bg-gray-50 rounded-lg p-6">
        <h3 className="font-bold mb-4">Support This Project</h3>
        <ModalPaymentQR username="yourname" />
      </aside>
    </div>
  );
}

With Form

tsx
export default function FormWithSupport() {
  return (
    <div className="max-w-2xl mx-auto">
      <form className="space-y-6 mb-8">
        {/* Form fields */}
      </form>
      
      <div className="border-t pt-8">
        <h3 className="text-lg font-bold mb-4">Like our service?</h3>
        <ModalPaymentQR username="yourname" />
      </div>
    </div>
  );
}

Multiple Modals

tsx
export default function MultiplePayments({ users }: { users: string[] }) {
  return (
    <div className="space-y-8">
      {users.map((user) => (
        <div key={user}>
          <h3 className="font-bold mb-4">{user}'s Support</h3>
          <ModalPaymentQR username={user} />
        </div>
      ))}
    </div>
  );
}

With Tabs

tsx
import ModalPaymentQR from '@/components/devfund/ModalPaymentQR';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';

export default function TabbedPage() {
  return (
    <Tabs defaultValue="info">
      <TabsList>
        <TabsTrigger value="info">Information</TabsTrigger>
        <TabsTrigger value="support">Support</TabsTrigger>
      </TabsList>
      
      <TabsContent value="info">
        <div>Content here...</div>
      </TabsContent>
      
      <TabsContent value="support">
        <ModalPaymentQR username="yourname" />
      </TabsContent>
    </Tabs>
  );
}

Accessibility Features

The Modal variant includes:

  • āœ… ARIA modal role and labels
  • āœ… Keyboard navigation (Escape to close)
  • āœ… Focus trap within modal
  • āœ… Screen reader announcements
  • āœ… Semantic HTML structure
  • āœ… Focus indicator on close button

Responsive Design

The Modal variant is fully responsive:

tsx
// Mobile: Full width with padding
// Tablet: Medium width centered
// Desktop: Standard width centered

className="max-w-sm w-full"  // Responsive width

Code Example - Full Implementation

tsx
'use client';

import ModalPaymentQR from '@/components/devfund/ModalPaymentQR';
import { useState } from 'react';

export default function ProjectPage() {
  const [tab, setTab] = useState('overview');
  
  return (
    <div className="min-h-screen bg-gray-50">
      <header className="bg-white border-b">
        <div className="max-w-4xl mx-auto px-4 py-6">
          <h1 className="text-3xl font-bold">My Project</h1>
        </div>
      </header>
      
      <main className="max-w-4xl mx-auto px-4 py-12">
        <div className="bg-white rounded-lg shadow">
          {/* Tab navigation */}
          <div className="flex border-b">
            <button
              onClick={() => setTab('overview')}
              className={`px-6 py-4 font-medium ${tab === 'overview' ? 'border-b-2 border-blue-500' : ''}`}
            >
              Overview
            </button>
            <button
              onClick={() => setTab('support')}
              className={`px-6 py-4 font-medium ${tab === 'support' ? 'border-b-2 border-blue-500' : ''}`}
            >
              Support
            </button>
          </div>
          
          {/* Tab content */}
          <div className="p-8">
            {tab === 'overview' && (
              <div>
                <h2 className="text-2xl font-bold mb-4">Project Overview</h2>
                <p>Project description here...</p>
              </div>
            )}
            
            {tab === 'support' && (
              <div>
                <h2 className="text-2xl font-bold mb-4">Support Development</h2>
                <p className="text-gray-600 mb-6">
                  Help support the development and maintenance of this project.
                </p>
                <ModalPaymentQR username="yourname" />
              </div>
            )}
          </div>
        </div>
      </main>
    </div>
  );
}

Browser Support

  • āœ… All modern browsers
  • āœ… Chrome/Edge 80+
  • āœ… Firefox 75+
  • āœ… Safari 13+
  • āœ… Mobile browsers

Performance

  • ⚔ Lightweight and fast
  • ⚔ Minimal dependencies
  • ⚔ Quick modal animations
  • ⚔ Efficient DOM updates

Comparison with Other Variants

FeatureModalCardGlassMinimalModern
ComplexityMediumMediumHighLowHigh
VersatilityHighMediumMediumHighMedium
Best ForGeneralProfessionalModernSimpleBold
Ease of UseEasyEasyMediumVery EasyMedium

Troubleshooting

Modal doesn't appear

Check that:

  1. The username prop is provided
  2. User profile exists in database
  3. Component is properly imported

Styles look different

Ensure Tailwind CSS is configured correctly in your project.

Keyboard navigation not working

The Escape key should close the modal. If not working, check for JavaScript errors in console.

Customization Examples

Add Custom Styling:

tsx
export default function CustomModalPage() {
  return (
    <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50">
      <div className="flex items-center justify-center min-h-screen">
        <div className="bg-white rounded-2xl shadow-2xl p-8 border-l-4 border-blue-500">
          <h2 className="text-2xl font-bold mb-6">Support Our Project</h2>
          <ModalPaymentQR username="yourname" />
        </div>
      </div>
    </div>
  );
}

Add Information Section:

tsx
export default function WithInfo() {
  return (
    <div className="max-w-2xl mx-auto py-12">
      <div className="bg-blue-50 rounded-lg p-6 mb-8">
        <h3 className="font-bold mb-2">Why Support?</h3>
        <ul className="space-y-2 text-sm">
          <li>āœ“ Helps maintain the project</li>
          <li>āœ“ Supports community development</li>
          <li>āœ“ Enables new features</li>
        </ul>
      </div>
      
      <ModalPaymentQR username="yourname" />
    </div>
  );
}

See Also