BERG Cloud Buttons

Avatar of Chris Coyier
Von Chris Coyier am

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

Die Buttons bei BERG Cloud sehen ziemlich gut aus. Es ist keine riesige Sache, aber die Art und Weise, wie sie erstellt wurden, verwendet zwei separate Bilder (nicht gesprited) und zwei interne Spans. In diesem Tutorial werden wir sie mit einigen modernen Tricks nachbilden. Wir werden keine Bilder verwenden (effizienter), weniger Markup (semantischer) und reibungsloser funktionieren (kein JavaScript, das Zustände steuert).

Hier sind die Originale, die wir nachbilden wollen

orig-buttons

Und unser Endergebnis auf CodePen

Einfaches Markup

Ein Link ist ein Link.

<a href="#" class="button blue">
  Find Out<br>More
</a>

In unserem Fall haben wir zusätzlich zu "button" die Klasse "blue". Solch einfache Dinge sind es wert, diskutiert zu werden. Zum einen würde ich im "echten Leben" die Klasse "button" einfach eigenständig funktionsfähig machen. Es wäre der gängigste Button-Stil auf der Seite. Um Variationen zu handhaben, habe ich früher zusätzliche Klassen hinzugefügt, wie oben gezeigt. Die zweite Klasse würde Überschreibungen behandeln. Ich würde sie wahrscheinlich so schreiben

.button.blue {
   /* overrides */
}

Aber ich fange an zu denken, dass das nicht so toll ist. Dieser Button hat die doppelte Spezifität des Originals und verwendet eine Klasse, die keinen eigenständigen Wert hat. In letzter Zeit habe ich mehrere Code-Basen gesehen, die einzelne Klassennamen für Variationen verwenden, wie z.B. "button-blue" als alleiniger Klassenname, der alles erledigt. Perfekt machbar in normalem CSS, aber besonders einfach in Sass mit @extend. Zum Beispiel

.button {
   /* Styles for basic button */
}

.button-blue {
   @extend .button;
   /* overrides */
}

Super saubere Autorenschaft, nicht aufgeblähter Output und ein einzelner Klassenname mit gleicher Spezifität wie jeder andere Button. Ich bin dafür.

Heilige box-shadow!

Der Kern dessen, was hier passiert, sind Schicht um Schicht von `box-shadow` auf einem Inline-Block-Link. Wenn Sie einen Box-Schatten mit 1 Pixel Versatz in zwei Richtungen und null Weichzeichnung erstellen, können Sie im Wesentlichen einen 1 Pixel breiten Rand von zwei Kanten einer Box erzeugen. Wenn Sie dies erneut tun, nur mit 2 Pixel Versatz in denselben Richtungen, beginnen Sie, eine kleine diagonale Linie zu bilden und geben der Box eine falsche Tiefe. Wenn Sie dies sechsmal tun, haben Sie einen ziemlich kräftig aussehenden Button.

Wenn Sie ihn nun verdoppeln und die Farben abwechseln (linker Versatz erhält einen Farbton, unterer Versatz einen anderen Farbton), können Sie eine gewisse Beleuchtung simulieren.

.button {
    box-shadow:
      /* Left Side */      /* Right Side */
      -1px 0px 1px #6fadcb, 0px 1px 1px #54809d,
      -2px 1px 1px #6fadcb, -1px 2px 1px #54809d,
      -3px 2px 1px #6fadcb, -2px 3px 1px #54809d;
      /* etc */
}

Mit derselben Technik, aber durch Umkehrung der Richtung und Beginn dort, wo Sie die Kanten beendet haben, können Sie dies mit einem wirklich weichen, transparenten Schwarz tun, um einen buchstäblich aussehenden Schatten zu erzeugen. Werfen Sie auch einen eingefügten Schatten mit einem weichen, transparenten Weiß darauf, und es verstärkt die Illusion, dass Licht auf den Button trifft.

