Appearance
Design Standards
Design guidelines for creating Mini Apps that integrate seamlessly with MiniPay.
Mobile-First Design
Mini Apps are primarily used on mobile devices. Design with mobile in mind:
Touch Targets
- ✅ Minimum touch target size: 44x44 pixels
- ✅ Adequate spacing between interactive elements
- ✅ Large, easy-to-tap buttons
Typography
- ✅ Use readable font sizes: 16px minimum for body text (avoid smaller than 14px)
- ✅ Sufficient contrast ratios (WCAG AA minimum)
Layout
- ✅ Single column layouts work best
- ✅ Avoid horizontal scrolling
- ✅ Use full-width elements where appropriate
- ✅ Consider safe areas (notches, status bars) — see UI & container for viewport and safe-area details
Wallet Integration UI
Phone-first identity
MiniPay users identify via their phone numbers. In your UI:
- ✅ Prefer showing phone number or a user-friendly identifier where possible (e.g. via phone number lookup).
- ❌ Avoid displaying raw
0x…wallet addresses to users unless necessary (e.g. for advanced or copy-to-clipboard use cases).
Connection errors
MiniPay abstracts connection away from the user. Don't surface "Connecting..." or "Connected" states; only show the user a message when connection fails.
tsx
// Only show an error when connection fails
{
connectionFailed && <div>Could not connect to MiniPay.</div>;
}Recovery options are limited: if your mini app implements EIP-6963, you can call requestProvider() so MiniPay re-announces the provider; otherwise the user may need to refresh the page (and success is not guaranteed).
Transaction States
Provide clear transaction feedback:
tsx
// Pending
<button disabled>Preparing transaction...</button>
// Confirming
<div>Transaction submitted. Waiting for confirmation...</div>
// Success
<div>✅ Transaction confirmed!</div>
// Error
<div>❌ Transaction failed. Please try again.</div>Balance Display
Format balances clearly:
tsx
// Good: Clear formatting
<div>
<span className="amount">1,234.56</span>
<span className="currency">USDC</span>
</div>;
// Good: With loading state
{
isLoading ? (
<div>Loading balance...</div>
) : (
<div>
{formattedBalance} {symbol}
</div>
);
}Color and Theming
Support Dark Mode
Consider supporting dark mode for better user experience. So that system UI (scrollbars, form controls) matches the theme, set the color-scheme meta tag in your <head> (e.g. <meta name="color-scheme" content="light dark">) or use color-scheme: light dark in CSS on :root. Prefer CSS for styling so the browser applies the theme without JavaScript:
css
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a1a;
--text: #e5e5e5;
}
}
@media (prefers-color-scheme: light) {
:root {
--bg: #ffffff;
--text: #1a1a1a;
}
}Use JavaScript (e.g. window.matchMedia('(prefers-color-scheme: dark)')) only when you need to branch logic, not for styling.
Contrast
Ensure sufficient contrast for readability:
- Text on background: 4.5:1 minimum (WCAG AA)
- Large text: 3:1 minimum
- Interactive elements: Clear visual feedback
Accessibility
Screen Readers
Make your app accessible to screen readers:
- ✅ Semantic HTML elements
- ✅ ARIA labels where needed
- ✅ Alt text for images
- ✅ Descriptive button labels
Error Messages
Provide clear, accessible error messages:
tsx
// Good: Clear, descriptive
<div role="alert">
<p>Transaction failed: Insufficient balance</p>
<p>You need at least 10 USDC to complete this transaction.</p>
</div>
// Bad: Vague
<div>Error occurred</div>Performance
Loading States
Always show loading states:
tsx
// Good: Clear loading indicator
{
isLoading ? (
<div>
<Spinner />
<p>Loading...</p>
</div>
) : (
<Content />
);
}Optimize Images
- ✅ Prefer SVG for icons and vector graphics (scalable, small)
- ✅ Use appropriate raster formats for photos (WebP, AVIF)
- ✅ Compress images
- ✅ Lazy load images below the fold
Minimize Bundle Size
- ✅ Code splitting
- ✅ Tree shaking
- ✅ Remove unused dependencies
- ✅ Optimize imports
User Experience
User-facing language (terminology)
Use simple, non-jargon language so users who are new to digital money feel at home:
| Use this | Not this |
|---|---|
| Network fee | Gas |
| Deposit | Onramp, Buy |
| Withdraw | Offramp, Sell |
| Stablecoin or Digital dollar | Crypto token |
Error Handling
Prefer error codes (e.g. from the JSON-RPC / provider error object) or standard error names over matching on message text, since provider messages can change. When codes don't identify the error, use a generic user-facing message.
Stable references: The JSON-RPC 2.0 spec defines standard error codes: -32700 (Parse error), -32600 (Invalid request), -32601 (Method not found), -32602 (Invalid params), -32603 (Internal error), -32604 (Permission denied). MiniPay uses these codes in JSON-RPC responses. Prefer checking error.code or error.name over error.message.
Provide helpful error messages:
tsx
function ErrorMessage({ error }: { error: Error & { code?: number } }) {
// Prefer code or name; avoid matching on message text (provider messages can change).
const message =
error.code === -32604 || error.name === "UserRejectedRequestError"
? "Transaction was cancelled"
: "Something went wrong";
return (
<div className="error">
<p>{message}</p>
<button onClick={retry}>Try Again</button>
</div>
);
}Empty States
Handle empty states gracefully:
tsx
{
items.length === 0 ? (
<div className="empty-state">
<p>No items found</p>
<button onClick={createItem}>Create Item</button>
</div>
) : (
<ItemList items={items} />
);
}Confirmation Dialogs
Use confirmation dialogs for important actions:
tsx
function ConfirmDialog({ onConfirm, onCancel }: Props) {
return (
<div className="modal">
<p>Are you sure you want to proceed?</p>
<button onClick={onConfirm}>Confirm</button>
<button onClick={onCancel}>Cancel</button>
</div>
);
}Best Practices
- Mobile only: Mini apps run on phones. Design for small viewports.
- Fast loading: Optimize for quick load times
- Clear feedback: Show loading, success, and error states
- Accessible: Follow WCAG guidelines
- Consistent: Use consistent patterns throughout your app
Next Steps
- Review best practices
- Check example implementations
- See wallet connection patterns