Perfekte Tooltips mit CSS Clipping und Maskierung

Avatar of Louis Hoebregts
Louis Hoebregts am

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

Clipping und Maskierung gibt es in CSS schon eine ganze Weile und sie haben eine ziemlich gute Browserunterstützung. Ich habe kürzlich an einem Projekt gearbeitet, bei dem eine Clipping-Technik für Tooltips über Links im Text verwendet werden musste.

Diese Tooltips haben zwei Designs, je nach Inhalt

Ein Design ist ein Tooltip, der einfachen Text gegen einen soliden Hintergrund enthält.
Das andere Design erlaubt es einem Bild, den gesamten Raum einzunehmen.

Man könnte denken, dass der Text-Tooltip keinerlei Clipping erfordert. Ein Pseudo-Element kann am unteren Rand positioniert werden, um die kleine Kerbe hinzuzufügen, richtig? Du hast absolut Recht! Da der Hintergrund des Tooltips eine einfarbige Farbe hat, gibt es wirklich keinen Bedarf für CSS-Tricksereien und Ähnliches.

Aber das Clipping des Bildes im zweiten Design ist es, wo die Dinge interessant werden…

Hier ist der Denkprozess, der mir durch den Kopf ging, als ich mit der Aufgabe begann.

Idee 1: clip-path & polygon

Die CSS-Eigenschaft clip-path ermöglicht es uns, ein benutzerdefiniertes Polygon mit Prozentwerten zu definieren, um den gewünschten Pfad zu erstellen.
Diese Lösung ist oft ausreichend, wenn die Form deines Pfades einfach genug ist. Im untenstehenden Demo verwende ich calc()-Werte, um sicherzustellen, dass der Ausschnitt vollständig responsiv ist, während das kleine Dreieck dieselbe Größe behält, egal wie sehr das Elternelement gestreckt wird.

.tooltip {
  clip-path: polygon(
    0% 0%, // Top left point
    100% 0%, // Top right point
    100% calc(100% - 10px), // Bottom right point
    calc(50% + 10px) calc(100% - 10px), // Center right of the triangle
    50% 100%, // Tip of the triangle
    calc(50% - 10px) calc(100% - 10px), // Center left of the triangle
    0% calc(100% - 10px) // Bottom left point
  );
}

Diese Lösung ist sehr sauber, aber in meinem Fall nicht gut genug, da ich keine gerade Dreiecks-Kerbe habe, sondern eine benutzerdefinierte Form.

Idee 2: clip-path und SVG