.button {
  box-shadow:
    
    /* Sides */
    -1px 0px 1px #6fadcb, 0px 1px 1px #54809d,
    -2px 1px 1px #6fadcb, -1px 2px 1px #54809d,
    -3px 2px 1px #6fadcb, -2px 3px 1px #54809d,
    /* etc */

    /* Shadow */
    -6px 7px  0 rgba(0, 0, 0, 0.05),
    -5px 8px  0 rgba(0, 0, 0, 0.05),
    -3px 9px  0 rgba(0, 0, 0, 0.04),
    /* etc */
  
    /* Light & Top Edge */
    inset 0 4px 5px -2px rgba(255, 255, 255, 0.5),
    inset 0 1px 0 0 rgba(0, 0, 0, 0.3);

}

Ich bin sicher, Sie können sich vorstellen, wie Sass hier auf clevere Weise hilft. Berechnung von Farbtönen, Schleifen, Variablen für Farben, engere Syntax usw.

Mehr CSS!

Die Vorderseite des Buttons ist ein sehr subtiler Farbverlauf (der ebenfalls zur Beleuchtung beiträgt). Kein Problem. Wahrscheinlich am besten, den Farbverlauf auf die Basisklasse "button" anzuwenden und ihn dann für Variationen zu überschreiben.

.button {
  /* ...shadows ... */

 background: linear-gradient(#a2d3e9, #7abedf);
}

Der Text innerhalb der Buttons hat einen leichten Schatten. Ganz einfach mit `text-shadow`. Stellen Sie einfach sicher, dass der Schatten zum Hintergrund passt. Wir haben zwei Variationen des Buttons erstellt und müssen im Wesentlichen jeden Teil der Farbgebung der gelben Version überschreiben, da es sich um dunkle auf heller Oberfläche statt helle auf dunkler Oberfläche handelt.

.button {
  /* ...shadows ... */
  /* ...background ... */

 text-shadow: -2px 2px 0 rgba(0, 0, 0, 0.2);
}

Natürlich gibt es noch viele andere CSS-Eigenschaften bei diesen Buttons, die Sie erwarten würden. Wie das Ausschalten von Unterstreichungen, Textfarben, das Einrichten von Abständen und anderer offensichtlicher Dinge.

Interaktion

Im Original wird beim Klicken oder Tippen auf den Button per JavaScript eine "active"-Klasse angewendet, die die "Drück"-Animation auslöst. Das ist etwas wacklig, da die Buttons in diesem Zustand hängen bleiben können. Versuchen Sie, den Button anzuklicken und mit der Maus wegzubewegen und loszulassen. Der Button bleibt gedrückt. Lösbar, wenn sie Dinge tun würden, wie z.B. die aktive Klasse bei `mouseleave` zu entfernen, aber leider. Wir müssen uns darum keine Sorgen machen, da wir den gedrückten Zustand über `:active` auslösen können, wodurch wir automatisch alle Interaktivitätsdetails erhalten.

Unsere Interaktion wird die gleiche Drück-Animation sein, bei der

  1. Alle `box-shadow` werden entfernt, wodurch der Button flach zur Oberfläche aussieht
  2. Der Button wird an eine Position verschoben, an der er so aussieht, als wäre er auf der Oberfläche

Ziemlich einfach.

.button {

  /* ... styling stuff ... */

  /* interaction */
  transition: all 0.1s linear;
  transform: translateZ(0);
}
.button:active {
  box-shadow: none;
  transform: translate3d(-6px, 6px, 0);
}

Die Übergangsfunktion erledigt den Großteil der Arbeit. Wenn diese angewendet wird, schleicht sich der `box-shadow` langsam davon, anstatt sofort zu verschwinden. Währenddessen bewegen wir den Button. Wir könnten den Button relativ positionieren und ihn mit `top`/`left`-Werten verschieben. Das Problem dabei ist, dass

  1. Verwirrt die Positionierung und Interaktivität. Wenn der Button absolut positioniert wäre, würde es schnell seltsam werden. Es gibt kein `top: wo-er-ist-plus-10;`
  2. Ist nicht so performant wie die Bewegung mittels `translate`.

Das zweite Bemerkenswerte ist die Verwendung von 3D-Transformationen. Wir verwenden 3D-Transformations-Eigenschaften, aber wir führen eigentlich keine 3D-Transformationen durch. Wir tun dies, weil

  1. Es wird die GPU eingeschaltet und sorgt für reibungslosere Leistung.
  2. Es wird den Text im *Standardzustand* des Buttons verunstalten (dünner machen), damit es kein überraschender Bildschirmflackern gibt, wenn die Animation beginnt.

WebKit hat diesen hässlichen (ich würde ihn als Bug bezeichnen), dass Text, der transformiert/animiert wird, dünn und hässlich aussieht. Ich habe versucht, mich damit auseinanderzusetzen unbefriedigend. Mein Freund Koop hat es einmal ziemlich gut zusammengefasst, indem er sagte: "Man entscheidet einfach, was hässlicher ist." Option A) Der Text ist die ganze Zeit verunstaltet, so dass es keine unbeholfenen Bildschirmflackern und Textverschiebungen gibt. Option B) Lass es so. Der Text sieht standardmäßig gut aus, aber Sie erhalten Flackern und Verdünnung, wenn sich der Button in einer Animation befindet oder transformiert wird.

