Gestapelte "Rahmen"

Avatar of Eric Meyer
Eric Meyer am

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

Vor einiger Zeit war ich dabei, Fokusstile für die Website von An Event Apart hinzuzufügen. Ein Teil davon war das Anwenden verschiedener Fokuseffekte in verschiedenen Bereichen des Designs, wie z. B. weiße Ringe im Header und Footer und orangefarbene Ringe im Haupttext. Aber an einer Stelle wollte ich Ringe, die auffälliger waren – etwas wie das Stapeln von zwei Rändern übereinander, um ungewöhnliche Formen zu schaffen, die ins Auge fallen.

A row of four images, the second of which includes a dashed red border.

Ich spielte mit der Idee, Elemente mit Rändern und einigen negativen Abständen zu verschachteln, um einen Rand über einen anderen zu ziehen, oder einen Rand in eine Kontur zu verschachteln und dann negative Abstände zu verwenden, um das Layout nicht zu beeinträchtigen. Aber nichts davon fühlte sich befriedigend an.

Es stellt sich heraus, dass es eine Reihe von Tricks gibt, um den Effekt des Stapelns eines Rands über einem anderen zu erzielen, indem ein Rand mit anderen CSS-Effekten kombiniert wird oder sogar ganz ohne die Verwendung von Rändern. Lassen Sie uns das untersuchen, ja?

Kontur und Schatten (Box-Shadow)

Wenn das zu mehrfach umrandete Objekt ein Rechteck ist – wissen Sie, wie praktisch alle Blockelemente – dann ist die Kombination einer Kontur und eines mit Ausdehnung versehenen harten Box-Shadows genau das Richtige.

Beginnen wir mit dem Box-Shadow. Wahrscheinlich sind Sie mit Box-Shadows wie diesen vertraut

.drop-me {
  background: #AEA;
  box-shadow: 10px 12px 0.5rem rgba(0,0,0,0.5);
}
A turquoise box containing the words div text and a heavy box shadow.

Das erzeugt einen unscharfen Schatten unter und rechts vom Element. Drop-Schatten, also letztes Jahrtausend! Aber es gibt Raum und Unterstützung für einen vierten Längenwert in box-shadow, der einen Ausdehnungsabstand definiert. Dieser vergrößert die Form des Schattens in alle Richtungen um die angegebene Länge und wird dann weichgezeichnet. Vorausgesetzt, es gibt eine Weichzeichnung.

Wenn wir also einem Box-Shadow keinen Versatz, keine Weichzeichnung und etwas Ausdehnung geben, zeichnet er sich selbst um das Element herum und sieht aus wie ein durchgehender Rand, ohne tatsächlich ein Rand zu sein.

.boxborder-me {
  box-shadow: 0 0 0 5px firebrick;
}
A red box containing a thick red border.

Dieser Box-Shadow-„Rand“ wird knapp außerhalb der äußeren Kante des Elements gezeichnet. Das ist derselbe Ort, an dem Konturen um Blockboxen gezeichnet werden, also müssen wir jetzt nur noch eine Kontur über den Schatten zeichnen. Etwas wie das hier

.boxborder-me {
  box-shadow: 0 0 0 5px firebrick;
  outline: dashed 5px darkturquoise;
}
A box containing a dashed red border with a turquoise background filling the dash gaps.

Bingo. Ein mehrfarbiger „Rand“, der in diesem Fall nicht einmal die Layoutgröße beeinträchtigt, da Schatten und Konturen nachdem die Elementgröße berechnet wurde gezeichnet werden. Die Kontur, die oben liegt, kann praktisch jeden Konturstil verwenden, was derselben Liste von Randstilen entspricht. Daher sind gepunktete und doppelte Konturen möglich. (Das gilt auch für alle anderen Stile, aber diese haben keine transparenten Teile, sodass der massive Schatten nur durch transluzente Farben sichtbar wäre.)

Wenn Sie einen Dreifarben-Effekt im Rand wünschen, können mehrere Box-Shadows mit einer durch Kommas getrennten Liste erstellt werden, und dann wird eine Kontur darüber gelegt. Zum Beispiel

.boxborder-me {
  box-shadow: 0 0 0 1px darkturquoise,
              0 0 0 3px firebrick,
              0 0 0 5px orange,
              0 0 0 6px darkturquoise;
  outline: dashed 6px darkturquoise;
}
A box containing a dashed border where the dashes are doubled with red and gold and a turquoise background filling in the dash gaps.

