ProgressiveBlurHeader

main

Drop-in SwiftUI component for sticky headers with progressive blur — like Apple Music, Photos, and App Store. Content scrolls underneath with increasing blur, never clipped.
dominikmartn/ProgressiveBlurHeader

ProgressiveBlurHeader

A drop-in SwiftUI component for sticky headers with progressive blur — like Apple Music, Photos, and the App Store.

Content scrolls freely underneath the header, becoming increasingly blurred and tinted. Never clipped, always visible.

Progressive blur header demo

The Problem

Building a progressive blur header in SwiftUI is surprisingly hard. There's no native API for variable-radius blur, and most attempts end up with either:

  • Hard edges where content gets clipped
  • Uniform blur that looks flat
  • Complex UIViewRepresentable wrappers that break with SwiftUI's layout system

AI coding assistants consistently struggle with this pattern — it typically takes many iterations to get right. This package gives you a working solution in one line.

How It Works

Three-layer ZStack:

Layer What Purpose
Back ScrollView Content scrolls freely, never clipped
Middle VariableBlurView + gradient Progressive blur + tint overlay
Front Your header Floats above the blur — no opaque background

The blur uses the same (obfuscated) private API that Apple uses internally, via VariableBlur by nikstar (495+ stars, App Store approved).

Installation

Swift Package Manager — add this to your Package.swift:

dependencies: [
    .package(url: "https://github.com/dominikmartn/ProgressiveBlurHeader", branch: "main"),
]

Or in Xcode: File → Add Package Dependencies → paste the URL.

Usage

import ProgressiveBlurHeader

struct MyView: View {
    var body: some View {
        StickyBlurHeader {
            // Your header — NO opaque background!
            HStack {
                Button("Back") { }
                Spacer()
                Text("Title").font(.headline)
                Spacer()
                Button("Settings") { }
            }
            .padding()
        } content: {
            // Your scrollable content
            ForEach(items) { item in
                ItemRow(item: item)
            }
        }
        .background(Color(.systemBackground))
    }
}

Customization

All parameters have sensible defaults — adjust what you need:

StickyBlurHeader(
    maxBlurRadius: 5,       // Blur intensity: 5 = subtle, 10 = moderate, 20 = strong
    fadeExtension: 64,      // How far (pt) blur extends below header
    tintOpacityTop: 0.7,    // Tint at screen top (behind Dynamic Island)
    tintOpacityMiddle: 0.5  // Tint at header center
) {
    headerView
} content: {
    contentView
}

Parameters

Parameter Default Effect
maxBlurRadius 5 Maximum blur at the top edge
fadeExtension 64 How far blur reaches below the header (pt)
tintOpacityTop 0.7 Darkening behind Dynamic Island / status bar
tintOpacityMiddle 0.5 Darkening at the header's vertical center

The tint automatically adapts to light/dark mode (white tint in light mode, black in dark).

Important Rules

Header must NOT have an opaque background. No .background(Color.xxx) — the VariableBlurView IS the background. Adding one hides the blur effect.

Don't clip the content. No .clipped() anywhere in the hierarchy. Content should always be visible, just blurred.

Header height is measured automatically via GeometryReader + PreferenceKey. It adapts dynamically if your header changes size.

What About iOS 26's .safeAreaBar(edge: .top)?

iOS 26 introduces .safeAreaBar(edge: .top) — a one-liner that adds a sticky bar with blur. But it gives you zero control over blur radius, tint intensity, or fade length. If you need to match a specific design, the custom approach is the way to go.

Requirements

  • iOS 16+
  • Swift 5.9+

Credits

License

MIT — see LICENSE.

Description

  • Swift Tools 5.9.0
View More Packages from this Author

Dependencies

Last updated: Sun Apr 26 2026 08:46:16 GMT-0900 (Hawaii-Aleutian Daylight Time)