Die `background-clip`-Eigenschaft und ihre Anwendungsfälle

Avatar of Ana Tudor
Ana Tudor am

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

background-clip ist eine dieser Eigenschaften, die ich seit Jahren kenne, aber selten verwendet habe. Vielleicht nur ein paar Mal als Teil einer Lösung für eine Stack Overflow-Frage. Bis letztes Jahr, als ich begann, meine riesige Sammlung von Schiebereglern zu erstellen. Einige der Designs, die ich reproduzieren wollte, waren etwas komplexer und ich hatte nur ein Element pro Schieberegler zur Verfügung, was ein input-Element war, was bedeutete, dass ich nicht einmal Pseudo-Elemente darauf verwenden konnte. Obwohl das in bestimmten Browsern funktioniert, ist die Tatsache, dass es funktioniert, tatsächlich ein Fehler und ich wollte mich nicht darauf verlassen. All dies bedeutete, dass ich viel mit Hintergründen, Rahmen und Schatten gearbeitet habe. Ich habe auch viel dabei gelernt und dieser Artikel teilt einige dieser Lektionen.

Bevor wir fortfahren, sehen wir uns an, was background-clip ist und was es tut.

Im folgenden Bild sehen wir das Box-Modell eines Elements.

das Box-Modell

Wenn padding 0 ist, dann ist die padding-box genau so groß wie die content-box, und die Content-Grenze fällt mit der Padding-Grenze zusammen.

mit padding: 0

Wenn border-width 0 ist, ist die border-box so groß wie die padding-box, und die Border-Grenze fällt mit der Padding-Grenze zusammen.

mit border-width: 0

Wenn sowohl padding als auch border-width 0 sind, dann haben alle drei Boxen (die content-box, die padding-box und die border-box) die gleiche Größe, und die Content-Grenze, die Padding-Grenze und die Border-Grenze fallen alle zusammen.

mit padding: 0 und border-width: 0

Standardmäßig bedecken Hintergründe die gesamte border-box (sie werden auch unter dem Rahmen angewendet), aber ihre background-position (und auch die %-basierte background-size) ist relativ zur padding-box.

Um dies besser zu verstehen, betrachten wir ein Beispiel. Wir nehmen eine Box mit zufälligen Abmessungen, geben ihr einen einfachen Gradientenhintergrund mit background-size: 50% 50% und einen gehaschten border (mittels border-image), damit wir durch die Hashes immer noch sehen können, was sich unter dem Rahmen befindet.

Siehe den Stift Standard `background-origin` und `background-clip` (Basisversion) von CSS-Tricks (@css-tricks) auf CodePen.

In dieser Demo sehen wir, dass der Gradientenhintergrund die gesamte border-box bedeckt (er ist auch unter dem gehaschten Rahmen sichtbar). Wir haben keine background-position angegeben, daher nimmt sie den Standardwert an – 0 0. Wir sehen, dass dies relativ zur padding-box ist, da sie von der oberen linken Ecke (die 0 0) dieser Box ausgeht. Wir sehen auch, dass die %-basierte background-size relativ zur padding-box ist.

standardmäßig bedecken Hintergründe die gesamte border-box, beginnen aber von der oberen linken Ecke der padding-box

Beim Festlegen von background-size für einen Gradienten (aber nicht für tatsächliche Bilder) benötigen wir normalerweise zwei Werte für konsistente Ergebnisse über Browser hinweg. Wenn wir nur einen Wert verwenden, nimmt Firefox den zweiten Wert als 100% (gemäß Spezifikation), während alle anderen Browser den zweiten Wert fälschlicherweise gleich dem ersten nehmen. Ein fehlender background-size-Wert wird als auto betrachtet und da Gradienten keine intrinsischen Abmessungen oder intrinsischen Proportionen haben, kann der auto-Wert nicht daraus abgeleitet werden, daher sollte er als 100% behandelt werden. Wenn wir also nicht wollen, dass beide Abmessungen unserer background-size 100% betragen, sollten wir zwei Werte verwenden.

Die Verwendung nur eines Werts zum Festlegen von background-size liefert keine konsistenten Ergebnisse über Browser hinweg (Live-Test); links: Firefox (gemäß Spezifikation, nimmt den zweiten Wert als 100%); rechts: Chrome/Opera, Safari, IE/Edge (nehmen fälschlicherweise den zweiten Wert gleich dem ersten)

Mit Hilfe von background-clip können wir den Hintergrund nur auf die padding-box oder nur auf die content-box beschränken. Clipping bedeutet, das auszuschneiden und nicht anzuzeigen, was außerhalb des Clipping-Bereichs liegt, wobei der Clipping-Bereich der Bereich innerhalb der gepunkteten Linie in der folgenden Abbildung ist.

Abbildung, was Clipping ist

Im Standardfall von background-clip: border-box ist der Clipping-Bereich die border-box, sodass wir den Hintergrund auch unter dem Rahmen haben.

background-clip: border-box

Wenn wir background-clip: padding-box einstellen, ist der Clipping-Bereich die padding-box, was bedeutet, dass der Hintergrund nur innerhalb der Grenze der padding-box angezeigt wird (er ragt nicht unter den Rahmen).

background-clip: padding-box

Und schließlich, wenn wir background-clip: content-box haben, ist der Clipping-Bereich die content-box, sodass der Hintergrund nur innerhalb der Grenze der content-box angezeigt wird.

background-clip: content-box

Diese drei Situationen werden in der folgenden Live-Demo veranschaulicht.

Siehe den Stift Hintergründe – Hilfsdemo #2 von CSS-Tricks (@css-tricks) auf CodePen.

Wir haben auch eine weitere Eigenschaft namens background-origin, die angibt, relativ zu welcher der drei Boxen background-position (und background-size, wenn in % angegeben) ist.

Betrachten wir ein Element wie zuvor, mit einem gehaschten border und diesmal einem sichtbaren padding. Wir legen ein tatsächliches Bild und einen Gradienten auf den Hintergrund. Beide haben background-size: 50% 50% und wiederholen sich nicht. Zusätzlich hat das Bild background-position: 100% 100% (wir lassen den Standardwert 0 0 für den Gradienten).

