Nachdem ich mehr als 14 Jahre in der Webentwicklungsbranche tätig bin, habe ich gute und schlechte CSS-Codes gesehen und geschrieben. Als ich vor fünf Jahren bei Ramsey Solutions anfing, wurde ich mit Sass bekannt gemacht. Es hat mich umgehauen, wie nützlich es war! Ich habe mich sofort damit beschäftigt und wollte alles darüber lernen. In den letzten fünf Jahren habe ich eine Reihe verschiedener Sass-Techniken und -Muster angewendet und mich in einige verliebt, die, um es mit Apples Worten zu sagen, einfach funktionieren.
In diesem Artikel werde ich eine breite Palette von Themen behandeln
- Die Macht des Ampersands
- Variablen und Geltungsbereiche
- Die Bedeutung von Imports
- Mixin' es auf
- Funktional werden
- Die Selektorreihenfolge, die Platzhalter durcheinanderbringen
Meiner Erfahrung nach ist das Finden des Gleichgewichts zwischen einfach und komplex die entscheidende Komponente für die Erstellung großartiger Software. Software sollte nicht nur für Menschen einfach zu bedienen sein, sondern auch für Sie und andere Entwickler in Zukunft wartbar. Ich würde diese Techniken als fortgeschritten, aber nicht unbedingt als clever oder absichtlich komplex bezeichnen!
„Jeder weiß, dass Debugging doppelt so schwierig ist wie das Schreiben eines Programms. Wenn Sie also beim Schreiben des Programms so clever wie möglich sind, wie werden Sie es jemals debuggen?“
—Die Elemente der Programmierung und des Stils (2. Auflage), Kapitel 2
In diesem Sinne wollen wir uns zunächst den Sass-Ampersand ansehen.
Die Macht des Ampersands
Es gibt viele verschiedene Namenskonventionen, mit denen Sie Ihre CSS organisieren können. Diejenige, die ich am liebsten verwende, ist SUIT, eine Variation von BEM (was kurz für Block, Element, Modifier steht). Wenn Sie mit SUIT oder BEM nicht vertraut sind, empfehle ich Ihnen, einen Blick darauf zu werfen, bevor Sie fortfahren. Ich werde die SUIT-Konvention im Rest dieses Artikels verwenden.
Welche Namenskonvention Sie auch wählen, die Grundidee ist, dass jedes gestylte Element seinen eigenen Klassennamen erhält, der mit dem Komponentennamen vorangestellt ist. Diese Idee ist wichtig für die Funktionsweise einiger der folgenden Organisationsmethoden. Außerdem ist dieser Artikel beschreibend, nicht vorschreibend. Jedes Projekt ist anders. Sie müssen tun, was für Ihr Projekt und Ihr Team am besten funktioniert.
Der Ampersand ist der Hauptgrund, warum ich SUIT, BEM und ähnliche Konventionen verwende. Er ermöglicht es mir, Verschachtelung und Geltungsbereiche zu nutzen, ohne dass beides durch Spezifität zurückschlägt. Hier ist ein Beispiel. Ohne den Ampersand müsste ich separate Selektoren erstellen, um -title und -content Elemente zu erstellen.
.MyComponent {
.MyComponent-title {}
}
.MyComponent-content {}
// Compiles to
.MyComponent .MyComponent-title {} // Not what we want. Unnecessary specificity!
.MyComponent-content {} // Desired result
Bei der Verwendung von SUIT möchte ich das zweite Ergebnis für -content so haben, wie ich alle meine Selektoren schreibe. Dazu müsste ich den Namen der Komponente immer wieder wiederholen. Das erhöht die Wahrscheinlichkeit, dass ich den Namen der Komponente beim Schreiben neuer Stile falsch tippe. Es ist auch sehr unübersichtlich, da der Anfang vieler Selektoren ignoriert wird, was dazu führen kann, dass offensichtliche Fehler übersehen werden.
.MyComponent {}
.MyComponent-title {}
.MyComponent-content {}
.MyComponent-author {}
// Etc.
Wenn dies normales CSS wäre, wären wir gezwungen, das oben Genannte zu schreiben. Da wir Sass verwenden, gibt es einen viel besseren Ansatz, indem wir den Ampersand verwenden. Der Ampersand ist erstaunlich, weil er eine Referenz auf den aktuellen Selektor zusammen mit allen Elternelementen enthält.
.A {
// & = '.A'
.B {
// & = '.A .B'
.C {
// & = '.A .B .C'
}
}
}
Sie können im obigen Beispiel sehen, wie der Ampersand jeden Selektor in der Kette referenziert, während er tiefer in den verschachtelten Code geht. Durch die Nutzung dieser Funktion können wir neue Selektoren erstellen, ohne jedes Mal den Namen der Komponente neu schreiben zu müssen.
.MyComponent {
&-title {}
&-content {}
}
// Compiles to
.MyComponent {}
.MyComponent-title {}
.MyComponent-content {}
Das ist großartig, weil wir den Ampersand nutzen können, um den Namen der Komponente einmal zu schreiben und den Komponentennamen einfach überall zu referenzieren. Dies verringert die Wahrscheinlichkeit, dass der Komponentennamen falsch geschrieben wird. Außerdem wird das Dokument als Ganzes leichter lesbar, ohne dass .MyComponent überall im Code wiederholt wird.
Es gibt Zeiten, in denen die Komponente ein Variante oder Modifikator benötigt, wie sie in SUIT und BEM genannt werden. Die Verwendung des Ampersand-Musters erleichtert die Erstellung von Modifikatoren.
<div class="MyComponent MyComponent--xmasTheme"></div>
.MyComponent {
&--xmasTheme {}
}
// Compiles to
.MyComponent {}
.MyComponent--xmasTheme {}
„Aber, was ist mit der Modifizierung von Kindelementen?“, könnten Sie fragen. „Wie werden diese Selektoren erstellt? Der Modifikator ist doch nicht auf jedes Element notwendig, oder?“
Hier können Variablen helfen!
Variablen und Geltungsbereiche
Früher habe ich Modifikatoren auf verschiedene Arten erstellt. Meistens habe ich den speziellen Thema-Namen, den ich beim Modifizieren des Elements anwenden möchte, neu geschrieben.
.MyComponent {
&-title {
.MyComponent--xmasTheme & {
}
}
&-content {
.MyComponent--xmasTheme & {
}
}
}
// Compiles to
.MyComponent-title {}
.MyComponent--xmasTheme .MyComponent-title {}
.MyComponent-content {}
.MyComponent--xmasTheme .MyComponent-content {}
Das erledigt die Arbeit, aber ich schreibe den Komponentennamen wieder an mehreren Stellen, ganz zu schweigen vom Modifikatornamen. Es gibt definitiv einen besseren Weg, dies zu tun. Hier kommen Sass-Variablen ins Spiel.
Bevor wir Sass-Variablen mit Selektoren untersuchen, müssen wir verstehen, wie sie zugewiesen werden. Sass-Variablen haben einen Geltungsbereich, genau wie in JavaScript, Ruby oder jeder anderen Programmiersprache. Wenn sie außerhalb eines Selektors deklariert werden, ist die Variable nach ihrer Deklaration für jeden Selektor im Dokument verfügbar.
$fontSize: 1.4rem;
.a { font-size: $fontSize; }
.b { font-size: $fontSize; }
Innerhalb eines Selektors deklarierte Variablen sind nur für diesen Selektor und seine Kindelemente zugewiesen.
$fontSize: 1.4rem;
.MyComponent {
$fontWeight: 600;
font-size: $fontSize;
&-title {
font-weight: $fontWeight; // Works!
}
}
.MyComponent2 {
font-size: $fontSize;
&-title {
font-weight: $fontWeight; // produces an "undefined variable" error
}
}
Wir wissen, dass Variablen Font-Namen, Ganzzahlen, Farben usw. speichern können. Wussten Sie, dass sie auch Selektoren speichern können? Mit String-Interpolation können wir neue Selektoren mit der Variable erstellen.
// Sass string interpolation syntax is #{VARIABLE}
$block: ".MyComponent";
#{$block} {
&-title {
#{$block}--xmasTheme & {
}
}
}
// Compiles to
.MyComponent {}
.MyComponent-title {}
.MyComponent--xmasTheme .MyComponent-title {}
Das ist cool, aber die Variable hat einen globalen Geltungsbereich. Wir können das beheben, indem wir die Variable $block innerhalb der Komponentendeklaration erstellen, wodurch sie auf diese Komponente beschränkt wird. Dann können wir die Variable $block in anderen Komponenten wiederverwenden. Dies hilft, den Thema-Modifikator DRY zu halten.
.MyComponent {
$block: '.MyComponent';
&-title {
#{$block}--xmasTheme & {
}
}
&-content {
#{$block}--xmasTheme & {
}
}
}
// Compiles to
.MyComponent {}
.MyComponent-title {}
.MyComponent--xmasTheme .MyComponent-title {}
.MyComponent-content {}
.MyComponent--xmasTheme .MyComponent-content {}
Das ist näher dran, aber wieder müssen wir den Thema-Namen immer wieder schreiben. Lass uns das auch in einer Variable speichern!
.MyComponent {
$block: '.MyComponent';
$xmasTheme: '.MyComponent--xmasTheme';
&-title {
#{$xmasTheme} & {
}
}
}
Das ist viel besser! Wir können das aber noch weiter verbessern. Variablen können auch den Wert des Ampersands speichern!
.MyComponent {
$block: &;
$xmasTheme: #{&}--xmasTheme;
&-title {
#{$xmasTheme} & {
}
}
}
// Still compiles to
.MyComponent {}
.MyComponent-title {}
.MyComponent--xmasTheme .MyComponent-title {}
Jetzt reden wir aber! Das „Zwischenspeichern“ des Selektors mit dem Ampersand ermöglicht es uns, unsere Modifikatoren oben zu erstellen und die Thema-Modifikationen bei dem Element zu belassen, das sie modifiziert.
„Sicher, das funktioniert auf der obersten Ebene“, sagen Sie. „Aber was ist, wenn man tief verschachtelt ist, wie acht Ebenen tief?“ Sie stellen gute Fragen.

