Documentation Index Fetch the complete documentation index at: https://docs.fanfare.io/llms.txt
Use this file to discover all available pages before exploring further.
User Experience Patterns
Create waiting experiences that keep consumers engaged, informed, and confident. A well-designed waiting experience can turn a potentially frustrating wait into a positive brand interaction.
Core UX Principles
Transparency
Always keep users informed about their status and what to expect.
Show position Display queue position or entry status clearly
Estimate waits Provide realistic time estimates when available
Explain the process Help users understand what happens next
Confirm actions Provide clear feedback for user actions
Engagement
Keep users engaged while they wait.
// Example: Rich waiting experience with progress updates
function WaitingExperience ({ queueStatus }) {
return (
< div className = "waiting-experience" >
{ /* Position indicator */ }
< PositionDisplay
position = {queueStatus. position }
total = {queueStatus. totalAhead }
/>
{ /* Progress visualization */ }
< ProgressBar progress = {queueStatus. progressPercent } />
{ /* Estimated time */ }
< TimeEstimate minutes = {queueStatus. estimatedWaitMinutes } />
{ /* Engagement content */ }
< ProductPreview product = { product } />
< BrandContent />
</ div >
);
}
Queue Experience Patterns
Position Display
Show queue position in a meaningful way.
function PositionDisplay ({ position , total }) {
// For small queues, show exact position
if ( total <= 100 ) {
return (
< div className = "position" >
< span className = "position-number" > { position } </ span >
< span className = "position-label" > of { total } in line </ span >
</ div >
);
}
// For large queues, use percentile or rounded numbers
const percentile = Math . round (( 1 - position / total ) * 100 );
return (
< div className = "position" >
< span className = "position-percent" > Top { 100 - percentile } % </ span >
< span className = "position-label" > You're ahead of most people </ span >
</ div >
);
}
Wait Time Estimation
Provide realistic wait time estimates.
function TimeEstimate ({ minutes }) {
if ( minutes === null || minutes === undefined ) {
return < p > Calculating estimated wait... </ p > ;
}
if ( minutes < 1 ) {
return < p > Almost there! Just a moment... </ p > ;
}
if ( minutes < 5 ) {
return (
< p >
About { minutes } minute { minutes > 1 ? "s" : "" } remaining
</ p >
);
}
// Round to 5-minute increments for longer waits
const rounded = Math . ceil ( minutes / 5 ) * 5 ;
return < p > Approximately { rounded } minutes remaining </ p > ;
}
Progress Visualization
Visual progress indicators help users understand their advancement.
function QueueProgress ({ position , initialPosition }) {
const progress = initialPosition > 0 ? (( initialPosition - position ) / initialPosition ) * 100 : 0 ;
return (
< div className = "queue-progress" >
< div className = "progress-bar" style = { { width: ` ${ progress } %` } } />
< span className = "progress-text" > { Math . round ( progress ) } % of the way there </ span >
</ div >
);
}
Draw Experience Patterns
Registration Confirmation
Clearly confirm draw registration.
function DrawRegistration ({ drawStatus }) {
return (
< div className = "draw-registration" >
< CheckCircleIcon className = "success-icon" />
< h2 > You're registered! </ h2 >
< p > Draw # { drawStatus . entryNumber } </ p >
< div className = "draw-details" >
< p >
< strong > Draw time: </ strong > { formatDateTime ( drawStatus . drawTime ) }
</ p >
< p >
< strong > Total entries: </ strong > { drawStatus . totalEntries . toLocaleString () }
</ p >
</ div >
< p className = "instructions" > We'll notify you when the draw is complete. No need to stay on this page. </ p >
</ div >
);
}
Draw Results
Present results clearly with appropriate emotional context.
function DrawResults ({ result }) {
if ( result . isWinner ) {
return (
< div className = "draw-result winner" >
< ConfettiAnimation />
< h2 > Congratulations! You've been selected! </ h2 >
< p > You have { result . timeRemaining } to complete your purchase. </ p >
< CountdownTimer deadline = { result . deadline } />
< Button onClick = { result . proceedToCheckout } > Complete Purchase </ Button >
</ div >
);
}
return (
< div className = "draw-result not-selected" >
< h2 > Not selected this time </ h2 >
< p > Thank you for participating. Better luck next time! </ p >
{ result . waitlistAvailable && < Button onClick = { result . joinWaitlist } > Join Waitlist for Updates </ Button > }
</ div >
);
}
Auction Experience Patterns
Bid Status Display
Keep bidders informed of their status.
function BidStatus ({ auction , userBid }) {
const isWinning = userBid ?. amount === auction . currentBid ;
const isOutbid = userBid && ! isWinning ;
return (
< div className = { `bid-status ${ isWinning ? "winning" : "" } ${ isOutbid ? "outbid" : "" } ` } >
{ isWinning && (
<>
< TrophyIcon />
< p > You're the highest bidder! </ p >
</>
) }
{ isOutbid && (
<>
< AlertIcon />
< p > You've been outbid. Place a new bid to stay in the running. </ p >
</>
) }
{ ! userBid && < p > Place a bid to participate </ p > }
</ div >
);
}
Error State Patterns
Friendly Error Messages
Convert technical errors into user-friendly messages.
const ERROR_MESSAGES = {
NETWORK_ERROR: {
title: "Connection issue" ,
message: "Please check your internet connection and try again." ,
action: "Retry" ,
},
RATE_LIMITED: {
title: "Too many requests" ,
message: "Please wait a moment before trying again." ,
action: "Wait" ,
},
SESSION_EXPIRED: {
title: "Session expired" ,
message: "Your session has expired. Please refresh the page." ,
action: "Refresh" ,
},
QUEUE_FULL: {
title: "Queue is full" ,
message: "This experience has reached capacity. Join the waitlist for future access." ,
action: "Join Waitlist" ,
},
};
function ErrorDisplay ({ error , onRetry }) {
const errorInfo = ERROR_MESSAGES [ error . code ] || {
title: "Something went wrong" ,
message: "Please try again or contact support if the problem persists." ,
action: "Try Again" ,
};
return (
< div className = "error-display" >
< h3 >{errorInfo. title } </ h3 >
< p >{errorInfo. message } </ p >
< Button onClick = { onRetry } > {errorInfo. action } </ Button >
</ div >
);
}
Loading States
Show clear loading states for asynchronous operations.
function LoadingState ({ operation }) {
const messages = {
entering: "Joining the queue..." ,
checking: "Checking your position..." ,
processing: "Processing your request..." ,
};
return (
< div className = "loading-state" >
< Spinner />
< p > { messages [ operation ] || "Loading..." } </ p >
</ div >
);
}
Mobile-First Design
Responsive Layout
Ensure the experience works well on mobile devices.
.queue-status {
/* Mobile-first base styles */
display : flex ;
flex-direction : column ;
gap : 1 rem ;
padding : 1 rem ;
/* Larger screens */
@ media ( min-width : 768 px ) {
flex-direction : row ;
padding : 2 rem ;
}
}
.position-number {
font-size : 3 rem ;
@ media ( min-width : 768 px ) {
font-size : 4 rem ;
}
}
Touch-Friendly Interactions
Make interactive elements easy to tap.
.action-button {
/* Minimum touch target size */
min-height : 44 px ;
min-width : 44 px ;
padding : 12 px 24 px ;
/* Clear visual feedback */
&: active {
transform: scale ( 0.98 );
}
}
Accessibility
Screen Reader Support
Ensure experiences are accessible to all users.
function QueuePosition ({ position }) {
return (
< div role = "status" aria-live = "polite" aria-atomic = "true" >
< span className = "visually-hidden" > Your current position in the queue is </ span >
< span className = "position-number" > { position } </ span >
</ div >
);
}
Keyboard Navigation
Support keyboard navigation for all interactions.
function ActionButton ({ onClick , children }) {
return (
< button
onClick = { onClick }
onKeyDown = { ( e ) => {
if ( e . key === "Enter" || e . key === " " ) {
e . preventDefault ();
onClick ();
}
} }
>
{ children }
</ button >
);
}
Engagement While Waiting
Use wait time to showcase products.
function ProductShowcase ({ product }) {
return (
< div className = "product-showcase" >
< h3 > While you wait... </ h3 >
< ImageCarousel images = { product . images } />
< ProductDetails details = { product . details } />
< SizeGuide />
</ div >
);
}
Brand Content
Build brand connection during the wait.
function BrandContent () {
return (
< div className = "brand-content" >
< video autoPlay muted loop playsInline src = "/brand-video.mp4" />
< div className = "brand-story" >
< h3 > About the Collection </ h3 >
< p > Learn about the inspiration behind this release... </ p >
</ div >
</ div >
);
}
Next Steps