Skip to content
Merged
  •  
  •  
  •  
12 changes: 11 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 6.0
// swift-tools-version: 6.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
// swiftlint:disable all

Expand All @@ -20,11 +20,21 @@ let package = Package(
targets: ["OversizeUI"]
),
],
/*
dependencies: [
.package(url: "https://github.com/SwiftGen/SwiftGenPlugin", .upToNextMajor(from: "6.6.2")),
],
*/
targets: [
.target(
name: "OversizeUI",
dependencies: [],
resources: [.process("Resources")]
/*
plugins: [
.plugin(name: "SwiftGenPlugin", package: "SwiftGenPlugin"),
]
*/
),
.testTarget(name: "OversizeUITests", dependencies: ["OversizeUI"]),
]
Expand Down
10 changes: 5 additions & 5 deletions Sources/OversizeUI/Controls/Avatar/Avatar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -216,19 +216,19 @@ public struct Avatar: View {

private var avatarSize: CGFloat {
#if os(tvOS)
return Space.xLarge.rawValue
return .xLarge
#else
switch controlSize {
case .mini:
return Space.medium.rawValue
return .medium
case .small:
return Space.large.rawValue
return .large
case .regular:
return Space.xLarge.rawValue
return .xLarge
case .large, .extraLarge:
return Space.xxxLarge.rawValue
@unknown default:
return Space.xLarge.rawValue
return .xLarge
}
#endif
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/OversizeUI/Controls/Background/Background.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public struct Background<Content: View>: View {
static var colorTertiary: Color { Color.backgroundTertiary }

/// Size
static var paddingMedium: CGFloat { Space.medium.rawValue }
static var paddingSmall: CGFloat { Space.small.rawValue }
static var paddingMedium: CGFloat { .medium }
static var paddingSmall: CGFloat { .small }
}

private let content: Content
Expand All @@ -48,7 +48,7 @@ public struct Background<Content: View>: View {
.padding(.all, paddingSize)
.frame(minWidth: 0, maxWidth: .infinity)
.background(backgroundColor)
.cornerRadius(Radius.medium)
.cornerRadius(.small)
}

private var paddingSize: CGFloat {
Expand Down
2 changes: 1 addition & 1 deletion Sources/OversizeUI/Controls/Badge/Badge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public typealias Bage<Label: View> = Badge<Label>

public struct Badge<Label: View>: View {
@Environment(\.theme) private var theme: ThemeSettings
@Environment(\.controlRadius) private var controlRadius: Radius
@Environment(\.controlRadius) private var controlRadius

private let label: Label
private let color: Color
Expand Down
2 changes: 1 addition & 1 deletion Sources/OversizeUI/Controls/Button/BarButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ public struct BarButton: View {
case let .image(image, _):
image
.renderingMode(.template)
.onSurfacePrimaryForeground()
.onSurfacePrimary()
case let .icon(icon, _):
IconDeprecated(icon, color: .onSurfaceSecondary)
}
Expand Down
114 changes: 70 additions & 44 deletions Sources/OversizeUI/Controls/Button/Button.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,27 @@ import SwiftUI
///
/// Use these button types to create consistent visual hierarchy in your interface:
/// - Use ``primary`` for the main call-to-action
/// - Use ``secondary`` for important but alternative actions
/// - Use ``secondary`` for important but alternative actions
/// - Use ``tertiary`` for supplementary actions
/// - Use ``quaternary`` for minimal actions with low visual weight
///
/// ```swift
/// Button("Save") { save() }
/// .buttonStyle(.primary)
///
///
/// Button("Cancel") { cancel() }
/// .buttonStyle(.tertiary)
/// ```
public enum ButtonType: Int, CaseIterable {
/// Primary button style for main call-to-action buttons.
case primary

/// Secondary button style for important alternative actions.
case secondary

/// Tertiary button style for supplementary actions.
case tertiary

/// Quaternary button style for minimal actions with low visual weight.
case quaternary
}
Expand Down Expand Up @@ -73,33 +73,33 @@ public enum ButtonType: Int, CaseIterable {
public struct OversizeButtonStyle: ButtonStyle {
/// Environment values for theme customization.
@Environment(\.theme) private var theme: ThemeSettings

/// Environment value indicating if the button is enabled.
@Environment(\.isEnabled) private var isEnabled: Bool

/// Environment value indicating if the button is in loading state.
@Environment(\.isLoading) private var isLoading: Bool

/// Environment value indicating if accent styling should be applied.
@Environment(\.isAccent) private var isAccent: Bool

/// Environment value for the button's elevation/shadow level.
@Environment(\.elevation) private var elevation: Elevation

/// Environment value for the button's border shape.
@Environment(\.controlBorderShape) var controlBorderShape: ControlBorderShape

/// Environment value indicating if the button should have a border.
@Environment(\.isBordered) var isBordered: Bool

#if !os(tvOS)
/// Environment value for the button's control size.
@Environment(\.controlSize) var controlSize: ControlSize
#endif

/// The semantic type of button determining its visual style.
private let type: ButtonType

/// Whether the button should expand to fill available width.
private let isInfinityWidth: Bool?

Expand All @@ -114,42 +114,68 @@ public struct OversizeButtonStyle: ButtonStyle {
}

public func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.body(.semibold)
.opacity(isLoading ? 0 : 1)
.foregroundColor(foregroundColor(for: configuration.role).opacity(foregroundOpacity))
.padding(.horizontal, horizontalPadding)
.padding(.vertical, verticalPadding)
.frame(maxWidth: maxWidth)
.background(background(for: configuration.role))
.overlay(loadingView(for: configuration.role))
.scaleEffect(configuration.isPressed ? 0.95 : 1)
.shadowElevation(isEnabled ? elevation : .z0)
if #available(iOS 26.0, macOS 26.0, tvOS 26.0, watchOS 26.0, *) {
configuration.label
.body(.semibold)
.opacity(isLoading ? 0 : 1)
.foregroundColor(foregroundColor(for: configuration.role).opacity(foregroundOpacity))
.padding(.horizontal, horizontalPadding)
.padding(.vertical, verticalPadding)
.frame(maxWidth: maxWidth)
.if(type != .quaternary, then: {
$0
.background(background(for: configuration.role))
.glassEffect(.clear.interactive())
}, else: {
$0
.scaleEffect(configuration.isPressed ? 1.15 : 1)
.animation(.spring(response: 0.3, dampingFraction: 0.6), value: configuration.isPressed)
})
.overlay(loadingView(for: configuration.role))
} else {
configuration.label
.body(.semibold)
.opacity(isLoading ? 0 : 1)
.foregroundColor(foregroundColor(for: configuration.role).opacity(foregroundOpacity))
.padding(.horizontal, horizontalPadding)
.padding(.vertical, verticalPadding)
.frame(maxWidth: maxWidth)
.background(background(for: configuration.role))
.overlay(loadingView(for: configuration.role))
.scaleEffect(configuration.isPressed ? 0.95 : 1)
.shadowElevation(isEnabled ? elevation : .z0)
}
}

@ViewBuilder
private func background(for role: ButtonRole?) -> some View {
if type != .quaternary {
switch controlBorderShape {
case .capsule:
Capsule()
.fill(backgroundColor(for: role)
.opacity(backgroundOpacity))
.overlay {
Capsule()
.strokeBorder(Color.onSurfacePrimary.opacity(0.15), lineWidth: 2)
.opacity(isBordered || theme.borderButtons ? 1 : 0)
}
if #available(iOS 26.0, macOS 26.0, tvOS 26.0, watchOS 26.0, *) {
Capsule()
.fill(backgroundColor(for: role).opacity(backgroundOpacity))

Comment on lines 151 to 155
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve border shape for iOS 26 buttons

On iOS 26+ this branch always returns a plain Capsule fill, so the controlBorderShape environment and the isBordered/theme.borderButtons stroke overlay used in the pre‑26 branch are ignored. The default rounded-rectangle shape (and any bordered style) will render as an unbordered capsule on iOS 26+, which is a visual regression for controls that rely on those settings.

Useful? React with 👍 / 👎.

case let .roundedRectangle(radius):
RoundedRectangle(cornerRadius: radius != .medium ? radius.rawValue : theme.radius, style: .continuous)
.fill(backgroundColor(for: role)
.opacity(backgroundOpacity))
.overlay {
RoundedRectangle(cornerRadius: radius != .medium ? radius.rawValue : theme.radius, style: .continuous)
.strokeBorder(Color.onSurfacePrimary.opacity(0.15), lineWidth: 2)
.opacity(isBordered || theme.borderButtons ? 1 : 0)
}
} else {
if type != .quaternary {
switch controlBorderShape {
case .capsule:
Capsule()
.fill(backgroundColor(for: role)
.opacity(backgroundOpacity))
.overlay {
Capsule()
.strokeBorder(Color.onSurfacePrimary.opacity(0.15), lineWidth: 2)
.opacity(isBordered || theme.borderButtons ? 1 : 0)
}

case let .roundedRectangle(radius):
RoundedRectangle(cornerRadius: radius != .medium ? radius.rawValue : theme.radius, style: .continuous)
.fill(backgroundColor(for: role)
.opacity(backgroundOpacity))
.overlay {
RoundedRectangle(cornerRadius: radius != .medium ? radius.rawValue : theme.radius, style: .continuous)
.strokeBorder(Color.onSurfacePrimary.opacity(0.15), lineWidth: 2)
.opacity(isBordered || theme.borderButtons ? 1 : 0)
}
}
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions Sources/OversizeUI/Controls/Button/FieldButtonStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ public struct FieldButtonStyle: ButtonStyle {
switch fieldPosition {
case .top, .bottom, .center:
#if os(iOS)
RoundedRectangleCorner(radius: Radius.medium, corners: backgroundShapeCorners)
RoundedRectangleCorner(radius: .xSmall, corners: backgroundShapeCorners)
.fill(isPressed ? Color.surfaceTertiary : Color.surfaceSecondary)
.overlay(
RoundedRectangleCorner(radius: Radius.medium, corners: backgroundShapeCorners)
RoundedRectangleCorner(radius: .xSmall, corners: backgroundShapeCorners)
.stroke(
theme.borderTextFields
? Color.border
Expand All @@ -38,11 +38,11 @@ public struct FieldButtonStyle: ButtonStyle {
)
)
#else
RoundedRectangle(cornerRadius: Radius.medium, style: .continuous)
RoundedRectangle(cornerRadius: .xSmall, style: .continuous)
.fill(isPressed ? Color.surfaceTertiary : Color.surfaceSecondary)
.overlay(
RoundedRectangle(
cornerRadius: Radius.medium,
cornerRadius: .xSmall,
style: .continuous
)
.stroke(
Expand All @@ -55,11 +55,11 @@ public struct FieldButtonStyle: ButtonStyle {
#endif

default:
RoundedRectangle(cornerRadius: Radius.medium, style: .continuous)
RoundedRectangle(cornerRadius: .xSmall, style: .continuous)
.fill(isPressed ? Color.surfaceTertiary : Color.surfaceSecondary)
.overlay(
RoundedRectangle(
cornerRadius: Radius.medium,
cornerRadius: .xSmall,
style: .continuous
)
.stroke(
Expand Down
4 changes: 2 additions & 2 deletions Sources/OversizeUI/Controls/Checkbox/Checkbox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@ public struct Checkbox<Label: View>: View {
@ViewBuilder
private func checkboxImage(isEnabled: Bool, isOn: Bool) -> some View {
ZStack {
RoundedRectangle(cornerRadius: Radius.small, style: .continuous)
RoundedRectangle(cornerRadius: .xxSmall, style: .continuous)
.strokeBorder(Color.onSurfaceTertiary.opacity(isEnabled ? 1 : 0.5), lineWidth: 2.5)
.frame(width: 24, height: 24)
.opacity(isOn ? 0 : 1)

RoundedRectangle(cornerRadius: Radius.small, style: .continuous).fill(Color.accent.opacity(isEnabled ? 1 : 0.5))
RoundedRectangle(cornerRadius: .xxSmall, style: .continuous).fill(Color.accent.opacity(isEnabled ? 1 : 0.5))
.frame(width: 24, height: 24)
.opacity(isOn ? 1 : 0)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public struct DefaultColorSelectorStyle: ColorSelectorStyle {
}

struct ColorSelectorStyleStyleKey: EnvironmentKey {
public static let defaultValue = AnyColorSelectorStyle(style: DefaultColorSelectorStyle())
static let defaultValue = AnyColorSelectorStyle(style: DefaultColorSelectorStyle())
}

public extension EnvironmentValues {
Expand Down
Loading
Loading