import SwiftUI /// Die einzige Quelle für Farben, Maße und Bewegung in Onyx. /// /// Onyx ist ein tiefschwarzer Stein mit feiner Bänderung und mattem Glanz — kein /// Glas. Das Panel benutzt deshalb bewusst **nicht** die Liquid-Glass-APIs von /// macOS 26, sondern eine dunkle Vibrancy-Ebene mit eigenem Tint darüber. public enum Onyx { // MARK: - Farben public enum Color { /// Grundfläche des Panels. public static let surface = SwiftUI.Color(red: 0.043, green: 0.043, blue: 0.051) /// Kacheln auf der Grundfläche. public static let elevated = SwiftUI.Color(red: 0.078, green: 0.078, blue: 0.090) /// Innenkante — der halbe „edle" Effekt steckt in dieser einen Linie. public static let hairline = SwiftUI.Color.white.opacity(0.08) public static let textPrimary = SwiftUI.Color(red: 0.949, green: 0.949, blue: 0.957) public static let textSecondary = SwiftUI.Color.white.opacity(0.58) public static let textTertiary = SwiftUI.Color.white.opacity(0.34) /// Die Farbe, mit der Onyx sich selbst zeichnet — einstellbar. /// /// Gelesen statt gespeichert: die Tokens holen den Wert bei jedem /// Zugriff aus `OnyxTheme`. Damit merkt SwiftUI die Abhängigkeit und /// zeichnet neu, sobald sich die Farbe ändert — bei einer Konstanten /// wären alle vierzig Fundstellen einzeln umzustellen gewesen. @MainActor public static var accent: SwiftUI.Color { OnyxTheme.shared.accent } /// Herunter- und Hochladen. Auch einstellbar: welche Richtung grün ist /// und welche rosa, ist Gewohnheit, keine Bedeutung. @MainActor public static var download: SwiftUI.Color { OnyxTheme.shared.download } @MainActor public static var upload: SwiftUI.Color { OnyxTheme.shared.upload } /// Die Signalfarben bleiben fest. Gelb heißt Warnung, Rot heißt /// kritisch, Grün heißt in Ordnung — wer das umstellen kann, kann die /// Bedeutung zerstören. public static let positive = SwiftUI.Color(red: 0.435, green: 0.812, blue: 0.592) public static let warning = SwiftUI.Color(red: 0.910, green: 0.706, blue: 0.290) public static let critical = SwiftUI.Color(red: 0.898, green: 0.404, blue: 0.361) /// Deckkraft des Tints über der Vibrancy-Ebene. Darunter liest man den /// Hintergrund durch, darüber wirkt das Panel wie eine schwarze Platte. public static let surfaceOpacity: Double = 0.74 } // MARK: - Maße public enum Metric { public static let panelCornerRadius: CGFloat = 26 public static let tileCornerRadius: CGFloat = 16 public static let panelPadding: CGFloat = 16 public static let tileSpacing: CGFloat = 10 public static let hairlineWidth: CGFloat = 1 } // MARK: - Bewegung public enum Motion { /// Ausfahren: ein Spring mit einem Hauch Nachschwingen. Das gibt dem /// Panel Masse, statt es nur größer werden zu lassen. public static let expand = Animation.spring(response: 0.34, dampingFraction: 0.86) /// Einfahren: kürzer und ohne Nachschwingen. /// /// Schließen muss schneller sein als Öffnen. Beim Öffnen sieht man zu, /// beim Schließen ist man schon woanders — dieselbe Kurve rückwärts /// fühlt sich dort zäh an, und das Nachschwingen liest sich als Ruckler. public static let collapse = Animation.spring(response: 0.24, dampingFraction: 1.0) /// Für Messwerte: sichtbar, aber ohne Pulsieren. public static let value = Animation.easeOut(duration: 0.25) } // MARK: - Typografie public enum Font { public static let title = SwiftUI.Font.system(size: 13, weight: .semibold) public static let body = SwiftUI.Font.system(size: 12, weight: .regular) public static let caption = SwiftUI.Font.system(size: 10, weight: .medium) /// Für alle Messwerte — ohne feste Ziffernbreite zappeln die Zahlen. public static let metric = SwiftUI.Font.system(size: 20, weight: .medium) .monospacedDigit() public static let metricSmall = SwiftUI.Font.system(size: 11, weight: .medium) .monospacedDigit() } }