mitmario.dev

Neues sicher einsetzen mit @supports

CSS Im Browser 4 Min Lesezeit 3 BeispieleLektion 1 von 8

Ankerpositionierung ist erst 2026 in allen großen Browsern angekommen. Scroll-getriebene Animationen kann Firefox noch gar nicht. Wer so etwas benutzen will, steht immer vor derselben Frage. Was passiert bei jemandem, dessen Browser das noch nicht kann?

Früher hat man dafür den User-Agent-String gelesen und daraus geraten, was der Browser wohl kann. Das war schon damals falsch, denn der String lügt, und zwei Versionen desselben Browsers können verschieden viel. @supports fragt stattdessen den Browser selbst, und zwar genau nach der einen Sache, um die es geht.

Die Frage steht in Klammern

Erst die Grundlage, dann die Aufwertung
*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 24px;
  font-family: system-ui, sans-serif;
  line-height: 1.5;
}

/* Die Grundlage: läuft in jedem Browser, den es gibt. */
.projekte {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

.karte {
  flex: 1 1 200px;
  padding: 16px;
  border: 1px solid #d4d4d8;
  border-radius: 12px;
}

/* Die Aufwertung: greift nur, wenn der Browser sie versteht. */
@supports (display: grid) {
  .projekte {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
  }
}

@supports (display: grid) fragt, ob dieser Browser die Deklaration display: grid versteht. Wenn ja, gilt alles im Block. Wenn nein, wird der ganze Block übersprungen, als stünde er nicht da.

Geschrieben wird immer ein vollständiges Paar aus Eigenschaft und Wert, nicht nur der Name. @supports (display) ist gar keine gültige Frage. Der Browser erkennt das nicht als Deklaration und antwortet grundsätzlich mit Nein. Der Block bleibt also überall aus, auch im allerneuesten Browser. Interessant ist ja nicht die Eigenschaft, sondern der Wert, und deshalb muss er dastehen.

Du kannst die Frage auch selbst stellen, ohne eine Regel dafür zu schreiben. In der Eingabezeile der Console antwortet CSS.supports('display', 'grid') mit true, CSS.supports('grid-template-rows', 'subgrid') ebenfalls, und CSS.supports('display') mit false. Das ist der Punkt von oben, in einer Zeile belegt statt behauptet.

Ob der Block wirklich gegriffen hat, steht im Auskunftsstreifen. Am Raster im ersten Beispiel steht display: grid, und bei der Breite, mit der die Spalte startet, auch grid-template-columns: 158px 158px. Stünde dort flex, wäre der Fallback im Einsatz. Dann hätte der Browser die Frage mit Nein beantwortet.

Wie gut der Browser die Sache umgesetzt hat, prüft @supports nicht. Die Antwort heißt nur “Ich verstehe diese Syntax”, nicht “Ich mache damit das Richtige”. Bei display: grid war das jahrelang der Unterschied, mit einem Ja auf die Frage und trotzdem Abweichungen in den Details, die man nur im echten Test findet.

Die Reihenfolge ist die halbe Lektion

Zuerst kommt die Fassung, die überall läuft. Danach kommt der @supports-Block, der sie aufwertet. Nie umgekehrt.

Dieselbe Absicht mit not geschrieben
*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 24px;
  font-family: system-ui, sans-serif;
  line-height: 1.5;
}

.karte {
  padding: 16px;
  border: 1px solid #d4d4d8;
  border-radius: 12px;
}

/* Hier steht die gute Fassung ungeschützt oben ... */
.projekte {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px;
}

/* ... und der Fallback hängt an einer Frage, die ein
   sehr alter Browser gar nicht erst versteht. Er
   ignoriert den ganzen Block und bleibt ohne Layout. */
@supports not (display: grid) {
  .projekte {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
  }
}

Im zweiten Beispiel ist es umgedreht. Oben steht die gute Fassung ungeschützt, unten hängt der Fallback an @supports not (...). Das sieht aus, als käme dasselbe heraus, und in modernen Browsern stimmt das auch. Der Unterschied zeigt sich genau bei dem Browser, für den du das Ganze gebaut hast.

