diff --git a/Sources/Orbit/Foundation/Elevations/Elevation.swift b/Sources/Orbit/Foundation/Elevations/Elevation.swift new file mode 100644 index 00000000000..e87a1897b9c --- /dev/null +++ b/Sources/Orbit/Foundation/Elevations/Elevation.swift @@ -0,0 +1,97 @@ +import SwiftUI + +/// Use elevation to bring content closer to users. +/// +/// Elevation levels with higher numbers are usually visually closer to the user. +public enum Elevation { + case level1 + case level2 + case level3 + case level4 +} + +public extension View { + + /// Elevates the view to make it more prominent. + func elevation(_ level: Elevation, cornerRadius: CGFloat = 0) -> some View { + modifier(ElevationModifier(level: level, cornerRadius: cornerRadius)) + } +} + +struct ElevationModifier: ViewModifier { + + let level: Elevation + let cornerRadius: CGFloat + + let baseShadowColor = Color(red: 79 / 255, green: 94 / 255, blue: 113 / 255) + let borderWidth: CGFloat = 0.1 + + func body(content: Content) -> some View { + content + .clipShape(shape) + .overlay(shape.stroke(baseShadowColor.opacity(0.25), lineWidth: borderWidth)) + .shadow(color: shadowColor, radius: shadowRadius, y: shadowOffsetY) + } + + var shape: some InsettableShape { + RoundedRectangle(cornerRadius: cornerRadius).inset(by: -borderWidth) + } + + var shadowColor: Color { + switch level { + case .level1: return baseShadowColor.opacity(0.2) + case .level2: return baseShadowColor.opacity(0.25) + case .level3: return baseShadowColor.opacity(0.3) + case .level4: return baseShadowColor.opacity(0.4) + } + } + + var shadowRadius: CGFloat { + switch level { + case .level1: return 2 + case .level2: return 4 + case .level3: return 6 + case .level4: return 8 + } + } + + var shadowOffsetY: CGFloat { + switch level { + case .level1: return 3 + case .level2: return 6 + case .level3: return 7 + case .level4: return 7 + } + } +} + +struct ElevationPreviews: PreviewProvider { + + static var previews: some View { + snapshot + .previewLayout(.sizeThatFits) + } + + static var snapshot: some View { + PreviewWrapper { + HStack(spacing: 90) { + titleWithBackground("Level 1") + .elevation(.level1, cornerRadius: BorderRadius.large) + titleWithBackground("Level 2") + .elevation(.level2, cornerRadius: BorderRadius.large) + titleWithBackground("Level 3") + .elevation(.level3, cornerRadius: BorderRadius.large) + titleWithBackground("Level 4") + .elevation(.level4, cornerRadius: BorderRadius.large) + } + } + .padding(40) + .fixedSize() + } + + static func titleWithBackground(_ title: String) -> some View { + Heading(title, style: .title1) + .frame(width: 260, height: 260) + .background(Color.white) + } +} diff --git a/Sources/Orbit/Orbit.docc/Foundation/Elevation.md b/Sources/Orbit/Orbit.docc/Foundation/Elevation.md new file mode 100644 index 00000000000..bb6ee7d2b7b --- /dev/null +++ b/Sources/Orbit/Orbit.docc/Foundation/Elevation.md @@ -0,0 +1,20 @@ +# Elevation + +Use elevation to bring content closer to users. + +## Overview + +Elevation makes a view stand out by adding a shadow. + +Note: [Orbit definition](https://orbit.kiwi/foundation/elevation/) + +## Adding elevation to a view. + +Use the `View.elevation(_:cornerRadius:)` modifier to add elevation to a view: + +```swift +Button("Tap me", style: .primarySubtle) + .elevation(.level3) +``` + +The higher the elevation level, the more the view stands out. diff --git a/Sources/Orbit/Orbit.docc/Foundation.md b/Sources/Orbit/Orbit.docc/Foundation/Foundation.md similarity index 95% rename from Sources/Orbit/Orbit.docc/Foundation.md rename to Sources/Orbit/Orbit.docc/Foundation/Foundation.md index ec5ec7afae6..792762121fb 100644 --- a/Sources/Orbit/Orbit.docc/Foundation.md +++ b/Sources/Orbit/Orbit.docc/Foundation/Foundation.md @@ -24,6 +24,8 @@ ### Elevation +- + ### Icons ### Illustrations