Skip to content

Shimmer Button

A gradient capsule button with a band of light that sweeps across it in a loop.

Shimmer Button, playing
Download .swift
Origin
Original work
Found by
shimmer · shine · button · light sweep · gradient · call to action · glossy · brillo · destello · botón · barrido de luz · degradado · llamada a la acción

A gradient capsule button with a band of light that sweeps across it in a loop.

With Reduce Motion on, the sweep is removed and the button stays still.

struct ShimmerButtonDemo: View {
@State private var isStarted = false
var body: some View {
VStack(spacing: 36) {
VStack(spacing: 10) {
Text("Unlock everything")
.font(.largeTitle.bold())
Text("Every feature, free for a week.")
.font(.body)
.foregroundStyle(.white.opacity(0.7))
}
ShimmerButton(
title: isStarted ? "Trial started" : "Start free trial",
systemImage: isStarted ? "checkmark" : "sparkles"
) {
isStarted.toggle()
}
}
.foregroundStyle(.white)
.multilineTextAlignment(.center)
.padding(24)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(red: 0.07, green: 0.06, blue: 0.16).ignoresSafeArea())
}
}
ParameterTypeDefault
titleLocalizedStringResourceRequired
systemImageString?nil
colors[Color][.indigo, .purple]
sweepDurationDouble1.1
pauseDouble0.9
action() -> VoidRequired

Copy this code into your project, or download the file.

ShimmerButton.swift
// SPDX-License-Identifier: MIT
// Copyright (c) 2026 Oscar Canton and SwiftUI Blocks contributors
import SwiftUI
/// A gradient capsule button with a band of light that sweeps across it in a loop.
///
/// With Reduce Motion on, the sweep is removed and the button stays still.
struct ShimmerButton: View {
let title: LocalizedStringResource
var systemImage: String?
var colors: [Color] = [.indigo, .purple]
var sweepDuration: Double = 1.1
var pause: Double = 0.9
let action: () -> Void
@Environment(\.accessibilityReduceMotion) private var reduceMotion
var body: some View {
Button(action: action) {
HStack(spacing: 10) {
if let systemImage {
Image(systemName: systemImage)
.accessibilityHidden(true)
}
Text(title)
}
.font(.title3.bold())
.foregroundStyle(.white)
.padding(.horizontal, 40)
.padding(.vertical, 20)
.background(LinearGradient(colors: colors, startPoint: .leading, endPoint: .trailing))
.overlay {
if !reduceMotion {
sweep
}
}
.clipShape(.capsule)
.contentShape(.capsule)
.shadow(color: (colors.first ?? .clear).opacity(0.5), radius: 18, y: 8)
}
.buttonStyle(ShimmerButtonPressStyle(reduceMotion: reduceMotion))
}
private var sweep: some View {
LinearGradient(colors: [.clear, .white.opacity(0.65), .clear], startPoint: .leading, endPoint: .trailing)
.scaleEffect(x: 0.4, y: 2.5)
.rotationEffect(.degrees(20))
.keyframeAnimator(initialValue: -1.1, repeating: true) { content, position in
content.visualEffect { effect, proxy in
effect.offset(x: position * proxy.size.width)
}
} keyframes: { _ in
KeyframeTrack {
LinearKeyframe(1.1, duration: sweepDuration)
LinearKeyframe(1.1, duration: pause)
}
}
.allowsHitTesting(false)
.accessibilityHidden(true)
}
}
/// The button dims and shrinks a little while it is pressed.
private struct ShimmerButtonPressStyle: ButtonStyle {
let reduceMotion: Bool
func makeBody(configuration: Configuration) -> some View {
configuration.label
.opacity(configuration.isPressed ? 0.8 : 1)
.scaleEffect(configuration.isPressed && !reduceMotion ? 0.96 : 1)
.animation(.snappy(duration: 0.2), value: configuration.isPressed)
}
}
// MARK: - Demo
struct ShimmerButtonDemo: View {
@State private var isStarted = false
var body: some View {
VStack(spacing: 36) {
VStack(spacing: 10) {
Text("Unlock everything")
.font(.largeTitle.bold())
Text("Every feature, free for a week.")
.font(.body)
.foregroundStyle(.white.opacity(0.7))
}
ShimmerButton(
title: isStarted ? "Trial started" : "Start free trial",
systemImage: isStarted ? "checkmark" : "sparkles"
) {
isStarted.toggle()
}
}
.foregroundStyle(.white)
.multilineTextAlignment(.center)
.padding(24)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(red: 0.07, green: 0.06, blue: 0.16).ignoresSafeArea())
}
}
#Preview {
ShimmerButtonDemo()
}