Claude
Skills
Sign in
Back

milestone-celebration

Included with Lifetime
$97 forever

Generates achievement celebration UI with confetti animations, badge unlocks, progress milestones, haptic feedback, and optional share-to-social. Use when user wants to celebrate achievements, show confetti, display milestone badges, or trigger rewards on key thresholds.

Design

What this skill does


# Milestone Celebration Generator

Generate a production milestone celebration system with confetti particle animations via `CAEmitterLayer`, achievement badge views with locked/unlocked states, a celebration overlay with spring animations, haptic feedback, and optional shareable achievement cards — all triggered automatically when users hit key thresholds.

## When This Skill Activates

Use this skill when the user:
- Asks to "add celebrations" or "celebrate achievements"
- Wants "confetti animation" or "particle effects for milestones"
- Mentions "achievement badges" or "badge unlock animation"
- Asks about "milestone rewards" or "progress milestones"
- Wants to "celebrate achievement" or "trigger celebration on threshold"
- Asks about "level-up animation" or "gamification celebrations"

## Pre-Generation Checks

### 1. Project Context Detection
- [ ] Check Swift version (requires Swift 5.9+)
- [ ] Check deployment target (iOS 17+ / macOS 14+ for `@Observable`, spring animations, `sensoryFeedback`)
- [ ] Check for `UIKit` availability (iOS — `CAEmitterLayer` for confetti, haptics)
- [ ] Identify source file locations

### 2. Conflict Detection
Search for existing celebration/animation code:
```
Glob: **/*Confetti*.swift, **/*Celebration*.swift, **/*Achievement*.swift, **/*Milestone*.swift, **/*Badge*.swift
Grep: "CAEmitterLayer" or "CAEmitterCell" or "UINotificationFeedbackGenerator" or "confetti" or "celebration"
```

If existing celebration or gamification library found (e.g., custom confetti, third-party particle libraries):
- Ask if user wants to replace or integrate with it
- If keeping, advise on best practices instead of generating

### 3. Platform Detection
Determine if generating for iOS (UIKit-based confetti + haptics) or macOS (Core Animation confetti, no haptics) or both (cross-platform with feature gates).

## Configuration Questions

Ask user via AskUserQuestion:

1. **Celebration type?** (multi-select)
   - Confetti burst (full-screen particle animation)
   - Badge unlock (icon reveal with glow animation)
   - Level-up (progress ring fill + title transition)
   - Custom (user defines their own celebration style)

2. **Haptic feedback?**
   - Yes — success haptic on celebration trigger (recommended)
   - No — silent celebrations only

3. **Shareable achievement card?**
   - Yes — render achievement as image for social sharing via `ShareLink`
   - No — in-app celebration only

4. **Sound effects?**
   - Yes — play system sound or bundled audio on celebration
   - No — visual only

## Generation Process

### Step 1: Read Templates
Read `templates.md` for production Swift code.

### Step 2: Create Core Files
Generate these files:
1. `Milestone.swift` — Model with id, title, description, threshold, icon, unlock state. `Codable` + `Sendable`.
2. `MilestoneTracker.swift` — `@Observable` class tracking progress toward milestones, checking thresholds, triggering celebrations. Persists unlock state via `UserDefaults` or file storage.
3. `ConfettiView.swift` — `UIViewRepresentable` wrapping `CAEmitterLayer` for confetti particle animation. Configurable colors, duration, density. Respects Reduce Motion.

### Step 3: Create UI Files
4. `CelebrationOverlay.swift` — Full-screen overlay combining confetti + badge reveal + congratulations message. Auto-dismisses after configurable duration. Uses `withAnimation(.spring)`.
5. `MilestoneBadgeView.swift` — Individual badge view with locked/unlocked states, SF Symbol icon, progress ring for partial progress.
6. `MilestoneCollectionView.swift` — Grid layout of all milestones showing locked/unlocked state with progress indicators.

