feat: initial public release
ConsentOS — a privacy-first cookie consent management platform. Self-hosted, source-available alternative to OneTrust, Cookiebot, and CookieYes. Full standards coverage (IAB TCF v2.2, GPP v1, Google Consent Mode v2, GPC, Shopify Customer Privacy API), multi-tenant architecture with role-based access, configuration cascade (system → org → group → site → region), dark-pattern detection in the scanner, and a tamper-evident consent record audit trail. This is the initial public release. Prior development history is retained internally. See README.md for the feature list, architecture overview, and quick-start instructions. Licensed under the Elastic Licence 2.0 — self-host freely; do not resell as a managed service.
This commit is contained in:
111
sdks/ios/ConsentOS/Sources/ConsentOSUI/BannerTheme.swift
Normal file
111
sdks/ios/ConsentOS/Sources/ConsentOSUI/BannerTheme.swift
Normal file
@@ -0,0 +1,111 @@
|
||||
#if canImport(SwiftUI)
|
||||
import SwiftUI
|
||||
import ConsentOSCore
|
||||
|
||||
// MARK: - Banner Theme
|
||||
|
||||
/// Resolved colour and typography theme for the consent banner.
|
||||
///
|
||||
/// Derived from ``ConsentConfig/BannerConfig`` values, falling back to sensible defaults.
|
||||
public struct BannerTheme: Sendable {
|
||||
|
||||
// MARK: - Colours
|
||||
|
||||
public let backgroundColor: Color
|
||||
public let textColor: Color
|
||||
public let accentColor: Color
|
||||
public let secondaryTextColor: Color
|
||||
public let dividerColor: Color
|
||||
public let acceptButtonBackground: Color
|
||||
public let acceptButtonTextColor: Color
|
||||
public let rejectButtonBackground: Color
|
||||
public let rejectButtonTextColor: Color
|
||||
|
||||
// MARK: - Typography
|
||||
|
||||
public let titleFont: Font
|
||||
public let bodyFont: Font
|
||||
public let buttonFont: Font
|
||||
public let captionFont: Font
|
||||
|
||||
// MARK: - Layout
|
||||
|
||||
public let cornerRadius: CGFloat
|
||||
public let horizontalPadding: CGFloat
|
||||
public let verticalPadding: CGFloat
|
||||
public let buttonHeight: CGFloat
|
||||
|
||||
// MARK: - Button Labels
|
||||
|
||||
public let acceptButtonText: String
|
||||
public let rejectButtonText: String
|
||||
public let manageButtonText: String
|
||||
public let title: String
|
||||
public let description: String
|
||||
|
||||
// MARK: - Defaults
|
||||
|
||||
static let defaultAccentHex = "#1A73E8" // Google-blue — overridden by brand config
|
||||
static let defaultBackgroundHex = "#FFFFFF"
|
||||
static let defaultTextHex = "#1A1A1A"
|
||||
|
||||
// MARK: - Factory
|
||||
|
||||
/// Creates a ``BannerTheme`` from the banner configuration returned by the API.
|
||||
///
|
||||
/// Hex values not present in the config fall back to neutral defaults.
|
||||
public static func from(config: ConsentConfig.BannerConfig) -> BannerTheme {
|
||||
let background = Color(hex: config.backgroundColor) ?? Color(.systemBackground)
|
||||
let text = Color(hex: config.textColor) ?? Color(.label)
|
||||
let accent = Color(hex: config.accentColor) ?? Color(hex: defaultAccentHex)!
|
||||
|
||||
return BannerTheme(
|
||||
backgroundColor: background,
|
||||
textColor: text,
|
||||
accentColor: accent,
|
||||
secondaryTextColor: text.opacity(0.6),
|
||||
dividerColor: text.opacity(0.12),
|
||||
acceptButtonBackground: accent,
|
||||
acceptButtonTextColor: .white,
|
||||
rejectButtonBackground: Color(.secondarySystemBackground),
|
||||
rejectButtonTextColor: text,
|
||||
titleFont: .headline,
|
||||
bodyFont: .subheadline,
|
||||
buttonFont: .subheadline.weight(.semibold),
|
||||
captionFont: .caption,
|
||||
cornerRadius: 12,
|
||||
horizontalPadding: 16,
|
||||
verticalPadding: 16,
|
||||
buttonHeight: 44,
|
||||
acceptButtonText: config.acceptButtonText ?? "Accept All",
|
||||
rejectButtonText: config.rejectButtonText ?? "Reject All",
|
||||
manageButtonText: config.manageButtonText ?? "Manage Preferences",
|
||||
title: config.title ?? "We value your privacy",
|
||||
description: config.description ?? "We use cookies to improve your experience and for analytics."
|
||||
)
|
||||
}
|
||||
|
||||
/// Default theme used when no configuration has been loaded yet.
|
||||
public static var defaultTheme: BannerTheme {
|
||||
from(config: ConsentConfig.BannerConfig())
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Colour Hex Extension
|
||||
|
||||
extension Color {
|
||||
/// Initialises a `Color` from a hex string such as `"#1A73E8"` or `"1A73E8"`.
|
||||
init?(hex: String?) {
|
||||
guard let hex else { return nil }
|
||||
let sanitised = hex.trimmingCharacters(in: CharacterSet(charactersIn: "#"))
|
||||
guard sanitised.count == 6,
|
||||
let value = UInt64(sanitised, radix: 16) else {
|
||||
return nil
|
||||
}
|
||||
let r = Double((value >> 16) & 0xFF) / 255
|
||||
let g = Double((value >> 8) & 0xFF) / 255
|
||||
let b = Double((value ) & 0xFF) / 255
|
||||
self.init(red: r, green: g, blue: b)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user