Benutzer-Werkzeuge

Webseiten-Werkzeuge


css:guidelines

CSS Guidelines

Dies ist eine kurze, unvollständige Zusammenfassung der weit ausführlicheren, sehr interessanten CSS Guidelines von Harry Roberts. Sie dient nur mir persönlich zur Erinnerung an einzelne Punkte; das Lesen der gesamten CSS Guidelines ist dringend empfohlen.

Namenskonventionen

Die folgenden Namenskonventionen helfen nicht direkt beim Schreiben der CSS. Im HTML beschreiben die Klassennamen allerdings, wie einzelne Elemente miteinander im Verhältnis stehen. Dies sorgt bei größeren Projekten für mehr Transparenz und unterstützt so die Teamkollegen.

Allgemein

  • Namen werden ausschließlich durch Bindestriche verknüpft, z.B. page-head
  • Bedeutet: keine Unterstriche, kein camelCase!
  • JavaScript: Eigene Klassennamen für JS verwenden, um unabhängig zu bleiben; z.B. class="btn js-btn"
  • Bei zusammenhängenden, verschachtelten Elementen wird BEM (Block, Element, Modifier) verwendet:

Block: ein eigenständiger Bestandteil einer Website
Element: ein Bestandteil, welches vom Block abhängig ist
Modifier: eine Anpassung oder Erweiterung des Blocks oder Elements

Schreibweise

.block {}
.block__element {}
.block--modifier {}

Regeln

  • Die Klassenbezeichnung soll nicht die DOM-Struktur abbilden (zu lange Klassennamen)!
    • Gut: Ein Modifier person__eye--blue
    • Schlecht: Verschachtelte Unterelemente .person__head__eye, stattdessen: .person__eye
  • Elemente direkt mit Klasse ansprechen: .nav__item statt .nav__list li {}
  • BEM soll nicht überall angewendet werden. Aus .logo innerhalb des Headers muss nicht header__logo werden!
  • to be continued …

Beispiel

<div class="nav  nav--main  block">
    <ul class="nav__list  level_1">
        <li class="nav__item"><a>Home</a></li>
        <li class="nav__item"><a>About us</a></li>
        <li class="nav__item"><a>Imprint</a></li>
    </ul>
</div>

CSS-Selektoren

Wahl des Selektors

Die gewünschte Absicht ist immer die Grundlage für die Wahl der Selektoren.

Wenn es die Absicht ist, das Hauptmenü im Header zu gestalten, sollte man nicht

header ul {}
// oder
.header ul {}

verwenden, da dies alle eventuell im Header befindlichen Listen trifft. Wird dort nachträglich eine Liste hinzugefügt, müsste man die CSS-Deklarationen der neuen Liste wieder überschreiben oder aber das ursprüngliche Hauptmenü ab diesem Zeitpunkt anders ansprechen.

Besser ist daher

.site-nav {}

als eindeutiger Selektor für das gewünschte Ergebnis.

Wiederverwendbarkeit

Für einen Komponenten-basierten Ansatz bei der Entwicklung von Websites ist es wichtig, Klassen innerhalb eines oder auch verschiedener Projekte wiederverwenden zu können.
Erhöhe hierfür die Anzahl der verwendeten Klassen innerhalb eines HTML-Elements und teile die CSS-Deklarationen sinnvoll auf (siehe auch „Architekturprinzipien“). IDs sollten nicht für das Styling verwendet werden, da sie nur einmal pro Seite vorkommen können und zu stark gewichtet sind.

<div class="box  box--large  promo">

Unabhängigkeit vom Einsatzort

Gestalte ein Objekt nicht abhängig davon, wo sie sind, sondern was sie sind. So kann es an anderer Stelle wiederverwendet (und ggf. mit einem Block-Modifier angepasst) werden.

Beispiel eines Call-to-Action-Buttons innerhalb einer Promotion-Box:

// Schlecht:
.promo a {}
 
// Besser:
.btn {}

Übertragbarkeit einer Klasse

Selektoren sollten nicht von HTML-Elementen abhängig sein, z.B.

// Schlecht:
input.btn {}

