Wie @supports funktioniert

Avatar of Chris Coyier
Chris Coyier am

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

CSS hat eine tolle Funktion, mit der wir testen können, ob der Browser eine bestimmte Eigenschaft oder eine Eigenschaft:Wert-Kombination unterstützt, bevor wir einen Stilblock anwenden – ähnlich wie eine @media-Abfrage übereinstimmt, wenn beispielsweise die Breite des Browserfensters kleiner als eine bestimmte Größe ist und dann die darin enthaltenen CSS wirksam werden. Im gleichen Sinne wird das CSS innerhalb dieser Funktion wirksam, wenn das getestete Eigenschaft:Wert-Paar im aktuellen Browser unterstützt wird. Diese Funktion heißt @supports und sieht so aus:

@supports (display: grid) {
  .main {
    display: grid;
  }
}

Warum? Nun, das ist etwas knifflig. *Persönlich* brauche ich es nicht wirklich oft. CSS hat natürliche Fallback-Mechanismen, so dass, wenn der Browser eine Eigenschaft:Wert-Kombination nicht versteht, er sie ignoriert und etwas verwendet, das zuvor deklariert wurde, falls vorhanden, dank der Kaskade. Manchmal kann das verwendet werden, um mit Fallbacks umzugehen und das Endergebnis ist etwas weniger wortreich. Ich bin sicherlich kein Typ, der sagt: "Es muss in jedem Browser gleich sein", aber ich bin auch kein Typ, der "aufwendige Fallbacks schreibt, um nahe heranzukommen". Ich bevorzuge im Allgemeinen eine Situation, in der ein natürliches Scheitern einer Eigenschaft:Wert-Kombination nichts Drastisches tut, um die Funktionalität zu zerstören.

Dennoch hat @supports sicherlich Anwendungsfälle! Und wie ich bei der Erstellung dieses Beitrags festgestellt habe, verwenden viele Leute es für viele interessante Situationen.

Ein klassischer Anwendungsfall

Das Beispiel, das ich in der Einleitung verwendet habe, ist ein klassisches Beispiel, das in vielen Texten zu diesem Thema zu finden ist. Hier ist es etwas ausführlicher:

/* We're gonna write a fallback up here in a minute */

@supports (display: grid) {
  .photo-layout {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    grid-gap: 2rem;
  }
}

Schönes Grid! Das wiederholte und automatische Füllen von Spalten ist eine tolle Funktion von CSS Grid. Aber natürlich gibt es Browser, die Grid nicht unterstützen, oder nicht alle spezifischen Funktionen davon, die ich oben verwende.

Zum Beispiel hat iOS die Unterstützung für CSS Grid in Version 10.2 eingeführt, aber iOS hat Flexbox-Unterstützung seit Version 7. Das ist eine beträchtliche Lücke von Leuten mit älteren iOS-Geräten, die Flexbox, aber nicht Grid unterstützen. Ich bin sicher, es gibt weitere solche Lücken, aber Sie verstehen wahrscheinlich, worum es geht.

Es kann akzeptabel sein, den Fallback dafür auf nichts zu belassen, je nach Anforderung. Zum Beispiel vertikal gestapelte Block-Elemente anstelle eines Multi-Column-Grid-Layouts. Das ist oft in Ordnung für mich. Aber nehmen wir an, das ist nicht in Ordnung, wie bei einer Fotogalerie oder etwas, das unbedingt eine grundlegende Grid-ähnliche Struktur haben muss. In diesem Fall könnte es besser funktionieren, mit Flexbox als Standard zu beginnen und @supports zu verwenden, um Grid-Funktionen anzuwenden, wo sie unterstützt werden...

.photo-layout {
  display: flex;
  flex-wrap: wrap;
  > div {
    flex: 200px;
    margin: 1rem;
  }
}

@supports (display: grid) {
  .photo-layout {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    grid-gap: 2rem;
    > div {
      margin: 0;
    }
  }
}

Der "Fallback" ist der Code außerhalb des @supports-Blocks (die Eigenschaften oberhalb des Blocks im obigen Beispiel), und der Grid-Code befindet sich entweder darin oder danach. Der @supports-Block ändert keine Spezifität, sodass wir die Reihenfolge der Quellen benötigen, um sicherzustellen, dass die Überschreibungen funktionieren.

Beachten Sie, dass ich den Rand bei den divs innerhalb des @supports-Blocks zurücksetzen musste. Das ist die Art von Sache, die ich etwas nervig finde. Es gibt gerade genug Überschneidungen zwischen den beiden Szenarien, dass man sich der gegenseitigen Auswirkungen sehr bewusst sein muss.

Wünscht man sich da nicht, dass es logisch vollständig getrennt sein könnte?

In @supports-Blöcken gibt es keine "Logik", aber das bedeutet nicht, dass sie immer verwendet werden sollten.

Jen Simmons hat dieses Beispiel in einem Artikel namens Using Feature Queries in CSS vor einigen Jahren veröffentlicht.