Die Verwendung eines SVG-Pfades schien eine gute Lösung zu sein. Zuerst exportierst du deinen SVG-Clipping-Pfad und verwendest ihn dann in deinem CSS mit dem Wert url(#clipPathId).

Schau dir das folgende Demo an. Siehst du ein Problem mit dem Pfad?

Der Pfeil wird basierend auf dem Seitenverhältnis des Bildes gestreckt. Da die kleine Kerbe Teil der gesamten Pfadform ist, wird sie genauso gestreckt wie der rechteckige Teil des Pfades in seiner Größe gestreckt wird.

Idee 3: mask-image

Nun, hier ist das, was ich mit der CSS-Eigenschaft mask-image in CSS entdeckt habe: Du kannst Maskenebenen kombinieren! Denk daran wie an ein background-image in CSS. Du kannst mehrere Farbverläufe oder Bilder auf ein einziges Element anwenden. Was wäre, wenn du all diese Ebenen kombinierst, um die endgültige Maske zu generieren, die du benötigst?

Das ist genau das, was wir hier mit zwei Ebenen tun werden:

  1. Ein großes Rechteck, das den gesamten Block außer einem Streifen unten bedeckt (in Grün dargestellt)
  2. Ein Bild des Pfeils (in Pink dargestellt)

Mit dieser Lösung kann sich das Rechteck entsprechend den Abmessungen unseres Tooltips dehnen, und der Pfeil behält immer seine feste Größe.

Der gesamte Code und die Demos unten sind prefix-frei und die Demos verwenden Autoprefixer. Zum Zeitpunkt des Schreibens dieses Artikels benötigen Edge, Chrome und Safari Präfixe.

Genau wie bei Hintergrund-Eigenschaften werden wir drei verschiedene Masken-Eigenschaften verwenden, um unsere beiden Ebenen zu definieren:

  • mask-image: Diese Eigenschaft erlaubt es uns, das Rechteck mit einem linearen Hintergrund und den Pfeil mit einem Inline-SVG zu zeichnen.
  • mask-position: Das Rechteck benötigt keine Position (da es von links oben beginnt), aber der Pfeil muss unten in der Mitte positioniert werden.
  • mask-repeat: Wir müssen beide Ebenen wiederholen vermeiden; andernfalls würde der lineare Farbverlauf das gesamte Element abdecken, wenn er wiederholt wird.
.tooltip {
  mask-image:
    linear-gradient(#fff, #fff), /* Rectangle */
    url('data:image/svg+xml;utf8,'); /* Bottom arrow mask-position: */
    0 0, /* Rectangle */
    50% 100%; /* Bottom arrow */
  mask-size:
    100% calc(100% - 18px), /* Rectangle */
    38px 18px; /* Bottom arrow */
  mask-repeat: no-repeat;
}

Tada! Ändere die Abmessungen des Tooltips oder ersetze das Bild und der untere Pfeil behält sein ursprüngliches Verhältnis.

Komplexere Formen

Lass uns ein bisschen schick werden und tiefer in diese Technik eintauchen. Ich wurde von der iMessage-App auf iOS inspiriert und versuchte, dieselben Tooltips mit dieser Maskierungstechnik zu reproduzieren.

Ich musste mehr Ebenen zeichnen, damit meine Maske jede abgerundete Ecke rendert:

  • vier Kreise, einer für jede Ecke (in Rot dargestellt)
  • ein horizontales Rechteck (in Blau dargestellt)
  • ein vertikales Rechteck (in Grün dargestellt)
  • ein SVG für den Pfeil (in Gelb dargestellt)

Der vollständige Code wird etwas länger sein, da wir mehr Ebenen zeichnen müssen, aber die Logik bleibt dieselbe. Die Ecken werden mit vier radialen Farbverläufen gezeichnet. Um das Rechteck zu füllen, benötigen wir zwei Rechtecke (eins vertikal, eins horizontal), wie oben gezeigt. Und schließlich unser kleiner Pfeil, der ein Inline-SVG verwendet.

.tooltip {
  --radius: 25px;
  mask-image:
    radial-gradient(#fff (var(--radius) - 1), #fff0 var(--radius)), /* Top left corner */
    radial-gradient(#fff (var(--radius) - 1), #fff0 var(--radius)), /* Top right corner */
    radial-gradient(#fff (var(--radius) - 1), #fff0 var(--radius)), /* Bottom left corner */
    radial-gradient(#fff (var(--radius) - 1), #fff0 var(--radius)), /* Bottom right corner */
    linear-gradient(#fff, #fff), /* Horizontal gradient */
    linear-gradient(#fff, #fff), /* Vertical gradient */
    url('data:image/svg+xml;utf8,'); /* Bottom right icon */
  mask-position: 
    0 0, /* Top left corner */
    100% 0, /* Top right corner */
    0 100%, /* Bottom left corner */
    100% 100%, /* Bottom right corner */
    0 var(--radius), /* Horizontal gradient */
    var(--radius) 0, /* Vertical gradient */
    100% 100%; /* Bottom right icon */
  mask-size:
    (var(--radius) * 2) (var(--radius) * 2),  /* Top left corner */
    (var(--radius) * 2) (var(--radius) * 2),  /* Top right corner */
    (var(--radius) * 2) (var(--radius) * 2),  /* Bottom left corner */
    (var(--radius) * 2) (var(--radius) * 2),  /* Bottom right corner */
    100% calc(100% - #{var(--radius) * 2}), /* Horizontal gradient */
    calc(100% - #{var(--radius) * 2}) 100%, /* Vertical gradient */
    (39px / 2) (25px / 2); /* Bottom right icon */
  mask-repeat: no-repeat;
}

Wie du siehst, können wir eine Version mit dem Pfeil links oder rechts erstellen, indem wir eine gespiegelte Version des Pfeils verwenden und ihn in einer anderen Ecke positionieren. Der Trick funktioniert auch gut bei Tooltips ohne Bilder. Aber wie ich am Anfang dieses Artikels sagte, brauchst du wahrscheinlich nicht so viel CSS, wenn du nur einen einfarbigen Hintergrund gestalten musst.


Wenn du mehr über Clipping und Maskierung in CSS erfahren möchtest, gibt es hier auf CSS-Tricks viele weitere großartige Artikel, die einen Blick wert sind.