Templates für eine Webseite organisieren ist eine Aufgabe, die häufig im Chaos endet. Und zur Unübersichtlichkeit gesellt sich dann gerne das Problem dazu, dass eine Änderung unzählige Seiteneffekte hat. Eine Methode das Problem anzugehen ist "Atomic Design" in Kombination mit einem BEM-Namensschema (Block Element Modifier).
Ich beschreibe hier ein Setup, das in etwa folgende Rahmenbedingungen voraussetzt:
- Template Engine
Da das Frontend der Webseite in einzelne Bausteine untergliedert ist und sich diese Komponenten gegenseitig inkludieren braucht es eine Template Engine. Dabei kann man von reinem PHP bis Twig eigentlich alles verwenden. Ich persönlich schwöre auf Twig und baue die Beispiele auch darauf auf. - SCSS / CSS Kompiler
Ob man nun SCSS oder reines CSS verwendet ist hier um Grunde egal. Wichtig ist nur, dass man pro Komponente später eine eigene Datei hat. Die kann man über eigenen PHP Code, die Template Engine oder über einen SCSS-Kompiler zusammenbauen, bzw. aneinanderreihen. Ich persönlich verwende SCSSPHP und lasse meine.scssDateien zur Laufzeit automatisch von PHP generieren (natürlich mit Cache). Das erspart mir einen extra Prozess und erleichtert mir später auch das Deployment.
Was ist Atomic Desing?
Organisation auf Dateiebene
Atomic Design1 besteht aus mehreren Elementen. Das Offensichtlichste ist die Organisation der Templates. Statt selbst ausgedachter Kategorien und Ordnerstrukturen folgt Atomic Design einer allgemeingültigen immer gleich funktionierenden Struktur:
- Atoms (Atome)
Die kleinsten Bausteine, aus denen eine Benutzeroberfläche besteht, wie Schaltflächen, Eingabefelder und Labels. - Molecules (Moleküle)
Gruppen von Atomen, die zusammenarbeiten, um eine einfache Funktionalität zu erfüllen, z.B. eine Suchleiste, die aus einem Eingabefeld und einer Schaltfläche besteht. - Organisms (Organismen)
Komplexe Bausteine, die aus einer Kombination von Molekülen und/oder Atomen bestehen und eine größere funktionale Einheit bilden, z.B. eine Navigationsleiste mit Logo, Menü und Suchleiste. - Templates (Vorlagen)
Rahmenstrukturen, die den Anordnung und Beziehung der verschiedenen Organismen innerhalb einer Seite oder eines Layouts definieren. - Pages (Seiten)
Die endgültigen Instanzen, die aus der Implementierung von Templates mit tatsächlichen Inhalten resultieren und die tatsächlichen Seiten einer Anwendung oder Website darstellen.
Dabei hat nun jedes erstellte Element - egal ob Atom, Molekül etc - seinen eigenen Ordner, in dem sich in der Regel die Template-Datei und die SCSS/CSS-Datei befindet.
Meine Empfehlung hierbei: Es muss sich nicht strikt an diese Struktur gehalten werden. Meine Persönliche Abwandlung: "Templates" sind Templates für die Ganze Seite und darüber habe ich "Pages" durch "Frame" ersetzt - also dem Seitenrahmen. Aber hier kann man durchaus je nach Projekt und eigenen Vorlieben abwandeln. Nur an den ersten drei (Atome, Moleküle und Organismen) sollte man evtl. nichts ändern.
Include Stuktur / Abhängigkeiten
Die Ordnerstruktur zielt im Grunde genommen darauf ab die Komponenten immer kleiner werdend zu Kategorisieren und über Includes möglichst jeweils kleinere Komponenten in größeren Einzubinden.
Komponenten sind in sich geschlossen
Includes werden möglichst immer mit with only genutzt. Das sorgt dafür, dass nur die übergebenen Variablen innerhalb des inkludierten Templates zur Verfügung stehen.
{% include '01_atoms/a-link/a-link.twig' with {
url : 'https://example.com',
content : 'Ein Link'
} only %}
Das hat den Vorteil, dass die einzelnen Komponenten nicht durch Datenmüll von außen beeinflusst werden und wirklich nur die explizit übergebenen Daten zur Verfügung stehen. Seiteneffekte von außen sind damit weitestgehend eliminiert. Und da das inkludierte Template nur mit den jeweils übergebenen Daten arbeitet, ist es auch schön isoliert und wiederverwendbar.
Gekapseltes Styling der Komponenten
Oft kommt es vor, dass Komponenten in einem jeweils anderen Kontext unterschiedlich gestyled werden müssen. Hierfür wird eine Klasse von der Elternvorlage in die inkludierte Vorlage übergeben:
m-figure.twig
{% include '@snippets/01_atoms/a-link/a-link.twig' with {
'href' : src_original,
'content' : '',
'icon_before' : 'zoom-in',
'class' : 'm-figure__zoom',
} only %}
Über ein Konstrukt, dass am Anfang jedes Templates steht, wird die Klasse mit dem Klassennamen des eigentlichen Templates verbunden / übergeben.
a-link.twig
{% set class = [class|join(" "), 'a-link']|filter(v=>v!='')|join(" ") %}
<a {# ... #} class="{{ class }}">
{# ... #}
</a>
Die Klasse für den a-Tag ist also: 'a-link m-figure__zoom'. Es kann also in der scss-Datei der m-figure Vorlage Einfluss auf den Link genommen werden - ohne dass Abhängigkeiten und Chaos in der SCSS-Datei des a-link Atoms entsteht.
Was ist BEM?
BEM (Block Element Modifier) ist ein Namensschema für CSS-Klassen. Es soll die Struktur klar und konsisten halten und vermeidet Kollisionen und Seiteneffekte.
Bestandteile
Grob Zusammengefasst sieht das Namensschema so aus:
.block__element--modifier
Block
Ein Block ist ein wiederverwendbares Modul, bzw. eine Komponente. In der Regel kommt im Atomic design der Block genau einmal vor und wird dann ansonsten quasi als Prefix verwendet. Ein Beispiel:
<div class="a-pill">
<div class="a-pill__graphic">
{% include "@snippets/01_atoms/a-svg-icon/a-svg-icon.twig" with {
'symbol' : symbol,
'class' : 'a-pill__svg',
} only %}
</div>
<div class="a-pill__content">
{{ content }}
</div>
</div>
Das Beispiel zeigt eine vereinfachte Version einer der Komponenten hier im Blog. a-pill wäre der Block. Diese Klasse umfasst die gesamte Komponente und kommt genau einmal vor. Entsprechend eingefasst ist auch die .scss Datei:
.a-pill {
// Alle Styles für diese Komponente kommen hier rein.
}
Element
Ein Element ist dann ein Bestandteil eines Blocks und hat diesen immer als Prefix vorne angehängt. Jedes Element innerhalb einer Komponente wird dann direkt benannt und angesprochen. Hier gibt es zwei Elemente: __graphic und __content um jeweils das Bild und den Text zu stylen. In der .scss Datei sieht das dann so aus:
.a-pill {
// Styles für die Komponente im allgemeinen
&__graphic {
// Styles für das Bild
}
&__content {
// Styles für den Text
}
}
Modifier
Modifier sind im Grunde genommen suffixe, die benutzt werden um Abwandlungen immer gleicher Elemente zu ermöglichen. Zum Beispiel ein Button, der im Grunde genommen immer gleich aussieht, jedoch je nach Funktion oder Zustand eine andere Icon-Farbe bekommen soll.
.a-button {
border: 0.1rem solid black;
&__icon {
color: black;
&--red {
color: red;
}
&--blue {
color: blue;
}
}
&__text {
}
}
-
Atomic Design wurde ursprünglich erdacht von Brad Frost (Atomic Design Website von Brad Frost) ↩