39 KiB
Zemail (iMail) — Design System & UI/UX Guidelines
Version: 1.1.0 — Last updated: 2026-03-06
This document is the single source of truth for every visual, interactive, and architectural decision in the Zemail project. Any developer or AI agent working on this codebase must read this document before making any frontend changes, and must update it after completing any UI/UX-related work.
Table of Contents
- Activated Skills & References
- Design Philosophy
- Tech Stack
- Aesthetic Preset: "DevTool Dark"
- Design Tokens (CSS Custom Properties)
- Typography
- Color System
- Spacing & Layout
- Component Library
- Animation & Micro-Interactions
- Real-Time UI Patterns
- Responsive Design Rules
- Accessibility
- Iteration History & Design Decisions
- Maintenance Protocol (MANDATORY)
1. Activated Skills & References
The following .agents/skills were activated during the initial design and development of this project. Any future developer or AI agent must activate the relevant skill before working in that domain.
| Skill | Path | When to Activate |
|---|---|---|
bento-landing-page-generator |
.agents/skills/bento-landing-page-generator/SKILL.md |
Building or modifying Bento-style landing pages, hero sections, feature grids. This was the primary skill used to establish the entire visual language of this project. |
tailwindcss-development |
.agents/skills/tailwindcss-development/SKILL.md |
Any styling work. Uses Tailwind CSS v4 with CSS-first @theme configuration. Never use deprecated v3 utilities. |
fluxui-development |
.agents/skills/fluxui-development/SKILL.md |
Building forms, modals, inputs, or interactive UI components. Uses the Flux UI Free edition. |
volt-development |
.agents/skills/volt-development/SKILL.md |
Creating single-file Livewire components. Check existing Volt components for functional vs. class-based style before creating new ones. |
cinematic-landing-page-builder |
.agents/skills/cinematic-landing-page-builder/SKILL.md |
Building cinematic, pixel-perfect landing pages with strict design system enforcement, micro-interactions, and GSAP animations. |
pest-testing |
.agents/skills/pest-testing/SKILL.md |
Writing or modifying tests. Uses Pest 3. |
External References Used
- Appwrite Dashboard — Dark UI inspiration for surface colors, card borders, and glow effects.
- Vercel — Minimal dark theme, clean typography hierarchy, terminal-style code blocks.
- Stripe / BentoNow — Asymmetric Bento Grid layout patterns, card span ratios.
- Heroicons — Default icon set. Search at heroicons.com. Never guess icon names.
2. Design Philosophy
Core Principles
- Premium-First: Every element must feel premium and state-of-the-art. No basic MVPs, no generic layouts. The user should be "wowed" at first glance.
- Dark Mode Native: The application is dark-mode-only (
class="dark"on<html>). All design tokens, colors, and contrasts are optimized for dark backgrounds. - Cinematic Motion: Interfaces should feel alive. Use GSAP for scroll-driven reveals, Alpine.js for instant state transitions, and CSS
transition-allfor micro-interactions. - Information Density: Mailbox UIs are information-dense by nature. Use compact typography (
text-[10px],text-[11px]), uppercase tracking (tracking-widest,tracking-[0.2em]), and monospace fonts for data. - No Placeholders: Never use placeholder images or
lorem ipsum. Build all visuals with HTML/CSS/SVG. Usegenerate_imagetool if real images are needed. - Glassmorphism & Glow: Frosted glass effects (
backdrop-blur-xl,bg-white/5) and colored glows (shadow-[0_0_30px_rgba(236,72,153,0.3)]) are signature visual elements.
The "Bento Box" Layout Rules
- Use asymmetric CSS Grids (e.g.,
grid-cols-1 md:grid-cols-3ormd:grid-cols-4). - Cards must have distinct spans (
col-span-2,row-span-2) to create a mosaic effect. - All cards use
rounded-2xlorrounded-3xlborder radii. - Cards use
bg-zinc-900surface color withborder border-white/5borders. - Background glows inside cards use
blur-2xlwith accent color at very low opacity (bg-pink-500/5).
3. Tech Stack
| Layer | Technology | Version |
|---|---|---|
| Backend | Laravel | v12 |
| Real-Time | Livewire | v3 |
| Client-Side Reactivity | Alpine.js | (bundled with Livewire) |
| Styling | Tailwind CSS | v4 |
| Animations | GSAP | 3.12.5 |
| Component Library | Flux UI Free | v2 |
| WebSocket | Laravel Reverb | v1 |
| Fonts | Google Fonts (via Bunny) | Inter, JetBrains Mono |
| Icons | Heroicons (via Flux) | — |
| QR Generator | QRious | 4.0.2 |
| Build Tool | Vite | v7 |
Asset Pipeline
resources/css/app.css → Tailwind v4 with @theme design tokens
resources/js/app.js → Laravel Echo + Pusher + WebSocket status dispatching
resources/views/ → Blade templates, Livewire components, anonymous components
All frontend changes require npm run build (or npm run dev during development) to take effect.
4. Aesthetic Preset: "DevTool Dark"
This project uses Preset A — "DevTool Dark", inspired by Appwrite and Vercel.
| Property | Value |
|---|---|
| Background | #09090B (bg-app-bg) |
| Surface | #18181B (bg-app-surface / bg-zinc-900) |
| Borders | #27272A (border-app-border / border-white/5) |
| Primary Accent | #EC4899 (Pink) |
| Secondary Accent | #10B981 (Emerald) |
| Text Primary | #FAFAFA |
| Text Secondary | #737373 (text-zinc-500) |
| Text Muted | #525252 (text-zinc-600) |
| Selection Highlight | rgba(236, 72, 153, 0.3) (selection:bg-[#EC4899]/30) |
5. Design Tokens (CSS Custom Properties)
All design tokens are defined in resources/css/app.css using Tailwind v4's CSS-first @theme directive.
@theme {
/* Core Application Colors */
--color-app-bg: #09090B;
--color-app-surface: #18181B;
--color-app-border: #27272A;
--color-app-red: #EC6A5F; /* macOS-style traffic light red */
--color-app-yellow: #F4BF4F; /* macOS-style traffic light yellow */
--color-app-green: #61C554; /* macOS-style traffic light green */
/* Zinc Scale (Dark Mode Neutrals) */
--color-zinc-50: #fafafa;
--color-zinc-500: #737373;
--color-zinc-600: #525252;
--color-zinc-800: #262626;
--color-zinc-900: #171717;
--color-zinc-950: #0a0a0a;
/* Primary (Purple/Indigo Scale — used for accents) */
--color-primary-500: #787ddc;
--color-primary-600: #615dce;
/* App Primary (oklch scale — for gradients and advanced color) */
--color-app-primary-500: oklch(0.589 0.137 290.02);
--color-app-primary-600: oklch(0.506 0.16 288.92);
/* Typography */
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif;
}
Important Token Rules
- Never hardcode colors that already exist as tokens. Use
bg-app-bg,text-zinc-500, etc. - Opacity modifiers use the Tailwind v4 slash syntax:
bg-pink-500/10, not the deprecatedbg-opacity-*. - New tokens must be added to
@theme {}inapp.css, not in atailwind.config.jsfile (Tailwind v4 is CSS-first).
6. Typography
Font Stack
| Usage | Font | Weight | Loaded Via |
|---|---|---|---|
| Body & UI | Inter | 400, 500, 600, 700 | Bunny Fonts CDN |
| Code, Data, Addresses | JetBrains Mono | 400, 500 | Bunny Fonts CDN |
Font Loading
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=inter:400,500,600,700|jetbrains-mono:400,500" rel="stylesheet" />
Typography Scale
| Element | Classes | Example Usage |
|---|---|---|
| Page Heading | text-2xl font-black tracking-tight uppercase italic |
Confirm modal title |
| Section Label | text-[10px] font-bold text-zinc-500 uppercase tracking-widest |
"Active Mailbox", "Your Sessions" |
| Category Label | text-[10px] font-bold text-zinc-600 uppercase tracking-[0.2em] |
Sidebar section headers |
| Data Display (email) | text-[11px] font-mono text-white break-all |
Email address display |
| Toast Type Label | text-[10px] font-black uppercase tracking-[0.2em] opacity-40 |
Toast "SUCCESS", "INFO" label |
| Toast Message | text-[11px] font-bold tracking-wide whitespace-pre-wrap |
Toast notification body |
| Button Text | text-xs font-bold |
Primary action buttons |
| Compact Button Text | text-[10px] font-black uppercase tracking-[0.2em] |
Modal confirm/cancel buttons |
| Timer Display | text-[10px] font-mono text-pink-500 |
Expiration countdown |
| Badge/Count | text-[10px] font-bold px-1.5 py-0.5 |
Unread count badge |
Typography Rules
- No comments in code unless logic is exceptionally complex. Use PHPDoc blocks instead.
- Uppercase + Wide Tracking is the signature style for labels and metadata text.
- Monospace (
font-mono) is used for any machine-readable data: email addresses, timers, code blocks. break-allmust be applied to email addresses to prevent overflow on small screens.
7. Color System
Semantic Color Mapping
These semantic colors are used consistently across all components (toasts, modals, buttons, indicators):
| Semantic | Background | Border | Text | Icon BG | Glow |
|---|---|---|---|---|---|
| Success | bg-emerald-500/10 |
border-emerald-500/20 |
text-emerald-100 |
bg-emerald-500/20 text-emerald-400 |
bg-emerald-500/10 |
| Info | bg-blue-500/10 |
border-blue-500/20 |
text-blue-100 |
bg-blue-500/20 text-blue-400 |
bg-blue-400/10 |
| Warning | bg-amber-500/10 |
border-amber-500/20 |
text-amber-100 |
bg-amber-500/20 text-amber-400 |
bg-amber-400/10 |
| Danger | bg-rose-500/10 |
border-rose-500/20 |
text-rose-100 |
bg-rose-500/20 text-rose-400 |
bg-rose-400/10 |
Important Color Rules
- Never use generic red/blue/green. Always use the curated semantic mapping above.
- Pink (
#EC4899/pink-500) is the project's primary accent. Used for highlights, CTAs, gradients, and the expiration progress bar. - Emerald (
emerald-500) is the secondary accent. Used for success states and the WebSocket connection indicator. - Rose (
rose-500) is used for destructive actions, errors, and the disconnected WebSocket indicator. - The expiration progress bar uses a gradient from pink to emerald:
bg-gradient-to-r from-pink-500 to-emerald-500. - Toast notification types:
success,info,warning,danger. Never useerror— usedangerinstead.
8. Spacing & Layout
Global Spacing Constants
| Context | Value | Tailwind Class |
|---|---|---|
| Card Padding | 16px | p-4 |
| Card Border Radius | 16px | rounded-2xl |
| Modal Border Radius | 32px | rounded-[32px] |
| Button Border Radius | 16px | rounded-2xl |
| Icon Container Radius | 12px | rounded-xl |
| Section Gap | 24px | space-y-6 |
| Inner Element Gap | 8px | space-y-2 / gap-2 |
| Toast Container Spacing | 12px | gap-3 |
| Screen Edge Margin | 24px | bottom-6 left-6 right-6 (responsive) |
Layout Architecture
The main mailbox view (mailbox.blade.php) uses a three-panel layout:
┌──────────┬──────────────┬──────────────────────┐
│ Sidebar │ Email List │ Email Detail │
│ (280px) │ (flex-1) │ (flex-1, lg only) │
│ │ │ │
│ - Active │ - Search │ - Header │
│ Mailbox│ - Email rows │ - Body (iframe) │
│ - Create │ - Pagination │ - Attachments │
│ - Sessions │ │
└──────────┴──────────────┴──────────────────────┘
- Sidebar uses
w-[280px]when open, collapses on smaller screens. overflow-hiddenon<body>prevents page-level scrolling. Each panel scrolls independently.- Use
scrollbar-hideutility class (custom CSS) to hide scrollbars in compact panels.
Body-Level Styling
<body class="bg-app-bg text-[#FAFAFA] antialiased selection:bg-[#EC4899]/30 h-full overflow-hidden">
9. Component Library
9.1 Global Toast Notification System
Location: resources/views/components/layouts/app.blade.php
Trigger: $this->dispatch('notify', message: '...', type: 'success') from Livewire, or addToast('...', 'success') from Alpine.js.
Event: @notify.window on <body>.
Architecture
<body x-data="{ toasts: [], wsConnected: true, addToast(msg, type) {...} }">
└── @notify.window="addToast($event.detail.message, $event.detail.type)"
└── @ws-status.window="wsConnected = $event.detail.connected"
└── <div class="fixed bottom-6 left-6 right-6 sm:left-auto sm:right-6 z-[100]">
└── <template x-for="toast in toasts">
└── Toast card with icon, type label, message, close button
Allowed Types
| Type | Visual | Use Case |
|---|---|---|
success |
Emerald green | Copy to clipboard, successful actions |
info |
Blue | New email received, informational alerts |
warning |
Amber/Orange | Cooldown period active, soft warnings |
danger |
Rose red | Address already in use, destructive errors |
Design Specifications
- Container:
fixed bottom-6 left-6 right-6 sm:left-auto sm:right-6 z-[100]- Mobile: Full-width with 24px margins on both sides.
- Desktop: Anchored to bottom-right.
- Toast Card:
w-full sm:min-w-[320px] p-4 rounded-2xl border backdrop-blur-xl shadow-2xl - Auto-dismiss: 4000ms timeout.
- Enter Animation:
transition ease-out duration-500— slides in from right with scale. - Leave Animation:
transition ease-in duration-300— fades out with scale. - Multiline Support:
whitespace-pre-wrapon message div. Use\nin PHP strings for line breaks. - Icon: 40×40px container (
w-10 h-10 rounded-xl) with SVG stroke icon. - Close Button:
p-1.5 rounded-lg hover:bg-white/5with X icon.
Toast Message Formatting Examples
// Single-line (copy confirmation)
$this->dispatch('notify', message: 'Address copied to clipboard', type: 'success');
// Multi-line (new email notification)
$this->dispatch('notify',
message: "Sender: {$sender}\nSubject: {$subject}",
type: 'info'
);
// Cooldown warning with precise timer
$this->dispatch('notify',
message: "Address is in cooldown. Try again in {$remaining}.",
type: 'warning'
);
// Error (address conflict)
$this->dispatch('notify', message: 'Address already in use.', type: 'danger');
9.2 Confirm Modal
Location: resources/views/components/bento/confirm-modal.blade.php
Trigger: $dispatch('open-confirm-modal', { title, message, confirmLabel, type, action })
Design Specifications
- Backdrop:
bg-zinc-950/80 backdrop-blur-xl— heavy frosted glass overlay. - Card:
max-w-[400px] bg-zinc-900 border border-white/10 rounded-[32px] p-8— super-rounded. - Glow:
w-64 h-64 rounded-full blur-[80px] opacity-20— type-colored radial glow behind the card. - Icon:
w-16 h-16 rounded-2xl— large, prominent, type-colored icon container. - Title:
text-2xl font-black text-white tracking-tight uppercase italic— bold, dramatic. - Message:
text-xs font-bold text-zinc-500 uppercase tracking-widest leading-relaxed— subdued. - Buttons:
grid grid-cols-2 gap-4— two equal-width buttons withpy-4 rounded-2xl. - Cancel:
bg-white/5 border border-white/10 text-zinc-400— ghost style. - Confirm: Solid type-colored background (e.g.,
bg-rose-600 text-whitefor danger).
Supported Types
Same four types as the toast system: danger, info, warning, success.
9.3 WebSocket Connection Indicator
Location: resources/views/livewire/mailbox.blade.php (Active Mailbox card)
State Source: Global wsConnected from <body x-data>.
<div class="w-1.5 h-1.5 rounded-full animate-pulse transition-colors duration-500"
:class="wsConnected ? 'bg-emerald-500' : 'bg-rose-500'"></div>
- Connected: Emerald green pulsing dot.
- Disconnected: Rose red pulsing dot.
- Transition:
duration-500smooth color transition between states. - The dot sits next to the "ACTIVE MAILBOX" label in the sidebar card.
9.4 Expiration Progress Bar
Location: resources/views/livewire/mailbox.blade.php
Design Specifications
- Track:
h-1 bg-white/5 rounded-full overflow-hidden - Fill:
bg-gradient-to-r from-pink-500 to-emerald-500 transition-all duration-1000 ease-linear - Timer Text:
text-[10px] font-mono text-pink-500 - Label:
text-[10px] text-zinc-500 uppercase font-black tracking-tighter— "EXPIRES IN"
Time Display Rules (User Iteration)
The expiration timer processes remaining time in seconds and displays with adaptive precision:
// Display hierarchy (most precise possible):
if (days > 0) → "14d 23h 59m 59s"
if (hours > 0) → "23h 59m 59s"
if (mins > 0) → "59m 59s"
else → "59s"
User Iteration Note: Originally showed "0 minutes" when only seconds remained. Fixed to always show seconds-level precision and cascade upward to days.
Auto-Expiration Behavior
When the timer reaches zero:
- Alpine.js sets
isExpired = trueand fires$wire.deleteMailbox(id). - This triggers a soft delete on the Mailbox model.
- The
deletedEloquent event fires cleanup (MongoDB email bodies, MariaDB email metadata). - UI refreshes to show a new auto-generated mailbox.
9.5 Cinematic Creation Overlay
Location: resources/views/livewire/mailbox.blade.php
Shown when a first-time visitor arrives or all mailboxes are deleted:
- Backdrop:
fixed inset-0 z-[200] bg-zinc-950/90— full-screen dark overlay. - Logo:
animate-pulsewith scale transition (scale-110 opacity-100→scale-90 opacity-0). - Spinner: Custom CSS spinner using
border-4 border-pink-500 border-t-transparent animate-[spin_1.5s_cubic-bezier(0.4,0,0.2,1)_infinite]with pink glow shadow. - Auto-dismiss:
setTimeout(() => $wire.finishAutoCreation(), 1000)— 1 second cinematic delay.
9.6 Action Button Patterns
Icon Buttons (Toolbar Style)
<!-- Standard action button -->
<button class="p-1.5 rounded-lg bg-white/5 text-zinc-500 hover:text-white hover:bg-white/10 transition-all cursor-pointer">
<svg class="w-3.5 h-3.5" ...></svg>
</button>
<!-- Destructive action button -->
<button class="p-1.5 rounded-lg bg-rose-500/10 text-rose-500/60 hover:text-rose-500 hover:bg-rose-500/20 transition-all cursor-pointer">
<svg class="w-3.5 h-3.5" ...></svg>
</button>
Full-Width CTA Button
<button class="w-full py-3 px-4 rounded-2xl bg-white/5 border border-white/10 text-white text-xs font-bold flex items-center justify-center gap-2 hover:bg-white/10 hover:border-pink-500/30 transition-all group cursor-pointer">
<div class="w-5 h-5 rounded-lg bg-pink-500/20 text-pink-500 flex items-center justify-center group-hover:scale-110 transition-transform">
<svg class="w-3.5 h-3.5" ...></svg>
</div>
Create New Mailbox
</button>
Key Button Rules
- All buttons must have
cursor-pointer. - Icon containers inside buttons use
group-hover:scale-110 transition-transformfor a micro-interaction. - Destructive buttons use the rose color scale at reduced opacity (
rose-500/10,rose-500/60). - Ghost buttons use
bg-white/5 border border-white/10.
10. Animation & Micro-Interactions
GSAP (Heavy Scroll Animations)
Loaded via CDN in the layout:
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>
- Used primarily on the landing page for cinematic scroll reveals.
- Initialize inside Alpine's
x-inithook:x-init="gsap.from($el, { opacity: 0, y: 50, scrollTrigger: $el })". - GSAP is not used in the mailbox application UI — Alpine.js handles all reactive transitions there.
Alpine.js (Instant UI State)
All immediate user interactions must be handled client-side with Alpine.js:
| Pattern | Usage |
|---|---|
x-show + x-transition |
Modals, overlays, conditional panels |
x-data + $watch |
Reactive state management (timers, mobile view switching) |
@click + $wire |
Livewire method calls from the client |
@click + $dispatch |
Triggering global events (toasts, modals) |
@resize.window |
Responsive sidebar behavior |
:class bindings |
Dynamic styling (WebSocket indicator, toast types) |
CSS Transitions
| Property | Duration | Easing | Usage |
|---|---|---|---|
transition-all |
default (150ms) | default | Button hover states |
transition-colors duration-500 |
500ms | default | WebSocket indicator color change |
transition-all duration-1000 ease-linear |
1000ms | linear | Progress bar width |
transition ease-out duration-500 |
500ms | ease-out | Toast enter |
transition ease-in duration-300 |
300ms | ease-in | Toast leave |
transition ease-out duration-700 |
700ms | ease-out | Full-screen overlay enter |
transition ease-in duration-500 |
500ms | ease-in | Full-screen overlay leave |
transition ease-out duration-500 |
500ms | ease-out | Modal card enter (with scale + translateY) |
Custom Keyframes
@keyframes slide-right {
from { opacity: 0; transform: translateX(-10px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes slide-up {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-slide-up {
animation: slide-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
11. Real-Time UI Patterns
WebSocket Architecture
app.js → Laravel Echo (Reverb broadcaster)
→ Pusher connection state listeners
→ Dispatches window events: 'ws-status'
app.blade.php → Listens for @ws-status.window
→ Updates global Alpine 'wsConnected' state
mailbox.blade.php → Reads 'wsConnected' for indicator color
Connection Status Lifecycle
// resources/js/app.js
window.Echo.connector.pusher.connection.bind('connected', () => dispatchStatus(true));
window.Echo.connector.pusher.connection.bind('unavailable', () => dispatchStatus(false));
window.Echo.connector.pusher.connection.bind('disconnected', () => dispatchStatus(false));
window.Echo.connector.pusher.connection.bind('failed', () => dispatchStatus(false));
Conditional Echo Initialization
Echo is only initialized on pages that need it, controlled by a data attribute:
<div data-requires-reverb="true"> ... </div>
if (document.querySelector('[data-requires-reverb]')) {
window.Echo = new Echo({ ... });
}
New Email Real-Time Flow
ProcessIncomingEmailjob saves email to MariaDB + MongoDB.NewEmailReceivedevent broadcasts onmailbox.{domain}channel.- Livewire listener
echo:mailbox.{domain},.new.emailfiresonNewEmail(). onNewEmail()dispatches a toast notification with sender + subject.onNewEmail()dispatches$refreshto reload the email list.
12. Responsive Design Rules
Breakpoint Strategy
| Breakpoint | Prefix | Behavior |
|---|---|---|
| < 640px | (none) | Mobile: full-width toast, collapsed sidebar, single-panel view |
| ≥ 640px | sm: |
Toast anchors to bottom-right, min-width restored |
| ≥ 1024px | lg: |
Email detail panel becomes visible. Mobile view switcher deactivated |
| ≥ 1280px | xl: |
Sidebar always open |
Mobile View Switching
On mobile (< 1024px), the mailbox uses an Alpine-driven view switcher:
$watch('selectedId', value => { if (value && window.innerWidth < 1024) mobileView = 'detail' });
$watch('mobileView', value => { if (value === 'list' && window.innerWidth < 1024) selectedId = null });
Toast Responsiveness (User Iteration)
User Iteration Note: The toast container originally used
fixed bottom-6 right-6with a hardmin-w-[320px], causing it to overflow and touch the left screen edge on small devices. This was fixed by:
- Container:
left-6 right-6 sm:left-auto sm:right-6— full-width with margins on mobile, bottom-right anchor on desktop.- Toast card:
w-full sm:min-w-[320px]— fluid on mobile, minimum width on desktop.
13. Accessibility
Required Patterns
- All interactive elements must have
cursor-pointer. - Icon-only buttons must have a
titleattribute for tooltip/screen-reader context. antialiasedis applied globally on<body>for smoother font rendering.selection:bg-[#EC4899]/30provides a branded text selection color.- Use semantic HTML elements wherever possible (
<nav>,<button>,<main>). - All interactive elements should have unique, descriptive IDs for browser testing.
Scrollbar Handling
.scrollbar-hide::-webkit-scrollbar { display: none; }
.scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; }
14. Iteration History & Design Decisions
This section documents every significant design iteration from the conversation thread, in chronological order. Each entry includes the rationale so future developers understand why a decision was made.
Iteration 1: Expiration Timer Precision
Problem: When a mailbox had only seconds remaining, the timer displayed "0 minutes" instead of showing seconds.
Solution: Rewrote the Alpine.js timer to process time in seconds and display with cascading precision: days → hours → minutes → seconds. The timer now always shows the most granular unit available.
Files Changed: resources/views/livewire/mailbox.blade.php
Iteration 2: Soft Deletes Instead of Hard Deletes
Problem: Clicking the delete button permanently removed the mailbox record from the database, losing all audit trail.
Solution: Added SoftDeletes trait to the Mailbox model. Created a migration to add deleted_at timestamps. All delete operations now soft-delete, preserving records for analytics and potential reclamation.
Files Changed: app/Models/Mailbox.php, database/migrations/2026_03_05_202748_add_soft_deletes_to_mailboxes_table.php
Iteration 3: Mailbox Duplication Prevention & Cooldown System
Problem: Random address generation could produce duplicates. Users could snipe deleted addresses instantly.
Solution:
- Random addresses:
do-whileloop checkswithTrashed()to ensure uniqueness. - Same user reclaiming: Automatically restores soft-deleted mailbox and updates expiration.
- Different user claiming: Tier-based cooldown enforced (Guest: 24h, Free: 12h, Pro: 6h, Enterprise: instant).
- Cooldown toast: Shows precise remaining time down to seconds (e.g., "11h 42m 18s").
Files Changed: app/Livewire/Mailbox.php
Iteration 4: Toast Notification Type Fix
Problem: Cooldown and error notifications were dispatched with type: 'error', which didn't match the global toast's four supported types (success, info, warning, danger). The toast would render without proper styling.
Solution: Changed "address already in use" to type: 'danger' (rose red) and cooldown warnings to type: 'warning' (amber). Rule: Never use 'error' as a toast type.
Files Changed: app/Livewire/Mailbox.php
Iteration 5: Session Linking on Login
Problem: Guest users creating mailboxes via session would lose access when they registered or logged in.
Solution: Added an Event::listen(Login::class) in AppServiceProvider to automatically transfer mailboxes from session_id to user_id when a guest authenticates.
Files Changed: app/Providers/AppServiceProvider.php
Iteration 6: Automatic Expiration Cleanup
Problem: If a user closed their browser, expired mailboxes would never be cleaned up.
Solution: Dual approach:
- Live UI Hook: Alpine.js timer triggers
$wire.deleteMailbox()when countdown reaches zero. - Background Worker:
CleanupExpiredMailboxesArtisan command runs every minute via Laravel Scheduler. - Data Cleanup: Eloquent
deletedevent onMailboxmodel wipes associatedEmailBody(MongoDB) andEmail(MariaDB) records.
Files Changed: app/Console/Commands/CleanupExpiredMailboxes.php, routes/console.php, app/Models/Mailbox.php, resources/views/livewire/mailbox.blade.php
Iteration 7: WebSocket Status Indicator
Problem: The "Active Mailbox" pulse indicator was always green, regardless of connection state.
Solution:
- Added
connected/disconnected/unavailable/failedlisteners to Echo inapp.js. - Stored
wsConnectedin global Alpine state on<body>. - Bound indicator color dynamically: emerald for connected, rose for disconnected.
Files Changed: resources/js/app.js, resources/views/components/layouts/app.blade.php, resources/views/livewire/mailbox.blade.php
Iteration 8: New Email Toast Notification
Problem: Users had no visual cue when a new email arrived while reading another email.
Solution: Updated onNewEmail() to dispatch a toast with sender and subject before refreshing the list. Used type: 'info' (blue) for the notification.
Files Changed: app/Livewire/Mailbox.php
Iteration 9: Multiline Toast Formatting
Problem: Toast notification for new emails displayed sender and subject on a single line, making it hard to scan.
Solution:
- Changed message format to
"Sender: {$sender}\nSubject: {$subject}"using\n. - Added
whitespace-pre-wrapto the toast message div to render line breaks.
Files Changed: app/Livewire/Mailbox.php, resources/views/components/layouts/app.blade.php
Iteration 10: Mobile Toast Overflow Fix
Problem: On small screens (< 320px viewport), the toast's min-w-[320px] caused it to overflow beyond the left edge of the screen.
Solution:
- Container: Changed from
fixed bottom-6 right-6tofixed bottom-6 left-6 right-6 sm:left-auto sm:right-6. - Toast card: Changed from
min-w-[320px]tow-full sm:min-w-[320px].
Files Changed: resources/views/components/layouts/app.blade.php
Iteration 11: Random Address Format
Problem: Random mailbox addresses included an underscore before the number suffix (e.g., john_doe_42@imail.app).
Solution: User manually removed the underscore from the format string. Addresses now generate as johndoe42@imail.app.
Files Changed: app/Livewire/Mailbox.php (user edit)
Appendix: File Reference Map
| File | Purpose |
|---|---|
resources/css/app.css |
Tailwind v4 theme tokens, Flux UI imports, dark mode config |
resources/js/app.js |
Laravel Echo init, WebSocket status dispatching |
resources/views/components/layouts/app.blade.php |
Global layout: fonts, GSAP, global Alpine state, toast system |
resources/views/livewire/mailbox.blade.php |
Main mailbox UI: sidebar, email list, detail view, modals |
resources/views/components/bento/confirm-modal.blade.php |
Reusable confirm modal with type-based theming |
resources/views/livewire/landing-page.blade.php |
Bento-style SaaS landing page |
app/Livewire/Mailbox.php |
Mailbox Livewire component: CRUD, WebSocket, analytics |
app/Models/Mailbox.php |
Mailbox Eloquent model: soft deletes, cleanup events |
app/Events/NewEmailReceived.php |
WebSocket broadcast event for incoming emails |
app/Console/Commands/CleanupExpiredMailboxes.php |
Scheduled cleanup of expired mailboxes |
For AI Agents: Before making any UI change, re-read the relevant sections of this document. Match existing patterns exactly. When in doubt, check sibling components for conventions. Always activate the appropriate skill from
.agents/skills/before starting work. After completing any UI/UX change, update this document per Section 15.
15. Maintenance Protocol (MANDATORY)
This document is a living document. Every developer or AI agent who makes a UI/UX-related change must update it before considering their work complete. This ensures no knowledge is lost and prevents future confusion or hallucination.
15.1 When to Update
| Scenario | What to Update | Priority |
|---|---|---|
| New component created | Add to §9 Component Library with full spec | 🔴 Required |
| Existing component modified | Update the relevant subsection in §9 | 🔴 Required |
| New design token added | Add to §5 Design Tokens | 🔴 Required |
| Color or typography changed | Update §6 Typography or §7 Color System | 🔴 Required |
| New animation or transition | Add to §10 Animation table | 🟡 Recommended |
| New real-time event or flow | Add to §11 Real-Time UI Patterns | 🔴 Required |
| Responsive behavior changed | Update §12 Responsive Design Rules | 🔴 Required |
| Any iterative design refinement | Add to §14 Iteration History | 🔴 Required |
| New file created that affects UI | Add to Appendix: File Reference Map | 🟡 Recommended |
| New external dependency added | Add to §3 Tech Stack table | 🔴 Required |
| New skill activated | Add to §1 Activated Skills table | 🟡 Recommended |
15.2 Iteration Entry Template
Every design change must be logged in §14 Iteration History using this exact format:
### Iteration N: [Short Descriptive Title]
**Date:** YYYY-MM-DD
**Conversation/PR:** [Link or ID if available]
**Problem:** One sentence describing what was wrong or what the user requested.
**Why:** One sentence explaining *why* this matters (UX impact, visual regression, accessibility, etc.).
**Solution:** Bullet points detailing what was changed. Include:
- Exact Tailwind classes added/removed/changed.
- Any new Alpine.js state or events introduced.
- Any new Livewire methods or events.
**Files Changed:** Comma-separated list of relative file paths.
**Design Rules Established:** (Optional) Any new rules that emerged from this change.
Example — Well-Written Entry
### Iteration 12: Attachment Preview Thumbnails
**Date:** 2026-03-10
**Conversation:** c1ba6ccd-8bd9-4670-94e4-50b936242f39
**Problem:** Attachments in the email detail view showed only filenames with no visual preview.
**Why:** Users couldn't quickly assess whether an attachment was an image, document, or archive without downloading it.
**Solution:**
- Added thumbnail preview for image attachments using `<img>` with `w-16 h-16 rounded-lg object-cover`.
- Non-image attachments show a colored icon badge: `bg-blue-500/10 text-blue-400` for documents, `bg-amber-500/10 text-amber-400` for archives.
- Thumbnails are lazy-loaded with `loading="lazy"` to avoid blocking the main render.
**Files Changed:** `resources/views/livewire/mailbox.blade.php`
**Design Rules Established:** All file-type indicators must use the semantic color mapping from §7. Image previews always use `rounded-lg object-cover`.
Example — Poorly-Written Entry (DO NOT DO THIS)
### Iteration 12: Fixed attachments
**Problem:** Attachments looked bad.
**Solution:** Made them look better.
**Files Changed:** mailbox.blade.php
⚠️ This lacks specificity. It doesn't explain why it mattered, what CSS classes were used, or what rules emerged. A future developer reading this would learn nothing and likely re-implement differently.
15.3 Component Documentation Template
When adding a new component to §9 Component Library, use this structure:
### 9.N [Component Name]
**Location:** `path/to/file.blade.php`
**Trigger:** How to invoke (dispatch event, Livewire property, Alpine method, etc.)
**State Source:** Where the component gets its data.
#### Design Specifications
- **Container:** Exact Tailwind classes for the outermost wrapper.
- **Card/Body:** Exact Tailwind classes for the main content area.
- **[Other visual elements]:** One bullet per distinct visual element.
#### Behavior
- How it appears (animation/transition).
- How it dismisses.
- How it responds to different screen sizes.
#### Supported Variants
Table of variants (types, sizes, states) if applicable.
#### Code Example
```php
// Minimal working example showing how to trigger/use the component.
### 15.4 Update Checklist for AI Agents
Before marking any UI/UX task as complete, run through this checklist:
- [ ] **Read** relevant sections of `DESIGN.md` before starting work.
- [ ] **Match** existing patterns from this document (colors, spacing, typography, animations).
- [ ] **Update §14** with a new Iteration entry if any visual or behavioral change was made.
- [ ] **Update §9** if a component was created or modified.
- [ ] **Update §5/§6/§7** if design tokens, fonts, or colors were added/changed.
- [ ] **Update §3** if a new dependency was added.
- [ ] **Update Appendix** if a new file was created.
- [ ] **Bump the version** at the top of the file (patch for fixes, minor for features).
- [ ] **Update the "Last updated" date** at the top of the file.
### 15.5 Versioning Convention
The version number at the top of this file follows **Major.Minor.Patch**:
| Change Type | Bump | Example |
|------------|------|--------|
| New section or major restructure | Major | 1.0.0 → 2.0.0 |
| New component, new iteration, new design token | Minor | 1.0.0 → 1.1.0 |
| Typo fix, clarification, minor class update | Patch | 1.0.0 → 1.0.1 |
### 15.6 Context Preservation Rules
To keep entries **light but sufficient** (no confusion, no hallucination):
1. **Always include exact Tailwind classes.** Don't write "made it rounded" — write `rounded-2xl`.
2. **Always include the file path.** Don't write "the modal" — write `resources/views/components/bento/confirm-modal.blade.php`.
3. **Always state the user's intent.** Don't write "changed the color" — write "User requested rose for destructive actions instead of generic red."
4. **Always state the technical reason.** Don't write "it was broken" — write "The toast type `'error'` had no matching `:class` binding in the Alpine template."
5. **Keep it to 3-5 sentences max per entry.** More detail goes in the component spec (§9), not the iteration log (§14).
6. **Reference existing sections.** Instead of re-explaining the color system, write "Uses semantic Danger mapping from §7."