Das ist bei diesen Buttons ziemlich dramatisch.

thick
Ohne Transformation (dick, normal)
thin
Mit Transformation (dünn)

In diesem seltenen Fall gefällt mir die dünnere Version sowieso, also das Beste aus beiden Welten.

Der Pfeil

Bei meinem ersten Versuch, diese Buttons zu erstellen, habe ich den Pfeil mit zwei Spans erstellt. Es funktionierte gut, aber keine große semantische Verbesserung gegenüber dem Original. Neil Kinnish hat meinen Pen geforkt und die Pfeile mit Pseudo-Elementen erstellt, was bedeutet, dass wir wieder nur einen glücklichen Anker-Link verwenden. Ich habe meinen Pen inzwischen mit seinem Code aktualisiert. Man würde denken, mein Verstand würde sofort auf Pseudo-Elemente gehen, da ich früher einen Vortrag ausschließlich über sie gehalten habe und ihre Erstaunlichkeit seit langem gepriesen habe. Ich weiß, dass man in WebKit immer noch kein Pseudo-Element animieren kann, aber ich war der Meinung, dass man sie auch nicht transformieren kann. Entweder war ich schon immer falsch oder das ist eine ziemlich neue Fähigkeit.

Der Trick besteht darin, die beiden kostenlosen Elemente zu verwenden, um ein kleines schwarzes Rechteck zu erstellen, das absolut rechts vom Button positioniert ist (dank `padding` wird dort immer Platz sein). Ein kleines schwarzes Rechteck wird um 45 Grad gedreht (via `transform`) und das andere um -45 Grad. Als sie verschachtelte Spans waren, war es am sinnvollsten, einen `filter: drop-shadow()` zu setzen (nur auf den oberen Span angewendet), aber jetzt mit Pseudo-Elementen funktioniert `box-shadow` auf beiden genauso gut.

.button:after, .button:before
    position: absolute;
    content: "";
    right: 15px;
    top: 14px;
    width: 6px;
    height: 18px;
    background: white;
    transform: rotate(-45deg);
    display: block;
    z-index: 2;
}
.button:before {
    height: 14px;
    top: 26px;
    right: 16px;
    z-index: 3; /* on top */
    transform: rotate(-137deg); /* hey, it works */
    filter: drop-shadow(0 -2px 0 rgba(0, 0, 0, 0.15)); 
}
.button:After {
   filter: drop-shadow(-2px 0 0 rgba((0, 0, 0, 0.2)); 
} 

Auch hier müssten Sie die gesamte Farbgebung für verschiedene Button-Variationen anpassen. Die Positionierung wäre aber dieselbe.

Fertig

Hier ist das Endergebnis wieder auf CodePen

Außerdem: OMG, Tiny Printer, so süß #willhaben.