-
-
Notifications
You must be signed in to change notification settings - Fork 0
Description
🎨 Enhancement: Rotating Thematic Loading Animations
Context
As part of the Caddy config reload feedback feature, we implemented a Boat on Waves animation representing Charon ferrying across the River Styx. This issue tracks the enhancement to create a hybrid system with multiple rotating animations, each representing different aspects of Charon mythology.
Related
- Base implementation: Caddy Reload UI Feedback feature
- Plan document:
docs/plans/current_spec.md
🚣 Animation Options
1. Boat on Waves (Currently Implemented)
Visual: Boat silhouette bobbing on animated waves
Theme: Charon ferrying across the River Styx
Messages:
- Primary: "Ferrying configuration..."
- Secondary: "Charon is crossing the Styx"
2. Coin Flip 💰
Visual: 3D spinning coin (payment for Charon's service)
Theme: Obol coin placed on eyes of the dead
Messages:
- Primary: "Payment accepted..."
- Secondary: "The obol secures passage"
Implementation:
<div className="w-12 h-12 rounded-full bg-gradient-to-br from-amber-400 to-amber-600 animate-coin-flip shadow-lg">
<div className="w-full h-full flex items-center justify-center text-amber-900 font-bold">Ⅰ</div>
</div>
// CSS:
@keyframes coin-flip {
0% { transform: rotateY(0deg); }
100% { transform: rotateY(360deg); }
}
.animate-coin-flip {
animation: coin-flip 2s linear infinite;
transform-style: preserve-3d;
}3. Rowing Oar
Visual: Oar rocking back and forth in rowing motion
Theme: Charon rowing the ferry
Messages:
- Primary: "Rowing across..."
- Secondary: "The ferryman guides your passage"
Implementation:
<div className="w-12 h-1 bg-blue-500 rounded-full origin-center animate-row" />
// CSS:
@keyframes row {
0%, 100% { transform: rotate(-25deg); }
50% { transform: rotate(25deg); }
}
.animate-row { animation: row 1.5s ease-in-out infinite; }4. Flowing River 🌊
Visual: Horizontal flowing particles representing the River Styx
Theme: The river itself
Messages:
- Primary: "Crossing the Styx..."
- Secondary: "Waters guide your journey"
Implementation:
<div className="relative w-24 h-6 overflow-hidden rounded-full bg-slate-800/50">
{[...Array(5)].map((_, i) => (
<div
key={i}
className="absolute w-2 h-2 bg-blue-400 rounded-full animate-flow"
style={{
left: '-8px',
top: '8px',
animationDelay: `${i * 0.3}s`
}}
/>
))}
</div>
// CSS:
@keyframes flow {
0% { transform: translateX(0); opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { transform: translateX(100px); opacity: 0; }
}🔧 Implementation Requirements
1. Refactor CharonLoader Component
File: frontend/src/components/LoadingStates.tsx
type CharonAnimation = 'boat' | 'coin' | 'oar' | 'river'
interface CharonLoaderProps {
size?: 'sm' | 'md' | 'lg'
animation?: CharonAnimation | 'random' // 'random' picks one at random
}
export function CharonLoader({
size = 'md',
animation = 'random'
}: CharonLoaderProps) {
const [selectedAnimation] = useState<CharonAnimation>(() => {
if (animation === 'random') {
const options: CharonAnimation[] = ['boat', 'coin', 'oar', 'river']
return options[Math.floor(Math.random() * options.length)]
}
return animation
})
// Render appropriate animation based on selectedAnimation
switch (selectedAnimation) {
case 'boat': return <BoatAnimation size={size} />
case 'coin': return <CoinAnimation size={size} />
case 'oar': return <OarAnimation size={size} />
case 'river': return <RiverAnimation size={size} />
}
}2. Message Mapping
const ANIMATION_MESSAGES: Record<CharonAnimation, { message: string; submessage: string }> = {
boat: {
message: "Ferrying configuration...",
submessage: "Charon is crossing the Styx"
},
coin: {
message: "Payment accepted...",
submessage: "The obol secures passage"
},
oar: {
message: "Rowing across...",
submessage: "The ferryman guides your passage"
},
river: {
message: "Crossing the Styx...",
submessage: "Waters guide your journey"
}
}3. Context-Aware Selection (Optional Enhancement)
Different operations could prefer different animations:
- Create: Boat (starting a new journey)
- Update: Oar (adjusting course mid-journey)
- Delete: River (returning to the source)
- Bulk: Coin (many souls, many coins)
✅ Acceptance Criteria
- Four distinct animations implemented (Boat, Coin, Oar, River)
- Each animation has matching thematic messages
-
CharonLoaderacceptsanimationprop with 'random' option - Random selection uses
useStateto persist during component lifecycle - All animations are pure CSS/SVG (no JS animation libraries)
- Animations work at all three sizes (sm, md, lg)
- Each animation component is separately testable
- Existing Boat animation remains default if no prop specified
- Documentation updated with all animation options
- Visual regression tests added for each animation variant
🎯 Benefits
- Unique branding: Multiple mythology-themed animations reinforce the Charon identity
- Visual variety: Random rotation prevents user fatigue from seeing the same animation
- Storytelling: Each animation tells a different part of the Charon mythology
- Delightful UX: Unexpected variety creates memorable user experience
- Extensible: Easy to add more mythology-themed animations in the future
📦 Out of Scope
- Animation sound effects
- User preference to select favorite animation
- Animation based on time of day / special dates
- Easter egg animations for special configurations
🔗 References
- Greek Mythology: Charon the Ferryman
- Obol coin: Payment for passage to the underworld
- River Styx: Boundary between Earth and Hades
- Current implementation:
frontend/src/components/LoadingStates.tsx
📝 Notes
This is a cosmetic enhancement that improves brand identity and UX without changing core functionality. Can be implemented independently and doesn't block other features.
Estimated Effort: 3-4 hours
- 2 hours: Implement 3 new animation components
- 1 hour: Message mapping and random selection logic
- 1 hour: Testing and documentation
Metadata
Metadata
Assignees
Labels
Projects
Status