Unabhängig davon, wie tief die Verschachtelung ist, funktioniert dieses Muster immer, da der Hauptkomponentennamen dank der SUIT-Namenskonvention und der Ampersand-Kombination nie an die Kindelemente angehängt wird.
.MyComponent {
$block: &;
$xmasTheme: #{&}--xmasTheme;
&-content {
font-size: 1.5rem;
color: blue;
ul {
li {
strong {
span {
&::before {
background-color: blue;
#{$xmasTheme} & {
background-color: red;
}
}
}
}
}
}
}
}
// Compiles to
.MyComponent-content {
font-size: 1.5rem;
color: blue;
}
.MyComponent-content ul li strong span::before {
background-color: blue;
}
/*
* The theme is still appended to the beginning of the selector!
* Now, we never need to write deeply nested Sass that's hard to maintain and
* extremely brittle: https://css-tricks.de/sass-selector-combining/
*/
.MyComponent--xmasTheme .MyComponent-content ul li strong span::before {
background-color: red;
}
Die Codeorganisation ist der Hauptgrund, warum ich dieses Muster verwende.
- Es ist relativ DRY
- Es unterstützt den „Opt-in“-Ansatz, der Modifikatoren bei den Elementen belässt, die sie modifizieren
- Das Benennen von Dingen ist schwierig, aber dies ermöglicht uns die Wiederverwendung gängiger Elementnamen wie „title“ und „content“
- Es ist einfach, einen Modifikator zu einer Komponente hinzuzufügen, indem die Modifikator-Klasse auf der Elternkomponente platziert wird
„Hhhmmmmm… wird das nicht schwer zu lesen, wenn man viele verschiedene Komponenten erstellt? Woher weiß man, wo man sich befindet, wenn alles &-title und &-content heißt?“
Sie stellen weiterhin tolle Fragen. Wer hat gesagt, dass der Quellcode-Sass in einer Datei sein muss? Wir können diese Komponenten importieren, also kommen wir zu diesem Thema!
Die Bedeutung von Imports