Diese Klasse ließe sich nicht auf andere HTML-Elemente anwenden und bringt in dieser Form auch keinen Mehrwert für die input-Elemente.
Falls es darum geht, zusätzliche CSS-Deklarationen bei einem bestimmten HTML-Element hinzuzufügen, wäre diese Herangehensweise okay, es ginge aber auch besser:

// Okay:
/* Fehlermeldungen allgemein */
.error {
  color: red;
  font-weight: bold;
}
 
/* Falls das Element ein div ist, wird es zudem als Box gestaltet */
div.error {
  border: 1px solid;
  padding: 10px;
}
 
 
// Besser:
.error-text {
  color: red;
  font-weight: bold;
}
 
.error-box {
  border: 1px solid;
  padding: 10px;
}

Namensgebung

Aus Gründen der Wiederverwendbarkeit sollte man Namen wählen, die sinnvoll, aber nicht zu eindeutig sind:

  • .primary-nav statt .site-nav
  • .sub-links statt .footer-links

Während .footer-links die Verwendung auf den Footer begrenzt, kann .sub-links auch an anderer Stelle verwendet werden.

Bezeichnungen sollten weder den konkreten Einsatzzweck noch die genaue Deklaration beschreiben:

// Schlecht: Die Farbe könnte sich mal ändern
.blue {
  color: blue;
}
 
// Schlecht: Ist vom Einsatzort abhängig
.header span {
  color: blue;
}
 
// Schlecht: Zu spezifisch, kann schlecht wiederverwendet werden
.header-color {
  color: blue;
}
 
// Gut: wiederverwendbar, unabhängig von späteren Änderungen
.highlight-color {
  color: blue;
}

Performance von Selektoren

Bei der Qualität der heutigen Browser ist es eher interessant als wichtig, wie schnell CSS-Selektoren den Knoten im DOM zugeordnet werden können.

Im Allgemeinen gilt: Je komplexer ein Selektor, desto aufwändiger ist er für den Browser zu verarbeiten.

body.home div.header ul {}

Browser lesen Selektoren von rechts nach links. Daher muss er folgende Arbeitsschritte durchführen:

  • finde alle ul-Elemente im DOM
  • prüfe nun, ob diese irgendwo innerhalb eines Elements mit der Klasse .header vorkommen
  • prüfe jetzt, ob die .header Klasse zu einem div gehört
  • prüfe nun, ob sich dies alles innerhalb eines Elements mit der Klasse .home befindet
  • prüfe als letztes, ob .home die Klasse des body-Elements ist

Hinzu kommt, dass sich zwischen body.home, div.header und ul weitere Elemente befinden können. Daher sucht der Browser solange dem DOM entlang nach dem nächsten Teilselektor, bis er ihn gefunden hat. Dies kann man zumindest mit foo > bar beheben, indem man den Browser nur die nächste Ebene durchsuchen lässt.

Logischerweise ist der folgende Selektor wesentlich effizienter:

.primary-nav {}

Der Schlüssel-Selektor

Da Browser die Selektoren von rechts nach links verarbeiten, ist der letzte (rechte) Teilselektor der Schlüssel-Selektor

Ein Extrembeispiel ist das folgende: So einfach der Selektor zuerst erscheint - schließlich kann eine ID nur einmal vorkommen - so verzwickt wird es mit dem Schlüssel-Selektor *.

#foo * {}

Dieser würde wirklich *alle* Knoten im DOM (inkl. <head>, <title> und <link>) finden und dann im nächsten Schritt abgleichen, welche dieser Elemente sich innerhalb von #foo befinden.

Zusammenfassung

  • Wähle das gewünschte Element direkt aus, statt es vom Umfeld abhängig zu machen.
  • Verwende viele Klassen, vermeide IDs; teile CSS-Deklarationen sinnvoll auf Klassen auf, um sie wiederverwenden zu können.
  • Verschachtele Selektoren nicht unnötig, da es den Einsatzzweck zu genau bestimmt und dies Einfluss auf die Wiederverwendbarkeit hat.
  • Beschränke den Selektor nicht unnötig auf ein bestimmtes HTML-Element.
  • Halte Selektoren so kurz wie möglich.

Gewichtung (Spezifizierung)

Hat jeder schon erlebt - eine ID im allgemeineren Selektor und die spezielle Klasse wird ignoriert:

#content table {}
 
