Steven
Steven7 Min. Lesezeit

Eine CSS-Variable, fünf Review-Runden und eine Swift-Toolchain, die log

GeekBye v2.0.7 machte das gesamte Overlay verstellbar durchscheinend — was nach einer einzeiligen CSS-Änderung klingt und definitiv keine war. Die eigentliche Geschichte ist, was das Code-Review aufdeckte: zwei Flächen, die sich weigerten zu verblassen, eine Aufnahmeleiste, die bei gleicher Deckkraft falsch aussah, und ein kompiliertes Binary, das um 672 Bytes hin und her sprang, weil unsere eigene Dokumentation einem Reviewer die falsche Swift-Version nannte.

Engineering
Design
Build
GeekBye-Releases
Eine CSS-Variable, fünf Review-Runden und eine Swift-Toolchain, die log

„Mach das Overlay durchscheinend" ist die Art Aufgabe, die nach einem Schieberegler und einer CSS-opacity-Eigenschaft klingt. GeekBye v2.0.7 lieferte genau dieses nutzerseitige Ergebnis — einen einzigen verstellbaren „Hellmodus", der dich durch die Panels des Assistenten hindurch auf das schauen lässt, was dahinter ist. Aber der interessante Teil dieses Releases ist nicht das Feature. Es sind die fünf Review-Runden, die nötig waren, um es richtig hinzubekommen, und eine Kriegsgeschichte über ein kompiliertes Binary, das seine Größe änderte, weil unsere eigene Dokumentation einen Reviewer belog.

Der richtige Weg, eine ganze UI durchscheinend zu machen

Die naive Variante besteht darin, einen Deckkraftwert über jede Panel-Komponente zu streuen und die Sache für erledigt zu erklären. Das ist eine Wartungsfalle: ein Dutzend fest verdrahteter Werte, die in dem Moment auseinanderdriften, in dem jemand einen davon anfasst.

Stattdessen nutzt v2.0.7 ein einziges geteiltes CSS-Utility und ein Token pro Fläche. Es gibt eine einzige overlay-surface-Klasse, die den Hintergrund eines Panels aus zwei Custom Properties berechnet: einer Basis-Deckkraft, die jede Fläche für sich selbst deklariert (die Steuerleiste, der Chat, die Transkript-Blasen, die Codeblöcke und so weiter wählen jeweils ihre eigene), und einem globalen Alpha-Multiplikator, der an der Overlay-Wurzel mitfährt und die Schieberegler-Einstellung des Nutzers repräsentiert. Jede Fläche liest denselben Multiplikator; jede behält ihren eigenen Charakter. Entscheidend: Nur der Panel-Hintergrund wird skaliert — Text, Icons und der backdrop-blur bleiben voll gerendert, sodass „durchscheinend" nie „unlesbar" bedeutet.

Zwei Entscheidungen in diesem Design sind hervorzuheben, weil sie den Unterschied zwischen einem Feature ausmachen, das hilft, und einem, das Support-Tickets erzeugt:

  • Der Standard ist voll deckend. Die Deckkraft-Einstellung wird bei 100 % ausgeliefert — ein No-op. Niemandem wurde das Overlay beim Update plötzlich durchsichtig; die Transluzenz ist da, wenn du danach greifst, und bis dahin unsichtbar.
  • Die Einstellung wird defensiv validiert. Es gibt eine einzige Quelle der Wahrheit für Minimum, Maximum, Standard und Schrittweite und einen Normalisierer, der den leeren/fehlenden Fall absichert. Das ist wichtig wegen einer JavaScript-Falle: Number(null) ist 0, also würde eine fehlende, naiv erzwungene Einstellung das Overlay in seinen durchsichtigsten Zustand kippen statt in seinen Standard. Der Normalisierer fängt das ab, bevor es passieren kann.

Zieh am Schieberegler, und das gesamte Overlay tönt sich live um, mitten in der Aufnahme, weil jede Fläche dieselbe Einstellung abonniert hat. Kein Neustart, kein Neuladen.

Was das Review aufdeckte (und was ein Test mit nur einem Monitor nie aufgedeckt hätte)

Hier hörte „eine CSS-Variable" auf, einfach zu sein. Drei Bugs tauchten im Review auf, und zwei davon sind dieselbe Lektion in unterschiedlicher Verkleidung.

Ein Panel, das nicht verblassen wollte. Das Tastaturkürzel-Overlay blieb hartnäckig deckend, während alles andere durchscheinend wurde. Die Ursache: Es rendert außerhalb des DOM-Teilbaums, der die Deckkraft-Variable trägt, also erbte es nie den Wert, der von der Overlay-Wurzel herunterkaskadierte. CSS-Custom-Properties werden durch den DOM-Baum vererbt — und wenn ein Knoten nicht tatsächlich Nachkomme dessen ist, wo du die Variable setzt, bekommt er stillschweigend nichts. Die Lösung war, die Variable auch auf diesem Panel lokal zu setzen.

Ein Hover-Farbton, der verschwand. Die Fn/Assist-Buttons verloren ihr cyanfarbenes Hover-Leuchten. Das Transluzenz-Utility setzte den Hintergrund mit !important (es muss sich gegen die Flächen-Standards durchsetzen), und das walzte den hover:-Hintergrundton auf demselben Element nieder. Spezifitäts-Kollision. Die Lösung war, den Hover-Ton ebenso nachdrücklich zu machen, damit er beim Hovern zurückgewinnen konnte.

Beide sind dieselbe zugrunde liegende Wahrheit: eine CSS-Variablen-Kaskade ist eine echte Korrektheitsgrenze, kein Styling-Detail. Vererbung und Spezifität entscheiden, ob dein Wert das Element überhaupt erreicht, und keine von beiden taucht in einem Screenshot auf, bevor es falsch ist.