Eine der besten Funktionen von Sass ist @import. Wir können separate Sass-Dateien (Partials) erstellen und sie in andere Sass-Dateien importieren, die zusammen kompiliert werden, wobei die importierte Datei an der Stelle ihrer Importierung platziert wird. Dies erleichtert die Bündelung verwandter Stile für Komponenten, Dienstprogramme usw. und das Einbinden in eine einzige Datei. Ohne @import müssten wir separate CSS-Dateien verlinken (was zu zahlreichen Netzwerkanfragen führt, was schlecht ist) oder alles in einer einzigen Stieltabelle schreiben (was schwer zu navigieren und zu warten ist).
.Component1 {
&-title {}
&-content {}
&-author {}
}
.Component2 {
&-title {}
&-content {}
&-author {}
}
.Component3 {
&-title {}
&-content {}
&-author {}
}
.Component4 {
&-title {}
&-content {}
&-author {}
}
.Component5 {
&-title {}
&-content {}
&-author {}
}
// A couple of hundred lines later...
.Component7384 {
&-title {}
&-content {}
&-author {}
}
// WHERE AM I?
Eine der beliebtesten Methoden zur Organisation von Sass-Dateien ist das 7-1 Muster. Das sind sieben verschiedene Ordner mit Sass-Dateien, die in eine einzige Sass-Datei importiert werden.
Diese Ordner sind
- abstrakte
- Basis
- Komponenten
- Layout
- Seiten
- Themen
- Lieferant
Verwenden Sie @import, um jede Sass-Datei in diesen Ordnern in eine Haupt-Sass-Datei zu ziehen. Wir möchten sie in der folgenden Reihenfolge importieren, um einen guten Geltungsbereich beizubehalten und Konflikte während der Kompilierung zu vermeiden
- abstrakte
- Lieferant
- Basis
- Layout
- Komponenten
- Seiten
- Themen
@import 'abstracts/variables';
@import 'abstracts/functions';
@import 'abstracts/mixins';
@import 'vendors/some-third-party-component';
@import 'base/normalize';
@import 'layout/navigation';
@import 'layout/header';
@import 'layout/footer';
@import 'layout/sidebar';
@import 'layout/forms';
@import 'components/buttons';
@import 'components/hero';
@import 'components/pull-quote';
@import 'pages/home';
@import 'pages/contact';
@import 'themes/default';
@import 'themes/admin';
Sie möchten vielleicht nicht alle diese Ordner verwenden (ich persönlich verwende den Themenordner nicht, da ich Themen bei ihren Komponenten belasse), aber die Idee, alle Stile in verschiedene Dateien zu trennen, erleichtert die Wartung und das Auffinden von Code.
Weitere Vorteile dieser Vorgehensweise
- Kleine Komponenten sind leichter zu lesen und zu verstehen
- Das Debugging wird einfacher
- Es ist klarer zu erkennen, wann eine neue Komponente erstellt werden soll – zum Beispiel, wenn eine einzelne Komponentendatei zu lang wird oder die Selektorkette zu komplex ist
- Dies betont die Wiederverwendung – zum Beispiel kann es sinnvoll sein, drei Komponentendateien, die im Wesentlichen dasselbe tun, zu einer Komponente zu verallgemeinern
Apropos Wiederverwendung, es gibt schließlich Muster, die oft verwendet werden. Dann können wir zu Mixins greifen.
Mixin' es auf
Mixins sind eine großartige Möglichkeit, Stile projektweit wiederzuverwenden. Lassen Sie uns eine einfache Mixin erstellen und ihr dann ein wenig Intelligenz verleihen.
Der Designer, mit dem ich regelmäßig zusammenarbeite, legt immer font-size, font-weight und line-height auf bestimmte Werte fest. Ich fand mich dabei wieder, alle drei jedes Mal zu tippen, wenn ich die Schriftarten für eine Komponente oder ein Element anpassen musste, also habe ich eine Mixin erstellt, um diese Werte schnell festzulegen. Es ist wie eine kleine Funktion, die ich verwenden kann, um diese Eigenschaften zu definieren, ohne sie vollständig ausschreiben zu müssen.
@mixin text($size, $lineHeight, $weight) {
font-size: $size;
line-height: $lineHeight;
font-weight: $weight;
}
An diesem Punkt ist die Mixin ziemlich einfach – sie ähnelt einer Funktion in JavaScript. Da ist der Name der Mixin (text) und sie nimmt drei Argumente entgegen. Jedes Argument ist an eine CSS-Eigenschaft gebunden. Wenn die Mixin aufgerufen wird, kopiert Sass die Eigenschaften und übergibt die Argumentwerte.
.MyComponent {
@include text(18px, 27px, 500);
}
// Compiles to
.MyComponent {
font-size: 18px;
line-height: 27px;
font-weight: 500;
}
Obwohl es eine gute Demonstration ist, ist diese spezielle Mixin etwas begrenzt. Sie geht davon aus, dass wir immer die Eigenschaften font-size, line-height und font-weight verwenden wollen, wenn sie aufgerufen wird. Lassen Sie uns also die if-Anweisung von Sass verwenden, um die Ausgabe zu steuern.
@mixin text($size, $lineHeight, $weight) {
// If the $size argument is not empty, then output the argument
@if $size != null {
font-size: $size;
}
// If the $lineHeight argument is not empty, then output the argument
@if $lineHeight != null {
line-height: $lineHeight;
}
// If the $weight argument is not empty, then output the argument
@if $weight != null {
font-weight: $weight;
}
}
.MyComponent {
@include text(12px, null, 300);
}
// Compiles to
.MyComponent {
font-size: 12px;
font-weight: 300;
}
Das ist besser, aber noch nicht ganz perfekt. Wenn ich versuche, die Mixin ohne die Verwendung von null als Parameter für die Werte zu verwenden, die ich nicht verwenden möchte oder die ich nicht angebe, generiert Sass einen Fehler
.MyComponent {
@include text(12px, null); // left off $weight
}
// Compiles to an error:
// "Mixin text is missing argument $weight."
Um dies zu umgehen, können wir Standardwerte für die Parameter hinzufügen, sodass wir sie beim Funktionsaufruf weglassen können. Alle optionalen Parameter müssen nach allen erforderlichen Parametern deklariert werden.
// We define `null` as the default value for each argument
@mixin text($size: null, $lineHeight: null, $weight: null) {
@if $size != null {
font-size: $size;
}
@if $lineHeight != null {
line-height: $lineHeight;
}
@if $weight != null {
font-weight: $weight;
}
}
.MyComponent {
&-title {
@include text(16px, 19px, 600);
}
&-author {
@include text($weight: 800, $size: 12px);
}
}
// Compiles to
.MyComponent-title {
font-size: 16px;
line-height: 19px;
font-weight: 600;
}
.MyComponent-author {
font-size: 12px;
font-weight: 800;
}
Standard-Argumentwerte machen die Mixin nicht nur einfacher zu verwenden, sondern wir gewinnen auch die Möglichkeit, Parameter zu benennen und ihnen Werte zu geben, die häufig verwendet werden. In Zeile 21 oben wird die Mixin mit den Argumenten in falscher Reihenfolge aufgerufen, aber da die Werte auch aufgerufen werden, weiß die Mixin, wie sie sie anwenden soll.
Es gibt eine spezielle Mixin, die ich täglich benutze: min-width. Ich bevorzuge es, alle meine Websites mobile first zu erstellen, also im Grunde mit dem kleinsten Viewport im Auge. Wenn der Viewport breiter wird, definiere ich Breakpoints, um das Layout und den Code dafür anzupassen. Hier greife ich zur min-width Mixin.
// Let's name this "min-width" and take a single argument we can
// use to define the viewport width in a media query.
@mixin min-width($threshold) {
// We're calling another function (scut-rem) to convert pixels to rem units.
// We'll cover that in the next section.
@media screen and (min-width: scut-rem($threshold)) {
@content;
}
}
.MyComponent {
display: block;
// Call the min-width mixin and pass 768 as the argument.
// min-width passes 768 and scut-rem converts the unit.
@include min-width(768) {
display: flex;
}
}
// Compiles to
.MyComponent {
display: block;
}
@media screen and (min-width: 48rem) {
.MyComponent {
display: flex;
}
}
Hier gibt es ein paar neue Ideen. Die Mixin hat eine verschachtelte Funktion namens @content. Im .MyComponent-Klassens werden wir also nicht mehr die Mixin allein aufrufen, sondern auch einen Codeblock, der innerhalb der generierten Media-Abfrage ausgegeben wird. Der resultierende Code wird dort kompiliert, wo @content aufgerufen wird. Dies ermöglicht es der Mixin, die @media-Deklaration zu übernehmen und trotzdem benutzerdefinierten Code für diesen speziellen Breakpoint zu akzeptieren.
Ich binde die Mixin auch innerhalb der .MyComponent-Deklaration ein. Manche Leute befürworten, alle responsiven Aufrufe in einer separaten Stieltabelle zu belassen, um die Anzahl der @media-Aufrufe in einer Stieltabelle zu reduzieren. Persönlich bevorzuge ich es, alle Variationen und Änderungen, die eine Komponente durchlaufen kann, bei der Deklaration dieser Komponente zu belassen. Es erleichtert die Verfolgung dessen, was passiert, und hilft bei der Fehlersuche in der Komponente, wenn etwas schiefgeht, anstatt mehrere Dateien zu durchsuchen.
Haben Sie die scut-rem-Funktion darin bemerkt? Das ist eine Sass-Funktion aus einer Sass-Bibliothek namens Scut, erstellt von David The Clark. Sehen wir uns an, wie das funktioniert.
Funktional werden
Eine Funktion unterscheidet sich von einer Mixin dadurch, dass Mixins dazu gedacht sind, gängige Eigenschaftsgruppen auszugeben, während eine Funktion Eigenschaften basierend auf Argumenten modifiziert, die ein neues Ergebnis zurückgeben. In diesem Fall nimmt scut-rem einen Pixelwert und wandelt ihn in einen Rem-Wert um. Dies ermöglicht es uns, in Pixeln zu denken, während wir hinter den Kulissen mit Rem-Einheiten arbeiten, um all diese Berechnungen zu vermeiden.
Ich habe scut-rem in diesem Beispiel vereinfacht, da es einige zusätzliche Funktionen enthält, die Schleifen und Listen verwenden, die außerhalb des Rahmens dessen liegen, was wir hier behandeln. Sehen wir uns die Funktion in ihrer Gesamtheit an und zerlegen Sie sie dann Schritt für Schritt.
// Simplified from the original source
$scut-rem-base: 16 !default;
@function scut-strip-unit ($num) {
@return $num / ($num * 0 + 1);
}
@function scut-rem ($pixels) {
@return scut-strip-unit($pixels) / $scut-rem-base * 1rem;
}
.MyComponent {
font-size: scut-rem(18px);
}
// Compiles to
.MyComponent {
font-size: 1.125rem;
}
Das erste, was zu beachten ist, ist die Deklaration in Zeile 2. Sie verwendet !default bei der Deklaration einer Variable, was Sass anweist, den Wert auf 16 zu setzen, es sei denn, diese Variable ist bereits definiert. Wenn also eine Variable früher in der Stieltabelle mit einem anderen Wert deklariert wurde, wird sie hier nicht überschrieben.
$fontSize: 16px;
$fontSize: 12px !default;
.MyComponent {
font-size: $fontSize;
}
// Compiles to
.MyComponent {
font-size: 16px;
}
Das nächste Puzzleteil ist scut-strip-unit. Diese Funktion nimmt einen px-, rem-, Prozent- oder anderen suffigierten Wert und entfernt die Einheit. Der Aufruf von scut-strip-unit(12px) gibt 12 anstelle von 12px zurück. Wie funktioniert das? In Sass wird eine Einheit geteilt durch eine Einheit desselben Typs die Einheit entfernen und die Ziffer zurückgeben.
12px / 1px = 12
Jetzt, wo wir das wissen, sehen wir uns die scut-strip-unit-Funktion noch einmal an.
@function scut-strip-unit ($num) {
@return $num / ($num * 0 + 1);
}
Die Funktion nimmt eine Einheit entgegen und teilt sie durch 1 derselben Einheit. Wenn wir also 12px übergeben, würde die Funktion wie folgt aussehen: @return 12px / (12px * 0 + 1). Gemäß der Reihenfolge der Operationen wertet Sass zuerst das aus, was in den Klammern steht. Sass ignoriert geschickt das px-Label, wertet den Ausdruck aus und hängt px wieder an, sobald es fertig ist: 12 * 0 + 1 = 1px. Die Gleichung lautet nun 12px / 1px, was bekanntermaßen 12 ergibt.
Warum ist das für scut-rem wichtig? Sehen wir es uns noch einmal an.
$scut-rem-base: 16 !default;
@function scut-rem ($pixels) {
@return scut-strip-unit($pixels) / $scut-rem-base * 1rem;
}
.MyComponent {
font-size: scut-rem(18px);
}
In Zeile 4 entfernt die Funktion scut-strip-unit px vom Argument und gibt 18 zurück. Die Basisvariable ist gleich 16, was die Gleichung in 18 / 16 * 1rem umwandelt. Denken Sie daran, Sass ignoriert jede Einheit bis zum Ende der Gleichung, also 18 / 16 = 1,125. Dieses Ergebnis multipliziert mit 1rem ergibt 1,125rem. Da Scut die Einheit vom Argument entfernt, können wir scut-rem mit einheitenlosen Werten aufrufen, wie z. B. scut-rem(18).
Ich schreibe nicht viele Funktionen, da ich versuche, die von mir erstellten Dinge so einfach wie möglich zu halten. Die Fähigkeit, einige komplexe Konvertierungen mit etwas wie scut-rem durchzuführen, ist jedoch hilfreich.
Die Selektorreihenfolge, die Platzhalter durcheinanderbringen

