Wenn CSS Inline-Conditionals erhält

Avatar of Geoff Graham
Geoff Graham am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Vor ein paar Wochen schrillten einige Alarmglocken, als die CSS Working Group (CSSWG) beschloss, eine if()-Bedingung zur Spezifikation des CSS Values Module Level 5 hinzuzufügen. Es war Lea Verous X-Post am selben Tag, der meine Aufmerksamkeit erregte

Lea ist diejenige, die das GitHub-Issue eröffnet hat, das zur Diskussion führte, und durch einen Zufall – oder vielleicht Schicksal – kam der Beschluss genau an ihrem Geburtstag. Das muss ein turbulenter Tag gewesen sein! Was hast du zum Geburtstag bekommen? „Oh, weißt du, nur einen angenommenen Vorschlag für die CSS-Spezifikation.“ Wahnsinn, einfach Wahnsinn.

Der angenommene Vorschlag ist grünes Licht für die CSSWG, an der Idee zu arbeiten, mit der Absicht, einen Spezifikationsentwurf für weiteres Feedback in Umlauf zu bringen, damit daraus hoffentlich ein empfohlenes CSS-Feature wird. Es wird also noch eine ganze Weile dauern, bis das Ganze spruchreif ist – also falls es jemals fertiggestellt wird.

Aber die Idee, Stile basierend auf einer bedingten Anforderung anzuwenden, ist extrem spannend und einen frühen Blick wert. Ich habe am selben Tag, an dem Lea auf X postete, einige Notizen dazu in meinem Blog verfasst und dachte mir, ich fasse diese hier für die Nachwelt zusammen und ergänze weitere Details, die seitdem aufgetaucht sind.

Dies ist keine neue Idee

Viele Vorschläge entstehen aus zuvor abgelehnten Ideen, und if() ist da keine Ausnahme. Tatsächlich haben wir in letzter Zeit mehrere CSS-Funktionen erhalten, die bedingtes Styling ermöglichen – :has() und Container Style Queries sind zwei der offensichtlichsten Beispiele. Lea zitiert sogar ein Ticket von 2018, das dem nun angenommenen Vorschlag sehr ähnlich sieht.

Der Unterschied?

Style Queries wurden bereits veröffentlicht, und wir konnten uns einfach auf dieselbe Syntax für Bedingungen beziehen (plus media() und supports() aus Tabs @when-Vorschlag), während im Vorschlag von 2018 die Funktionsweise von Bedingungen weitgehend undefiniert war.

Lea Verou, „Inline conditionals in CSS?“

Mir gefällt, wie Lea aufzeigt, dass CSS schon immer eine bedingte Sprache war

Leute... CSS hatte von Anfang an Bedingungen. Jeder Selektor ist im Grunde eine Bedingung!

Lea Verou, „Inline conditionals in CSS?“

Stimmt! Die Kaskade ist das Instrument, um Selektoren auszuwerten und sie den HTML-Elementen auf einer Seite zuzuordnen. Was if() mitbringt, ist eine Möglichkeit, Inline-Bedingungen direkt bei den Werten zu schreiben.

Syntax

Es läuft auf Folgendes hinaus

<if()> = if( <container-query>, [<declaration-value>]{1, 2} )

…wobei

  • Werte können verschachtelt werden, um mehrere Zweige zu erzeugen.
  • Wenn kein drittes Argument angegeben wird, entspricht dies einem leeren Token-Stream.

All dies ist im Moment rein konzeptionell und nichts ist in Stein gemeißelt. Es ist wahrscheinlich, dass sich Dinge ändern, während die CSSWG an dem Feature arbeitet. Nach aktuellem Stand dreht sich die Idee jedoch darum, eine Bedingung anzugeben und einen von zwei deklarierten Stilen festzulegen – einen als „Standard“-Stil und einen als „aktualisierten“ Stil, wenn die Bedingung zutrifft.