Um zu einfacheren Effekten zurückzukehren: Die Kombination einer gestrichelten Kontur über einem ausgedehnten Box-Shadow mit einem durchgehenden Rand derselben Farbe wie der Box-Shadow erzeugt noch einen weiteren Effekt

.boxborder-me {
  box-shadow: 0 0 0 5px firebrick;
  outline: dashed 5px darkturquoise;
  border: solid 5px darkturquoise;
}
A box with a dashed red border and a turquoise background that not only fills the dash gaps, but overflows the red border toward the inside edge of the box.

Der zusätzliche Bonus hier ist, dass, obwohl ein Box-Shadow verwendet wird, dieser den Hintergrund des Elements nicht füllt, sodass Sie den Hintergrund dahinter sehen können. So verhalten sich Box-Shadows immer: Sie werden nur außerhalb der äußeren Randkante gezeichnet. Der „Rest des Schattens“, der Teil, den Sie vielleicht hinter dem Element vermuten, existiert nicht. Er wird nie gezeichnet. So erhalten Sie Ergebnisse wie diese

A box with a turquoise border and heavy box shadow toward the bottom right edge that is set against a turquoise background.

Dies ist das Ergebnis der expliziten Formulierung in CSS Background and Borders Module, Level 3, Abschnitt 7.1.1

Ein äußerer Box-Shadow wirft einen Schatten, als ob der Randkasten des Elements opak wäre. Bei einem Ausdehnungsabstand von Null hat seine Kontur die exakt gleiche Größe und Form wie der Randkasten. Der Schatten wird nur außerhalb der Randkante gezeichnet: Er wird innerhalb des Randkastens des Elements beschnitten.

(Hervorhebung hinzugefügt.)

Rand und Box-Shadow

Wo wir gerade von Rändern sprechen, vielleicht gibt es eine Möglichkeit, Ränder und Box-Shadows zu kombinieren. Schließlich können Box-Shadows mehr als nur Drop-Schatten sein. Sie können auch nach innen gerichtet (inset) sein. Was wäre also, wenn wir den vorherigen Schatten nach innen drehen und einen Rand darüber legen würden?

.boxborder-me {
  box-shadow: 0 0 0 5px firebrick inset;
  border: dashed 5px darkturquoise;
}
A box with a dashed turquoise border and a solid red border that lies on the inside of the dashed border.

Das ist… nicht das, was wir wollten. Aber so funktionieren Inset-Schatten: Sie werden innerhalb des äußeren Polsterungsrands (auch bekannt als innerer Randrand) gezeichnet und darüber beschnitten

Ein innerer Box-Shadow wirft einen Schatten, als ob alles außerhalb des Polsterungsrands opak wäre. Bei einem Ausdehnungsabstand von Null hat seine Kontur die exakt gleiche Größe und Form wie die Polsterbox. Der Schatten wird nur innerhalb des Polsterungsrands gezeichnet: Er wird außerhalb der Polsterbox des Elements beschnitten.

(Ebenda; Hervorhebung hinzugefügt.)

Wir können also keinen Rand über einen Inset-Box-Shadow stapeln. Vielleicht könnten wir einen Rand über etwas anderes stapeln…?

Rand und mehrere Hintergründe

Inset-Schatten können auf den äußeren Polsterungsrand beschränkt sein, aber Hintergründe nicht. Der Hintergrund eines Elements füllt standardmäßig den Bereich bis zum äußeren Rand. Füllen Sie den Elementhintergrund mit einer durchgehenden Farbe, geben Sie ihm einen dicken, gestrichelten Rand, und Sie sehen die Hintergrundfarbe zwischen den sichtbaren Teilen des Rands.

Was wäre also, wenn wir einige Hintergründe übereinander stapeln und somit die durchgehende Farbe zeichnen, die wir hinter dem Rand haben wollen? Hier ist Schritt eins

.multibg-me {
  border: 5px dashed firebrick;
  background:
    linear-gradient(to right, darkturquoise, 5px, transparent 5px);
  background-origin: border-box;
}
A box with a dashed red border and a turquoise background filling in the dash gaps along the left edge of the box.

Wir sehen links den blauen Hintergrund, der durch die transparenten Teile des gestrichelten roten Rands sichtbar ist. Fügen Sie drei weitere hinzu, eine für jede Kante der Elementbox, und