Seien Sie vorsichtig, was Sie erweitern
Ich habe versucht, einige Beispiele zu schreiben, um zu demonstrieren, warum die Verwendung von @extend problematisch sein kann, aber ich habe sie so wenig verwendet, dass ich keine vernünftigen Beispiele erstellen kann. Als ich Sass zum ersten Mal lernte, war ich von Teamkollegen umgeben, die bereits die Prüfungen und Schwierigkeiten durchgemacht hatten. Mein Freund Jon Bebee hat einen äußerst ausgezeichneten Artikel darüber geschrieben, wie @extend Sie in Schwierigkeiten bringen kann. Es ist eine schnelle Lektüre und die Zeit wert, also werde ich warten.
Über diese Platzhalter…
Jon schlägt die Verwendung von Platzhaltern als Lösung für das Problem vor, das er darstellt: Platzhalter geben keinen Code aus, bis sie mit @extend verwendet werden.
// % denotes an extended block
%item {
display: block;
width: 50%;
margin: 0 auto;
}
.MyComponent {
@extend %item;
color: blue;
}
// Compiles to
.MyComponent {
display: block;
width: 50%;
margin: 0 auto;
}
.MyComponent {
color: blue;
}

Okay, warte. Es hat also .MyComponent zweimal ausgegeben? Warum hat es die Selektoren nicht einfach kombiniert?
Das sind die Fragen, die ich hatte, als ich anfing, Platzhalter zu verwenden (und sie dann anschließend wieder einstellte). Der Hinweis ist der Name selbst. Platzhalter halten einfach eine Referenz auf die Stelle in der Stieltabelle, an der sie deklariert wurden. Während eine Mixin die Eigenschaften an den Ort kopiert, an dem sie verwendet wird, kopieren Platzhalter den Selektor an den Ort, an dem der Platzhalter definiert wurde. Infolgedessen kopiert es den .MyComponent-Selektor und platziert ihn dort, wo %item deklariert ist. Betrachten Sie das folgende Beispiel
%flexy {
display: flex;
}
.A {
color: blue;
}
.B {
@extend %flexy;
color: green;
}
.C {
@extend %flexy;
color: red;
}
// Compiles to
.B, .C {
display: flex;
}
.A {
color: blue;
}
.B {
color: green;
}
.C {
color: red;
}
Auch wenn B und C weiter unten in der Stieltabelle deklariert sind, platziert der Platzhalter die erweiterten Eigenschaften bis dorthin, wo er ursprünglich deklariert wurde. Das ist in diesem Beispiel keine große Sache, da es sich sehr nah an der Quelle befindet, wo es verwendet wird. Wenn wir uns jedoch an etwas wie das 7-1 Muster halten, das wir zuvor behandelt haben, dann würden Platzhalter in einem Partial im abstrakten Ordner definiert werden, was eine der ersten importierten Dateien ist. Das platziert viel Stil zwischen der Stelle, an der die Erweiterung beabsichtigt ist, und der Stelle, an der sie tatsächlich verwendet wird. Das kann schwer zu warten und auch schwer zu debuggen sein.
Sass Guidelines (natürlich) macht eine gute Arbeit bei der Abdeckung von Platzhaltern und Erweiterung und ich empfehle, sie zu lesen. Sie erklärt nicht nur die Erweiterungsfunktion, sondern plädiert am Ende auch dagegen.
Die Meinungen scheinen extrem gespalten zu sein bezüglich der Vorteile und Probleme von
@extend, bis zu dem Punkt, an dem viele Entwickler, einschließlich mir selbst, dagegen plädiert haben, […]
Es gibt viele weitere Features von Sass, die ich hier nicht behandelt habe, wie Schleifen und Listen, aber ich habe mich ehrlich gesagt nicht so sehr auf diese Features verlassen wie auf die, die wir in diesem Artikel behandelt haben. Werfen Sie einen Blick in die Sass-Dokumentation, wenn auch nur, um zu sehen, was die Dinge tun. Sie finden vielleicht nicht sofort eine Verwendung für alles, aber eine Situation kann sich ergeben und dieses Wissen in der Hinterhand zu haben, ist unbezahlbar.
Lassen Sie mich wissen, wenn ich etwas übersehen oder falsch gemacht habe! Ich bin immer offen für neue Ideen und würde mich freuen, es mit Ihnen zu besprechen!
Weitere Lektüre
- Sass Style Guide – Gedanken und Empfehlungen, um Sass sauber und wartbar zu halten.
- Was eine CSS-Code-Überprüfung beinhalten könnte – Konzentriert sich hauptsächlich auf CSS, enthält aber Tipps zur Überprüfung von Sass-Dateien.
- Technische Schulden definieren und damit umgehen – Wir haben in diesem Artikel viel über DRY-Methoden und Effizienz gesprochen. Dieser Beitrag spricht über die verschiedenen Konsequenzen von ausführlichem oder ineffizientem Code.
- Schleifen in CSS-Präprozessoren – Wir haben hier keine Schleifen behandelt, aber Miriam Suzanne geht in diesem Artikel tief darauf ein.
- Sass-Snippets – Eine Sammlung nützlicher Sass-Mixins und anderer Sass-Muster.
Diese Beiträge sind immer hilfreich, um einige vergessene Grundlagen wieder zu lernen und sich an Best Practices zu erinnern. Es gibt so viel zu lernen, dass man die einfachen Dinge aus den Augen verlieren kann. Danke für die Zusammenfassung, ich werde definitiv meine Ampersand-Nutzung und die Variablengeltungsbereiche wieder besuchen.
Danke! Es ist immer hilfreich, die Grundlagen zu wiederholen. Der Ampersand ist eines meiner Lieblingstools.
Hier ist mein Lieblings-SASS-Trick
Dies ermöglicht es Ihnen, dieselbe Datei mehrmals zu importieren, erhalten aber nur eine Kopie der Stile im endgültigen Build; à la Webpack. Dies hat die Vorteile, dass
– Sie offensichtlich eine kleinere CSS-Dateigröße erhalten, aber auch eine verbesserte Leistung Ihrer Stile (keine redundanten Regeln) und eine viel lesbarere Inspector-Inhalt.
– Sie können die Datei frei @importieren, wo immer sie von anderen Stilen benötigt wird, was für die Selbstdokumentation und auch für die Stabilität enorm vorteilhaft ist.
Das ist ziemlich cool! Verwendest du das 7-1 Muster? Wie sieht deine Haupt-Stieltabelle aus?
Das tun wir nicht; unsere SCSS-Architektur spiegelt unsere React-Komponentenstruktur wider – eine SCSS-Datei pro .react.js-Datei. Wir haben zwar eine Datei mit einer Handvoll globaler Stile oben, aber fast alles lebt in diesen Komponenten. Dies macht es nicht nur einfach, die für das Markup relevanten Stile zu finden, sondern vereinfacht auch die Imports. Wenn Ihre JS-Datei eine weitere Komponente importiert, muss die entsprechende SCSS-Datei die SCSS-Datei dieser Komponente ebenfalls importieren. Der Einstiegspunkt importiert einfach die „App“-Level-Komponente und alles andere kümmert sich um seine eigenen Abhängigkeiten.
nett! React ist etwas, das ich gerade durch Wes Bos‘ Kurs lerne.
ICH LIEBE ES! Großartiger Ansatz und Umsetzung einiger großartiger Sass-Techniken.
Ich muss der vorgeschlagenen Verwendung von Ampersands in SCSS teilweise entschieden widersprechen. Ich persönlich glaube, dass die Verwendung des Ampersands in partiellen Klassennamen den Code unmöglich wartbar macht – es sei denn, Sie sind die Person, die ihn geschrieben hat – und das erst kürzlich.
Unter Verwendung eines Beispiel-Snippets aus Ihrem Beitrag (da es die Verwendung von Ampersands auf zwei verschiedene Arten zeigt)
Wenn ich die Klasse
.MyComponent--titlesehe, die einem Element im DOM zugewiesen ist, würde ich erwarten, dass ich diese kopieren und dann die Codebasis durchsuchen kann, um die genaue Definition(en) für diese Klasse zu finden. Die Verwendung des Ampersands bricht diese Fähigkeit, was bedeutet, dass Entwickler mit der Codebasis bereits vertraut sein müssen, um sie zu warten/erweitern (Sourcemaps können definitiv helfen, aber nehmen wir an, wir haben sie nicht). Wenn ich eine solche Ampersand-Verwendung in einem CR sehe, bitte ich den Entwickler immer, sie zu ändern. Ein paar zusätzliche Zeichen, die jetzt eingegeben werden, sparen ihm, oder wichtiger, einem zukünftigen Entwickler, viel Zeit, wenn er versucht, die Quelle zum Bearbeiten zu finden. Suchen und Ersetzen existiert in jeder IDE/jedem Texteditor, daher ist es nicht viel schwieriger, dies in Zukunft zu refaktorieren, fallsMyComponentzuMyNewComponentwird.Die zweite Verwendung des Ampersands in diesem Snippet,
.MyComponent--xmasTheme &, ist jedoch vollkommen akzeptabel – wenn auch nichts, was ich persönlich immer verwenden möchte. Der Grund, warum ich damit einverstanden bin, ist, dass es höchst unwahrscheinlich ist, dass Sie jemals eine Suche in der Codebasis nach dem resultierenden kompilierten CSS,.MyComponent .MyComponent--xmasTheme, durchführen werden. Ich hoffe, die meisten Leute werden einfach einen Klassennamen nehmen und die Codebasis danach durchsuchen.Würde gerne Ihre Gedanken hören..
Es gibt definitiv starke Meinungen auf beiden Seiten, insbesondere im Hinblick auf die Wartbarkeit: https://css-tricks.de/sass-selector-combining/
Es gibt definitiv Überlegungen auf beiden Seiten. Für mein Team und mich hatten wir damit nie Probleme. Wenn wir eine Architektur wie diese wählen, stellen wir sicher, dass jede Komponente, die erstellt wird, ihre eigene Datei hat. Anstatt nach dem Klassennamen zu greppen, rufen wir einfach die Datei für die jeweilige Komponente auf. Wir verlangen vom Team, dass es sorgfältig bei der Erstellung von 1 Komponente -> 1 Datei ist, und hatten wirklich keine Probleme.
Da wir auch bestrebt sind, unsere Dateien einfacher und insgesamt mit weniger Code zu halten, können wir eine Datei öffnen und schnell scannen, um zu finden, was wir suchen.
Auch hier gibt es Vor- und Nachteile für beide Ansätze, und Sie müssen nur das auswählen, was für Sie und Ihr Team am besten funktioniert.
Das ist kein Problem, wenn Sie den ITCSS*-Weg zur Organisation von Projektdateien verwenden und die von Harry Roberts (CSS Wizardry) festgelegten CSS-Namensraumkonventionen befolgen, sowie seine Vorschläge zu BEM. Wenn Sie „c-MyComponent–title“ im DOM sehen, gehen Sie zu Ihrem CSS-Ordner „components“-Ordner, d.h. „sass>components/_MyComponent.scss“; es ist einfach. Sie sollten niemals die Notwendigkeit haben, das Projekt nach diesem Text zu durchsuchen, da jeder Verweis darauf nur in dieser einen Datei vorhanden sein sollte.
Hier steht „c-“ für „component“, ITCSS hat eine Komponentenschicht, die einem „component“-Ordner entspricht (die ursprüngliche ITCSS-Empfehlung ist, Dateinamen mit der Schicht voranzustellen, wie component-myComponent.scss, und alle Partials in einer Datei zu haben, aber ich habe in meiner Firma ihnen gesagt, sie sollen die ITCSS-Schichten in Ordner stecken).
Unsere Konvention ist, die Datei genauso zu benennen wie die „Block“-Klasse in BEM. Wie gesagt, es ist so einfach, das zu finden, was man braucht, man braucht nicht einmal eine Source Map, um seine Sachen zu finden. Die kombinierten Prinzipien von ITCSS, BEM und Namespacing beheben weitgehend die Probleme, über die Sie meiner Meinung nach sprechen, und Kaskadenprobleme. Sie wissen genau, wo Ihr CSS gespeichert ist, indem Sie einfach den DOM lesen. Schauen Sie es sich an, es wird Ihr Leben verändern!
*Funktioniert nur bei einem Monolithen, versagt in einer verteilten, komponenten-basierten Umgebung, in der Stile von überall hergezogen werden könnten. Hier ist CSS-in-JS gut.
Eine Warnung vor dem Schreiben von Dingen wie „&-title“: Es macht den Code so schwer zu warten, weil man nicht einfach nach Dingen wie „component1-title“ suchen kann.
Wenn Sie eine Source Map verwenden, existiert das Problem nicht mehr, oder?
SASS ist wirklich großartig. Aber was ich an der gesamten Precompiler-Welt hasse, sind die Build-Tools. Webpack, Gulp, blabla sind so verdammt kompliziert einzurichten. Es ist so schwer geworden.
Als Designer wünsche ich mir eine GUI, um all diese Build-Tools einzurichten. Und jeder Entwickler sagt: „Was, das ist einfach, lies einfach die Doku“. Aber dafür müsste ich zuerst Ägyptologie studieren, um all diesen Kram zu verstehen. Und das Debugging vergessen. Wo ist es fehlgeschlagen? In Webpack? In Gulp? In NPM? Niemand weiß es. Und alles, was ich tun wollte, war Sass zu schreiben und es zu minifizieren.
Wenn ich mir etwas auf dieser Seite wünschen würde, wäre es ein „Build-Tools-Leitfaden für Idioten“.
Eines der großartigen Dinge an SASS ist, dass Sie das Ganze – Importe, Transpilierung, sogar Minifizierung – mit dem Standard-Kommandozeilentool und ohne Konfigurationsdatei erledigen können. Kein Gulp, Webpack oder Ähnliches erforderlich. Sie haben jedoch definitiv Recht, dass der Web-Build-Tool-Bereich zu komplex und fragil für sein eigenes Wohl ist.
Aber es gibt ein solches Werkzeug :) … schauen Sie sich CodeKit an!
Ich habe zuvor Ampersands verwendet, um BAMS-ähnliche Selektoren zu verschachteln, aber ich habe mich davon abgewendet. Ich habe festgestellt, dass es für andere Entwickler schwierig ist, Selektoren zu finden, nach denen sie suchen, wenn sie diese im DOM finden. Zum Beispiel suchen sie nach
.Component1-title, können es aber nicht finden, weil es in der SCSS-Datei aufgeteilt ist.Vielleicht ist das nur ein Problem für größere Teams, die alle an demselben Framework arbeiten? Ich bin gespannt, ob andere das schon erlebt haben?
In meiner Entwicklungsumgebung arbeiten etwa 160 Entwickler, davon ca. 40 Frontend-Entwickler, wenn ich mich richtig erinnere. Wir sind in Teams von 2 bis 5 aufgeteilt, die verschiedene Geschäftsbereiche abdecken. Die meisten unserer Frontend-Entwickler coden auf diese Weise und wir hatten keine Probleme, neue Entwickler in den Prozess einzubinden. Einer der Schlüsselfaktoren ist, einen Stil zu wählen und ihn im jeweiligen Projekt beizubehalten. Wenn Sie Ampersand-Verschachtelung wie oben beschrieben verwenden möchten, sollte dies im gesamten Projekt geschehen. Wenn Sie die Komponentennamen ausschreiben möchten, dann sollten sie immer ausgeschrieben werden.
Toller Artikel. Danke :)
Toller Beitrag. Ich verwende die
&__und&--Tricks oft für BEM und ich mag, wie organisiert die Dinge aussehen.Ich habe einen wirklich guten Anwendungsfall für Platzhalter und Extend gefunden. Ich bin kürzlich von der Softwareentwicklung zum Agentur-Webdesign/-entwicklung gewechselt und hier integrieren wir viele Drittanbietersysteme (Ticketing), bei denen wir keine Kontrolle über das HTML haben, aber trotzdem das Design an das Hauptdesign anpassen müssen. Und wir haben möglicherweise zwei oder drei verschiedene 3PIs, die im selben Projekt bearbeitet werden müssen, und das bei sehr schnellen Durchlaufzeiten. Also schreibe ich die wichtigsten Marken-Elemente als Modelle während der Hauptentwicklung und nutze dann
@extendin den 3PI-CSS-Dateien/en, wie hier:usw. Dann ist in unserem Hauptcode nur noch:
Das bedeutet, ich stelle sicher, dass ich diese Stile unabhängig von unserem spezifischen HTML schreibe und ich kann sie ganz einfach auf jedes benötigte Element anwenden und muss keine vielen Übersetzungen für das verrückte HTML durchführen, mit dem ich es zu tun habe. Und wenn sich Dinge am Hauptteil ändern, der den Großteil der Designzeit bekommt, werden alle 3PI-Dateien automatisch aktualisiert, so dass es keine Abweichungen gibt. Wenn es sich zum Beispiel um eine Version von Bootstrap handelt, was oft der Fall ist. Dann ist es nur noch:
(
#{$s}ist mein Spezifitäts-Booster, den ich in meinem Themer eingestellt habe, da man nie genau weiß, wo CSS erscheinen wird, oder womit es kämpft.)Und wenn Sie kein Framework oder Komponentensystem oder irgendetwas anderes haben, und es nur das undokumentierte Frontend von jemand anderem ist, voll von zufälligen Ansammlungen von Jahrzehnten, das sie nicht aktualisieren können (wegen all der Integrationen!), können Sie etwas Ordnung schaffen, indem Sie Modelle definieren, wie dies ein Call-to-Action ist, dies eine sekundäre oder Seiten-Navigation ist usw. Dann können Sie Seite für Seite gehen und das zufällige HTML grob in diese Gruppen mit
extendsortieren und schnell (eine Art von) konsistentem Look erzeugen, ohne Mega-Bloat (wenn kein Zugriff auf Gzip besteht, zum Beispiel).Ich denke also, es gibt wirklich gute Anwendungen für
extendhier in den Schützengräben, obwohl ich sie in der Softwarewelt nie verwendet habe und zustimme, dass es bessere Wege gibt, wenn man das Ganze aufbaut.„Das ist großartig, weil wir den Ampersand nutzen können, um den Namen der Komponente einmal zu schreiben und einfach auf den Komponentennamen zu verweisen.“
Aber die Suche in der IDE wird dadurch einfach zerstört. Wie kann ich nach „.MyComponent-title“ im gesamten Projekt suchen mit Code wie diesem?
.MyComponent {
&-title {}
}
Bitte Leute, macht das nicht in SCSS. Ich arbeite oft an Codebasen anderer Leute, bei denen diese Technik angewendet wird, und bin die meiste Zeit völlig verloren. Autovervollständigung und Sprung zur Quelle in meiner IDE sind kaputt.
Nur weil man clevere Dinge in Sass tun kann, heißt das nicht, dass man es tun sollte. Bleiben Sie nah an CSS – was der Browser (und die Menschen) verstehen. Sehen und lernen Sie die Kaskade. Verwenden Sie Sass-Variablen für Konsistenz. Kürzerer Quellcode ist kaum ein Argument, und das spart Ihnen auch keine Zeit – es kostet den nächsten Entwickler mehr Zeit.
Tolle Argumente von Harry Roberts hier: https://csswizardry.com/2017/02/code-smells-in-css-revisited/: „Ich schaue viel eher nach einer Klasse als danach, sie umzubenennen.“
@Joris äh… Der Artikel, den Sie verlinkt haben, befürwortet die Verwendung einer BEM-ähnlichen Syntax. Was ich in diesem Artikel befürworte.
EDIT
Oder plädieren Sie gegen den Ampersand-Selektor?
Oder plädieren Sie gegen den Ampersand-Selektor?
Ja, der Ampersand-Selektor.
wusste nicht, dass es so viel Einsatzmöglichkeiten gibt, ich verwende es normalerweise nur für :hover und ereignisbezogene Dinge, um meinen Code sauber zu halten. Aber Ihr Artikel ist ein Augenöffner, so viele Möglichkeiten. Danke fürs Teilen