background: linear-gradient(to right bottom, 
      #e18728, #be4c39, #9351a6, #4472b9), 
  url(tiger_lily.jpg) 100% 100%;
background-repeat: no-repeat;
background-size: 50% 50%;

Die folgende Demo veranschaulicht, was für jeden der drei möglichen Werte für background-origin geschieht – border-box, padding-box und content-box.

Siehe den Stift Hintergründe – Hilfsdemo #3 von CSS-Tricks (@css-tricks) auf CodePen.

Das von background-position des tatsächlichen Bildes angegebene 100% 100% ist das 100% 100% der von background-origin angegebenen Box. Gleichzeitig bedeutet das von background-size angegebene 50% 50% die halbe Breite und die halbe Höhe der von background-origin angegebenen Box.

In der background-Kurzschreibweise können background-origin und background-clip in dieser Reihenfolge am Ende der Ebene angegeben werden. Da sie beide einen Box-Wert annehmen, werden beide auf diesen gesetzt, wenn nur ein Box-Wert angegeben wird. Wenn zwei Box-Werte angegeben werden, wird background-origin auf den ersten und background-clip auf den zweiten gesetzt. Wenn keine Box-Werte angegeben werden, nehmen sie einfach die Standardwerte an (padding-box für background-origin und border-box für background-clip).

In Ordnung! Nun wollen wir sehen, wie wir das zu unserem Vorteil nutzen können!

Transparenter Abstand zwischen Rahmen und Hintergrund

Manche erinnern sich vielleicht, dass wir mit background-clip halbtransparente Rahmen erzielen können. Aber wir können auch einen Abstand zwischen dem Rahmen und dem Bereich mit einem Hintergrund einführen, ohne ein zusätzliches Element zu verwenden. Der einfachste Weg, dies zu tun, ist, zusätzlich zu einem border ein padding zu haben und background-clip auf content-box zu setzen. Indem wir dies in der Kurzschreibweise mit nur einem Box-Wert tun, setzen wir auch background-origin auf content-box, aber das ist in diesem Fall in Ordnung, es hat keine unerwünschte Auswirkung.

border: solid .5em #be4c39;
padding: .5em;
background: #e18728 content-box;

Das Beschneiden des Hintergrunds auf content-box bedeutet, dass er nicht über die Content-Grenze hinausragt. Darüber hinaus haben wir keinen Hintergrund, sodass wir sehen, was sich unter unserem Element befindet. Das Hinzufügen eines border bedeutet, dass wir diesen border zwischen der Padding-Grenze und der Border-Grenze sehen. Wenn das padding jedoch nicht null ist, haben wir immer noch einen transparenten Bereich zwischen der Content-Grenze und der Padding-Grenze.

Hervorhebung des Rahmen-, Padding- und Content-Bereichs über Entwicklertools

Wir können es live mit diesem Pen testen.

Siehe den Stift Abstand zwischen Rahmen und Hintergrund von Ana Tudor (@thebabydino) auf CodePen.

Wir können die Dinge interessanter gestalten, indem wir einen drop-shadow()-Filter hinzufügen, der dem Ganzen einen gelblichen Glanz verleiht.

Siehe den Stift Abstand zwischen Rahmen und Hintergrund v2 von Ana Tudor (@thebabydino) auf CodePen.

Hinweis zu Präfixen: Ich habe viele Ressourcen gesehen, die -moz- und -ms- Präfixe für CSS-Filter hinzufügen. Bitte tun Sie das nicht! CSS-Filter wurden in Firefox seit ihrer ersten Implementierung (Firefox 34, Herbst 2014) ohne Präfixe verwendet und jetzt sind sie in Edge hinter einem Flag gelandet – ebenfalls ohne Präfix! CSS-Filter benötigten also nie die Präfixe -moz- oder -ms-, es ist völlig unnötig, sie hinzuzufügen, das Einzige, was sie tun, ist, das Stylesheet aufzublähen.

Wir können auch einen coolen Effekt erzielen, wenn wir für background-image und border-image einen Farbverlauf verwenden. Wir erstellen einen Farbverlauf, der von einem durchgehenden Orange/Rot oben beginnt und dann bis zur vollständigen Transparenz verblasst. Da nur die Farbtöne unterschiedlich sind und ansonsten die verwendeten Farbverläufe identisch sind, erstellen wir eine Sass-Funktion.

@function fade($c) {
  return linear-gradient($c, rgba($c, 0) 80%);
}

div {
  border: solid 0.125em;
  border-image: fade(#be4c39) 1;
  padding: 0.125em;
  background: fade(#e18728) content-box;
}

Wir können es live in diesem Pen testen.

Siehe den Stift Abstand zwischen Rahmen und Hintergrund v3 von Ana Tudor (@thebabydino) auf CodePen.

Dieser Ansatz, das Padding zu verwenden, um den Abstand zwischen dem Hintergrund und dem Rahmen zu schaffen, ist nicht der beste, es sei denn, wir haben nur kurzen Text in der Mitte. Wenn wir viel mehr Text haben... nun, es sieht schrecklich aus.

Siehe den Stift Abstand zwischen Rahmen und Hintergrund v4 von Ana Tudor (@thebabydino) auf CodePen.

Das Problem ist, dass der Text direkt vom Rand des orangen background beginnt und wir kein padding hinzufügen können, weil wir es bereits für den transparenten Spalt verwendet haben. Wir könnten ein zusätzliches Element hinzufügen... oder wir könnten box-shadow verwenden!

box-shadow kann 2, 3 oder 4 Längenwerte annehmen. Der erste ist der x-Offset (bestimmt, wie weit der Schatten nach rechts verschoben wird), der zweite ist der y-Offset (wie weit der Schatten nach unten verschoben wird), der dritte ist der Unschärferadius (bestimmt, wie unscharf die Kante des Schattens ist) und der vierte ist der Ausdehnungsradius (bestimmt, wie stark sich der Schatten in alle Richtungen ausdehnt).

Die folgende interaktive Demo ermöglicht das Spielen mit diesen Werten – klicken Sie auf einen beliebigen davon, und ein Popup mit einem Schieberegler wird angezeigt.

Siehe den Stift wie `box-shadow` funktioniert v2 von CSS-Tricks (@css-tricks) auf CodePen.

Beachten Sie, dass der Unschärferadius zwar immer größer oder gleich null sein muss, die Offsets und der Ausdehnungsradius jedoch negativ sein können. Ein negativer Offset verschiebt den Schatten einfach in die negative Richtung seiner Achse (links oder oben). Ein negativer Ausdehnungsradius bedeutet, dass der Schatten schrumpft statt sich auszudehnen.

Eine weitere wichtige Sache, die hier zu beachten ist – da es für unseren Anwendungsfall praktisch ist – ist, dass der box-shadow niemals unter dem von der border-box belegten Bereich sichtbar ist, nicht einmal, wenn dieser Bereich (halb-)transparent ist.

Wenn wir die Offsets und den Unschärferadius auf null belassen, aber dem Ausdehnungsradius einen positiven Wert geben, erhalten wir etwas, das wie ein zweiter durchgehender Rahmen gleicher Breite in alle Richtungen aussieht, der von der Grenze des tatsächlichen Rahmens ausgeht und nach außen geht.

Rahmen mit box-shadow emulieren

Beachten Sie, dass diese Art von Rahmen nicht unbedingt in allen Richtungen die gleiche Breite haben muss. Wir können ihm unterschiedliche Rahmenbreiten geben, indem wir die Offset-Werte und den Ausdehnungsradius anpassen, mit der Einschränkung, dass die Summe der Breiten der horizontalen Rahmen der Summe der Breiten der vertikalen Rahmen entspricht. Die Verwendung mehrerer Schatten könnte uns helfen, diese Einschränkung zu überwinden, wenn der Rahmen nicht halbtransparent sein muss, aber dies wäre die Art von Lösung, die die Dinge komplizierter macht, anstatt sie zu vereinfachen.

Zurück zu unserer Demo: Wir verwenden die Ausdehnung des box-shadow, um den Rahmen zu simulieren, verwenden den tatsächlichen border, um den transparenten Abstand zu erzeugen, setzen background-clip auf padding-box und lassen das padding seine Arbeit tun.

border: solid 1em transparent;
padding: 1em;
box-shadow: 0 0 0 1em #be4c39;
background: #e18728 padding-box;
Hervorhebung des Rahmen-, Padding- und Content-Bereichs über Entwicklertools

Es kann in Aktion im folgenden Pen gesehen werden.

Siehe den Stift Abstand zwischen Rahmen und Hintergrund v5 von Ana Tudor (@thebabydino) auf CodePen.

Wir könnten auch einen Rahmen simulieren, indem wir einen inset-Schatten emulieren. In diesem Fall beginnt er an der Padding-Grenze (der Grenze zwischen den padding- und border-Bereichen) und erstreckt sich nach innen, so weit wie die Ausdehnung angibt.

einen zweiten Rahmen mit einem inset-box-shadow emulieren

Da wir mehrere Box-Schatten haben können, können wir auf diese Weise mehrere Rahmen simulieren. Nehmen wir den Fall, dass wir zwei haben, einer davon ist ein inset-Schatten. Wenn der tatsächliche border des Elements nicht null und transparent ist und background-clip auf padding-box gesetzt ist, erhalten wir einen simulierten doppelten Rahmen mit einem transparenten Bereich (dem tatsächlichen Rahmenbereich) zwischen seinen inneren und äußeren Komponenten. Beachten Sie, dass dies auch eine Erhöhung des padding erfordert, um den vom inset-box-shadow beanspruchten Platz auszugleichen.

border: solid 1em transparent;
padding: 2em; // increased from 1em to 2em to compensate for inner "border"
box-shadow: 
  0 0 0 0.25em #be4c39 /* outer "border" */,
  inset 0 0 0 1em #be4c39 /* inner "border" */;
background: #e18728 padding-box;

Es kann live in diesem Pen getestet werden, wo wir auch einen drop-shadow()-Filter für einen Glüheffekt haben.

Siehe den Stift Abstand zwischen Rahmen und Hintergrund v6 von Ana Tudor (@thebabydino) auf CodePen.

Einzelnes Element (keine Pseudoelemente) Ziel mit glatten Kanten

Nehmen wir an, wir möchten ein Ziel wie das unten erreichen, mit der Einschränkung, dass wir nur ein Element und keine Pseudoelemente verwenden dürfen.

das zu CSS-kodierende Ziel

Die erste Idee wäre, einen repeating-radial-gradient zu verwenden. Unser Ziel ist ungefähr so strukturiert:

Zielstruktur-Abbildung

Die Hälfte des Ziels wären also 9 Einheiten, was bedeutet, dass die horizontalen und vertikalen Abmessungen des Ziels jeweils 18 Einheiten betragen. Unser sich wiederholender radialer Farbverlauf ist für die erste Einheit schwarz, dann transparent bis zur dritten Einheit, dann wieder schwarz und dann wieder transparent... und das klingt nach einer Wiederholung. Außer, dass wir nur eine Einheit von 0 bis 1 haben, das erste Mal haben wir einen schwarzen Bereich, aber dann, das zweite Mal haben wir einen schwarzen Bereich, er geht von 3 bis 5 – das sind zwei Einheiten! Also... wir sollten dort nicht von 0 ausgehen, sondern von -1, richtig? Nun, das sollte funktionieren, gemäß der Spezifikation.

$unit: 1em;

background: repeating-radial-gradient(
  #000 (-$unit), #000 $unit, 
  transparent $unit, transparent 3*$unit
);

Dieser Pen veranschaulicht die Idee.

Siehe den Stift repeating-radial-gradient für Ziel von Ana Tudor (@thebabydino) auf CodePen.

Das erste Problem hier ist, dass IE eine andere Meinung darüber hat, wie das funktionieren sollte.

repeating-radial-gradient mit negativem erster Stopp: erwartetes Ergebnis (links) vs. IE (rechts)

Glücklicherweise wurde dies in Edge behoben, aber wenn IE-Unterstützung benötigt wird, ist es immer noch ein Problem. Eines, das wir durch die Verwendung eines einfachen radialen Farbverlaufs lösen können, da wir ohnehin nicht so viele Kreise benötigen. Es ist mehr Code, aber es ist nicht so schlimm...

background: radial-gradient(
  #000 $unit, transparent 0, 
  transparent 3*$unit, #000 0, 
  #000 5*$unit, transparent 0, 
  transparent 7*$unit, #000 0, 
  #000 9*$unit, transparent 0
);

Wir können es in Aktion in diesem Pen sehen.

Siehe den Stift radial-gradient für Ziel von Ana Tudor (@thebabydino) auf CodePen.

Die Kreise sind jetzt in allen Browsern gleich verteilt, aber wir haben immer noch ein weiteres Problem: Die Kanten sind in IE/Edge möglicherweise nicht so glatt wie im Originalbild, aber in Firefox und Chrome sehen sie unschön aus!

Original (oben links) vs. IE/Edge (oben rechts) vs. Firefox (unten links) vs. Chrome (unten rechts)

Wir könnten den Trick mit nicht scharfen Übergängen verwenden.

background: radial-gradient(
  #000 calc(#{$unit} - 1px), 
  transparent $unit, 
  transparent calc(#{3*$unit} - 1px), 
  #000 3*$unit, 
  #000 calc(#{5*$unit} - 1px), 
  transparent 5*$unit, 
  transparent calc(#{7*$unit} - 1px), 
  #000 7*$unit, 
  #000 calc(#{9*$unit} - 1px), 
  transparent 9*$unit
);

Live-Test

Siehe den Stift radial-gradient für Ziel v2 von Ana Tudor (@thebabydino) auf CodePen.

Nun, das verbessert die Dinge in IE (wo das Ergebnis bereits gut aussah) und Firefox, aber die Kanten sehen in Chrome immer noch unschön aus.

Original (oben links) vs. IE (oben rechts) vs. Firefox (unten links) vs. Chrome (unten rechts)

Vielleicht sind radiale Farbverläufe doch nicht die beste Lösung. Was wäre, wenn wir die background-clip- und box-shadow-Lösung aus dem vorherigen Abschnitt an dieses Problem anpassen würden? Wir können einen äußeren box-shadow für den äußeren Kreis und einen inset-Schatten für den inneren Kreis verwenden. Der Raum dazwischen wird von einem transparenten Rahmen eingenommen. Wir setzen auch background-clip auf content-box und geben dem Element genügend padding, damit wir einen transparenten Bereich zwischen der zentralen Scheibe und dem inneren Kreis haben.

border: solid 2*$unit transparent;
padding: 4*$unit;
width: 2*$unit; height: 2*$unit;
border-radius: 50%;
box-shadow: 
  0 0 0 2*$unit #000, 
  inset 0 0 0 2*$unit #000;
background: #000 content-box;

Wir können es im folgenden Pen in Aktion sehen, keine gezackten Kanten und keine Probleme.

Siehe den Stift CSS-Ziel mit glatten Kanten von CSS-Tricks (@css-tricks) auf CodePen.

Realistisch wirkende Bedienelemente

Ich kam zuerst auf diese Idee, als ich versuchte, den Daumen, die Schiene und für Nicht-WebKit-Browser den Fortschritt (Füllung) eines Bereichsreglers zu gestalten. Browser stellen Pseudoelemente für diese Komponenten bereit.

Für die Schiene haben wir -webkit-slider-runnable-track, -moz-range-track und -ms-track. Für den Daumen -webkit-slider-thumb, -moz-range-thumb und -ms-thumb. Und für den Fortschritt/die Füllung haben wir -moz-range-progress, -ms-fill-lower (beide links vom Daumen) und -ms-fill-upper (rechts vom Daumen). WebKit-Browser stellen kein Pseudoelement zur Verfügung, das die Gestaltung des Teils vor dem Daumen anders als den Teil danach ermöglichen würde.

Diese sehen inkonsistent und hässlich aus, aber was noch hässlicher ist, ist, dass wir nicht alle Browserversionen für dieselbe Komponente zusammen auflisten können, um sie zu gestalten. So etwas wird nicht funktionieren.

input[type='range']::-webkit-slider-thumb, 
input[type='range']::-moz-range-thumb, 
input[type='range']::-ms-thumb { /* styles here */ }

Wir müssen sie immer so schreiben:

input[type='range']::-webkit-slider-thumb { /* styles here */ }
input[type='range']::-moz-range-thumb { /* styles here */ } 
input[type='range']::-ms-thumb { /* styles here */ }

Was wie eine sehr NASSe Art ist, Code zu schreiben, und es ist in vielen Fällen auch so – obwohl angesichts der vielen Browser-Inkonsistenzen bei Schiebereglern, es auch nützlich ist, um die Dinge auf dem Feld auszugleichen. Meine Lösung dafür war, einen thumb()-Mixin zu verwenden und ihm vielleicht Argumente zur Behandlung von Inkonsistenzen zu geben. Zum Beispiel so:

@mixin thumb($flag: false) {
  /* styles */
	
  @if $flag { /* more styles */ }
}

input[type='range'] {
  &::-webkit-slider-thumb { @include thumb(true); }
  &::-moz-range-thumb { @include thumb(); } 
  &::-ms-thumb { @include thumb(); }
}

Aber kehren wir dazu zurück, wie wir Dinge gestalten können. Wir können nur Pseudoelemente zu diesen Komponenten für Chrome/Opera hinzufügen, was bedeutet, dass wir bei der Nachbildung ihres Aussehens so nah wie möglich daran herankommen müssen, ohne uns auf Pseudoelemente zu verlassen. Dies überlässt Hintergründen, Rahmen und Schatten auf dem Daumenelement selbst.

Sehen wir uns ein paar Beispiele an!

Weiches Kunststoffelement

Als visuelles Beispiel denken Sie an den Daumen des folgenden Schiebereglers.

Schieberegler mit weichem Kunststoff-Daumen

Der erste Gedanke wäre, dass es so einfach ist wie ein Farbverlauf-background und ein Farbverlauf für border-image, dann einfach ein box-shadow dort und das war's für ein solches Bedienelement.

border: solid 0.375em;
border-image: linear-gradient(#fdfdfd, #c4c4c4) 1;
box-shadow: 0 0.375em 0.5em -0.125em #808080;
background: linear-gradient(#c5c5c5, #efefef);

Und das funktioniert (mit einem button-Element anstelle des Schieberegler-Daumens, um die Dinge zu vereinfachen).

Siehe den Stift weiche Kunststofftaste (quadratisch) von Ana Tudor (@thebabydino) auf CodePen.

Außer, dass unser Bedienelement rund und nicht quadratisch ist. Wir müssen ihm dann nur border-radius: 50% zuweisen, richtig? Nun... das funktioniert nicht, weil wir border-image verwenden, was border-radius auf dem Element selbst ignorieren lässt, obwohl es lustigerweise immer noch auf den box-shadow angewendet wird, wenn einer vorhanden ist.

Was sollen wir dann tun? Nun, verwenden Sie background-clip! Wir geben dem Element zuerst ein nicht-null padding, keinen Rahmen und machen es mit border-radius: 50% rund. Dann überlagern wir zwei Farbverlaufshintergründe, der obere ist auf die content-box beschränkt (beachten Sie, dass das Clipping als Teil der Hintergrund-Kurzschreibweise angewendet wird). Schließlich fügen wir zwei Box-Schatten hinzu, der erste ist ein dunkler, der den Schatten unter dem Bedienelement erzeugt, und der zweite ist ein inset-Schatten, der den unteren und seitlichen Teil des äußeren Bereichs des Bedienelements etwas abdunkeln soll.

border: none; /* makes border-box ≡ padding-box */
padding: .375em;
border-radius: 50%;
box-shadow: 0 .375em .5em -.125em #808080, 
      inset 0 -.25em .5em -.125em #bbb;
background: 
  linear-gradient(#c5c5c5, #efefef) content-box, 
  linear-gradient(#fdfdfd, #c4c4c4);

Das Endergebnis ist in diesem Pen zu sehen.

Siehe den Stift weiche Kunststofftaste (wirklich rund!) von Ana Tudor (@thebabydino) auf CodePen.

Matte Kontrolle

So etwas wie der Daumen des Schiebereglers im folgenden Bild.

Schieberegler mit mattem Daumen

Dies sieht dem vorherigen Fall ziemlich ähnlich, außer dass wir jetzt eine hellere Linie oben und einen inset-Schatten für den mittleren Teil haben. Wenn wir einen äußeren box-shadow verwenden, um die hellere Linie zu erzeugen, wäre das Ganze nicht mehr rund, es sei denn, wir verringern auch seine Höhe, um den Schatten auszugleichen. Das würde bedeuten, dass wir mehr Berechnungen durchführen müssten, um die Position des inneren Teils zu bestimmen. Wenn wir stattdessen einen inset-Schatten verwenden, können wir diesen nicht für den dunklen Schatten des inneren Teils verwenden. Wir könnten ihn jedoch mit einem radial-gradient emulieren, der bequem dimensioniert, positioniert und auf die content-box zugeschnitten ist. Das bedeutet die gleiche Strategie wie im vorherigen Fall, außer dass wir einen zusätzlichen radial-gradient über den anderen Hintergründen legen. Die tatsächliche background-size des radialen Farbverlaufs ist größer als die content-box, sodass wir ihn nach unten verschieben können, ohne seine obere Kante in die Content-Grenze zu bringen.

border: none; /* makes border-box ≡ padding-box */
padding: .625em;
width: 1.75em; height: 1.75em;
border-radius: 50%;
box-shadow: 
  0 1px .125em #444 /* dark lower shadow */, 
  inset 0 1px .125em #fff /* light top hint */;
background: 
  /* inner shadow effect */
  radial-gradient(transparent 35%, #444) 
    50% calc(50% + .125em) content-box, 
  
  /* inner background */
  linear-gradient(#bbb, #bbb) content-box, 
  
  /* outer background */
  linear-gradient(#d0d3d5, #d2d5d7);
background-size: 
  175% 175% /* make radial-gradient bg larger */, 
  100% 100%, 100% 100%;

Wir können es live in diesem Pen sehen.

Siehe den Stift matte Taste von Ana Tudor (@thebabydino) auf CodePen.

3D-Kontrolle

Zum Beispiel der Daumen des folgenden Schiebereglers.

Schieberegler mit 3D-Daumen

Diese ist etwas komplexer und erfordert, dass die drei Boxen (content-box, padding-box und border-box) alle unterschiedlich sind, damit wir Hintergründe schichten und background-clip verwenden können, um den gewünschten Effekt zu erzielen.

Für den Hauptteil des Schiebereglers haben wir einen Gradienten-background, der auf die content-box zugeschnitten ist, überlagert mit einem weiteren, der auf die padding-box zugeschnitten ist, beide über einem dritten linear-gradient, der auf die border-box zugeschnitten ist. Wir nutzen auch einen inset-box-shadow, um die Padding-Grenze (zwischen den padding- und border-Bereichen) hervorzuheben.

border: solid .25em transparent;
padding: .25em;
border-radius: 1.375em;
box-shadow: 
  inset 0 1px 1px rgba(#f7f7f7, .875) /* top */, 
  inset 0 -1px 1px rgba(#bbb, .75) /* bottom */;
background: 
  linear-gradient(#9ea1a6, #fdfdfe) content-box, 
  linear-gradient(#fff, #9c9fa4) padding-box, 
  linear-gradient(#eee, #a4a7ab) border-box;

Das Ergebnis für diesen Teil ist in diesem Pen zu sehen.

Siehe den Stift 3D-Taste von Ana Tudor (@thebabydino) auf CodePen.

Nun kommt der kleine runde Teil. Dies ist ein Fall, in dem ich das Gefühl hatte, dass ein Pseudoelement wirklich benötigt wurde. Ich habe diese Route für Blink eingeschlagen, konnte aber für die restlichen Browser einen anständig aussehenden Fallback erzielen, indem ich zwei radiale Farbverläufe über die linearen legte.

Siehe den Stift 3D-Taste #2 von Ana Tudor (@thebabydino) auf CodePen.

Wir könnten dem gewünschten Ergebnis mit besser gewählten Farbtönen, zusätzlichen radialen Farbverläufen oder sogar der Verwendung von background-blend-mode näher kommen, aber dafür fehlt mir der künstlerische Sinn.

Mit einem Pseudoelement ist es viel einfacher, das gewünschte Ergebnis zu erzielen – wir müssen es zuerst richtig positionieren und dimensionieren, es mit border-radius: 50% rund machen. Dann geben wir ihm ein padding, keinen Rahmen, und verwenden zwei Farbverläufe für den background, wobei der obere ein radialer ist, der auf die content-box zugeschnitten ist.

padding: .125em;
background: 
  radial-gradient(circle at 50% 10%, 
      #f7f8fa, #9a9b9f) content-box, 
  linear-gradient(#ddd, #bbb);

Das Ergebnis hierfür ist im folgenden Pen zu sehen.

Siehe den Stift 3D-Taste #3 von Ana Tudor (@thebabydino) auf CodePen.

Für den tatsächlichen Schieberegler-Daumen habe ich überall den gleichen Daumen-background mit den darüber liegenden radialen Farbverläufen verwendet und das Pseudoelement für Blink direkt über den radialen Farbverläufen hinzugefügt. Dies liegt daran, dass Schieberegler-Daumenstile in Safari über ::-webkit-slider-thumb angewendet werden, aber Safari keine Pseudoelemente auf dem Daumen (oder der Schiene) unterstützt. Wenn ich also das Fallback von den Stilen, die auf ::-webkit-slider-thumb angewendet werden, entfernen würde, würde Safari den runden Teil überhaupt nicht anzeigen.

Illusion von Tiefe

Die Schiene des folgenden Schiebereglers illustriert diese Idee.

Schieberegler mit Tiefe in der Schiene

Wie zuvor machen wir dies, indem wir dem Element einen nicht-null, transparenten border, ein padding geben und Hintergründe mit unterschiedlichen background-clip-Werten schichten (denken Sie daran, dass die mit content-box-Werten über denen mit padding-box-Werten liegen müssen, die über denen mit border-box-Werten liegen müssen). In diesem Fall haben wir einen helleren linear-gradient, um den border-Bereich abzudecken, einen dunkleren plus ein paar radiale, die wir kleiner und nicht wiederholend machen, um die Enden weiter abzudunkeln für den padding-Bereich, und einen sehr dunklen für den Content-Bereich. Dann geben wir dem Ganzen einen border-radius, der mindestens der halben height des Content-Bereichs plus dem doppelten padding und dem doppelten border entspricht. Wir fügen auch einen inset-box-shadow hinzu, um die Padding-Grenze dezent hervorzuheben.

border: solid .375em transparent;
padding: 1em 3em;
width: 15.75em; height: 1.75em;
border-radius: 2.25em;
background: 
  linear-gradient(#090909, #090909) content-box, 
  radial-gradient(at 5% 40%, #0b0b0b, transparent 70%) 
    no-repeat 0 35% padding-box /* left */, 
  radial-gradient(at 95% 40%, #111, transparent 70%) 
    no-repeat 100% 35% padding-box /* right */, 
  linear-gradient(90deg, #3a3a3a, #161616) padding-box,
  linear-gradient(90deg, #2b2d2c, #2a2c2b) border-box;
background-size: 100%, 9em 4.5em, 4.5em 4.5em, 100%, 100%;

Aber es gibt ein Problem damit, und es ist im folgenden Pen zu sehen.

Siehe den Stift Illusion von Tiefe von Ana Tudor (@thebabydino) auf CodePen.

Aufgrund der Funktionsweise von border-radius – der Radius für den Inhaltsbereich ist der von uns angegebene Wert minus der border-width minus der padding, was am Ende negativ wird – gibt es keine Abrundung für die Ecken des Inhaltsbereichs. Nun, das können wir beheben! Wir können die gewünschte Form durch die Verwendung von linearen und radialen Verläufen für den Inhaltsbereich nachahmen.

Dies tun wir, indem wir zuerst sicherstellen, dass die width unseres Inhaltsbereichs ein Vielfaches seiner height ist (in unserem Fall 15,75em = 9 * 1,75em). Wir überlagern zunächst einen nicht wiederholenden linear-gradient, der mittig positioniert ist und die gesamte height des Inhaltsbereichs vertikal abdeckt, aber am linken und rechten Ende einen Abstand von der halben height des Inhaltsbereichs lässt. Darüber legen wir einen radial-gradient mit einer background-size, die sowohl horizontal als auch vertikal der height des Inhaltsbereichs entspricht.

Siehe den Pen illusion of depth #2 von Ana Tudor (@thebabydino) auf CodePen.

Metallische Bedienelemente

Zum Beispiel etwas wie der unten abgebildete Knopf

metallischer Regler

Das ist etwas komplexer, also zerlegen wir es. Zuerst machen wir den Knopf kreisförmig, indem wir ihm gleiche width und height geben und border-radius: 50% setzen. Dann stellen wir sicher, dass er box-sizing: border-box hat, damit border-width und padding nach innen gehen (von den gesetzten Dimensionen abgezogen werden). Bisher haben wir das:

$d-btn: 27em; /* control diameter */
$bw: 1.5em; /* border-width */

button {
  box-sizing: border-box;
  border: solid $bw transparent;
  padding: 1.5em;
  width: $d-btn; height: $d-btn;
  border-radius: 50%;
}

Es sieht noch nach nichts aus, und das liegt daran, dass der gesamte Look mit Hilfe von nur zwei Eigenschaften erzielt wird, die wir noch nicht hinzugefügt haben: box-shadow und background.

Siehe den Pen metallic control – step 0 von Ana Tudor (@thebabydino) auf CodePen.

Bevor wir etwas anderes tun, zerlegen wir den Regler ein wenig

Zerlegung des metallischen Reglers

Von außen nach innen haben wir:

  • ein dicker äußerer Ring mit LEDs
  • ein dünner innerer Ring
  • ein perforierter Bereich (der den Cyan-Glow um den folgenden inneren Teil einschließt)
  • ein großer zentraler Teil

Der dicke äußere Ring ist der Randbereich, der zentrale Teil ist der Inhaltsbereich und alles dazwischen (der dünne innere Ring und der perforierte Teil) ist der Polsterbereich. Wir erstellen den dünnen inneren Ring mit eingesetzten Box-Schatten

box-shadow: 
  /* discrete dark shadow to act as separator from outer ring */
  inset 0 0 1px #666, 

  /* darker top area */
  inset 0 1px .125em #8b8b8b, 
  inset 0 2px .25em #a4a2a3, 

  /* darker bottom area */
  inset 0 -1px .125em #8b8b8b, 
  inset 0 -2px .25em #a4a2a3, 

  /* the base circular strip for the inner ring */
  inset 0 0 0 .375em #cdcdcd;

Wir fügen auch zwei weitere äußere Box-Schatten hinzu, einen für den helleren oberen Glanz des äußeren Rings und den zweiten für den dezenten dunklen Schatten unter dem Regler, sodass wir nun haben

box-shadow: 
  0 -1px 1px #eee, 
  0 2px 2px #1d1d1d, 
  inset 0 0 1px #666, 
  inset 0 1px .125em #8b8b8b, 
  inset 0 2px .25em #a4a2a3, 
  inset 0 -1px .125em #8b8b8b, 
  inset 0 -2px .25em #a4a2a3, 
  inset 0 0 0 .375em #cdcdcd;

Immer noch nicht viel, aber mehr als vorher

Siehe den Pen metallic control – step 1 von Ana Tudor (@thebabydino) auf CodePen.

Jetzt müssen wir drei Arten von Hintergründen übereinander legen, von oben nach unten: begrenzt auf die content-box (wodurch der zentrale Bereich entsteht), begrenzt auf die padding-box (wodurch der perforierte Bereich und der Cyan-Glow entstehen) und begrenzt auf die border-box (wodurch der dicke äußere Ring und die LEDs entstehen).

Wir beginnen mit dem zentralen Bereich, in dem wir einige dezente kreisförmige Linien haben, die mit drei sich wiederholenden radialen Verläufen übereinander erstellt werden, wobei die Werte der Stopps auf dem Zikadenprinzip und konischen Reflexionen basieren, die mit einem konischen Verlauf erstellt werden. Beachten Sie, dass konische Verläufe derzeit noch in keinem Browser unterstützt werden, daher müssen wir zu diesem Zeitpunkt einen Polyfill verwenden.

background:
  /* ======= content-box ======= */
  /* circular lines - 13, 19, 23 being prime numbers */
  repeating-radial-gradient(
      rgba(#e4e4e4, 0) 0, 
      rgba(#e4e4e4, 0) 23px, 
      rgba(#e4e4e4, .05) 25px, 
      rgba(#e4e4e4, 0) 27px) content-box, 
  repeating-radial-gradient(
      rgba(#a6a6a6, 0) 0, 
      rgba(#a6a6a6, 0) 13px, 
      rgba(#a6a6a6, .05) 15px, 
      rgba(#a6a6a6, 0) 17px) content-box, 
  repeating-radial-gradient(
      rgba(#8b8b8b, 0) 0, 
      rgba(#8b8b8b, 0) 19px, 
      rgba(#8b8b8b, .05) 21px, 
      rgba(#8b8b8b, 0) 23px) content-box, 
  /* conic reflections */
  conic-gradient(/* random variations of some shades of grey */
      #cdcdcd, #9d9d9d, #808080, 
      #bcbcbc, #c4c4c4, #e6e6e6, 
      #dddddd, #a1a1a1, #7f7f7f, 
      #8b8b8b, #bfbfbf, #e3e3e3, 
      #d2d2d2, #a6a6a6, #858585, 
      #8d8d8d, #c0c0c0, #e5e5e5, 
      #d6d6d6, #9e9e9e, #828282, 
      #8f8f8f, #bdbdbd, #e3e3e3, #cdcdcd) 
    content-box;

Jetzt fängt es endlich an, nach etwas auszusehen!

Siehe den Pen metallic control – step 2 von Ana Tudor (@thebabydino) auf CodePen.

Wir machen weiter mit dem perforierten Bereich. Der Cyan-Glow ist nur ein radialer Verlauf zu Transparenz im äußeren Teil, während die Perforationen auf dem Carbonfaser-Muster aus der Galerie basieren, die Lea Verou vor etwa fünf Jahren zusammengestellt hat – immer noch verdammt nützlich für künstlerisch unbegabte Leute wie mich.

$d-hole: 1.25em; /* perforation diameter*/
$r-hole: .5*$d-hole; /* perforation radius */

background: 
  /* ======= padding-box ======= */
  /* cyan glow */
  radial-gradient(
      #00d7ff 53%, transparent 65%) padding-box, 
  /* holes */
  radial-gradient(
      #272727 20%, transparent 25%) 
    0 0 / #{$d-hole} #{$d-hole} 
    padding-box,
  radial-gradient(
      #272727 20%, transparent 25%) 
    $r-hole $r-hole / #{$d-hole} #{$d-hole} 
    padding-box,
  radial-gradient(#444 20%, transparent 28%) 
    0 .125em / #{$d-hole} #{$d-hole} 
    padding-box,
  radial-gradient(#444 20%, #3d3d3d 28%) 
    #{$r-hole} #{$r-hole + .125em} / #{$d-hole} #{$d-hole} 
    padding-box

Es sieht aus, als ob wir uns etwas Gutem nähern

Siehe den Pen metallic control – step 3 von Ana Tudor (@thebabydino) auf CodePen.

Der dicke äußere Grundring (ohne LEDs) wird mit einem einzigen konischen Verlauf erstellt

conic-gradient(
  #b5b5b5, #8d8d8d, #838383, 
  #ababab, #d7d7d7, #e3e3e3, 
  #aeaeae, #8f8f8f, #878787, 
  #acacac, #d7d7d7, #dddddd, 
  #b8b8b8, #8e8e8e, #848484, 
  #a6a6a6, #d8d8d8, #e3e3e3, 
  #8e8e8e, #868686, #a8a8a8, 
  #d5d5d5, #dedede, #b5b5b5) border-box;

Jetzt haben wir einen metallischen Regler!

Siehe den Pen metallic control – step 4 von Ana Tudor (@thebabydino) auf CodePen.

Er hat zu diesem Zeitpunkt keine LEDs, also beheben wir das!

Jede LED besteht aus zwei nicht wiederholenden radialen Verläufen, die übereinander gestapelt sind. Der obere ist die eigentliche LED, während der untere, leicht vertikal versetzte, den helleren Glanz im unteren Teil der LED erzeugt. Es ist praktisch der gleiche Effekt, der für die Löcher im perforierten Bereich verwendet wird. Der untere Verlauf ist immer derselbe, aber der obere unterscheidet sich je nachdem, ob die LEDs ein- oder ausgeschaltet sind.

Wir gehen davon aus, dass die LEDs bis zur $k-ten eingeschaltet sind. Bis dahin verwenden wir die Cyan-Variante für den oberen Verlauf, danach die graue.

Wir haben 24 LEDs, die auf einem Kreis positioniert sind, der durch die Mitte ihres Randbereichs verläuft. Ihr Radius ist also der Radius des Reglers abzüglich der halben Randbreite.

Wir generieren all diese Verläufe mit Sass. Zuerst erstellen wir eine leere Liste von Verläufen, dann durchlaufen wir sie und fügen in jeder Iteration zwei Verläufe zur Liste hinzu. Ihre Positionen werden so berechnet, dass sie auf dem zuvor erwähnten Kreis liegen. Der erste Verlauf hängt vom Schleifenindex ab, während der zweite immer derselbe ist (nur an einer anderen Position auf dem Kreis).

$d-btn: 27em;
$bw: 1.5em;
$r-pos: .5*($d-btn - $bw);
$n-leds: 24;
$ba-led: 360deg/$n-leds;
$d-led: 1em;
$r-led: .5*$d-led;
$k: 7;
$leds: ();

@for $i from 0 to $n-leds {
  $a: $i*$ba-led - 90deg;
  $x: .5*$d-btn + $r-pos*cos($a) - $r-led;
  $y: .5*$d-btn + $r-pos*sin($a) - $r-led;
  $leds: $leds, 
    if($i < $k, 
      (radial-gradient(circle, #01d6ff, 
          #178b98 .5*$r-led, 
          rgba(#01d6ff, .35) .7*$r-led, 
          rgba(#01d6ff, 0) 1.3*$r-led) no-repeat 
        #{$x - $r-led} #{$y - $r-led} / 
        #{2*$d-led} #{2*$d-led} border-box), 
      (radial-gradient(circle, #898989, 
          #4d4d4d .5*$r-led, #999 .65*$r-led, 
          rgba(#999, 0) .7*$r-led) no-repeat 
        $x $y / #{$d-led} #{$d-led} border-box)
    ), 
    radial-gradient(circle, 
        rgba(#e8e8e8, .5) .5*$r-led, 
        rgba(#e8e8e8, 0) .7*$r-led) no-repeat 
      $x ($y + .125em) / #{$d-led} #{$d-led} 
      border-box;
}

Das Endergebnis ist in diesem Pen zu sehen.

Siehe den Pen 1 element (no pseudos) metallic control von Ana Tudor (@thebabydino) auf CodePen.

Schatten in einer senkrechten Ebene

Betrachten wir den Fall von Bedienelementen in der vertikalen Ebene des Bildschirms, für die wir einen Schatten in einer horizontalen Ebene darunter haben wollen. Etwas wie im folgenden Bild

Bedienelemente mit Schatten in einer horizontalen Ebene darunter

Was wir wollen, ist, diesen Effekt mit nur einem Element und ohne Pseudo-Elemente nachzubilden.

Das Übereinanderlegen von Hintergründen mit unterschiedlichen background-clip- und background-origin-Werten erledigt auch in diesem Fall die Arbeit. Wir erstellen den eigentlichen Knopf mit zwei Hintergründen, der obere ist auf die content-box zugeschnitten und der darunterliegende auf die padding-box, und verwenden einen radial-gradient()-Hintergrund mit background-clip und background-origin auf border-box gesetzt, um den Schatten zu erzeugen.

Das grundlegende Styling ist dem des metallischen Reglers im vorherigen Abschnitt ziemlich ähnlich

$l: 6.25em;
$bw: .1*$l;

border: solid $bw transparent;
padding: 3px;
width: $l; height: $l;
border-radius: 1.75*$bw;

Wir geben ihm einen etwas dickeren transparenten Rand rundherum, damit wir genug Platz haben, um diesen Schatten im unteren Randbereich nachzubilden. Wir tun dies für alle Ränder, nicht nur für den unteren, weil wir die gleiche Art von symmetrischer Abrundung für alle Ecken der padding-box wünschen (wenn Sie eine Auffrischung benötigen, wie das funktioniert, schauen Sie sich Lea Verou's exzellanten border-radius Talk an).

Siehe den Pen border-width & padding-box corner rounding von Ana Tudor (@thebabydino) auf CodePen.

Der erste background von oben ist ein conic-gradient(), um die konischen Metallreflexionen zu erzeugen. Dieser ist auf die content-box zugeschnitten. Direkt darunter haben wir einen einfachen linear-gradient(), der auf die padding-box zugeschnitten ist. Wir verwenden drei eingesetzte Box-Schatten, um diesen zweiten Hintergrund weniger flach zu machen – fügen Sie eine weitere Schattierung rundherum mit einem Schatten ohne Weichzeichner und positiver Streuung hinzu, machen Sie ihn oben heller mit einem halbtransparenten weißen Schatten und unten dunkler mit einem halbtransparenten schwarzen Schatten.

box-shadow: 
  inset 0 0 0 1px #eedc00, 
  inset 0  1px 2px rgba(#fff, .5), 
  inset 0 -1px 2px rgba(#000, .5);
background: 
  conic-gradient(
      #edc800, #e3b600, #f3cf00, #ffe800, 
      #ffe900, #ffeb00, #ffe000, #ebc500, 
      #e0b100, #f1cc00, #fcdc00, #ffe500, 
      #fad900, #eec200, #e7b900, #f7d300, 
      #ffe800, #ffe300, #f5d100, #e6b900, 
      #e3b600, #f4d000, #ffe400, #ebc600, 
      #e3b600, #f6d500, #ffe900, #ffe90a, 
      #edc800) content-box, 
  linear-gradient(#f6d600, #f6d600) padding-box

Das ergibt den metallischen Knopf (noch ohne Schatten)

Siehe den Pen metallic button – no shadow von Ana Tudor (@thebabydino) auf CodePen.

Für den Schatten legen wir einen dritten Hintergrund an, für den wir sowohl background-clip als auch background-origin auf border-box setzen. Dieser Hintergrund ist ein nicht wiederholender radial-gradient(), dessen Position wir an bottom (und horizontal in der Mitte) anheften und den wir vertikal schrumpfen, sodass er in diesen unteren Randbereich passt und sogar etwas Platz lässt – wir nehmen ihn zum Beispiel auf etwa .75 der border-width.

radial-gradient(rgba(#787878, .9), rgba(#787878, 0) 70%) 
  50% bottom / 80% .75*$bw no-repeat border-box

Und das ist es! Sie können mit den Schaltflächen im folgenden Pen spielen

Siehe den Pen metallic buttons with shadow (no pseudos) von Ana Tudor (@thebabydino) auf CodePen.


background-clip hat sicherlich seine Anwendungsfälle! Besonders beim Überlagern mehrerer Effekte an den Rändern von Elementen.