Responsive CSS-Stylesheet mit Ruby erstellen
Alexander Trust, den 19. Januar 2016Wir nutzen unsere Computer viel zu wenig. Deshalb möchte ich euch zeigen, wie man ein Responsive CSS-Stylesheet mit Ruby erstellen kann. Wer noch Anfänger in Ruby ist, sollte sich möglicherweise erst mit der Syntax vertraut machen, wenngleich sie nicht allzu schwer ist.
Ruby ist als Scriptsprache auf jedem Mac mit OS X vorinstalliert. Auch viele Linux-Distributionen kommen von Haus aus mit Ruby daher. Unter Windows kann man es nachträglich installieren. Deshalb ist es ein Einfaches, „mal eben schnell“ ein Ruby-Script zu schreiben, um eigene Aufgaben zu erledigen. Ein guter Ausgangspunkt sich mit Ruby vertraut zu machen, ist http://ruby-doc.org/gettingstarted/.
Responsives CSS-Stylesheet
Was genau bedeutet eigentlich „responsiv“? Man nutzt in der Stilvorlage sogenannte Media Queries (@media
, vgl. https://wiki.selfhtml.org/wiki/CSS/Media_Queries), um unterschiedliche Ausgabegeräte (Displays, Drucker, etc.) zu unterscheiden. Bei der Ausgabe über den Typus screen
für die Ausgabe im Browser am Bildschirm kann man zusätzlich entscheiden, wie groß denn das Display ist. Zum Beispiel würden Stilangaben für folgende Media Query
@media only screen and (min-width: 960px) {
# CSS-Anweisungen hierhin
}
nur interpretiert, wenn das Display, auf dem die Webseite angezeigt wird, mindestens 960 Pixel Platz in der Breite anbietet. Auf diese Weise lassen sich für Smartphones, Tablets und Desktop-Computer Layouts entwerfen, die an die unterschiedliche Displaygröße angepasst sind.
Vorüberlegungen
Mit Hilfe von solchen Größenangaben kann man für dieselben Klassen, IDs und HTML-Elemente je unterschiedliche Werte eintragen. Meist, wenn man mit Media Queries arbeitet, wiederholen sich die darin enthaltenen Elemente. Aus der Programmierung weiß man, wenn das der Fall ist, ist es Zeit für eine Funktion oder ein Script. Denn es gilt das Mantra DRY. Das Akronym steht für engl. „Don’t repeat yourself“, dt. also „Wiederhole Dich nicht“.
Bevor wir nun also Ruby ein Script schreiben lassen, müssen wir die Bestandteile separieren, die sich wiederholen werden. Dabei sollte man schon zu Beginn erst einmal nur eine Vorlage ausarbeiten und die übrigen dann vom Computer erstellen lassen und Elemente herauspicken und zusammenschreiben. Also Überschriften, Textvariablen etc.
CSS-Code extrahieren und abstrahieren
Im zweiten Schritt abstrahiert man dann, indem die konkreten Werte durch Variablen ersetzt werden. Wer also eigentlich einen font-size: 36px;
eingestellt hat, der wird ihn in seinem Ruby-Script ersetzen durch eine Variable. Variablen sollten sprechende Namen haben, damit man sie inhaltlich im Quellcode identifizieren kann. Später wird man dann in einem Schleifen-Konstrukt einige Male den Code durchlaufen lassen, die Variablen ersetzen, und eine Ausgabe erzeugen mit konkreten Werten für unterschiedliche Media Queries.
Praxisbeispiel: responsiver Text nach Googles Material Design
Wer Googles Konzept vom Material Design kennt, der weiß auch, dass es dafür bereits einige CSS-Frameworks gibt, die das Design nacheifern. Eines der bekanntesten ist MaterializeCSS. Dort habe ich mich inspirieren lassen, dass man Textgröße (font-size
) mit größer werdendem Display mitwachsen lassen kann.
Meist ist für Überschriften und Fließtext nur eine Größe im Stylesheet vorgesehen und zwar eine, die am Desktop gut aussieht aber auf dem Smartphone viel zu klein ist. Oder man findet darin umgekehrt, Schrifteinstellungen, die auf mobilen Endgeräten „funktionieren“, auf dem Desktop aber viel zu winzig sind.
Im Stylesheet von MaterializeCSS gibt es die Klasse .flow-text
, mit der man jeden beliebigen Text auszeichnen kann, der dann „mitwächst“. Das tut die Klasse in kleinen Schritten von je 30 Pixel Unterschied zwischen Displaygrößen die kleiner oder gleich 360 Pixel sind und solchen die 960 Pixel oder größer sind. Insgesamt gibt es 22 Media Queries in 30er-Schritten. Ich wollte in diesen die Überschriften (h1
bis h6
) ebenfalls mitwachsen lassen. Man überlegt sich also einen Schritt, den so eine Überschrift von einem Media Query zum nächsten kleiner werden soll.
Also entwarf ich die Vorlage, trug einige Variablen mit den Ausgangswerten ein und ließ das Ruby-Script in 30er-Schritten zwischen 360 und 960 iterieren. Das Ergebnis wird in einer Datei ausgegeben, die ich entweder so fertig als CSS-Datei verwenden kann, oder aber öffne und den Inhalt in meine globale CSS-Datei integriere.
h1Size = 4.2
h2Size = 3.56
h3Size = 2.92
h4Size = 2.28
h5Size = 1.64
h6Size = 1
h1MarginTop = 2.1
h1MarginBottom = 1.68
h2MarginTop = 1.78
h2MarginBottom = 1.424
h3MarginTop = 1.46
h3MarginBottom = 1.168
h4MarginTop = 1.14
h4MarginBottom = 0.912
h5MarginTop = 0.820
h5MarginBottom = 0.656
h6MarginTop = 0.5
h6MarginBottom = 0.4
flowSize = 1.68
ms = 0.024
mm = 0.012
s = 960
e = 360
i = 0
File.open('stylesheet-magic.css','w') do |f|
while e <= 960
f.puts "@media only screen and (min-width: #{s}px) {"
f.puts ".flow-text {font-size: #{(flowSize-(ms*i)).round(3)}rem;}"
f.puts "h1 {font-size: #{(h1Size-(ms*i)).round(3)}rem;line-height: 110%;margin: #{(h1MarginTop-(mm*i)).round(3)}rem 0 #{(h1MarginBottom-(mm*i)).round(3)}rem 0;}"
f.puts "h2 {font-size: #{(h2Size-(ms*i)).round(3)}rem;line-height: 110%;margin: #{(h2MarginTop-(mm*i)).round(3)}rem 0 #{(h2MarginBottom-(mm*i)).round(3)}rem 0;}"
f.puts "h3 {font-size: #{(h3Size-(ms*i)).round(3)}rem;line-height: 110%;margin: #{(h3MarginTop-(mm*i)).round(3)}rem 0 #{(h3MarginBottom-(mm*i)).round(3)}rem 0;}"
f.puts "h4 {font-size: #{(h4Size-(ms*i)).round(3)}rem;line-height: 110%;margin: #{(h4MarginTop-(mm*i)).round(3)}rem 0 #{(h4MarginBottom-(mm*i)).round(3)}rem 0;}"
f.puts "h5 {font-size: #{(h5Size-(ms*i)).round(3)}rem;line-height: 110%;margin: #{(h5MarginTop-(mm*i)).round(3)}rem 0 #{(h5MarginBottom-(mm*i)).round(3)}rem 0;}"
f.puts "h6 {font-size: #{(h6Size-(ms*i)).round(3)}rem;line-height: 110%;margin: #{(h6MarginTop-(mm*i)).round(3)}rem 0 #{(h6MarginBottom-(mm*i)).round(3)}rem 0;}"
f.puts "}"
e = e + 30
s = s - 30
i+=1
end
end
So also könnt ihr ein Responsive CSS-Stylesheet mit Ruby erstellen. Im Ergebnis bekam ich 21 Media Queries (eines muss ich wg. dem max-width
statt min-width
aber nur kopieren und kurz anpassen. Gerechnet hat für mich der Computer, und die Werte eingetragen. Bei der Schriftgröße bin ich um je 0.024rem
kleiner geworden, beim Außenabstand der Überschriften habe ich die Hälfte davon genutzt (0.012rem
). Das kleinste Beispiel schaut wie folgt aus:
@media only screen and (min-width: 360px) {
.flow-text {
font-size: 1.68rem;
}
h1 {
font-size: 4.2rem;
line-height: 110%;
margin: 2.1rem 0 1.68rem 0;
}
h2 {
font-size: 3.56rem;
line-height: 110%;
margin: 1.78rem 0 1.424rem 0;
}
h3 {
font-size: 2.92rem;
line-height: 110%;
margin: 1.46rem 0 1.168rem 0;
}
h4 {
font-size: 2.28rem;
line-height: 110%;
margin: 1.14rem 0 0.912rem 0;
}
h5 {
font-size: 1.64rem;
line-height: 110%;
margin: 0.82rem 0 0.656rem 0;
}
h6 {
font-size: 1.0rem;
line-height: 110%;
margin: 0.5rem 0 0.4rem 0;
}
}
Das Schreiben der wenigen Zeilen Ruby-Code hat ein paar Minuten gedauert. Das Script hat in nicht mal einer Sekunde eine 11KB große CSS-Datei erzeugt. Von Hand hätte ich mit „Copy-Paste“-Algorithmus wahrscheinlich zu viele Flüchtigkeitsfehler eingebaut und außerdem trotzdem viel zu viel Zeit damit zugebracht.
Das Script aus unserem Beispiel erzeugt die Media Queries in abfallender Reihenfolge. Bei CSS wird jedoch die letzte Anweisung immer auch zuletzt interpretiert und die vorherigen Werte überschrieben. Mann muss also die Media Queries noch in die richtige Reihenfolge bringen. Mir lag zu Beginn aber der Ausgangswert für den Desktop vor, weshalb ich vergessen habe, erst denjenigen für die mobile Variante zu berechnen. Dann hätte man die Schleife im Ruby-Script eine aufsteigende Reihenfolge ausgeben lassen können.
Komplexe Stylesheets entwickeln
Man kann neben dem hier präsentierten einfachen Beispiel deutlich komplexere anfertigen und sogar sein komplettes Stylesheet auf diese Weise produzieren lassen. Am sinnvollsten erscheint mir, einen Hash (für PHPler: ein Array) mit allen Fällen anzulegen, die man vorsehen mag. Darüber iteriert man und fragt mit einer case
-Struktur (für PHPler: Switch), die einzelnen Fälle ab. So kann man Sonderfälle für den Drucker berücksichtigen.