Das Grün, das überall auftauchte, war die System-Akzentfarbe: `.tint` und `.accentColor` werden ohne eigene Angabe dorthin durchgereicht, und das betraf alle Popovers und das ganze Einstellungsfenster. Die Notch-Widgets dagegen benutzten Onyx' eigenes Blau. Zwei Farben, ungewollt. Jetzt eine, und die ist einstellbar. Ein neuer Reiter „Farben": Akzentfarbe mit sechs Vorschlägen und einem Knopf, der die Systemfarbe übernimmt, dazu das Paar für Herunter- und Hochladen samt Tauschknopf. Die Signalfarben bleiben fest und stehen nur zur Ansicht dort. Gelb heißt Warnung, Rot heißt kritisch, Grün heißt in Ordnung — bei Temperatur, Akku, Lüfter und Fehlern. Wer sie umstellen kann, kann ihre Bedeutung zerstören; ein rotes „alles gut" liest niemand richtig. Sichtbar sind sie trotzdem, sonst sucht man die Warnfarbe im Reiter und findet sie nirgends. Umgesetzt über die Tokens statt über vierzig Fundstellen: `Onyx.Color.accent` ist keine Konstante mehr, sondern liest bei jedem Zugriff aus `OnyxTheme`. Damit merkt SwiftUI die Abhängigkeit und zeichnet neu, sobald die Farbe sich ändert. Dazu ein `.tint` an den vier Wurzeln — Panel, Popovers, Einstellungen, Einrichtung —, damit auch Haken, Regler und Auswahlfelder mitziehen. Nebenbei die Beschriftung: „Je Programm" heißt jetzt „Programme". Großgesetzt las sich das als englisches „J-E". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
89 lines
4.2 KiB
Swift
89 lines
4.2 KiB
Swift
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()
|
|
}
|
|
}
|