### Step 4: Create Optional Files
Based on configuration:
- `HapticManager.swift` — If haptic feedback selected (thin wrapper around `UINotificationFeedbackGenerator` / `UIImpactFeedbackGenerator`)
- `ShareableMilestoneCard.swift` — If shareable selected (renders milestone as image via `ImageRenderer` + `ShareLink`)

### Step 5: Determine File Location
Check project structure:
- If `Sources/` exists -> `Sources/MilestoneCelebration/`
- If `App/` exists -> `App/MilestoneCelebration/`
- Otherwise -> `MilestoneCelebration/`

## Output Format

After generation, provide:

### Files Created
```
MilestoneCelebration/
├── Milestone.swift              # Model with threshold, icon, unlock state
├── MilestoneTracker.swift       # @Observable tracker with persistence
├── ConfettiView.swift           # CAEmitterLayer confetti animation
├── CelebrationOverlay.swift     # Full-screen celebration overlay
├── MilestoneBadgeView.swift     # Badge with locked/unlocked + progress ring
├── MilestoneCollectionView.swift # Grid of all milestones
├── HapticManager.swift          # Celebration haptics (optional)
└── ShareableMilestoneCard.swift # Achievement share card (optional)
```

### Integration Steps

**Trigger celebration on threshold:**
```swift
struct WorkoutCompleteView: View {
    @State private var tracker = MilestoneTracker()
    @State private var celebratingMilestone: Milestone?

    var body: some View {
        VStack {
            // ... workout summary content ...

            Button("Save Workout") {
                saveWorkout()
                let newCount = totalWorkouts + 1
                if let milestone = tracker.checkThreshold(value: newCount, category: .workouts) {
                    celebratingMilestone = milestone
                }
            }
        }
        .overlay {
            if let milestone = celebratingMilestone {
                CelebrationOverlay(milestone: milestone) {
                    celebratingMilestone = nil
                }
            }
        }
    }
}
```

**Badge collection screen:**
```swift
struct ProfileView: View {
    @State private var tracker = MilestoneTracker()

    var body: some View {
        NavigationStack {
            ScrollView {
                MilestoneCollectionView(
                    milestones: tracker.allMilestones,
                    columns: 3
                )
            }
            .navigationTitle("Achievements")
        }
    }
}
```

**Share an achievement:**
```swift
struct MilestoneDetailView: View {
    let milestone: Milestone
    @State private var showShareCard = false

    var body: some View {
        VStack {
            MilestoneBadgeView(milestone: milestone, size: .large)
            Text(milestone.title).font(.title2.bold())
            Text(milestone.milestoneDescription).foregroundStyle(.secondary)

            if milestone.isUnlocked {
                Button("Share Achievement") {
                    showShareCard = true
                }
                .buttonStyle(.borderedProminent)
            }
        }
        .sheet(isPresented: $showShareCard) {
            ShareableMilestoneCard(milestone: milestone, brandName: "FitApp")
        }
    }
}
```

### Testing

```swift
@Test
func milestoneUnlocksAtThreshold() {
    let tracker = MilestoneTracker(store: InMemoryMilestoneStore())
    let milestone = Milestone(
        id: "first-10",
        title: "First 10",
        milestoneDescription: "Complete 10 workouts",
        threshold: 10,
        iconName: "flame.fill"
    )
    tracker.register(milestone)

    let result = tracker.checkThreshold(value: 10, for: milestone.id)
    #expect(result != nil)
    #expect(result?.isUnlocked == true)
    #expect(result?.unlockedDate != nil)
}

@Test
func milestoneDoesNotUnlockBelowThreshold() {
    let tracker = MilestoneTracker(store: InMemoryMilestoneStore())
    let milestone = Milestone(
        id: "first-10",
        title: "First 10",
        milestoneDescription: "Complete 10 workouts",
        threshold: 10,
        iconName: "flame.fill"
    )
    tracker.register(milestone)

    let result = tracker.checkThreshold(value: 9, for: milestone.id)
    #expect(result == nil)
}

@Test
func confettiRespectsReduceMotion() {
    // When Reduce Motion is enabled, ConfettiView should not emit particles
    let config = ConfettiConfiguration(reduce

Related in Design