-
-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Enhancement: Rotating Thematic Loading Animations
Issue Type: Enhancement
Priority: Low
Status: Future
Component: Frontend UI
Related: Caddy Reload UI Feedback Implementation
📋 Summary
Implement a hybrid approach for loading animations that randomly rotates between multiple thematic variations for both Charon (proxy operations) and Cerberus (security operations) themes. This adds visual variety and reinforces the mythological branding of the application.
🎯 Motivation
Currently, each operation type displays the same loading animation every time. While functional, this creates a repetitive user experience. By rotating between thematically consistent animation variants, we can:
- Reduce Visual Fatigue: Users won't see the exact same animation on every operation
- Enhance Branding: Multiple mythological references deepen the Charon/Cerberus theme
- Maintain Consistency: All variants stay within their respective theme (blue/Charon or red/Cerberus)
- Add Delight: Small surprises in UI create more engaging user experience
- Educational: Each variant can teach users more about the mythology (e.g., Charon's obol coin)
🎨 Proposed Animation Variants
Charon Theme (Proxy/General Operations)
Color Palette: Blue (#3B82F6, #60A5FA), Slate (#64748B, #475569)
| Animation | Description | Key Message Examples |
|---|---|---|
| Boat on Waves (Current) | Boat silhouette bobbing on animated waves | "Ferrying across the Styx..." |
| Rowing Oar | Animated oar rowing motion in water | "Pulling through the mist..." / "The oar dips and rises..." |
| River Flow | Flowing water with current lines | "Drifting down the Styx..." / "Waters carry the change..." |
Coin Theme (Authentication)
Color Palette: Gold (#F59E0B, #FBBF24), Amber (#D97706, #F59E0B)
| Animation | Description | Key Message Examples |
|---|---|---|
| Coin Flip (Current) | Spinning obol (ancient Greek coin) on Y-axis | "Paying the ferryman..." / "Your obol grants passage" |
| Coin Drop | Coin falling and landing in palm | "The coin drops..." / "Payment accepted" |
| Token Glow | Glowing authentication token/key | "Token gleams..." / "The key turns..." |
| Gate Opening | Stone gate/door opening animation | "Gates part..." / "Passage granted" |
Cerberus Theme (Security Operations)
Color Palette: Red (#DC2626, #EF4444), Amber (#F59E0B), Red-900 (#7F1D1D)
| Animation | Description | Key Message Examples |
|---|---|---|
| Three Heads Alert (Current) | Three heads with glowing eyes and pulsing shield | "Guardian stands watch..." / "Three heads turn..." |
| Shield Pulse | Centered shield with pulsing defensive aura | "Barriers strengthen..." / "The ward pulses..." |
| Guardian Stance | Simplified Cerberus silhouette in alert pose | "Guarding the threshold..." / "Sentinel awakens..." |
| Chain Links | Animated chain links representing binding/security | "Chains of protection..." / "Bonds tighten..." |
🛠️ Technical Implementation
Architecture
// frontend/src/components/LoadingStates.tsx
type CharonVariant = 'boat' | 'coin' | 'oar' | 'river'
type CerberusVariant = 'heads' | 'shield' | 'stance' | 'chains'
interface LoadingMessages {
message: string
submessage: string
}
const CHARON_MESSAGES: Record<CharonVariant, LoadingMessages[]> = {
boat: [
{ message: "Ferrying across...", submessage: "Charon guides the way" },
{ message: "Crossing the Styx...", submessage: "The journey begins" }
],
coin: [
{ message: "Paying the ferryman...", submessage: "The obol tumbles" },
{ message: "Coin accepted...", submessage: "Passage granted" }
],
oar: [
{ message: "Pulling through the mist...", submessage: "The oar dips and rises" },
{ message: "Rowing steadily...", submessage: "Progress across dark waters" }
],
river: [
{ message: "Drifting down the Styx...", submessage: "Waters carry the change" },
{ message: "Current flows...", submessage: "The river guides all" }
]
}
const CERBERUS_MESSAGES: Record<CerberusVariant, LoadingMessages[]> = {
heads: [
{ message: "Three heads turn...", submessage: "Guardian stands watch" },
{ message: "Cerberus awakens...", submessage: "The gate is guarded" }
],
shield: [
{ message: "Barriers strengthen...", submessage: "The ward pulses" },
{ message: "Defenses activate...", submessage: "Protection grows" }
],
stance: [
{ message: "Guarding the threshold...", submessage: "Sentinel awakens" },
{ message: "Taking position...", submessage: "The guardian stands firm" }
],
chains: [
{ message: "Chains of protection...", submessage: "Bonds tighten" },
{ message: "Links secure...", submessage: "Nothing passes unchecked" }
]
}
// Randomly select variant on component mount
export function ConfigReloadOverlay({ type = 'charon', operationType }: Props) {
const [variant] = useState(() => {
if (type === 'cerberus') {
const variants: CerberusVariant[] = ['heads', 'shield', 'stance', 'chains']
return variants[Math.floor(Math.random() * variants.length)]
} else {
const variants: CharonVariant[] = ['boat', 'coin', 'oar', 'river']
return variants[Math.floor(Math.random() * variants.length)]
}
})
const [messages] = useState(() => {
const messageSet = type === 'cerberus'
? CERBERUS_MESSAGES[variant as CerberusVariant]
: CHARON_MESSAGES[variant as CharonVariant]
return messageSet[Math.floor(Math.random() * messageSet.length)]
})
// Render appropriate loader component based on variant
const Loader = getLoaderComponent(type, variant)
return (
<div className="fixed inset-0 bg-slate-900/70 backdrop-blur-sm flex items-center justify-center z-50">
<div className={/* theme styling */}>
<Loader size="lg" />
<div className="text-center">
<p className="text-slate-200 font-medium text-lg">{messages.message}</p>
<p className="text-slate-400 text-sm mt-2">{messages.submessage}</p>
</div>
</div>
</div>
)
}New Loader Components
Each variant needs its own component:
// Charon Variants
export function CharonCoinLoader({ size }: LoaderProps) {
// Spinning coin with heads/tails alternating
}
export function CharonOarLoader({ size }: LoaderProps) {
// Rowing oar motion
}
export function CharonRiverLoader({ size }: LoaderProps) {
// Flowing water lines
}
// Cerberus Variants
export function CerberusShieldLoader({ size }: LoaderProps) {
// Pulsing shield with defensive aura
}
export function CerberusStanceLoader({ size }: LoaderProps) {
// Guardian dog in alert pose
}
export function CerberusChainsLoader({ size }: LoaderProps) {
// Animated chain links
}📐 Animation Specifications
Charon: Coin Flip
- Visual: Ancient Greek obol coin spinning on Y-axis
- Animation: 360° rotation every 2s, slight wobble
- Colors: Gold (#F59E0B) glint, slate shadow
- Message Timing: Change text on coin flip (heads vs tails)
Charon: Rowing Oar
- Visual: Oar blade dipping into water, pulling back
- Animation: Arc motion, water ripples on dip
- Colors: Brown (#92400E) oar, blue (#3B82F6) water
- Timing: 3s cycle (dip 1s, pull 1.5s, lift 0.5s)
Charon: River Flow
- Visual: Horizontal flowing lines with subtle particle drift
- Animation: Lines translate-x infinitely, particles bob
- Colors: Blue gradient (#1E3A8A → #3B82F6)
- Timing: Continuous flow, particles move slower than lines
Cerberus: Shield Pulse
- Visual: Shield outline with expanding aura rings
- Animation: Rings pulse outward and fade (like sonar)
- Colors: Red (#DC2626) shield, amber (#F59E0B) aura
- Timing: 2s pulse interval
Cerberus: Guardian Stance
- Visual: Simplified three-headed dog silhouette, alert posture
- Animation: Heads swivel slightly, ears perk
- Colors: Red (#7F1D1D) body, amber (#F59E0B) eyes
- Timing: 3s head rotation cycle
Cerberus: Chain Links
- Visual: 4-5 interlocking chain links
- Animation: Links tighten/loosen (scale transform)
- Colors: Gray (#475569) chains, red (#DC2626) accents
- Timing: 2.5s cycle (tighten 1s, loosen 1.5s)
🧪 Testing Strategy
Visual Regression Tests
- Capture screenshots of each variant at key animation frames
- Verify animations play smoothly (no janky SVG rendering)
- Test across browsers (Chrome, Firefox, Safari)
Unit Tests
describe('ConfigReloadOverlay - Variant Selection', () => {
it('randomly selects Charon variant', () => {
const variants = new Set()
for (let i = 0; i < 20; i++) {
const { container } = render(<ConfigReloadOverlay type="charon" />)
// Extract which variant was rendered
variants.add(getRenderedVariant(container))
}
expect(variants.size).toBeGreaterThan(1) // Should see variety
})
it('randomly selects Cerberus variant', () => {
const variants = new Set()
for (let i = 0; i < 20; i++) {
const { container } = render(<ConfigReloadOverlay type="cerberus" />)
variants.add(getRenderedVariant(container))
}
expect(variants.size).toBeGreaterThan(1)
})
it('uses variant-specific messages', () => {
const { getByText } = render(<ConfigReloadOverlay type="charon" />)
// Should find ONE of the Charon messages
const hasCharonMessage =
getByText(/ferrying/i) ||
getByText(/coin/i) ||
getByText(/oar/i) ||
getByText(/river/i)
expect(hasCharonMessage).toBeTruthy()
})
})Manual Testing
- Trigger same operation 10 times, verify different animations appear
- Verify messages match animation theme (e.g., "Coin" messages with coin animation)
- Check performance (should be smooth at 60fps)
- Verify accessibility (screen readers announce state)
📦 Implementation Phases
Phase 1: Core Infrastructure (2-3 hours)
- Create variant selection logic
- Create message mapping system
- Update
ConfigReloadOverlayto accept variant prop - Write unit tests for variant selection
Phase 2: Charon Variants (3-4 hours)
- Implement
CharonOarLoadercomponent - Implement
CharonRiverLoadercomponent - Create messages for each variant
- Add Tailwind animations
Phase 3: Coin Variants (3-4 hours)
- Implement
CoinDropLoadercomponent - Implement
TokenGlowLoadercomponent - Implement
GateOpeningLoadercomponent - Create messages for each variant
- Add Tailwind animations
Phase 4: Cerberus Variants (4-5 hours)
- Implement
CerberusShieldLoadercomponent - Implement
CerberusStanceLoadercomponent - Implement
CerberusChainsLoadercomponent - Create messages for each variant
- Add Tailwind animations
Phase 5: Integration & Polish (2-3 hours)
- Update all usage sites (ProxyHosts, WafConfig, etc.)
- Visual regression tests
- Performance profiling
- Documentation updates
Total Estimated Time: 15-19 hours
🎯 Success Metrics
- Users see at least 3 different animations within 10 operations
- Animation performance: 60fps on mid-range devices
- Zero accessibility regressions (WCAG 2.1 AA)
- Positive user feedback on visual variety
- Code coverage: >90% for variant selection logic
🚫 Out of Scope
- User preference for specific variant (always random)
- Custom animation timing controls
- Additional themes beyond Charon/Cerberus
- Sound effects or haptic feedback
- Animation of background overlay entrance/exit
📚 Research References
- Charon Mythology: Wikipedia - Charon
- Cerberus Mythology: Wikipedia - Cerberus
- Obol Coin: Payment for Charon's ferry service in Greek mythology
- SVG Animation Performance: CSS-Tricks SVG Guide
- React Loading States: Best practices for UX during async operations
🔗 See Also
- Main Implementation:
docs/plans/current_spec.md - Charon Documentation:
docs/features.md - Cerberus Documentation:
docs/cerberus.md
Auto-created from rotating-loading-animations.md
Metadata
Metadata
Assignees
Labels
Projects
Status