Kürzlich wurden viele neue coole Funktionen zu CSS hinzugefügt, wie z. B. Benutzerdefinierte Eigenschaften und neue Funktionen. Während diese Dinge unser Leben erheblich erleichtern können, können sie auch auf lustige Weise mit Präprozessoren wie Sass interagieren.
Dies wird also ein Beitrag über die Probleme sein, auf die ich gestoßen bin, wie ich sie umgehe und warum ich Sass auch heute noch für notwendig halte.
Die Fehler
Wenn Sie mit den neuen Funktionen min() und max() experimentiert haben, sind Sie möglicherweise auf eine Fehlermeldung wie diese gestoßen, wenn Sie mit verschiedenen Einheiten gearbeitet haben: „Incompatible units: vh and em.“

min()/ max()Das liegt daran, dass Sass seine eigene min()-Funktion hat und die CSS-Funktion min() ignoriert. Außerdem kann Sass keine Berechnungen mit zwei Werten durchführen, die Einheiten haben, zwischen denen keine feste Beziehung besteht.
Beispielsweise haben die Einheiten cm und in eine feste Beziehung zueinander, sodass Sass das Ergebnis von min(20in, 50cm) ermitteln kann und keinen Fehler auslöst, wenn wir versuchen, es in unserem Code zu verwenden.
Dasselbe gilt für andere Einheiten. Winkelmaße beispielsweise haben alle eine feste Beziehung zueinander: 1turn, 1rad oder 1grad ergeben immer die gleichen deg-Werte. Dasselbe gilt für 1s, was immer 1000ms ist, 1kHz, was immer 1000Hz ist, 1dppx, was immer 96dpi ist, und 1in, was immer 96px ist. Deshalb kann Sass zwischen ihnen konvertieren und sie in Berechnungen und innerhalb von Funktionen wie seiner eigenen min()-Funktion mischen.
Aber die Dinge brechen, wenn diese Einheiten keine feste Beziehung zueinander haben (wie im früheren Fall mit den Einheiten em und vh).
Und es sind nicht nur unterschiedliche Einheiten. Der Versuch, calc() in min() zu verwenden, führt ebenfalls zu einem Fehler. Wenn ich etwas wie calc(20em + 7px) versuche, lautet der Fehler: „calc(20em + 7px) ist keine Zahl für min.“

calc(), verschachtelt in der Funktion min()Ein weiteres Problem entsteht, wenn wir eine CSS-Variable oder das Ergebnis einer mathematischen CSS-Funktion (wie calc(), min() oder max()) in einem CSS-Filter wie invert() verwenden möchten.
In diesem Fall erhalten wir die Meldung „$color: 'var(--p, 0.85) ist keine Farbe für invert.“

var() in filter: invert() FehlerDasselbe passiert für grayscale(): „$color: ‚calc(.2 + var(--d, .3))‚ ist keine Farbe für grayscale.“

calc() in filter: grayscale() Fehleropacity() verursacht dasselbe Problem: „$color: ‚var(--p, 0.8)‚ ist keine Farbe für opacity.“

var() in filter: opacity() FehlerAndere filter-Funktionen – darunter sepia(), blur(), drop-shadow(), brightness(), contrast() und hue-rotate() – funktionieren jedoch problemlos mit CSS-Variablen!
Es stellt sich heraus, dass das, was passiert, dem Problem mit min() und max() ähnelt. Sass hat keine integrierten Funktionen für sepia(), blur(), drop-shadow(), brightness(), contrast(), hue-rotate(), aber es hat seine eigenen grayscale()-, invert()- und opacity()-Funktionen, und ihr erster Parameter ist ein $color-Wert. Da sie dieses Argument nicht findet, löst sie einen Fehler aus.
Aus demselben Grund geraten wir auch in Schwierigkeiten, wenn wir eine CSS-Variable verwenden möchten, die mindestens zwei hsl()- oder hsla()-Werte auflistet.

var() in color: hsl() Fehler.Auf der anderen Seite ist color: hsl(9, var(--sl, 95%, 65%)) gültiges CSS und funktioniert ohne Sass einwandfrei.
Genau dasselbe passiert mit den Funktionen rgb() und rgba().

var() in color: rgba() Fehler.Darüber hinaus, wenn wir Compass importieren und eine CSS-Variable in einem linear-gradient() oder einem radial-gradient() verwenden wollen, erhalten wir einen weiteren Fehler, obwohl die Verwendung von Variablen in conic-gradient() einwandfrei funktioniert (vorausgesetzt, der Browser unterstützt es).

