Shimmer Text
Text with a band of light that sweeps across its letters through a mask.

- Text
- iOS 18.0+
- beginner
- Origin
- Original work
- Found by
- shimmer · shine · highlight · loading · mask · text animation · slide to unlock · brillo · destello · reflejo · cargando · máscara · animación de texto
Description
Section titled “Description”A band of light that sweeps across the letters, masked by the text itself.
With Reduce Motion on, the text is drawn plainly in the highlight color.
struct ShimmerTextDemo: View { private let dimmed = Color.white.opacity(0.35)
var body: some View { ZStack { LinearGradient(colors: [Color(white: 0.16), .black], startPoint: .top, endPoint: .bottom) .ignoresSafeArea()
VStack(spacing: 28) { ShimmerText(text: "Shimmer", baseColor: dimmed, highlightColor: .white) .font(.system(.largeTitle, design: .rounded, weight: .black)) .scaleEffect(1.5)
ShimmerText(text: "slide to unlock", baseColor: dimmed, highlightColor: .white, duration: 1.8) .font(.title3.weight(.medium))
Label { ShimmerText(text: "Writing your summary...", baseColor: .purple.opacity(0.5), highlightColor: .white) } icon: { Image(systemName: "sparkles") .foregroundStyle(.purple) .accessibilityHidden(true) } .font(.headline) .padding(.horizontal, 20) .padding(.vertical, 14) .background(.white.opacity(0.08), in: .capsule) } .padding(24) } }}API reference
Section titled “API reference”| Parameter | Type | Default | Description |
|---|---|---|---|
text | String | Required | |
baseColor | Color | .secondary | |
highlightColor | Color | .primary | |
duration | Double | 2.2 | Seconds for one sweep. |
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 band of light that sweeps across the letters, masked by the text itself.////// With Reduce Motion on, the text is drawn plainly in the highlight color.struct ShimmerText: View { let text: String var baseColor: Color = .secondary var highlightColor: Color = .primary /// Seconds for one sweep. var duration: Double = 2.2
/// Half the width of the band of light, as a fraction of the text's width. private let bandWidth = 0.3
@Environment(\.accessibilityReduceMotion) private var reduceMotion
var body: some View { Text(text) .foregroundStyle(reduceMotion ? highlightColor : baseColor) .overlay { if !reduceMotion { TimelineView(.animation(minimumInterval: 1.0 / 60.0)) { context in band(at: context.date) } .mask { Text(text).foregroundStyle(.black) } .accessibilityHidden(true) } } }
/// Start and end points beyond 0...1 place the band off to the side, between sweeps. private func band(at date: Date) -> LinearGradient { let cycle = date.timeIntervalSinceReferenceDate.truncatingRemainder(dividingBy: duration) / duration let center = -bandWidth + (1 + 2 * bandWidth) * cycle
return LinearGradient( colors: [.clear, highlightColor, .clear], startPoint: UnitPoint(x: center - bandWidth, y: 0.5), endPoint: UnitPoint(x: center + bandWidth, y: 0.5) ) }}
// MARK: - Demo
struct ShimmerTextDemo: View { private let dimmed = Color.white.opacity(0.35)
var body: some View { ZStack { LinearGradient(colors: [Color(white: 0.16), .black], startPoint: .top, endPoint: .bottom) .ignoresSafeArea()
VStack(spacing: 28) { ShimmerText(text: "Shimmer", baseColor: dimmed, highlightColor: .white) .font(.system(.largeTitle, design: .rounded, weight: .black)) .scaleEffect(1.5)
ShimmerText(text: "slide to unlock", baseColor: dimmed, highlightColor: .white, duration: 1.8) .font(.title3.weight(.medium))
Label { ShimmerText(text: "Writing your summary...", baseColor: .purple.opacity(0.5), highlightColor: .white) } icon: { Image(systemName: "sparkles") .foregroundStyle(.purple) .accessibilityHidden(true) } .font(.headline) .padding(.horizontal, 20) .padding(.vertical, 14) .background(.white.opacity(0.08), in: .capsule) } .padding(24) } }}
#Preview { ShimmerTextDemo()}