.multibg-me {
  border: 5px dashed firebrick;
  background:
    linear-gradient(to top, darkturquoise, 5px, transparent 5px),
    linear-gradient(to right, darkturquoise, 5px, transparent 5px),
    linear-gradient(to bottom, darkturquoise, 5px, transparent 5px),
    linear-gradient(to left, darkturquoise, 5px, transparent 5px);
  background-origin: border-box;
}
A box with a dashed red border and a turquoise background that fills in the dash gaps.

In jedem Fall läuft der Hintergrundverlauf fünf Pixel lang als durchgehender dunkel-türkisfarbener Hintergrund, und dann gibt es einen Farbstop, der augenblicklich zu transparent übergeht. Das lässt den „Hintergrund“ durch das Element scheinen und gibt uns trotzdem einen „gestapelten Rand“.

Ein großer Vorteil hier ist, dass wir nicht auf durchgehende lineare Verläufe beschränkt sind – wir können jeden beliebigen Verlauf beliebiger Komplexität verwenden, um die Dinge ein wenig aufzupeppen. Nehmen Sie dieses Beispiel, bei dem der gestrichelte Rand größtenteils transparent gemacht wurde, damit wir die vier verschiedenen Verläufe vollständig sehen können

.multibg-me {
  border: 15px dashed rgba(128,0,0,0.1);
  background:
    linear-gradient(to top,    darkturquoise, red 15px, transparent 15px),
    linear-gradient(to right,  darkturquoise, red 15px, transparent 15px),
    linear-gradient(to bottom, darkturquoise, red 15px, transparent 15px),
    linear-gradient(to left,   darkturquoise, red 15px, transparent 15px);
  background-origin: border-box;
}
A diagram showing the same box with a dashed red border and turquoise background, but with transparency to show how the stacked borders overlap.

Wenn Sie sich die Ecken ansehen, werden Sie feststellen, dass die Hintergrundverläufe rechteckig sind und sich überlappen. Sie treffen sich nicht sauber, wie es Randverbindungen tun. Dies kann ein Problem sein, wenn Ihr Rand transparente Teile in den Ecken hat, wie es bei border-style: double der Fall wäre.
Außerdem ist dies eine recht unbeholfene Methode, um diesen Effekt zu erzielen, wenn Sie nur eine durchgehende Farbe hinter dem Rand wünschen. Sicherlich muss es einen besseren Ansatz geben?

Rand und Hintergrundbeschneidung

Ja, gibt es! Es beinhaltet die Änderung der Beschneidungsbereiche für zwei verschiedene Ebenen des Elementhintergrunds. Das erste, was einem in den Sinn kommen könnte, ist etwas wie das hier

.multibg-me {
  border: 5px dashed firebrick;
  background: #EEE, darkturquoise;
  background-clip: padding-box, border-box;
}

Aber das funktioniert nicht, denn CSS verlangt, dass nur der letzte (und damit unterste) Hintergrund mit einem <color>-Wert gesetzt werden darf. Jede andere Hintergrundebene muss ein Bild sein.

Also ersetzen wir die sehr hellgraue Hintergrundfarbe durch einen Verlauf von dieser Farbe zu dieser Farbe: Das funktioniert, weil Verläufe Bilder sind. Mit anderen Worten

