Seit einigen Jahren ist der Backdrop-Filter "blur" in allen modernen Browsern verfügbar. Er ermöglicht es, den Hintergrund eines HTML-Elements verschwimmen zu lassen wie bei einer Milchglasscheibe. So kann man Layouts farbneutral halten, ohne dass es langweilig aussieht, und mehrere Ebenen in der Tiefe darstellen.

Wir wollen heute die Grenzen dieses CSS-Features ausreizen und einen Blur-Gradient erstellen. Also einen Übergang vom Unscharfen zum Scharfen. Das ist in CSS nicht so simpel umzusetzen, wie man zunächst vermuten mag. Aber es ist möglich.

Am Ende dieses Blogposts wirst du ein praktisches Tool haben, welches deine nächste Website auf ein neues Level heben wird.

Der Effekt

So sieht der Blur-Gradient in Action aus:

ezgif-3-49057aa3cc.gif

Und hier kannst du ihn live sehen: Codepen

So wird es gemacht

Wir schauen uns das Ganze anhand eines fixierten Headers an, wie oben dargestellt.

Das HTML sieht so aus:

<header>
  <div class="content">
    ...
  </div>
</header>
<main>
  ...
</main>

Und so fangen wir mit dem Styling an:

:root {
  --header-height: 4rem;
}

header {
  height: var(--header-height);
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
}

main {
  margin-top: var(--header-height);
}

Der Header ist fixiert und der Seiteninhalt lässt sich dahinter scrollen. Momentan hat der Header noch keinen Hintergrund, aber das ändern wir gleich.

Man müsste jetzt meinen, für so einen Blur-Gradient bräuchte es nur den Backdrop-Filter und eine Maske mit einem Verlauf.

Leider reicht das nicht.

Das Problem ist, dass ein halb-transparenter, sehr unscharfer Hintergrund, einen "Beschlagenes Fenster"-Effekt erzeugt.

Screenshot_2024-07-01_at_15.33.22.png

Das ist nicht, was wir wollen. Wir wollen einen echten Übergang von unscharf zu scharf.

Um den gewünschten Effekt zu erzielen, brauchen wir deshalb mehrere Schichten.

Jede dieser Schichten hat eine Maske, die einen graduellen Übergang von opak zu transparent erzeugt. Die erste Schicht hat dabei eine sehr leichte Unschärfe von 0.5px, die zweite von 1px, die dritte von 1.5px und so weiter. Dadurch wird der "Beschlagenes Fenster"-Effekt vermieden.

Gehen wir zunächst einmal von drei Schichten aus. Das ist tatsächlich eher zu wenig, aber reicht zur Erklärung.

Wir werden die Styles einmal hardcoden, um das Prinzip zu verstehen und anschließend Code schreiben, der die Styles dynamisch generiert.

Zum Header fügen wir die Schichten in Form von Divs mit einer Klasse "background" hinzu:

<header>
  <div class="background"></div>
  <div class="background"></div>
  <div class="background"></div>
  <div class="content">
    ...
  </div>
</header>

Im CSS schreiben wir:

header .background {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: -1;
}

header .background:nth-child(1) {
  backdrop-filter: blur(0.5px);
  mask-image: linear-gradient(to bottom, black 90%, transparent 100%);
}

header .background:nth-child(2) {
  backdrop-filter: blur(1px);
  mask-image: linear-gradient(to bottom, black 80%, transparent 90%);
}

header .background:nth-child(3) {
  backdrop-filter: blur(1.5px);
  mask-image: linear-gradient(to bottom, black 70%, transparent 80%);
}

Warum 90% zu 100% und 80% zu 90% usw.? Warum nicht 0% zu 100% bei allen Schichten?

Wir wollen ja, dass der Hintergrund des Headers größtenteils unscharf ist und dann, in diesem Fall über 30% der Höhe hinweg, scharf wird.

Der erste Layer ist also bis 90% opak und wird dann bis 100% transparent. Der zweite Layer ist bis 80% opak und wird dann bis 90% transparent. Der dritte Layer ist bis 70% opak und ist bei 80% durchsichtig.

Jede Schicht übernimmt also einen Teil des Übergangs und die Unschärfe summiert sich.

Falls du nicht alles verstanden hast, macht das nichts. Wir bauen jetzt ein Tool, welches dir die Arbeit und auch das Denken abnimmt. Natürlich ist es dennoch wichtig, das Prinzip zu verstehen, um den Code später den eigenen Wünschen anpassen zu können.

Hier würde ich dir raten, einfach ein bisschen herumzuspielen und Werte zu ändern um ein besseres Verständnis für das Prinzip zu bekommen.

Dynamische Styles

Ich bin tatsächlich ein Freund von reinem CSS, aber da es dort (noch) keine Loops gibt, brauchen wir SCSS. Das ist ein Preprocessor für CSS, den du kennen solltest. Falls nicht, schau dir die Dokumentation dazu an.

Wir schreiben also folgendes in der SCSS-Datei:

:root {
  --header-height: 8rem;
}

... /* andere Styles */

header .background {
  --gradient-height: 4rem;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: -1;

  $layerCount: 5;
  @for $i from 1 through $layerCount {
    &:nth-child(#{$i}) {
      backdrop-filter: blur(#{$i * .5}px);
      mask-image: linear-gradient(
        to bottom,
        black calc(100% - var(--gradient-height) / #{$layers} * #{$i}),
        transparent calc(100% - var(--gradient-height) / #{$layerCount} * #{$i - 1})
      );
    }
  }
}

Um die Höhe des Gradients zu ändern, kannst du die Variable --gradient-height anpassen. Je nach dem, wie hoch dein Header sein soll und wie weich du den Übergang haben möchtest, kannst du hier entsprechend variieren.

Wir haben die Höhe des Headers auf 8rem hochgeschraubt, da der Übergang alleine schon 4rem hoch sein soll. Die Hälfte des Headers ist also unscharf und von der Mitte bis zum unteren Rand wird der Hintergrund scharf.

Bei $layerCount kannst du die Anzahl der Schichten einstellen, die du verwendest. Je mehr Schichten, desto stärker der Blur-Effekt.

Momentan ist der Inhalt des Headers noch genauso hoch wie der Header selbst. Allerdings empfiehlt es sich, den Inhalt etwas kleiner zu machen, da der Header im unteren Bereich durch die abnehmende Unschärfe zu unruhig wird. Etwa 70% haben sich als guter Wert herausgestellt.

header .content {
  height: 70%;
}

Abschließend können wir noch einen dezenten Farbverlauf für den Hintergrund festlegen, um den Inhalt des Headers besser hervorzuheben.

header {
  ...
  background-image: linear-gradient(to bottom, rgba(0, 0, 0, .2), transparent);
}

Fazit

Bisher habe ich Blur-Gradients nur in Apple UIs gesehen und war immer fasziniert davon. Es ist ein subtiler Effekt, der eine elegante Alternative zu harten Borders darstellt.

Falls du Fragen hast, schreib mir gerne!

Den kompletten Code findest du hier: Codepen