// diese Klasse wird immer vom oberen Selektor überschrieben:
.my-new-table {}

Was tun, wenn es zu umständlich wird, das bestehende Projekt umzubauen? Der neue Selektor muss noch eine noch stärkere Gewichtung erhalten! Und wenn ich später eine weitere Variante einführen möchte? … damit setzt man eine Abwärtsspirale in Gang, die Selektoren müssen eine immer stärkere Gewichtung erhalten.

Am besten hält man die Gewichtung möglichst niedrig, indem man

  • keine IDs in Selektoren verwendet,
  • Selektoren nicht verschachtelt,
  • Klassen nicht an HTMl-Elemente bindet,
  • nicht mehrere Klassen im Selektor verwendet.

!important

Wer die Gewichtung niedrig hält, wird !important nicht gebrauchen müssen, um Fehler auszubügeln.
Seine Daseinsberechtigung hat das Schlüsselwort vor allem bei proaktivem Gebrauch, also wenn es nichts reparieren soll, sondern die Funktion von Vornhinein sicherstellen:

.hidden {
  display: none !important;
}

Wer diese Klasse an einem HTML-Element verwendet, möchte auf jeden Fall, dass es nicht angezeigt wird.

Gewichtung steuern

Irgendwann kommt immer der Punkt, an dem man trotz geringer Gewichtung etwas tricksen muss.

Wenn ein Selektor mit zwei Klassen überschrieben werden muss, kann man einfach den Schlüssel-Selektor zweimal notieren (ohne Leerzeichen). So bleibt auch die Übertragbarkeit in andere Seitenbereiche gegeben.

// muss überschrieben werden:
.header .nav {}
 
// Lösung: dieselbe Klasse zweimal im Selektor, ohne Leerzeichen:
.nav.nav {}

Falls mal ein HTML-Element mit ID gestaltet werden muss, dessen Quellcode man nicht um eine Klasse ergänzen kann, hilft folgender Trick:

// Handhabung der ID als Attribut:
[id="third-party-widget"] {}

Architekturprinzipien

CSS ist keine Programmiersprache, dennoch kann es hilfreich sein, ein Architekturprinzip als Grundlage für seine Stylesheets zu nutzen. Gerade bei größeren Projekten mit Teamarbeit sorgt dies für eine einheitliche, skalierbare Arbeitsweise.

Architekturen sind jeweils allumfassende Sammlungen von Richtlinien, die dem Anwender eine reglementierte Grundlage für seine Arbeit bieten. Details wie Syntax oder Namensgebungen bleiben hierbei üblicherweise dem Anwender überlassen.

Die gewählte Architektur sollte

  • eine vernünftige, einheitliche Grundlage bieten,
  • Änderungen ermöglichen,
  • Skalierung der Codebasis sicherstellen,
  • Wiederverwendbarkeit und Effizienz fördern,
  • Produktivität steigern.

Für gewöhnlich führt dies zu einer Klassen- und Komponenten-basierten Arbeitsweise, in übersichtliche Module aufgeteilt. Ein Präprozessor wie SASS oder LESS ist hier hilfreich.

Objektorientierung (OOCSS)

Bei objektorientiertem CSS wird das User Interface in Struktur und Erscheinungsbild unterteilt: UI-Komponenten werden auf ihre formgebenden Teile (Abstände, Layout, …) reduziert und können über separate Klassen um ihre gewünschte Gestaltung (Farben, Schriften, …) ergänzt werden.

Man kann sich die Struktur als ein Grundgerüst vorstellen, eine wiederverwendbare Vorlage ohne weitere Gestaltung.

.btn {
  display: inline-block;
  padding: 1em 2em;
  vertical-align: middle;
}
 
.btn--positive {
  background-color: green;
  color: white;
}
 
.btn--negative {
  background-color: red;
  color: white;
}
<button class="btn  btn--negative">Delete</button>

Die .btn-Klasse gibt dem button-Element lediglich die gewünschte Struktur, das Erscheinungsbild wird von einer separaten Klasse gesteuert. So ist .btn in jedem neuen Projekt wiederverwendbar.

Das Single-Responsibility-Prinzip

