Components PulseSpotlight
PulseSpotlight
A hero surface with restraint.
interactive demo
PULSEUI 1.0
Build momentum.
A native design language for ambitious products.
A live browser stand-in using the exact tokens, motion and API of the SwiftUI component.
Want all components at once?
Open the studio
A hero surface with restraint.
A focused launch and product moment surface with typography first and one controlled accent glow.
Hero composition
Static performance-safe glow
Dynamic Type
Light / dark appearance
Usage
PulseSpotlight(eyebrow: "New", title: "Build momentum") { actions } Install
$ pulse add spotlight Source files
Sources/PulseUI/Components/PulseSpotlight/PulseSpotlight.swift
Full source Sources/PulseUI/Components/PulseSpotlight/PulseSpotlight.swift
import SwiftUI
// MARK: - PulseSpotlight
/// A restrained hero surface for important product moments and launch screens.
public struct PulseSpotlight<Content: View>: View {
@Environment(\.pulseTheme) private var theme
@Environment(\.accessibilityReduceMotion) private var reduceMotion
private let eyebrow: String?
private let title: String
private let message: String?
private let content: () -> Content
public init(
eyebrow: String? = nil,
title: String,
message: String? = nil,
@ViewBuilder content: @escaping () -> Content
) {
self.eyebrow = eyebrow
self.title = title
self.message = message
self.content = content
}
public var body: some View {
VStack(alignment: .leading, spacing: theme.spacing.md) {
if let eyebrow {
Text(eyebrow.uppercased())
.font(theme.typography.overline.font)
.tracking(1.4)
.foregroundStyle(theme.colors.accent)
}
Text(title)
.font(theme.typography.hero.font)
.foregroundStyle(theme.colors.foreground)
if let message {
Text(message)
.font(theme.typography.body.font)
.foregroundStyle(theme.colors.foregroundSecondary)
}
content()
}
.padding(theme.spacing.xl)
.frame(maxWidth: .infinity, alignment: .leading)
.background {
ZStack(alignment: .topTrailing) {
theme.colors.surfaceElevated
Circle()
.fill(theme.colors.accent.opacity(0.2))
.frame(width: 220, height: 220)
.blur(radius: reduceMotion ? 20 : 36)
.offset(x: 70, y: -90)
}
}
.clipShape(RoundedRectangle(cornerRadius: theme.radius.xxl, style: .continuous))
.overlay {
RoundedRectangle(cornerRadius: theme.radius.xxl, style: .continuous)
.stroke(theme.colors.border.opacity(0.8), lineWidth: 1)
}
.accessibilityElement(children: .contain)
.accessibilityLabel(title)
}
}