SCSS Workshop
Raphael Koch, Inware AG
Mehr SCSS
Variablen
Neben der normalen Variablendeklaration gibt es auch noch das Flag !default.
Man kann es verstehen wie:
«Es sei denn, diese ist bereits zugewiesen»
@import '../../definitions';
$gap: 1rem !default;
.grid {
display: grid;
gap: $gap;
}
Mixins
SCSS erlaubt es bei Mixins auch Default-Werte mitzugeben. So kann ein Mixin mit oder ohne Parameterangabe aufgerufen werden.
Parameter mit Default-Wert müssen immer nach Parameter ohne Default-Wert kommen.
SCSS erlaubt es auch vorhergehende Parameter als Default-Wert zu benutzen und auch deren Default-Wert.
Mixins können auch in anderen Mixins verwendet werden und mit @content; kann man einen Content-Block im Mixin platzieren.
@mixin rectangle($width, $height: 100px) {
width: $width;
height: $height;
}
@mixin square_default($width: 100px, $height: $width) {
width: $width;
height: $height;
}
.box {
@include rectangle(50px);
&--square {
@include square_default();
}
}
@mixin content_wrap($value) {
.wrap {
@include square_default($value);
@content;
}
}
Logik und if-Konstrukt
SCSS hat Standard Logik, wie man sie so kennt.
Dazu auch das if-else um Fallunterscheidungen in Funktionen oder Mixins zu nutzen. Es kann aber auch ausserhalb von Funktionen und Mixins benutzt werden.
Achtung: Das ist nur on-compile-time.
$true: true;
$false: false;
// Boolean Logic
$and: $true and $false;
$or: $true or $false;
$not: not $true;
$combined: not ($true and $false) or $false;
// equality
$equal: 2 == $var;
$unequal: 2 != $var;
$larger_2: 2 < $var;
$smaller_2: 2 > $var;
// if-else
@if $gap == 2rem {
.gap::before {
content: 'gap is 2rem';
}
} @else if $gap == 3rem {
.gap::before {
content: 'gap is 3rem';
}
} @else {
.gap::before {
content: 'gap is something else';
}
}
Funktionen
SCSS erlaubt es eigene Funktionen zu definieren. So kann man einfach neue Funktionalitäten hinzufügen.
Bei unserem SCSS-Compiler habe ich bereits einige Funktionen PHP-seitig eingebaut, welche ihr nutzen könnt:
stripUnit($value)- entfernt die Einheit von der ZahltoEm($value)- Pixel zu em Transformation auf Basis 16toRem($value)- Pixel zu rem Transformation auf Basis 16toPx($value)- rem zu Pixel Transformation auf Basis 16toRgb($value)- HEX zu RGB TransformationtoData($path)- Datei zu Data-URItoDataRecolor($path, $color)- SVG zu Data-URI und ersetzen voncurrentColorauf$colorWert
@function percent($target, $container) {
@return ($target / $container) * 100%;
}
@function isOdd($number) {
@return $number % 2 == 0;
}
.block {
$container-width: 1000px;
width: percent(200px, $container-width);
@if isOdd(stripUnit($container-width)) {
&--odd {
background-color: blue;
}
} @else {
&--even {
background-color: red;
}
}
}
Listen und Maps
Ähnlich wie andere Programmiersprachen unterstützt SCSS Arrays und Hash-Maps. Hier genannt Lists und Maps. Diesen kommen zusammen mit den entsprechenden Funktionen um sie nutzen zu können.
Auch mehr-dimensionale Listen oder Maps sind möglich, oder Maps mit Listen etc.
Achtung: Listen in SCSS sind 1-indexiert!
Die Listen-Funktion length kann auch auf Maps genutzt werden.
// Define a list
$countries: 'usa', 'japan', 'china', 'france', 'sweden';
// Inspect a list
$length: length($countries);
$nth: nth($countries, 1); // Starts at 1
$index: index($countries, 'france');
// Add element to a list
$countries: append($countries, 'mexico');
// Join lists
$more-countries: 'egypt', 'thailand';
$countries: join($countries, $more-countries);
// Define a map
$category-colors: ('news': #558899, 'sport': #182939, 'art': #c7d829,);
// Get a value
$news-color: map-get($category-colors, news);
$news-color: map-get($category-colors, 'news');
// Check if a map has a value
$has_economy: map-has-key($category-colors, economy); // false
$has_news: map-has-key($category-colors, news); // true
// Remove key-value pair
$category-colors: map-remove($category-colors, news);
// Merge maps (also used to add a new pair)
$economy: ('economy': #3549dd,);
$category-colors: map-merge($category-colors, $economy);
Interpolation
Interpolation nennt man das «Escapen» was ich bei calc erwähnt hatte. Sie können jedoch
weitaus mehr. So können Properties dynamisch gesetzt, oder Klassennamen automatisch generiert werden.
Besonders nützlich sind diese in Kombination mit Iteratoren oder Mixins.
Notwendig sind Interpolationen in Klassennamen, Properties, in Strings und überall wo CSS arithmetische Operationen erlaubt, wie CSS-Variablen oder calc.
$color: red;
$padding: 1em;
$icon: 'sparta';
@mixin position($top-or-bottom, $left-or-right) {
position: absolute;
#{$top-or-bottom}: 0;
#{$left-or-right}: 0;
}
.#{$color}-box {
--color: #{$color};
@include position(top, left);
width: calc(100px - #{$padding});
content: 'Color is #{$color}';
background-image: url("/icons/#{$icon}.svg");
}
Iteratoren
SCSS hat drei Arten von Iteratoren for-Loop, each-Loop und while-Loop. Diese sind nützlich wenn mal viele ähnliche Klassen definieren muss.
Zum Beispiel in der Grid-Datei welche wir benutzen.
Achtung: Iteratoren können sehr schnell riesige CSS-Dateien erstellen, seid deshalb vorsichtig mit diesen.
// @for
@for $i from 1 to 3 {
.box-#{$i} {
content: 10px * $i;
}
}
@for $i from 1 through 3 {
.alt-box-#{$i} {
content: 10px * $i;
}
}
// @each
$category-colors: ('news': #558899, 'sport': #182939, 'art': #c7d829,);
@each $country in $countries {
.#{$country} {
content: 10px * index($countries, $country);
}
}
@each $category, $color in $category-colors {
.#{$category} {
color: $color;
}
}
// @while
$column: 4;
@while $column > 0 {
.cols-#{$column} {
width: 10px * $column;
}
$column: $column - 2;
}
Extend
Extend ist inspiriert von OOP. So kann man ganz einfach Code-Wiederholungen verhindern und das compilierte CSS klein halten.
Mit Extend kommen auch noch die Placeholder Selectors. Das sind Selectors welche nur extended werden können. Sie werden vom Compiler sonst ignoriert.
Die Definition von Placeholder Selectors ist gleich wie normale Class-Selectors, aber statt ein Punkt verwendet man ein Prozent-Zeichen.
Das Limit von Extend ist bei Nesting, Suffixe können nicht vererbt werden.
Achtung: Auch die Parents von der Klasse werden geerbt!
%centered {
text-align: center;
margin: auto;
&:focus {
color: blue;
}
}
.box-1 { @extend %centered; }
.box-wrapper {
.box-2:hover {
@extend %centered;
color: red;
}
}
.box-3 { @extend .box-1; }
.box-4 { @extend .box-2; }
// CSS Output
.box-1, .box-3, .box-wrapper .box-2:hover, .box-wrapper .box-4:hover {
text-align: center;
margin: auto;
}
.box-1:focus, .box-3:focus,
.box-wrapper .box-2:focus:hover,
.box-wrapper .box-4:focus:hover {
color: blue;
}
.box-wrapper .box-2:hover,
.box-wrapper .box-4:hover {
color: red;
}
Was mach das wohl?
SCSS
@mixin inline-animation($durations, $name: 'animation', $default: 2s, $iterations: infinite) {
@if length($durations) > 0 and (false != index($durations, $default)){
$animation: #{$name}-#{unique-id()}; // built-in function
@keyframes #{$animation} { @content; }
@each $duration in $durations {
$suffix: '';
@if $duration != $default { $suffix: '-#{$duration}'; }
.#{$name}#{$suffix} { animation-duration: $duration; }
}
$list: '';
@for $i from 1 through length($durations) {
$duration: nth($durations, $i);
$suffix: '';
@if $duration != $default { $suffix: '-#{$duration}'; }
@if ($i != length($durations)) { $suffix: '#{$suffix},'; }
$list: '#{$list} .#{$name}#{$suffix}';
}
#{$list} {
animation-name: $animation;
animation-iteration-count: $iterations;
}
}
}
$durations: 2s, 4s, 8s, 16s;
@include inline-animation($durations, 'pulse') {
from { background-color: yellow }
to { background-color: red }
}
@include inline-animation($durations, 'pulse-blue') {
from { background-color: blue }
to { background-color: cyan }
}
CSS
@keyframes pulse-u75ba8f30 {
from { background-color: yellow; }
to { background-color: red; }
}
.pulse { animation-duration: 2s; }
.pulse-4s { animation-duration: 4s; }
.pulse-8s { animation-duration: 8s; }
.pulse-16s { animation-duration: 16s; }
.pulse, .pulse-4s, .pulse-8s, .pulse-16s {
animation-name: pulse-u75ba8f30;
animation-iteration-count: infinite;
}
@keyframes pulse-blue-ue37bb233 {
from { background-color: blue; }
to { background-color: cyan; }
}
.pulse-blue { animation-duration: 2s; }
.pulse-blue-4s { animation-duration: 4s; }
.pulse-blue-8s { animation-duration: 8s; }
.pulse-blue-16s { animation-duration: 16s; }
.pulse-blue, .pulse-blue-4s, .pulse-blue-8s, .pulse-blue-16s {
animation-name: pulse-blue-ue37bb233;
animation-iteration-count: infinite;
}
Tipps und Tricks
An Stelle von repetitivem Code zu schreiben nutzt die Placeholder Selectors und @extend.
Persönlich erstelle ich eine generics-Datei wo sich solche Placeholder Selectors befinden welche ich über verschieden
Entrytypes oder auch Components nutzen will.
Nutzt die Mixins und Variablen, so können einfache anpassungen über die Definitionen gemacht werden (Dies gilt auch für eure LESS Projekte).
Zum Beispiel, gibt es in der definitions-Datei von SCSS das zusätzliche Mixin hover-only. So kann man Hover-Effekte ganz einfach
damit wrappen und man hat keine komischen Farbänderungen etc. mehr auf Touchgeräten!
Ihr habt im vorigen Beispiel gesehen, SCSS-Code kann man ziemlich komplex schreiben. Versucht also euren Code leserlich zu halten und
auch hier wie überall sonst, Kommentare im Code können durchaus hilfreich sein.
@import "../../generics";
.Entrytype__button {
@extend %button;
&--outline {
@extend %button-outline;
}
}
@mixin truncate($width, $height: auto) {
white-space: nowrap;
overflow:hidden;
text-overflow: ellipsis;
width: $width;
height: auto;
}
a {
color: $blue;
@include truncate(toRem(320px));
@include hover-only {
&:hover {
color: $red;
}
}
}