var() in background: linear-gradient() Fehler.Das liegt daran, dass Compass über linear-gradient() und radial-gradient() Funktionen verfügt, aber nie eine conic-gradient() Funktion hinzugefügt hat.
Die Probleme in all diesen Fällen ergeben sich daraus, dass Sass oder Compass gleichnamige Funktionen haben und davon ausgehen, dass dies die Funktionen sind, die wir in unserem Code verwenden wollten.
Verflixt!
Die Lösung
Der Trick dabei ist, sich daran zu erinnern, dass Sass Groß- und Kleinschreibung beachtet, CSS jedoch nicht.
Das bedeutet, wir können Min(20em, 50vh) schreiben und Sass erkennt es nicht als seine eigene min()-Funktion. Es werden keine Fehler ausgelöst und es ist immer noch gültiges CSS, das wie beabsichtigt funktioniert. Ähnlich ermöglicht das Schreiben von HSL()/ HSLA()/ RGB()/ RGBA() oder Invert(), die zuvor erwähnten Probleme zu vermeiden.
Was die Verläufe betrifft, bevorzuge ich normalerweise linear-Gradient() und radial-Gradient(), einfach weil es näher an der SVG-Version liegt, aber die Verwendung mindestens eines Großbuchstabens darin funktioniert einwandfrei.
Aber warum?
Fast jedes Mal, wenn ich etwas Sass-bezogenes twitte, werde ich belehrt, dass es jetzt nicht mehr verwendet werden sollte, da wir CSS-Variablen haben. Ich dachte, ich greife das auf und erkläre, warum ich dem widerspreche.
Erstens, obwohl ich CSS-Variablen immens nützlich finde und sie in den letzten drei Jahren für fast alles verwendet habe, sollte man bedenken, dass sie mit Leistungskosten verbunden sind und dass die Fehlersuche, wo etwas in einem Labyrinth von calc()-Berechnungen schiefgelaufen ist, mit unseren aktuellen DevTools mühsam sein kann. Ich versuche, sie nicht zu überbeanspruchen, um nicht in ein Territorium zu geraten, in dem die Nachteile ihrer Verwendung die Vorteile überwiegen.

calc()-Ausdrücke ist.Im Allgemeinen, wenn es sich wie eine Konstante verhält, sich von Element zu Element oder von Zustand zu Zustand nicht ändert (in welchem Fall benutzerdefinierte Eigenschaften definitiv der richtige Weg sind) oder die Menge an kompiliertem CSS reduziert (löst das Wiederholungsproblem, das durch Präfixe entsteht), dann werde ich eine Sass-Variable verwenden.
Zweitens waren Variablen schon immer ein ziemlich kleiner Teil des Grundes, warum ich Sass verwende. Als ich Ende 2012 anfing, Sass zu verwenden, war es hauptsächlich zum Schleifen gedacht, eine Funktion, die wir in CSS immer noch nicht haben. Obwohl ich einige dieser Schleifen in einen HTML-Präprozessor verschoben habe (weil es den generierten Code reduziert und vermeidet, sowohl das HTML als auch das CSS später ändern zu müssen), verwende ich immer noch Sass-Schleifen in vielen Fällen, z. B. beim Generieren von Wertelisten, Stopplisten innerhalb von Gradientenfunktionen, Listen von Punkten innerhalb einer Polygonfunktion, Listen von Transformationen usw.
Hier ist ein Beispiel. Ich habe früher n HTML-Elemente mit einem Präprozessor generiert. Die Wahl des Präprozessors spielt weniger eine Rolle, aber ich werde hier Pug verwenden.
- let n = 12;
while n--
.item
Dann würde ich die Variable $n in Sass festlegen (und sie müsste mit der in HTML übereinstimmen) und bis dahin schleifen, um die Transformationen zu generieren, die jedes Element positionieren würden
$n: 12;
$ba: 360deg/$n;
$d: 2em;
.item {
position: absolute;
top: 50%; left: 50%;
margin: -.5*$d;
width: $d; height: $d;
/* prettifying styles */
@for $i from 0 to $n {
&:nth-child(#{$i + 1}) {
transform: rotate($i*$ba) translate(2*$d) rotate(-$i*$ba);
&::before { content: '#{$i}' }
}
}
}
Das bedeutete jedoch, dass ich sowohl Pug als auch Sass ändern müsste, wenn sich die Anzahl der Elemente ändert, was den generierten Code sehr repetitiv machte.