/* Considered a BAD PRACTICE, at least if you're supporting IE 11 and iOS 8 and older */
@supports not (display: grid) {
   /* Isolated code for non-support of grid */
}
@supports (display: grid) {
   /* Isolated code for support of grid */
}

Beachten Sie den not-Operator im ersten Block. Dieser prüft auf Browser, die Grid *nicht* unterstützen, um bestimmte Stile für diese Browser anzuwenden. Der Grund, warum dieser Ansatz als schlechte Praxis gilt, ist, dass die Browserunterstützung für @supports selbst berücksichtigt werden muss! Das macht die Sache so verdammt knifflig.

Es ist sehr verlockend, Code in logisch getrennten @supports-Blöcken zu schreiben, da er jedes Mal von vorne beginnt und keine vorherigen Werte überschreiben muss und sich mit diesen logischen Gedankenspielen auseinandersetzen muss. Aber kehren wir zur gleichen iOS-Situation zurück, die wir zuvor betrachtet haben... @supports wurde in iOS in Version 9 (direkt zwischen der Einführung von Flexbox in iOS 7 und Grid in iOS 10.2) eingeführt. Das bedeutet, dass jeder Flexbox-Fallback-Code in einem @supports-Block, der den not-Operator verwendet, um die Unterstützung für (display: grid) {} zu prüfen, weder in iOS 7 noch 8 funktionieren würde, was bedeutet, dass der Fallback nun einen Fallback benötigt, um in Browsern zu funktionieren, in denen er ansonsten funktioniert hätte. Puh!

Der Hauptgrund, auf @supports zurückzugreifen, ist die Berücksichtigung *sehr unterschiedlicher* Implementierungen von etwas, abhängig von der Feature-Unterstützung, wo es einfacher wird, zwischen diesen Implementierungen zu argumentieren und sie zu unterscheiden, wenn die Codeblöcke getrennt sind.

Wir werden wahrscheinlich an einen Punkt gelangen, an dem wir gegenseitig ausschließende Blöcke wie diese ohne Sorge verwenden können. Davon abgesehen...

@supports wird wahrscheinlich im Laufe der Zeit nützlicher werden.

Sobald @supports in allen Browsern, die Sie unterstützen müssen, unterstützt wird, können Sie es aggressiver einsetzen und ohne die Komplexität, ob @supports selbst unterstützt wird, zu berücksichtigen. Hier ist das Support-Grid dazu:

Diese Browser-Support-Daten stammen von Caniuse, das mehr Details enthält. Eine Zahl bedeutet, dass der Browser die Funktion ab dieser Version und aufwärts unterstützt.

Desktop

ChromeFirefoxIEEdgeSafari
2822Nein129

Mobil / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
1271274.49.0-9.2

Grundsätzlich sind IE 11 und alle iOS-Geräte, die auf iOS 8 feststecken, die Problemzonen. Wenn Ihre Anforderungen bereits über diese hinausgehen, können Sie @supports freier nutzen.

Die Ironie ist, dass es nicht *tonnenweise* CSS-Features gab, die große klare @supports-Anwendungsfälle hatten – aber es gibt einige! Anscheinend ist es möglich, neue schicke Sachen wie Houdini zu testen.

(Ich bin mir nicht ganz sicher, was man in den @supports-Block schreiben würde, um das zu tun. Hat das jemand anderes schon gemacht?)

Wenn @supports nichts Nützliches tut

Ich habe eine ganze Menge @supports-Verwendungen in freier Wildbahn gesehen, bei denen das Endergebnis genau dasselbe ist, als wenn es nicht verwendet worden wäre. Zum Beispiel...

@supports (transform: rotate(5deg)) {
  .avatar {
    transform: rotate(5deg);
  }
}

Auf einer bestimmten Ebene ergibt das eine perfekte logische Logik. Wenn Transformationen unterstützt werden, verwende sie. **Aber es ist unnötig, wenn im Fall ohne Unterstützung nichts anderes passiert.** In diesem Fall kann die Transformation ohne den @supports-Block fehlschlagen und das Ergebnis ist dasselbe.

Hier ist ein weiteres Beispiel dafür.

Es gibt Browser-Erweiterungen zum Spielen mit @supports

Es gibt zwei davon!

Sie basieren beide auf der Idee, dass wir @supports-Blöcke in CSS schreiben und sie dann so ein- und ausschalten können, als ob wir eine Darstellung des Codes in einem Browser betrachten würden, der dieses Feature unterstützt oder nicht.

Hier ist ein Video von Keiths Tool, angewendet auf das Szenario mit Grid und einem Flexbox-Fallback:

Das macht Spaß zum Ausprobieren und ist eine sehr clevere Technologie. Aber in genau diesem Szenario würde ich wahrscheinlich einfach Flexbox verwenden, wenn ich das Layout identisch damit umsetzen könnte, und diesen kleinen Teil technischer Schuld sparen.

Ires Werkzeug, worüber sie im Artikel Creating The Feature Queries Manager DevTools Extension geschrieben hat, hat einen etwas anderen Ansatz, da es die Feature-Abfragen anzeigt, *die Sie tatsächlich geschrieben haben*, und Toggles bietet, um sie ein- und auszuschalten. Ich glaube nicht, dass es durch iframes funktioniert, daher habe ich den Debug-Modus geöffnet, um es auf CodePen zu verwenden.

