#
Components
QuickDapp uses a simple component system built on Radix UI primitives with TailwindCSS for styling. Components focus on essential functionality and consistency.
#
Base Components
#
Button
Simple button component with variants and loading state:
// src/client/components/Button.tsx
import * as React from "react"
import { cn } from "../utils/cn"
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: "default" | "outline" | "ghost" | "error"
size?: "sm" | "default" | "lg"
loading?: boolean
}
const BUTTON_VARIANTS = {
default: "bg-anchor text-black hover:bg-anchor/90",
outline: "border border-anchor text-anchor hover:bg-anchor hover:text-black",
ghost: "hover:bg-accent hover:text-accent-foreground",
error: "bg-red-600 text-white hover:bg-red-700",
} as const
const BUTTON_SIZES = {
sm: "h-8 px-3 text-sm",
default: "h-10 px-4 py-2",
lg: "h-11 px-8 text-lg",
} as const
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant = "default", size = "default", loading, children, disabled, ...props }, ref) => {
return (
<button
className={cn(
"inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-anchor focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
BUTTON_VARIANTS[variant],
BUTTON_SIZES[size],
className,
)}
ref={ref}
disabled={disabled || loading}
{...props}
>
{loading && (
<svg className="animate-spin -ml-1 mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
)}
{children}
</button>
)
},
)
export { Button }
#
Dialog
Dialog components built on Radix UI primitives:
// src/client/components/Dialog.tsx
import * as DialogPrimitive from "@radix-ui/react-dialog"
import * as React from "react"
import { cn } from "../utils/cn"
const Dialog = DialogPrimitive.Root
const DialogTrigger = DialogPrimitive.Trigger
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPrimitive.Portal>
<DialogPrimitive.Overlay className="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm" />
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-slate-700 bg-slate-900 p-6 shadow-lg duration-200 rounded-lg",
className,
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100">
<span className="h-4 w-4">✕</span>
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPrimitive.Portal>
))
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)} {...props} />
)
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn("text-lg font-semibold leading-none tracking-tight text-white", className)}
{...props}
/>
))
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-slate-400", className)}
{...props}
/>
))
export { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogTrigger }
#
Form Components
Simple form components for input handling:
// src/client/components/Form.tsx
import * as React from "react"
import { cn } from "../utils/cn"
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string
error?: string
}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, label, error, ...props }, ref) => {
return (
<div className="space-y-1">
{label && (
<label className="text-sm font-medium text-slate-300">
{label}
</label>
)}
<input
className={cn(
"flex h-10 w-full rounded-md border border-slate-600 bg-slate-800 px-3 py-2 text-sm text-white placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-anchor focus:border-transparent disabled:cursor-not-allowed disabled:opacity-50",
error && "border-red-500 focus:ring-red-500",
className
)}
ref={ref}
{...props}
/>
{error && (
<p className="text-sm text-red-500">{error}</p>
)}
</div>
)
}
)
export { Input }
#
Layout Components
#
Header
Simple header with logo, wallet connection, and notifications:
// src/client/components/Header.tsx
import type { FC } from "react"
import { useAuthContext } from "../contexts/AuthContext"
import logoSvg from "../images/logo.svg"
import { cn } from "../utils/cn"
import { ConnectWallet } from "./ConnectWallet"
import { NotificationsIndicator } from "./notifications/NotificationsIndicator"
const Logo: FC = () => (
<img src={logoSvg} alt="QuickDapp Logo" className="w-8 h-8" />
)
export const Header: FC<{ className?: string }> = ({ className }) => {
const { isAuthenticated } = useAuthContext()
return (
<header className={cn(
"w-screen z-10 flex-0 bg-background flex flex-row place-content-between items-center px-2",
className,
)}>
<a href="/" aria-label="homepage" className="no-anchor-hover-styles clickable">
<Logo />
</a>
<div className="flex flex-row justify-end items-center gap-2">
{isAuthenticated && <NotificationsIndicator />}
<ConnectWallet showNetwork={true} />
</div>
</header>
)
}
QuickDapp uses a single-page application with a simple header layout. There is no sidebar - the application focuses on essential token management functionality.
#
Application Components
QuickDapp includes several specialized components for Web3 functionality:
#
ConnectWallet
Wallet connection component with network display:
// Handles wallet connection using RainbowKit
import { ConnectButton as RainbowConnectButton } from '@rainbow-me/rainbowkit'
export function ConnectWallet({ showNetwork }: { showNetwork?: boolean }) {
return (
<RainbowConnectButton.Custom>
{({ account, chain, openAccountModal, openChainModal, openConnectModal, mounted }) => {
// Custom wallet connection UI
// Shows connect button, account info, or chain selector
}}
</RainbowConnectButton.Custom>
)
}
#
Token Management
Components for creating and managing ERC-20 tokens:
// CreateTokenDialog - Modal for deploying new tokens
// SendTokenDialog - Modal for transferring tokens
// TokenList - List view of user's tokens
// ContractValue - Display contract values and balances
#
Notifications
Real-time notification system:
// NotificationsIndicator - Bell icon with unread count
// NotificationsDialog - Modal showing notification history
// NotificationComponents - Individual notification renderers
#
Utility Components
Simple utility components:
// Loading component
export function Loading() {
return (
<div className="flex justify-center items-center p-4">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-anchor"></div>
</div>
)
}
// Error boundary for handling component crashes
export class ErrorBoundary extends React.Component {
// Catches and displays component errors
}
// Toast notifications
export function Toast({ message, type }: { message: string, type: 'success' | 'error' }) {
// Shows temporary toast messages
}
#
Styling
QuickDapp uses TailwindCSS with a dark theme and custom colors:
// src/client/tailwind.config.ts
const config = {
content: ['./src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {
colors: {
background: '#0f0f10',
foreground: '#ffffff',
anchor: '#00ff88', // Primary green accent
accent: '#1a1a1b',
'accent-foreground': '#ffffff',
}
}
},
plugins: []
}
#
Component Structure
Components follow these patterns:
- Use
cn()
utility for className merging - Forward refs for DOM elements
- TypeScript interfaces for props
- Simple, focused functionality
- Consistent styling with TailwindCSS
The component system emphasizes simplicity and essential functionality for Web3 token management rather than a comprehensive design system.