import SwiftUI // MARK: - PulsingRecordButton /// A minimalist, line-drawn record button suitable for a business productivity app. /// Completely avoids heavy neon glows, replacing them with thin outlines, a clean scale animation, /// and an outline mic icon. struct PulsingRecordButton: View { /// Closure triggered on tap. let action: () -> Void /// Controls the subtle pulse animation. @State private var isAnimating: Bool = false var body: some View { ZStack { // Precise outer thin outline (pulses slightly) Circle() .stroke(Color.primary.opacity(0.1), lineWidth: 1) .frame(width: 120, height: 120) .scaleEffect(isAnimating ? 1.08 : 0.98) .animation( .easeInOut(duration: 2.0).repeatForever(autoreverses: true), value: isAnimating ) // Middle thin outline (expanding and fading pulse) Circle() .stroke(Color.primary.opacity(0.08), lineWidth: 1) .frame(width: 100, height: 100) .scaleEffect(isAnimating ? 1.25 : 1.0) .opacity(isAnimating ? 0.0 : 0.8) .animation( .easeOut(duration: 2.5).repeatForever(autoreverses: false), value: isAnimating ) // Inner button body - pure line circle with translucent background Circle() .fill(Color.primary.opacity(0.03)) .frame(width: 90, height: 90) .overlay( Circle() .stroke(Color.primary.opacity(0.2), lineWidth: 1) ) // Inner core indicator - simple line microphone icon Image(systemName: "mic") .font(.system(size: 24, weight: .light)) .foregroundColor(.primary) .scaleEffect(isAnimating ? 1.02 : 0.98) .animation( .easeInOut(duration: 2.0).repeatForever(autoreverses: true), value: isAnimating ) } .frame(width: 120, height: 120) .contentShape(Circle()) .onTapGesture { HapticManager.trigger(.recordStart) action() } .onAppear { isAnimating = true } } } // MARK: - Preview #Preview { ZStack { Color.spaceBlack.ignoresSafeArea() PulsingRecordButton { print("Record tapped") } } }