WeWrite uses a unified banner system to show important notifications to users at the top of the application. The system prioritizes banners to ensure users only see one thing at a time, preventing cognitive overload.
Related Documentation:
- Header System - Header components that banners appear below
- Save Banner - Shows when user has unsaved changes (sticky at top)
- Email Verification Banner - Shows when user's email is not verified
- Username Setup Banner - Shows when user has a temporary
user_*username - PWA Installation Banner - Shows when user can install WeWrite as an app
- Only ONE banner is shown at any given time
- Higher priority banners always take precedence
- Lower priority banners are hidden until higher priority ones are dismissed
- This prevents banner stacking and reduces user confusion
Purpose: Prompt users to verify their email address
Priority: Highest (1)
Component: app/components/utils/VerifyEmailBanner.tsx
Behavior:
- Shows only when
user.emailVerified === false - Matches PWA banner design exactly
- Two action buttons: "Later", "How?"
- "How?" button opens help modal with verification steps
- "Later" = 24-hour dismissal (localStorage)
- Help modal includes:
- Troubleshooting steps (check spam, verify email address)
- "Check Email in Settings" button
- "Send Again" button with progressive cooldown (10s → 60s → 2min → 5min)
Admin Control:
- Admin Switch: "Show unverified email banner" toggle in
/admin - Testing Override: When enabled, banner shows for ALL users (even verified ones)
- Persistent: Setting persists across browser sessions
- Usage: Toggle switch in admin → Navigate to main app → See banner immediately
Purpose: Prompt users to set a permanent username
Priority: Medium (3)
Component: app/components/utils/UsernameSetupBanner.tsx
Behavior:
- Shows only when user has temporary
user_*username - Shows only when Email Verification Banner is NOT showing
- "Later" = 24-hour dismissal
- "Don't remind me" = permanent dismissal
Purpose: Encourage users to install WeWrite as a Progressive Web App
Priority: Lower (4)
Component: app/components/utils/PWABanner.tsx
Behavior:
- Shows only when higher priority banners are NOT showing
- Only appears on mobile devices
- Only shows when not already running as PWA
- Three action buttons: "Never", "Later", "Yes!"
- "Yes!" opens installation instructions modal
Purpose: Indicate unsaved changes during editing
Priority: Highest (1) - appears above all content
Component: app/components/editor/StickySaveHeader.tsx
Behavior:
- Shows only when editing content with unsaved changes
- Sticky at top of viewport
- Contains "Revert" and "Save" buttons
- Managed separately via
setSaveBannerVisiblein BannerProvider
File: app/providers/BannerProvider.tsx
The BannerProvider manages banner visibility and priority:
const {
showEmailBanner,
showUsernameBanner,
showPWABanner,
showSaveBanner,
bannerOffset,
setSaveBannerVisible
} = useBanner();Key Logic:
- Admin Override: Checks
localStorage.getItem('wewrite_admin_email_banner_override')first - Email Verification: Shows if user is unverified and hasn't dismissed
- PWA Priority: Shows ONLY if email banner is NOT showing
- Automatic Management: Priority system ensures one-at-a-time display
Admin Override Implementation:
// BannerProvider checks admin override first
const shouldShowEmailBanner = () => {
// Admin testing override takes precedence
const adminOverride = localStorage.getItem('wewrite_admin_email_banner_override');
if (adminOverride === 'true') return true;
// Normal logic for unverified users
if (user.emailVerified) return false;
// ... other checks
};Location: app/components/layout/GlobalNavigation.tsx
{/* Banner system - shows at top of content */}
<VerifyEmailBanner />
<PWABanner />Important: Banners are rendered in GlobalNavigation for authenticated users only.
All banners must follow the same design pattern:
<div className="relative mx-4 mb-4 md:hidden">
<div className="bg-muted/50 border border-border rounded-xl px-4 py-3 flex flex-col transition-all duration-300 ease-in-out overflow-hidden backdrop-blur-sm">
{/* Icon + Message */}
<div className="flex items-center space-x-2 mb-2">
<Icon className="h-4 w-4 text-primary" />
<span className="text-sm font-medium text-foreground">Message</span>
</div>
{/* Three Action Buttons */}
<div className="grid grid-cols-3 gap-2">
<Button variant="outline" size="sm" className="h-9 text-xs text-foreground">
Never
</Button>
<Button variant="outline" size="sm" className="h-9 text-xs text-foreground">
Later
</Button>
<Button variant="default" size="sm" className="h-9 text-xs">
Action
</Button>
</div>
</div>
</div>- Collapse Animation: 300ms duration with scale and translate effects
- Smooth Transitions: All state changes use CSS transitions
- Consistent Timing: All banners use identical animation timing
- Banners only show on mobile (
md:hidden) - Responsive button sizing and text
- Touch-friendly button targets (minimum 44px)
Follow the established pattern in VerifyEmailBanner.tsx:
- Use the standard visual design
- Implement collapse animations
- Add analytics tracking
- Handle dismissal states with localStorage
Add new banner logic to BannerProvider.tsx:
- Add state management for new banner
- Implement priority logic (higher priority = lower number)
- Update context interface
Update this guide with:
- New banner's priority level
- Purpose and behavior
- Admin controls (if any)
Add appropriate events to app/constants/analytics-events.ts:
export const NEW_BANNER_EVENTS = {
BANNER_ACTION: 'new_banner_action',
// ... other events
};
export const EVENT_CATEGORIES = {
// ... existing categories
NEW_BANNER: 'New_Banner'
};- Admin Switch: "Show unverified email banner" toggle in
/admin - Testing Override: Forces banner to show for ALL users (even verified)
- Persistent: Setting saved in localStorage across sessions
- Testing Flow:
- Go to
/admin - Toggle "Show unverified email banner" → ON
- Navigate to any main app page
- Banner appears immediately at top of page
- Toggle OFF to hide banner
- Go to
- Control: Automatic based on device capabilities and email verification status
- No Admin Override: Always shows when conditions are met (mobile + not PWA + email verified)
- Visual Reference: View banner styling in
/admin/design-system - Component Showcase: Static examples of both banner types
- Design Documentation: Technical implementation details and CSS classes
- Card System: Banners use identical styling to the universal card system
- Never Stack Banners: Always use the priority system
- Consistent Design: Follow the established visual pattern
- Mobile-First: Banners are mobile-only by design
- Analytics: Track all user interactions
- Dismissal Respect: Honor user dismissal preferences
- One Thing at a Time: Never show multiple banners simultaneously
- Clear Actions: Always provide clear, actionable buttons
- Respectful Dismissal: "Never" means never - respect user choice
- Progressive Disclosure: Higher priority items first
- Concise Messaging: Keep banner text short and clear
- Action-Oriented: Focus on what user should do
- Benefit-Focused: Explain why the action matters
- Help-First Approach: Provide help before asking for action
When adding new banners, consider priority order:
- Security/Critical (Email verification, account security)
- Feature Adoption (PWA installation, new features)
- Engagement (Tips, tutorials, promotions)
The current system supports unlimited banners through the priority system. New banners simply need:
- Priority number assignment
- Provider integration
- Component creation
Key Files:
app/providers/BannerProvider.tsx- Priority managementapp/components/utils/VerifyEmailBanner.tsx- Email verificationapp/components/utils/UsernameSetupBanner.tsx- Username setupapp/components/utils/PWABanner.tsx- PWA installationapp/components/editor/StickySaveHeader.tsx- Save/revert bannerapp/components/layout/GlobalNavigation.tsx- Rendering location
Key Principle: One banner at a time, highest priority wins
Users reported being unable to dismiss the email verification banner in PWA mode, while it worked fine in browsers and on desktop.
The issue was likely related to:
- Touch Event Handling: PWA apps handle touch events differently than browsers
- Event Propagation: Touch events might not propagate correctly in PWA context
- Button Touch Targets: Insufficient touch target sizes for PWA interaction
The new unified banner system addresses these issues through:
- Consistent Event Handling: Both banners use identical touch event patterns
- Proper Touch Targets: All buttons meet 44px minimum touch target requirements
- Event Delegation: Improved event handling that works across PWA and browser contexts
- Progressive Enhancement: Touch events work as fallbacks to click events
- Test banner dismissal in both browser and PWA modes
- Verify touch targets are accessible on various mobile devices
- Confirm event handling works with different PWA installation methods
- View Design: Go to
/admin/design-system→ Scroll to "Banner System" - Test Functionality:
- Go to
/admin - Toggle "Show unverified email banner" → ON
- Navigate to
/or any main page - Banner appears at top
- Go to
- Test Priority: Email banner hides PWA banner when both should show
- Reset: Toggle admin switch OFF to return to normal behavior
- Admin Override: Forces banner for ALL users (ignores email verification status)
- Persistent: Setting survives page refreshes and browser sessions
- Mobile Only: Banners only appear on mobile devices (
md:hidden) - Priority Order: Email verification → PWA installation
- Dismissal: "Never" = permanent, "Later" = 24 hours