Gleiche Deckkraft, anderes Aussehen. Der subtilste: Die Aufnahmeleiste und die Ruheleiste waren auf die identische Basis-Deckkraft gesetzt und sahen trotzdem sichtbar unterschiedlich aus. Warum? Die Aufnahme-Pille nutzte einen kräftigeren backdrop-blur, und ein starker Blur über einem dunklen Hintergrund mittelt sich zu einem dunkleren Ergebnis — also erzeugte gleiches Alpha ungleiches Aussehen. Sie hatte außerdem einen dezenten Rand, wo die Ruheleiste eine helle Ring-Verlaufs-Kante hatte. Die Lösung glich das Blur-Level und den Ring an. Die Lektion blieb hängen: gleiches Alpha ist kein gleiches Aussehen, sobald Blur ins Spiel kommt. Transluzenz ist ein System interagierender Effekte, keine einzelne Zahl.

Das Binary, das seine Größe änderte, weil die Dokumentation log

Jetzt die Kriegsgeschichte, und es ist meine Lieblingssorte — die, in der Werkzeug und Dokumentation sich widersprechen und ein sorgfältiger Reviewer ins Kreuzfeuer gerät.

GeekBye liefert einige vorkompilierte Swift-Binaries aus (native Helfer für Dinge wie Meeting-Erkennung). Weil ein kompiliertes Binary ins Repo eingecheckt ist, muss jeder Entwickler es mit exakt derselben Swift-Version kompilieren — unterschiedliche Toolchains erzeugen aus identischem Quellcode unterschiedliche Bytes, und das würde jedes Mal einen Phantom-Git-Konflikt bedeuten, wenn jemand neu baut. Also gibt es ein Skript, das eine bestimmte Version erzwingt: Swift 6.3.x, geprüft zur Build-Zeit.

Während des Reviews dieses Releases merkte ein Reviewer an, dass ein Binary neu gebaut werden sollte, um zur „vom Repo vorgeschriebenen Toolchain" zu passen. Der Entwickler tat genau das — und die Größe des Binaries änderte sich von 89,184 Bytes auf 88,512. Die Commit-Nachricht sagte sogar, sie „stelle byte-stabile Ausgabe wieder her", indem sie mit Swift 6.2.1 neu baute, „der vom Repo vorgeschriebenen Toolchain".

Nur war 6.2.1 die falsche Version. Das Build-Prüfskript verlangte 6.3.x. Das Problem war, dass die Prosa-Dokumentation — das README und der Beitragenden-Leitfaden — an ein paar Stellen immer noch 6.2.1 sagte. So folgten ein gewissenhafter Reviewer und ein gewissenhafter Entwickler, beide bemüht, das Richtige zu tun, der Dokumentation schnurstracks dahin, genau das Artefakt zu committen, das das Prüfskript ablehnen soll. Ein 6.2.1-Binary in einem 6.3.x-Repo ist genau die Drift, die jeden sauberen Neubau anderer Entwickler kaputtmachen würde.

Der nächste Commit behob es — und die Behebung ist der springende Punkt. Er baute das Binary nicht nur zurück in seine 6.3.x-Form (Größe wiederhergestellt auf 89,184, genau dort, wo sie anfing). Er korrigierte auch die veralteten „6.2.1"-Verweise in der Dokumentation, damit die Falle nicht erneut zuschnappen konnte. Das Binary sprang über zwei Commits um 672 Bytes und wieder zurück, und der eigentliche Bug war nie das Binary. Er war, dass zwei Quellen der Wahrheit — das Prüfskript und die geschriebene Dokumentation — sich widersprachen und ein Mensch der falschen vertraute.

Drei Dinge, die dieses Release lehrte

  1. Eine CSS-Variablen-Kaskade ist eine Korrektheitsgrenze. Zwei der drei Review-Bugs liefen auf eine Variable hinaus, die nicht vererbt wurde, und ein !important, das einen Hover-Zustand niederwalzte. Wenn ein Wert ein Element über Vererbung erreichen oder über Spezifität gewinnen muss, ist dieser Pfad Logik — prüfe ihn wie Logik, nicht wie Styling.
  2. Gleiche Parameter können ungleiche Ergebnisse erzeugen. Die Aufnahmeleiste und die Ruheleiste teilten sich eine Deckkraft und sahen trotzdem unterschiedlich aus, weil ein anderer Blur das Ergebnis änderte. Wenn du ein visuelles System auf eine Zahl vereinheitlichst, prüfe, dass die anderen Variablen rund um jede Fläche nicht heimlich verändern, was diese Zahl bedeutet.
  3. Wenn Dokumentation und Prüfung sich widersprechen, behebe den Widerspruch — nicht nur das Symptom. Der Bug war kein schlechtes Binary; es war eine Dokumentation, die die falsche Toolchain nannte, und ein Prüfskript, das etwas anderes sagte. Das Binary neu zu bauen behandelt das Symptom. Alle Quellen der Wahrheit in Einklang zu bringen ist die eigentliche Lösung, denn der nächste Reviewer wird der Dokumentation ebenfalls vertrauen.

GeekBye v2.0.7 lieferte die einheitliche Transluzenz aus, alle Randfälle geprüft und die Toolchain-Dokumentation geradegerückt. Es ist das letzte der „leisen Handwerk"-Releases vor dem Zuverlässigkeits-Sprint, der folgte. Für das Fundament darunter siehe was eine Version 2 wirklich kostet (v2.0.0); für das Schwester-Release des Feinschliffs ruhige Software: der Flicker-Fix und der Antwortmodus-Chip (v2.0.3 + v2.0.5); und dafür, wie das Overlay während eines Anrufs verborgen bleibt, wie du beim Bildschirmteilen unsichtbar bleibst.