.element {
  background-color:
    /* If the style declares the following custom property: */
    if(style(--variant: success),
      var(--color-green-50), /* Matched condition */
      var(--color-blue-50);  /* Default style */
    );
}

In diesem Fall suchen wir nach einer style()-Bedingung, bei der eine CSS-Variable namens --variant deklariert und auf den Wert success gesetzt ist, und

  • …wenn --variant auf success gesetzt ist, setzen wir den Wert von success auf --color-green-50, eine Variable, die auf einen grünlichen Farbwert verweist.
  • …wenn --variant nicht auf success gesetzt ist, setzen wir den Wert von success auf --color-blue-50, eine Variable, die auf einen bläulichen Farbwert verweist.

Der Standardstil wäre optional, daher denke ich, dass er in einigen Fällen für eine etwas bessere Lesbarkeit weggelassen werden kann

.element {
  background-color:
    /* If the style declares the following custom property: */
    if(style(--variant: success),
      var(--color-green-50) /* Matched condition */
    );
}

Die Syntaxdefinition oben erwähnt, dass wir ein drittes Argument zusätzlich zur erfüllten Bedingung und dem Standardstil unterstützen könnten, was es uns ermöglicht, Bedingungen in Bedingungen zu verschachteln

background-color: if(
  style(--variant: success), var(--color-success-60), 
    if(style(--variant: warning), var(--color-warning-60), 
      if(style(--variant: danger), var(--color-danger-60), 
        if(style(--variant: primary), var(--color-primary)
      )
    ),
  )
);

Uff, das sieht nach einer wilden Verschachtelung aus! Lea schlägt daraufhin eine Syntax vor, die zu einer viel flacheren Struktur führen würde

<if()> = if( 
  [ <container-query>, [<declaration-value>]{2}  ]#{0, },
  <container-query>, [<declaration-value>]{1, 2} 
)

Mit anderen Worten: Verschachtelte Bedingungen sind viel flacher, da sie außerhalb der ursprünglichen Bedingung deklariert werden können. Dasselbe Konzept wie zuvor, aber eine andere Syntax

background-color: if(
  style(--variant: success), var(--color-success-60), 
  style(--variant: warning), var(--color-warning-60),
  style(--variant: danger), var(--color-danger-60), 
  style(--variant: primary), var(--color-primary)
);

Anstatt also eine if()-Anweisung innerhalb einer anderen if()-Anweisung zu haben, können wir alle möglichen passenden Bedingungen in einer einzigen Anweisung zusammenfassen.

Wir versuchen, eine if()-Bedingung zu erfüllen, indem wir die Stile eines Elements abfragen. Es gibt keine entsprechende size()-Funktion zur Abfrage von Dimensionen – Container Queries setzen Größe implizit voraus

.element {
  background: var(--color-primary);

  /* Condition */
  @container parent (width >= 60ch) {
    /* Applied styles */
    background: var(--color-success-60);
  }
}

Und Container Queries werden zu Style Queries, wenn wir stattdessen die style()-Funktion aufrufen

.element {
  background: orangered;

  /* Condition */
  @container parent style(--variant: success) {
    /* Applied styles */
    background: dodgerblue;
  }
}

Style Queries ergeben für mich viel mehr Sinn, wenn man sie im Kontext von if() betrachtet. Ohne if() kann man die allgemeine Nützlichkeit von Style Queries leicht in Frage stellen. Aber in diesem Licht wird klar, dass Style Queries Teil eines viel größeren Bildes sind, das über reine Container Queries hinausgeht.

Bei der if()-Syntax gibt es noch viel zu klären. Zum Beispiel beschreibt Tab Atkins ein mögliches Szenario, das zu Verwirrung darüber führen könnte, was die zutreffende Bedingung und was die Standardstil-Parameter sind. Wer weiß also, wie das Ganze am Ende ausgeht!

Bedingungen, die andere Bedingungen unterstützen

Wie wir bereits festgestellt haben, ist if() bei weitem nicht die einzige Art von bedingter Prüfung in CSS. Wie würde es aussehen, eine Inline-Bedingung zu schreiben, die andere Bedingungen wie @supports und @media prüft?