Ein Browser, der @supports überhaupt nicht kennt, ignoriert beide Blöcke, und das war zuletzt der Internet Explorer. Bei der ersten Fassung bleibt die Flexbox stehen, und die Seite ist benutzbar. Bei der zweiten bleibt gar nichts stehen. Die Regel für den Fallback liegt ja in einem Block, den er nicht versteht. Aus dem Sicherheitsnetz wird so eine zweite Fallgrube.

Dieselbe Regel gilt für alles andere. Die Grundlage steht ungeschützt, das Neue im Block. Steht irgendwo ein not, sehe ich mir die Reihenfolge noch einmal an.

and, or und der Blick auf Selektoren

Verknüpfen und nach Selektoren fragen
body {
  margin: 0;
  padding: 24px;
  font-family: system-ui, sans-serif;
  line-height: 1.5;
}

.pruefung {
  margin: 0 0 8px 0;
  padding: 16px;
  border-radius: 8px;
  background-color: #f4f4f5;
  color: #52525b;
}

/* Beide Bedingungen müssen zutreffen. */
@supports (display: grid) and (gap: 1rem) {
  .a {
    background-color: #dcfce7;
    color: #15803d;
  }
}

/* Eine von beiden reicht. */
@supports (rotate: 45deg) or (transform: rotate(45deg)) {
  .b {
    background-color: #dcfce7;
    color: #15803d;
  }
}

/* Keine Eigenschaft, sondern ein Selektor. */
@supports selector(:has(*)) {
  .c {
    background-color: #dcfce7;
    color: #15803d;
  }
}

Mehrere Bedingungen verbindest du mit and und or, und mit weiteren Klammern kannst du sie gruppieren. Das braucht man selten. Meistens hängt ein Feature an genau einer Eigenschaft, und die reicht als Frage.

@supports selector(...) fragt nicht nach einer Eigenschaft, sondern nach einem Selektor. So findest du heraus, ob der Browser :has() versteht, oder ::backdrop, oder eine der Pseudoklassen aus Abschnitt 2. Die Syntax ist dieselbe, nur steht in den Klammern eben ein Selektor.

Im Beispiel sind alle drei Zeilen grün, der Browser kann also alles davon. An jeder steht color: rgb(21, 128, 61) auf rgb(220, 252, 231). Grau wären sie, wenn die Bedingung nicht zugetroffen hätte. Anders macht sich ein @supports-Block gar nicht bemerkbar, man sieht ihn nur an den Werten, die danach gelten.

Baseline statt Prozentzahl

Die alte Antwort war eine Prozentzahl wie “94 Prozent der Nutzer können das”. Damit kann man wenig anfangen, denn niemand weiß, ob die fehlenden 6 Prozent gerade die eigenen Kunden sind.

Brauchbarer ist Baseline. Dort steht nicht, wie viele Leute etwas nutzen können, sondern seit wann alle großen Browser mitmachen. “Newly available” heißt, gerade eben sind alle so weit, und dann lohnt @supports noch. “Widely available” heißt, das ist seit rund zweieinhalb Jahren so, und dann kannst du es einfach hinschreiben.

Bei allem, was in diesem Abschnitt kommt, sehe ich nach, wo es steht, und frage im Zweifel. Das kostet drei Zeilen. Warten muss man auf nichts.

In der Aufgabe fehlt einer Karte die Anordnung ihrer drei Teile, und zwar in zwei Fassungen.

Zum Mitnehmen

@supports fragt den Browser, ob er eine Deklaration versteht, und zwar immer als Paar aus Eigenschaft und Wert. Erst steht, was überall läuft, darunter die Aufwertung im Block, nie umgekehrt mit not.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

Was in dieser Lektion steckt

  • Artikel mit 3 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe im Editor, direkt im Browser geprüft

    Öffnet sich mit dem Basis Konto.