Laut diesem Prinzip hat jede Klasse nur eine fest definierte Aufgabe zu erfüllen. Bezogen auf CSS werden Deklarationen in kleinere Blöcke aufgeteilt. Jede Deklaration ist nur noch für einen Zweck da und wird für das gewünschte Ergebnis mit weiteren kombiniert.

.error-message {
  display: block;
  padding: 10px;
  border-top: 1px solid #f00;
  border-bottom: 1px solid #f00;
  background-color: #fee;
  color: #f00;
  font-weight: bold;
}
 
.success-message {
  display: block;
  padding: 10px;
  border-top: 1px solid #0f0;
  border-bottom: 1px solid #0f0;
  background-color: #efe;
  color: #0f0;
  font-weight: bold;
}

Diese beiden Klassen steuern Layout, Struktur und Gestaltung; viele Deklarationen wiederholen sich. Wenn man dieses CSS anhand von OOCSS umschreibt und dabei noch das Single-Responsibility-Prinzip anwendet, erhält man vier kleinere Klassen:

.box {
  display: block;
  padding: 10px;
}
 
 
.message {
  border-style: solid;
  border-width: 1px 0;
  font-weight: bold;
}
 
.message--error {
  background-color: #fee;
  color: #f00;
}
 
.message--success {
  background-color: #efe;
  color: #0f0;
}

Jetzt haben wir ein allgemeines „Box“-Objekt erhalten, dass wir an vielen Stellen wiederverwenden können, ebenso wie ein „Nachrichten“-Objekt, dass mit den beiden anderen Klassen erweitert werden kann.

Das Open-Closed-Prinzip

Dieses Prinzip gehört ebenfalls zur objektorientierten Programmierung. Es besagt, dass Module für Erweiterungen offen sein sollen, aber geschlossen gegenüber Modifikationen.

Bei CSS werden Anpassungen demnach nicht direkt an der bestehenden Klasse vorgenommen, sondern anhand einer zweiten:

// Falsch:
.box {
  display: block;
  padding: 10px;
}
.content .box {
  padding: 20px;
}
 
// Richtig:
.box {
  display: block;
  padding: 10px;
}
.box--large {
  padding: 20px;
}

Beim schlechten Beispiel wird die Anpassung über einen unnötig stark gewichteten, einsatzortabhängigen Selektor vorgenommen - und die .box-Klasse wird direkt überschrieben, was dem Open-Closed-Prinzip widerspricht. Der Teamkollege kann sich nicht länger auf die ursprüngliche Funktion der .box-Komponente verlassen!

DRY: Don't Repeat Yourself!

Laut diesem Prinzip sollte man Wiederholungen auf ein nötiges Minimum reduzieren. Man darf es nicht übertreiben, da man sonst zu komplizierten Code schreibt und unpraktische Abhängigkeiten entstehen können.

.btn {
  display: inline-block;
  padding: 1em 2em;
  font-weight: bold;
}
.page-title {
  font-size: 3rem;
  line-height: 1.4;
  font-weight: bold;
}
.user-profile__title {
  font-size: 1.2rem;
  line-height: 1.5;
  font-weight: bold;
}

Obwohl hier dreimal font-weight: bold; auftaucht, ist es unsinnig, diese Deklaration auszulagern und über ein mixin oder @extend einzufügen.
Wenn jedoch z.B. ein Webfont verwendet wird, der bei jeder Nutzung über font-family auch ein font-weight: bold; benötigt, haben wir einen sinnvollen Einsatz gefunden:

@mixin my-web-font() {
    font-family: "My Web Font", sans-serif;
    font-weight: bold;
}
 
.btn {
  display: inline-block;
  padding: 1em 2em;
  @include my-web-font();
}
.page-title {
  font-size: 3rem;
  line-height: 1.4;
  @include my-web-font();
}
.user-profile__title {
  font-size: 1.2rem;
  line-height: 1.5;
  @include my-web-font();
}

Wenn der Webfont nun ausgetauscht werden soll und dadurch auf font-weight: normal; umgestellt werden muss, wird dies nur an einer zentralen Stelle nötig.

Man sollte also nur Wiederholungen reduzieren, wo eine thematische Verbindung besteht.

css/guidelines.txt · Zuletzt geändert: 2015/12/03 19:32 (Externe Bearbeitung)