Skip to content

Gradient Border Button

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

Gradient Border Button, playing
Download .swift
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

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())
}
}
ParameterTypeDefault
colors[Color][.pink, .orange, .yellow, .mint, .blue, .purple, .pink]
lineWidthDouble3
periodDouble3
action() -> VoidRequired
label() -> ContentRequired

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

GradientBorderButton.swift
// 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()
}