Ich habe inzwischen dazu übergegangen, Pug die Indizes als benutzerdefinierte Eigenschaften generieren zu lassen und diese dann in der transform-Deklaration zu verwenden.
- let n = 12;
body(style=`--n: ${n}`)
- for(let i = 0; i < n; i++)
.item(style=`--i: ${i}`)
$d: 2em;
.item {
position: absolute;
top: 50%;
left: 50%;
margin: -.5*$d;
width: $d;
height: $d;
/* prettifying styles */
--az: calc(var(--i)*1turn/var(--n));
transform: rotate(var(--az)) translate(2*$d) rotate(calc(-1*var(--az)));
counter-reset: i var(--i);
&::before { content: counter(i) }
}
Dies reduziert den generierten Code erheblich.

Das Schleifen in Sass ist jedoch immer noch notwendig, wenn ich etwas wie einen Regenbogen generieren möchte.
@function get-rainbow($n: 12, $sat: 90%, $lum: 65%) {
$unit: 360/$n;
$s-list: ();
@for $i from 0 through $n {
$s-list: $s-list, hsl($i*$unit, $sat, $lum)
}
@return $s-list
}
html { background: linear-gradient(90deg, get-rainbow()) }
Sicher, ich könnte es als Listenvariable von Pug generieren, aber das würde die dynamische Natur von CSS-Variablen nicht nutzen und den an den Browser gelieferten Code nicht reduzieren, sodass kein Vorteil daraus entsteht.
Ein weiterer wichtiger Teil meiner Sass- (und Compass-) Nutzung hängt mit integrierten mathematischen Funktionen (wie trigonometrischen Funktionen) zusammen, die jetzt Teil der CSS-Spezifikation sind, aber noch in keinem Browser implementiert sind. Sass bietet diese Funktionen ebenfalls nicht, aber Compass tut dies, und deshalb muss ich oft Compass verwenden.
Und klar, ich könnte meine eigenen solchen Funktionen in Sass schreiben. Das habe ich am Anfang gemacht, bevor Compass inverse trigonometrische Funktionen unterstützte. Ich brauchte sie wirklich, also schrieb ich meine eigenen basierend auf der Taylor-Reihe. Aber Compass bietet diese Art von Funktionen heutzutage an und sie sind besser und leistungsfähiger als meine.
Mathematische Funktionen sind für mich äußerst wichtig, da ich ein Techniker und kein Künstler bin. Die Werte in meinem CSS ergeben sich normalerweise aus mathematischen Berechnungen. Es sind keine magischen Zahlen oder etwas, das rein für ästhetische Zwecke verwendet wird. Ein Beispiel ist die Generierung von Listen von Clip-Pfadpunkten, die reguläre oder quasi-reguläre Polygone erstellen. Denken Sie an den Fall, in dem wir Dinge wie nicht-rechteckige Avatare oder Aufkleber erstellen möchten.
Betrachten wir ein regelmäßiges Polygon mit Scheitelpunkten auf einem Kreis mit einem Radius von 50% des quadratischen Elements, von dem wir ausgehen. Das Verschieben des Schiebereglers in der folgenden Demo ermöglicht es uns, zu sehen, wo die Punkte für verschiedene Anzahlen von Scheitelpunkten platziert sind
Wenn wir das in Sass-Code umwandeln, haben wir
@mixin reg-poly($n: 3) {
$ba: 360deg/$n; // base angle
$p: (); // point coords list, initially empty
@for $i from 0 to $n {
$ca: $i*$ba; // current angle
$x: 50%*(1 + cos($ca)); // x coord of current point
$y: 50%*(1 + sin($ca)); // y coord of current point
$p: $p, $x $y // add current point coords to point coords list
}
clip-path: polygon($p) // set clip-path to list of points
}
Beachten Sie, dass wir hier auch Schleifen und Dinge wie Bedingungen und Modulo verwenden, die bei der Verwendung von CSS ohne Sass sehr mühsam sind.
Eine etwas weiter entwickelte Version davon könnte das Drehen des Polygons beinhalten, indem derselbe Offset-Winkel ($oa) zum Winkel jedes Scheitelpunkts addiert wird. Dies ist in der folgenden Demo zu sehen. Dieses Beispiel fügt ein Stern-Mixin hinzu, das auf ähnliche Weise funktioniert, außer dass wir immer eine gerade Anzahl von Scheitelpunkten haben und jeder ungeradzahlige Scheitelpunkt auf einem Kreis mit einem kleineren Radius liegt ($f*50%, wobei $f sub-unitary ist)
Wir können auch fette Sterne wie diese haben
Oder Aufkleber mit interessanten border-Mustern. In dieser speziellen Demo wird jeder Aufkleber mit einem einzigen HTML-Element erstellt und das border-Muster mit clip-path, Schleifen und Mathematik in Sass erstellt. Ziemlich viel davon, tatsächlich.
Ein weiteres Beispiel sind diese Kartenhintergründe, bei denen Schleifen, die Modulus-Operation und Exponentialfunktionen zusammenarbeiten, um die dithering-Pixel-Hintergrundebenen zu generieren
Diese Demo setzt zufällig auch stark auf CSS-Variablen.
Dann gibt es die Verwendung von Mixins, um zu vermeiden, dass dieselben Deklarationen immer wieder geschrieben werden, wenn Dinge wie Bereichseingaben gestylt werden. Verschiedene Browser verwenden unterschiedliche Pseudoelemente, um die Komponenten eines solchen Steuerelements zu stylen, daher müssen wir für jede Komponente die Stile festlegen, die ihr Aussehen bei mehreren Pseudoelementen steuern.
Leider, so verlockend es auch sein mag, dies in unser CSS einzufügen
input::-webkit-slider-runnable-track,
input::-moz-range-track,
input::-ms-track { /* common styles */ }
… können wir das nicht tun, weil es nicht funktioniert! Der gesamte Regelblock wird gelöscht, wenn auch nur einer der Selektoren nicht erkannt wird. Und da kein Browser alle drei der oben genannten erkennt, werden die Stile in keinem Browser angewendet.
Wir brauchen so etwas, wenn wir wollen, dass unsere Stile angewendet werden
input::-webkit-slider-runnable-track { /* common styles */ }
input::-moz-range-track { /* common styles */ }
input::-ms-track { /* common styles */ }
Aber das kann viele identische Stile bedeuten, die dreimal wiederholt werden. Und wenn wir beispielsweise den background des Tracks ändern wollen, müssen wir ihn in den ::-webkit-slider-runnable-track-Stilen, in den ::-moz-range-track-Stilen und in den ::-ms-track-Stilen ändern.
Die einzig vernünftige Lösung, die wir haben, ist die Verwendung eines Mixins. Die Stile werden im kompilierten Code wiederholt, weil sie dort wiederholt werden müssen, aber wir müssen nicht mehr dasselbe dreimal schreiben.
@mixin track() { /* common styles */ }
input {
&::-webkit-slider-runnable-track { @include track }
&::-moz-range-track { @include track }
&::-ms-track { @include track }
}
Fazit: Ja, Sass ist im Jahr 2020 immer noch sehr notwendig.
Wundervoller Artikel. Macht viele Dinge klar. Ich habe festgestellt, dass sich CSS- und Sass-Variablen nicht gut vermischen lassen. Der Punkt über die Groß- und Kleinschreibung hat meinen Verstand gesprengt. Danke!! Sie sind ein begabter CSS-Programmierer
Hallo Ana,
Sie können viele dieser Probleme vermeiden, indem Sie die neueste Version von Dart Sass verwenden. Ich empfehle dringend, zu diesem Zeitpunkt von Node/LibSass abzuraten. Wir haben im letzten Jahr große Verbesserungen an Sass vorgenommen, aber die Projekte LibSass & NodeSass hinken diesen Updates hinterher.
Das löst alle Ihre Probleme mit Farbfunktionen sowie Ihre Beispiele für
min()&max(). Es gibt noch einige verbleibende Konflikte, wie Sie oben erwähnt haben. Wir arbeiten daran, diese so schnell wie möglich zu beheben. Wenn Sie Dart Sass (die offizielle Kernimplementierung der Sprache) verwenden, erhalten Sie diese Updates viel schneller.Dart Sass verfügt auch über ein leistungsfähiges Modulsystem, das uns helfen wird, diese Probleme in Zukunft zu vermeiden.
Und für das Protokoll, die Umstellung auf das neueste DartSass ist ein aktives Projekt bei CodePen! Es ist noch nicht live, aber es wird nicht mehr lange dauern.
In Dart Sass 1.26.9 erhalte ich immer noch die Kompilierungsfehler „Incompatible units“ für
min(),max()undclamp()für Dinge, die Ana erwähnt hat…font-size: clamp(1em, .5rem + 1vw, 2em);
width: min(960px, 90%);
Die Art und Weise, wie ich es umgangen habe, ist, die Werte in eine
calc()-Funktion einzuschließen…font-size: calc(clamp(1em, .5rem + 1vw, 2em));
width: calc(min(960px, 90%));
Für alle anderen, die solche Probleme haben, funktioniert das jetzt, ist gültiges Vanilla-CSS und wird (hoffentlich) auch dann noch einwandfrei funktionieren, wenn Sass aktualisiert wird, um diese neueren CSS-Funktionen zu unterstützen – falls es bis zum Zeitpunkt, an dem Sie dies lesen, noch nicht geschehen ist!
Hallo Greg!
Dies ist ein Problem, auf das ich gestoßen bin. Es liegt an der Berechnung innerhalb des Werts der Deklaration. Das Gute ist, dass dies verfolgt wird, um es zu beheben
https://github.com/sass/sass/issues/2860
Ich hatte einige Erfolge damit, den Wert der Deklaration in eine CSS Custom Property zu setzen und sie so aufzurufen.
Das ist auch eine gute Idee, Stuart! Ich bleibe vorerst bei der
calc()-Funktion, aber das gefällt mir.Gut, dass die Korrektur für
clamp()kommt.Sie können auch alle widersprüchlichen Funktionen maskieren, indem Sie sie maskieren, z. B.
#{'min(50%, 3rem)'}. Das ist es, was ich in letzter Zeit getan habe, um min() zu verwenden.Ja, das dachte ich gerade… Ist das Maskieren nicht eher kaskadenartig, indem man das nächste geeignete Programm (in diesem Fall den CSS-Interpreter des Browsers) das Problem lösen lässt? Wenn ich
Min()undmin()im Code sehe, bevor ich diesen Artikel lese, würde mir nicht kommunizieren, was passiert. Wenn ich SASS maskiere, damit CSS die Arbeit erledigt, und mir mitteilt, was passiert. Die Verwendung von Groß-/Kleinschreibung zur Lösung des Problems funktioniert, wenn Sie die einzige Person sind, die programmiert, aber die Verwendung von Groß-/Kleinschreibung ist für Teams ein No-Go!Das Ändern der Funktionsgroß-/Kleinschreibung erzeugt für meine Augen etwas Rauschen, aber es ist eine interessante und einfache Umgehungslösung.
Ich habe eine andere Wahl getroffen: die SASS-Funktion
hslmit einer benutzerdefinierten Funktion überschreiben (als Paket verfügbar).Eine Alternative zum Umgehen der Mehrdeutigkeit zwischen
min()etc. mit Groß-/Kleinschreibung oder der Verwendung von Modulen in neuerem SASS wäre, wenn SASS solche Funktionen als Optimierungschancen behandeln würde, wenn es sie auswerten kann und ansonsten unverändert lässt. Meiner Meinung nach wäre das ein echter Grund, SASS zu verwenden. Andernfalls könnte die Verwendung von JavaScript mit einem geeigneten Template-Tag sauberer und einfacher sein und gleichzeitig ein weiteres Tool aus der Vielzahl von Build-Zeit-Abhängigkeiten entfernen. Wohlgemerkt, JavaScript bietet bereits die gewünschte trigonometrische Funktionalität. Kurz gesagt, SASS hat hier eine echte Chance, einen Mehrwert zu schaffen, der sonst nicht leicht zu realisieren ist.Mir ist bewusst, dass dies nur einen kleinen Teil der Leser betreffen mag, aber ich muss auch sagen, dass CSS-Variablen zwar schön sind… sie sind überhaupt nicht mit IE11 kompatibel. Wen kümmert IE11? Leider einige meiner Kunden (je nach Branche).
Und hier kommt die Bequemlichkeit von SASS zur Rettung; wir können viel tun und trotzdem mit schlechteren, nicht gepflegten, älteren Browsern kompatibel bleiben. Wenn ich es aufgeben würde, könnte ich sehen, dass die Rückwärtskompatibilität noch zeitaufwändiger wäre.
Ich muss IE11 unterstützen und verwende CSS-Variablen.
Besorgen Sie sich diesen Ponyfill – https://github.com/jhildenbiddle/css-vars-ponyfill
Stellen Sie außerdem sicher, dass Sie CSS-Variablen im Element
:rootdeklarieren.In Bezug auf das letzte Beispiel ist es besser und wiederverwendbarer,
@contentim Mixin zu verwenden, um die Stile den Präfixen zuzuweisen.Wenn Sie jedoch Autoprefixer verwenden, ist das Hinzufügen von Präfixen über Sass nicht erforderlich.
Einige großartige Sachen hier, aber es fühlt sich irgendwie wie 2 verschiedene Artikel an. Einer über den Umgang mit neuen CSS-Funktionen, die denselben Namen wie Funktionen in Sass haben, und ein anderer darüber, warum Sass in der modernen Webentwicklung immer noch nützlich ist.
Vielen Dank für eine so detaillierte Ausarbeitung, ich bin definitiv auf das
min-max-Problem gestoßen und konnte es nicht lösen.Was Ihren letzten Teil angeht, wir haben ein anderes Mixin dafür
Wir verwenden es so
Division scheint auch unmöglich zu sein
https://jsbin.com/zaduhiq/1/edit?html,css,js,output