.multibg-me {
  border: 5px dashed firebrickred;
  background: linear-gradient(to top, #EEE, #EEE), darkturquoise;
  background-clip: padding-box, border-box;
}
A box with a red dashed border, a turquoise background to fill in the dash gaps, and a light gray background set inside the box.

Der hellgraue „Verlauf“ füllt den gesamten Hintergrundbereich, wird aber mit background-clip auf den Polsterkasten zugeschnitten. Das Dunkel-Türkis füllt den gesamten Bereich und wird auf den Randkasten zugeschnitten, wie es Hintergründe standardmäßig immer getan haben. Wir können die Farbverläufe und die Richtung beliebig ändern und so einen tatsächlich sichtbaren Verlauf erstellen oder ihn in Weiß oder jede andere lineare Effektausgabe ändern, die wir wünschen.

Der Nachteil hier ist, dass es keine Möglichkeit gibt, den Polsterungsbereich transparent zu machen, sodass der Elementhintergrund durch das Element sichtbar ist. Wenn der lineare Verlauf transparent gemacht wird, wird der gesamte Elementhintergrund mit Dunkel-Türkis gefüllt. Oder genauer gesagt, wir werden das Dunkel-Türkis sehen können, das schon immer da war.

In vielen Fällen spielt es keine Rolle, dass der Elementhintergrund nicht durchsichtig ist, aber es ist dennoch eine frustrierende Einschränkung. Gibt es keine Möglichkeit, den Effekt von gestapelten Rändern ohne verrückte Hacks und verlorene Fähigkeiten zu erzielen?

Randbilder

Was wäre, wenn wir ein Bild des gestapelten Rands, den wir in der Welt sehen wollen, nehmen, es zerlegen und dieses als Rand verwenden könnten? Zum Beispiel wird dieses Bild zu diesem Rand?

An image of two stacked boxes, the top a square box with a red dashed border and a turquoise and gold striped background filling in the dash gaps. The bottom box is a demonstration using the top box as a border image for the bottom box.

Hier ist der Code, um genau das zu tun

.borderimage-me {
  border: solid 5px;
  border-image: url(triple-stack-border.gif) 15 / 15px round;
}

Zuerst setzen wir einen durchgehenden Rand mit einer gewissen Breite. Wir könnten auch eine Farbe als Fallback festlegen, aber das ist nicht wirklich notwendig. Dann geben wir eine Bild-URL an, definieren die Schnittpunkte (slice inset(s)) auf 15 und die Breite des Rands auf 15px und schließlich das Wiederholungsmuster round.

Es gibt mehr Optionen für Randbilder, die hier etwas zu komplex sind, um sie zu behandeln, aber die Quintessenz ist, dass Sie ein Bild nehmen, neun Schnitte davon mit Offset-Werten definieren und diese Bilder verwenden können, um einen vollständigen Rand um ein Bild zu synthetisieren. Das geschieht durch die Definition von Offsets von den Rändern des Bildes selbst, die in diesem Fall 15 betragen. Da das Bild ein GIF und somit pixelbasiert ist, werden die Offsets in Pixeln gemessen, sodass die „Schnittlinien“ 15 Pixel von den Bildrändern entfernt liegen. (Im Falle eines SVG werden die Offsets in Bezug auf das Koordinatensystem des SVG gemessen.) Das sieht so aus

A diagram outlining how the border image is sliced and positioned along the box's edges, corner's and offsets.

Jeder Schnitt wird der Ecke oder Seite der Elementbox zugewiesen, die ihm entspricht; d. h., der Schnitt für die untere rechte Ecke wird in der unteren rechten Ecke des Elements platziert, der obere (mittlere) Schnitt wird entlang der oberen Kante des Elements verwendet und so weiter.

Wenn ein Kantenschnitt kleiner ist als die Länge der Kante des Elements – was fast immer vorkommt und hier sicherlich der Fall ist – dann wird der Schnitt auf eine von mehreren Arten wiederholt. Ich habe round gewählt, das so viele Wiederholungen wie möglich einfügt und sie dann gerade so skaliert, dass die Kante gefüllt wird. Bei einem 70 Pixel langen Schnitt, wenn die Kante 1.337 Pixel lang ist, gibt es 19 Wiederholungen des Schnitts, die jeweils 70,3 Pixel breit skaliert werden. Oder wahrscheinlicher generiert der Browser ein einzelnes Bild mit 19 Wiederholungen, das 1.330 Pixel breit ist, und dehnt dieses Bild dann um die zusätzlichen 7 Pixel.

Sie denken vielleicht, der Nachteil hier ist die Browserunterstützung, aber das ist nicht der Fall.

Diese Browserunterstützungsdaten stammen von Caniuse, wo Sie weitere Details finden. Eine Zahl bedeutet, dass der Browser die Funktion ab dieser Version unterstützt.

Desktop

ChromeFirefoxIEEdgeSafari
565011129.1

Mobil / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
1271271279.3

Achten Sie nur auf die wenigen Fehler (wirklich, Implementierungsgrenzen), die bei einigen Implementierungen noch bestehen, und dann sind Sie auf der sicheren Seite.

Fazit

Auch wenn es selten vorkommen mag, dass Sie mehrere „Rand“-Effekte kombinieren oder übereinander stapeln möchten, ist es gut zu wissen, dass CSS eine Reihe von Möglichkeiten bietet, die Aufgabe zu erledigen, und dass die meisten davon bereits weitgehend unterstützt werden. Und wer weiß? Vielleicht gibt es eines Tages eine einfache Möglichkeit, solche Effekte mit einer einzigen Eigenschaft zu erzielen, anstatt mehrere zu mischen. Bis dahin, viel Spaß beim Randstapeln!