Im Code

background-color: if(
  supports( /* etc. */ ),
  @media( /* etc. */ )
);

Die Herausforderung bestünde darin, dass Container Größenabfragen unterstützen. Wie bereits erwähnt, gibt es keine explizite size()-Funktion; stattdessen verhält sie sich eher wie eine anonyme Funktion.

@andruud hat die Herausforderung in der GitHub-Diskussion treffend beschrieben

Ich sehe nicht, warum wir nicht supports() und media() machen könnten, aber Größenabfragen würden Zyklen mit dem Layout verursachen, die schwer oder gar unmöglich zu erkennen sind. (Das ist der Grund, warum wir überhaupt die Einschränkungen brauchten, die wir derzeit für Größen-CQs haben.)

„Können wir das nicht schon mit dem [X]-Ansatz machen?“

Als wir uns vorhin die Syntax angesehen haben, ist Ihnen vielleicht aufgefallen, dass es bei if() genauso sehr um Custom Properties geht wie um Bedingungen. Über die Jahre sind mehrere Workarounds entstanden, um das zu imitieren, was wir gewinnen würden, wenn wir den Wert einer Custom Property bedingt setzen könnten, darunter:

  • Die Verwendung von Custom Properties als Boolean, um Stile anzuwenden oder nicht, je nachdem, ob sie gleich 0 oder 1 sind. (Ana hat dazu einen wunderbaren Artikel geschrieben.)
  • Die Verwendung einer Platzhalter-Custom-Property mit leerem Wert, die gesetzt wird, wenn eine andere Custom Property gesetzt ist, d.h. „der Custom Property Toggle-Trick“, wie Chris ihn beschreibt.
  • Container Style Queries! Das Problem (neben der mangelnden Implementierung) ist, dass Container Stile nur auf ihre Nachfahren anwenden, d. h. sie können Stile nicht auf sich selbst anwenden, wenn sie eine bestimmte Bedingung erfüllen, sondern nur auf ihren Inhalt.

Lea geht in einem separaten Post mit dem Titel „Inline conditional statements in CSS, now?“ tief darauf ein. Er enthält eine Tabelle, die Ansätze skizziert und vergleicht, die ich hier einfach einfüge. Die Erklärungen stecken voller komplexer CSS-Nerderie, sind aber extrem hilfreich, um die Notwendigkeit von if() zu verstehen und zu sehen, wie es im Vergleich zu den cleveren „Hacks“ abschneidet, die wir jahrelang verwendet haben.

MethodeEingabewerteAusgabewerteProContra
Binäre lineare InterpolationZahlenQuantitativKann als Teil eines Wertes verwendet werdenBegrenzter Ausgabebereich
Togglesvar(--alias) (tatsächliche Werte sind zu seltsam, um sie direkt offenzulegen)AlleKann in einem Teil eines Wertes verwendet werdenSeltsame Werte, die aliased werden müssen
Pausierte AnimationenZahlenAlleNormale, entkoppelte DeklarationenÜbernimmt die animation-Eigenschaft

Kaskaden-Kuriositäten
Type GrindingKeywordsJeder vom syntax-Deskriptor unterstützte WertHohe Flexibilität für exponierte APIGute KapselungCSS muss in das Light DOM eingefügt werden

Mühsamer Code (kann aber mit Build-Tools automatisiert werden)

Keine Firefox-Unterstützung (obwohl sich das gerade ändert)
Variabler AnimationsnameKeywordsAlleNormale, entkoppelte DeklarationenUnpraktisch außerhalb des Shadow DOM aufgrund von Namenskonflikten

Übernimmt die animation-Eigenschaft

Kaskaden-Kuriositäten

Alles Gute zum Geburtstag, Lea!

Zwei Wochen zu spät, aber danke, dass du die Geschenke deines großen Tages mit uns teilst! 🎂

Referenzen