Eine Designschicht statt hundertfach gesetzter Zahlen

Token für Schrift, Symbolgröße, Abstand, Radius und Bewegung, dazu sieben
Bauteile, die vorher in jeder Kachel neu gebaut wurden — Karte, Abschnittstitel,
Hinweis, Pillenknopf, Regler, Ablegefläche, Haarlinie.

Symbole bekommen eine **eigene** Staffel und nicht die der Schrift: ein
9-Punkt-Symbol neben 9-Punkt-Text wirkt kleiner, nicht gleich groß. Beide in
eine Staffel zu zwingen hieße, an jeder Stelle gegen sie zu arbeiten.

Das Flächenrezept — Tint, Bänderung, Kante — stand zweimal da, einmal für das
Panel und einmal für Fenster. Es ist jetzt `OnyxStone`, generisch über die Form,
damit auch die animierende Notch-Silhouette es benutzen kann.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-05 21:04:51 +02:00
co-authored by Claude Opus 5
parent 49a3ff0548
commit ecb39fbe5f
11 changed files with 1195 additions and 184 deletions
@@ -0,0 +1,59 @@
import Testing
import SwiftUI
@testable import OnyxDesign
@Suite("Werkstoffe")
@MainActor
struct NotchMaterialTests {
@Test("Stein ist deckend — sonst hätte er an der Hardware-Notch eine Kante")
func stoneIsOpaque() {
#expect(NotchMaterial.stone.fillOpacity == 1.0)
// Und braucht deshalb keine Weichzeichnung darunter.
#expect(!NotchMaterial.stone.showsVibrancy)
}
@Test("Je durchlässiger, desto weniger Deckkraft")
func opacityDecreasesWithTranslucency() {
#expect(NotchMaterial.stone.fillOpacity > NotchMaterial.blur.fillOpacity)
#expect(NotchMaterial.blur.fillOpacity > NotchMaterial.glass.fillOpacity)
}
@Test("Je durchlässiger, desto mehr Glanz")
func sheenGrowsWithTranslucency() {
// Auf deckendem Schwarz genügt ein Hauch; auf Glas ist der Glanz das,
// was die Fläche überhaupt als Körper lesbar macht.
#expect(NotchMaterial.stone.sheen < NotchMaterial.blur.sheen)
#expect(NotchMaterial.blur.sheen < NotchMaterial.glass.sheen)
}
@Test("Alle durchscheinenden Werkstoffe brauchen die Vibrancy-Ebene")
func translucentMaterialsNeedVibrancy() {
#expect(NotchMaterial.blur.showsVibrancy)
#expect(NotchMaterial.glass.showsVibrancy)
}
@Test("Kein Werkstoff ist unsichtbar")
func nothingIsFullyTransparent() {
// Eine Fläche mit Deckkraft null wäre kein Werkstoff, sondern ein
// fehlendes Panel — und niemand fände die Einstellung zurück.
for material in NotchMaterial.allCases {
#expect(material.fillOpacity > 0.3)
}
}
@Test("Jeder Werkstoff überlebt das Speichern")
func roundTripsThroughDefaults() {
for material in NotchMaterial.allCases {
#expect(NotchMaterial(rawValue: material.rawValue) == material)
}
}
@Test("Ein unbekannter Name fällt auf Stein zurück, nicht ins Nichts")
func unknownNameFallsBack() {
let defaults = UserDefaults(suiteName: "onyx.test.material")!
defaults.removePersistentDomain(forName: "onyx.test.material")
defaults.set("liquidUnobtainium", forKey: "onyx.theme.material")
#expect(OnyxTheme(defaults: defaults).material == .stone)
}
}