Weitere reale Anwendungsfälle für @supports

Hier ist einer von Erik Vorhes. Er formatiert einige benutzerdefinierte Checkboxen und Radio-Buttons, aber verpackt alles in einem @supports-Block. Keine der Formatierungen wird angewendet, es sei denn, der Block besteht den Support-Check.

@supports (transform: rotate(1turn)) and (opacity: 0) {
  /* all the styling for Erik's custom checkboxes and radio buttons */
}

Hier sind einige weitere, die ich entdeckt habe:

  • Joe Wright und Tiago Nunes erwähnten die Verwendung für position: sticky;. Ich würde gerne eine Demo sehen! Also, wo Sie für position: sticky; gehen, aber dann etwas anderes tun müssen, als es für einen nicht unterstützenden Browser fehlschlagen zu lassen.
  • Keith Grant und Matthias Ott erwähnen die Verwendung für object-fit: contain;. Matthias hat eine Demo, bei der Positionierungstricks verwendet werden, um ein Bild in einen Container zu füllen, was dann durch diese Eigenschaft einfacher und besser gemacht wird, wenn sie verfügbar ist.
  • Ryan Filler erwähnt die Verwendung für mix-blend-mode. Sein Beispiel setzt mehr Deckkraft auf ein Element, aber wenn mix-blend-mode unterstützt wird, verwendet es etwas weniger, und diese Eigenschaft kann den Effekt haben, dass man ein Element für sich allein durchsieht.
  • .thing {
      opacity: 0.5;
    }
    @supports (mix-blend-mode: multiply) {
      .thing {
        mix-blend-mode: multiply;
        opacity: 0.75;
      }
    }
  • Rik Schennink erwähnte die backdrop-filter-Eigenschaft. Er sagt: "Wenn sie unterstützt wird, muss die Deckkraft der Hintergrundfarbe oft fein abgestimmt werden."
  • Nour Saud erwähnte, dass es verwendet werden kann, um Edge über eine bestimmte herstellerspezifische Eigenschaft zu erkennen: @supports (-ms-ime-align:auto) { }.
  • Amber Weinberg erwähnte die Verwendung für clip-path, da die Anpassung der Größe oder des Abstands eines Elements die Verfügbarkeit des Ausschnitts berücksichtigt, wenn dieser nicht verfügbar ist.
  • Ralph Holzmann erwähnte die Verwendung zum Testen der "Notch"-Sachen (Umgebungsvariablen), wie dieser hier.
  • Stacy Kvernmo erwähnte die Verwendung für die Vielzahl von Eigenschaften, die für Drop-Cap-Zeichen benötigt werden. Jen Simmons erwähnt diesen Anwendungsfall auch in ihrem Artikel. Es gibt eine CSS-Eigenschaft initial-letter, die für Drop Caps ziemlich fantastisch ist, aber in Verbindung mit anderen Eigenschaften verwendet wird, die Sie möglicherweise gar nicht anwenden möchten, wenn initial-letter nicht unterstützt wird (oder wenn es ein völlig anderes Fallback-Szenario gibt).

Hier ist ein Bonus-Tipp von Nick Colley, der nicht @supports, sondern @media ist! Der Geist ist derselbe. Es kann den "festgefahrenen" Hover-Zustand auf Touch-Geräten wie diesem verhindern.

@media (hover: hover) {
  a:hover {
    background: yellow;
  }
}

Logik in @supports

Grundlegend

@supports (initial-letter: 4) {

}

Nicht

@supports not (initial-letter: 4) {

}

Und

@supports (initial-letter: 4) and (transform: scale(2)) {

}

Oder

@supports (initial-letter: 4) or (-webkit-initial-letter: 4) {

}

Kombinationen

@supports ((display: -webkit-flex) or
          (display: -moz-flex) or
          (display: flex)) and (-webkit-appearance: caret) {

}

JavaScript-Variante

JavaScript verfügt über eine API dafür. Um zu testen, ob sie existiert...

if (window.CSS && window.CSS.supports) {
  // Apparently old Opera had a weird implementation, so you could also do:
  // !!((window.CSS && window.CSS.supports) || window.supportsCSS || false)
}

Um sie zu verwenden, übergeben Sie entweder die Eigenschaft im ersten Parameter und den Wert im zweiten.

const supportsGrid = CSS.supports("display", "grid");

...oder übergeben Sie alles in einem String, der die CSS-Syntax widerspiegelt.

const supportsGrid = CSS.supports("(display: grid)");

Selektor-Tests

Zum Zeitpunkt der Verfassung unterstützt nur Firefox diese Art von Tests (hinter einem experimentellen Flag), aber es gibt eine Möglichkeit, die Unterstützung von Selektoren mit @supports zu testen. MDNs Demo

@supports selector(A > B) {
}

Sie?

Natürlich würden wir uns freuen, Pens mit @supports-Anwendungsfällen in den Kommentaren zu sehen. Also teilt sie!