Gradient Border Button
A capsule button whose border is an angular gradient that keeps rotating.

- Buttons
- iOS 18.0+
- beginner
- Origin
- Original work
- Found by
- gradient border · angular gradient · rotating · glow · outline · rainbow · button · borde degradado · degradado angular · giratorio · resplandor · contorno · arcoíris · botón
Description
Section titled “Description”A capsule button whose border is an angular gradient that keeps rotating.
With Reduce Motion on, the gradient stays still.
struct GradientBorderButtonDemo: View { @State private var isUpgraded = false
var body: some View { GradientBorderButton { isUpgraded.toggle() } label: { Label(isUpgraded ? "Upgraded" : "Upgrade to Pro", systemImage: "crown.fill") .foregroundStyle(.white) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color(red: 0.05, green: 0.05, blue: 0.1).ignoresSafeArea()) }}API reference
Section titled “API reference”| Parameter | Type | Default |
|---|---|---|
colors | [Color] | [.pink, .orange, .yellow, .mint, .blue, .purple, .pink] |
lineWidth | Double | 3 |
period | Double | 3 |
action | () -> Void | Required |
label | () -> Content | Required |
Copy this code into your project, or download the file.
// SPDX-License-Identifier: MIT// Copyright (c) 2026 Oscar Canton and SwiftUI Blocks contributors
import SwiftUI
/// A capsule button whose border is an angular gradient that keeps rotating.////// With Reduce Motion on, the gradient stays still.struct GradientBorderButton<Content: View>: View { var colors: [Color] = [.pink, .orange, .yellow, .mint, .blue, .purple, .pink] var lineWidth: Double = 3 var period: Double = 3 let action: () -> Void @ViewBuilder let label: () -> Content
@Environment(\.accessibilityReduceMotion) private var reduceMotion
var body: some View { Button(action: action) { label() .font(.title3.bold()) .padding(.horizontal, 40) .padding(.vertical, 20) .background(.ultraThinMaterial, in: .capsule) .contentShape(.capsule) } .buttonStyle(.plain) .overlay { TimelineView(.animation(minimumInterval: nil, paused: reduceMotion)) { context in border(at: context.date) } .allowsHitTesting(false) .accessibilityHidden(true) } }
private func border(at date: Date) -> some View { let elapsed = date.timeIntervalSinceReferenceDate.truncatingRemainder(dividingBy: period) let gradient = AngularGradient( colors: colors, center: .center, angle: .degrees(reduceMotion ? 0 : elapsed / period * 360) )
return ZStack { Capsule() .strokeBorder(gradient, lineWidth: lineWidth) .blur(radius: 12) .opacity(0.8) Capsule() .strokeBorder(gradient, lineWidth: lineWidth) } }}
// MARK: - Demo
struct GradientBorderButtonDemo: View { @State private var isUpgraded = false
var body: some View { GradientBorderButton { isUpgraded.toggle() } label: { Label(isUpgraded ? "Upgraded" : "Upgrade to Pro", systemImage: "crown.fill") .foregroundStyle(.white) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color(red: 0.05, green: 0.05, blue: 0.1).ignoresSafeArea()) }}
#Preview { GradientBorderButtonDemo()}