Skip to content

Implement Hybrid Rotating Loading Animations with Charon Mythology Theme #308

@Wikid82

Description

@Wikid82

🎨 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
  • CharonLoader accepts animation prop with 'random' option
  • Random selection uses useState to 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

  1. Unique branding: Multiple mythology-themed animations reinforce the Charon identity
  2. Visual variety: Random rotation prevents user fatigue from seeing the same animation
  3. Storytelling: Each animation tells a different part of the Charon mythology
  4. Delightful UX: Unexpected variety creates memorable user experience
  5. 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

No one assigned

    Labels

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions