Kapitel 4. HTML
Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com
Eine der ersten Herausforderungen, mit denen du als Frontend-Architekt konfrontiert wirst, ist der Umgang mit dem Markup, das deine Entwickler schreiben und dein CMS produzieren soll. Das Web existiert nicht ohne HTML. Wenn du CSS und JavaScript entfernst, bleiben dir nur noch rohe Inhalte und Steuerelemente.
Text, Bilder, Links, Formulare und Schaltflächen zum Absenden: Das ist alles, was das Web wirklich braucht, und es ist die Grundlage für alles, was du jemals im Web erstellen wirst. Wenn du mit schlechtem Markup anfängst, wirst du schlechtes CSS und schlechtes JavaScript schreiben, um es auszugleichen. Wenn du mit gutem Markup anfängst, wirst du in der Lage sein, skalierbares und wartbares CSS und JavaScript zu schreiben.
Das Markup der Vergangenheit des Webs
Es ist noch gar nicht so viele Jahre her, dass unsere Herangehensweise an HTML-Markup dem Layout einer Broschüre, eines Magazins oder einer Zeitung sehr ähnlich war. Das ist auch nicht weiter verwunderlich, denn viele von uns kommen aus dem Printbereich oder haben mit Designern oder Produktverantwortlichen zusammengearbeitet, die einen Print-Hintergrund haben. Bevor Responsive Design zum Standard wurde (und auch noch einige Zeit danach), wurden die meisten Webangebote wie ein mehrseitiges Printprojekt behandelt. Wenn die Arbeit aufgeteilt wurde, wurde dir eine Seite zugewiesen und du hast oben angefangen und dich durch das DOM nach unten gearbeitet.
In der Vergangenheit fiel unser Markup in der Regel in eines von zwei Lagern: prozedural oder statisch. Schauen wir uns das mal an.
Prozedurales Markup: 100% Automatisierung, 0% Kontrolle
In der Welt des Webpublishings war es üblich, dass das Frontend-Team wenig bis gar keine Kontrolle über das Markup hatte. Das lag oft daran, dass die Entwicklung der Funktionen (zu denen auch die HTML-Ausgabe gehörte) Wochen oder sogar Monate vor dem Frontend-Team stattfand. Erschwerend kam hinzu, dass die Ursprünge des Markups durch komplexe Rendering-Prozesse verschleiert wurden und nicht aus einer einzigen Vorlage stammten. Das bedeutete, dass die Aktualisierung des Markups für jeden, der nicht mit den Komplexitäten des CMS-Backends vertraut war, extrem schwierig war. Außerdem hatten die Backend-Entwickler zu diesem Zeitpunkt bereits andere Aufgaben übernommen und hatten selten Zeit, größere Änderungen vorzunehmen.
Diese Einschränkung hat zur Folge, dass CMS und Backend-Entwickler zu viel Markup und zu viele Klassen verwenden würden, um dem "Themer" die Möglichkeit zu geben, sich in das HTML einzuklinken. Am Ende würden wir das hier bekommen:
<div
id=
"header"
class=
"clearfix"
>
<div
id=
"header-screen"
class=
"clearfix"
>
<div
id=
"header-inner"
class=
"container-12 clearfix"
>
<div
id=
"nav-header"
role=
"navigation"
>
<div
class=
"region region-navigation"
>
<div
class=
"block block-system block-menu"
>
<div
class=
"block-inner"
>
<div
class=
"content"
>
<ul
class=
"menu"
>
<li
class=
"first leaf"
>
<a
href=
"/start"
>
Get Started</a>
Dieser kleine Ausschnitt, der direkt von der Drupal.org-Homepage stammt, zeigt, wie dein Header 10 Verschachtelungsebenen haben kann, bevor er zu einem einzigen Inhalt kommt. Das Traurige daran ist, dass dies ein relativ zahmes Beispiel ist! Aus Erfahrung kann ich dir sagen, dass es noch viel weiter gehen kann.
Diese "Div-Suppe" hat uns vielleicht geholfen, als unsere Aufgabe darin bestand, eine statische Photoshop-Komposition an eine Seite voller Markup anzupassen, aber als unsere Bedürfnisse reifer wurden, sehnten wir uns nach mehr Kontrolle.
Statisches Markup: 0% Automatisierung, 100% Kontrolle
Wenn wir an einem kleinen Projekt arbeiteten oder einfach nur eine Seite hatten, auf der wir ein großes Feld ausfüllen mussten, war es ziemlich einfach, das Markup zu kontrollieren. Diese Situation bot zwar große Flexibilität, bedeutete aber auch, dass wir für die Pflege des gesamten Codes verantwortlich waren. Änderungen, die in einem CMS-Template einfach zu bewerkstelligen sind, mussten von Hand auf alle Seiten übertragen werden. Also schrieben wir Markup wie folgt:
<header>
<section>
<nav>
<div>
<ul>
<li>
<a
href=
"/products"
>
Products</a>
<ul>
<li>
<a
href=
"/socks"
>
Socks</a>
Um die Dinge einfach zu halten, wurde ein "semantisches" Markup bevorzugt, das sich auf HTML5-Elemente und ihre relative Positionierung stützt, um Stile anstelle von Klassen anzuwenden. Ohne Klassen in unserem Markup und aufgrund der Tatsache, dass die Stile der primären Navigation auf die sekundären Navigationsanker abfärbten, endeten wir oft mit langen Selektorenketten wie dieser:
header > section > nav > div > ul > li > a { color: white; } header > section > nav > div > ul > li > ul > li > a { color: blue; }
Dieser Alptraum der Spezifität sorgte dafür, dass jeder Selektor, den wir für unsere Hover- und Aktiv-Zustände geschrieben haben, mindestens so lang war. Du willst gar nicht sehen, wie die tertiäre Navigation gestylt ist. Lassen wir die Vergangenheit hinter uns und werfen wir einen Blick auf aktuelle Praktiken.
Ein Gleichgewicht zwischen Kontrolle und Automatisierung finden
Als der Frontend-Architekt musst du die Prozesse bewerten, die dein Markup erzeugen. Wie viel Kontrolle hast du über die Reihenfolge der Inhalte, die verwendeten Elemente und die CSS-Klassen, die auf sie angewendet werden? Wie schwierig wird es sein, diese Dinge in Zukunft zu ändern? Sind Templates leicht zugänglich, oder musst du einen Backend-Entwickler mit der Änderung beauftragen? Basiert dein Markup überhaupt auf einem Templating-System? Kannst du Änderungen in deinem gesamten System verbreiten oder ist das ein manueller Prozess? Die Antworten auf diese Fragen können deine Herangehensweise an die Erstellung von HTML und CSS grundlegend verändern.
Modulares Markup: 100% Automatisierung, 100% Kontrolle
Der utopische Zustand von, den wir alle anstreben, ist eine Situation, in der jede HTML-Zeile auf unserer Website programmgesteuert erstellt wird, wir als Frontend-Entwickler aber die volle Kontrolle über die Templates und Prozesse haben, die zur Erstellung dieses Markups verwendet werden. Leider wirst du diesen Zustand nicht oft erreichen. Selbst in den besten Fällen gibt es nutzergenerierte Inhalte, die kaum oder gar nicht automatisiert erstellt werden. Unabhängig davon, ob ein CMS in der Lage ist, HTML-Vorlagen bereitzustellen, ist es manchmal einfacher, das CMS das Markup für Dinge wie Formulare oder die Navigation bestimmen zu lassen. Aber selbst wenn du bei 90 % bleibst, bietet dir ein modularer Ansatz für dein Markup die Flexibilität, die du willst, und die Automatisierung, die du brauchst.
Modulares Markup unterscheidet sich von prozeduralem Markup dadurch, dass wir nicht mehr die Macht an das CMS abtreten, um zu bestimmen, welches Markup für die Ausgabe eines bestimmten Inhalts verwendet werden soll. So können wir dasselbe Markup für zwei verschiedene Navigationsinstanzen verwenden, auch wenn das CMS ein völlig anderes Markup verwendet hat. Modulares Markup unterscheidet sich von statischem Markup auch dadurch, dass es programmatisch gerendert wird und es uns erlaubt, ein System von Klassen auf das Markup anzuwenden und uns nicht auf die Tags und die Position der Elemente zu verlassen, um ihr visuelles Erscheinungsbild zu bestimmen. Schauen wir uns das Navigationsbeispiel noch einmal mit einem modularen Ansatz im BEM-Stil an:
<nav
class=
"nav"
>
<ul
class=
"nav__container"
>
<li
class=
"nav__item"
>
<a
href=
"/products"
class=
"nav__link"
>
<ul
class=
"nav__container--secondary"
>
<li
class=
"nav__item--secondary"
>
<a
href=
"/socks"
class=
"nav__link--secondary"
>
Auf den ersten Blick scheint dieser Ansatz ziemlich langatmig zu sein! Das will ich auch gar nicht bestreiten, aber ich behaupte, dass es das perfekte Maß an Ausführlichkeit ist. Mit einer Klasse für jedes Element müssen wir uns nicht mehr auf Styling-Tags oder die Position eines Elements verlassen, um sein Aussehen zu bestimmen. Im Vergleich zum dynamischen Markup ist dieses Markup viel sauberer und - ich wage es zu sagen - "modularer". Dieses Navigationsmuster kann an mehreren Stellen auf der Website verwendet werden, wobei genau dasselbe Markup zum Einsatz kommt. Es handelt sich also nicht um ein Markup, das vom CMS erstellt und dann gestylt wurde, sondern um ein Markup, das erstellt, gestylt und dann in das Navigationssystem der Website integriert wurde.
Alles führt zu einem Designsystem
Wie können wir also mit diesem modularen Ansatz beginnen? Nun, alles beginnt damit, dass wir die Art und Weise ändern, wie wir unsere Seiten erstellen. Du siehst, es gibt keine Seite. Das ist ein Mythos. Eine Website-Seite ist ein Relikt unserer Vergangenheit. Was ist eine Seite? Ist es der Inhalt unter einer bestimmten URL? Was garantiert dir, dass der Inhalt einer bestimmten URL bei jedem Besuch derselbe bleibt? Was passiert, wenn du eingeloggt bist? Was passiert, wenn der Inhalt dieser Seite nach der Tageszeit, deinem Standort oder deiner Surfaktivität gefiltert wird? Je eher wir begreifen, dass wir keine Seiten mehr bauen, sondern Systeme entwerfen, desto eher können wir anfangen, erstaunliche Dinge im Web zu schaffen.
Ein Designsystem ist die programmatische Darstellung der visuellen Sprache einer Website. Die visuelle Sprache, die von unseren Designern erstellt wird, ist ein Artefakt, das ausdrückt, wie die Website ihre Botschaft an die Nutzer visuell vermittelt. Sie ist eine Sammlung von Farben, Schriftarten, Schaltflächen, Bildstilen, typografischen Rhythmen und UI-Mustern, die dazu dienen, Stimmung, Bedeutung und Absicht zu vermitteln.
So wie eine gesprochene Sprache in Substantive, Verben und Adjektive zerlegt werden kann, ist es unsere Aufgabe als Frontend-Entwickler, die visuelle Sprache in ihre kleinsten Teile zu zerlegen. Auf diese Weise können wir Regeln erstellen, wie wir sie wieder zusammensetzen können. Indem wir die visuelle Sprache zerlegen, lernen wir, wie wir unsere sprichwörtlichen Sätze, Absätze, Kapitel und Romane erstellen können. Das Ziel dieser Umwandlung ist es, eine skalierbare und wartbare Codebasis zu schaffen, die alles, was die Sprache ausdrücken kann, getreu wiedergibt.
In späteren Kapiteln werden wir uns näher mit der Erstellung von Designsystemen befassen, aber es ist wichtig, dass wir verstehen, was wir erstellen, denn bevor wir das tun, müssen wir entscheiden, wie das Designsystem mit unserem Markup verbunden werden soll.
Die vielen Gesichter der modularen CSS-Methoden
gibt es heute fast so viele CSS-Methoden wie CSS- oder JavaScript-Frameworks. Aber im Gegensatz zu CSS- oder JavaScript-Frameworks, bei denen es in der Regel um alles oder nichts geht und die einen gewissen Ballast mit sich bringen, handelt es sich bei einer CSS-Methodik eher um eine Philosophie über die Beziehung zwischen HTML und CSS als um eine vorgefertigte Code-Basis.
Es scheint, als ob du fast jeden Tag von einem neuen Ansatz hörst, bei dem ein neuer Namensraum verwendet wird, Datenattribute genutzt werden oder sogar das gesamte CSS in JavaScript verschoben wird. Das Tolle an diesen Methoden ist, dass sie alle etwas Interessantes mit sich bringen und dir helfen, etwas Neues darüber zu lernen, wie HTML und CSS miteinander verbunden werden können.
Es gibt keinen perfekten Ansatz, und du wirst vielleicht feststellen, dass ein Projekt am besten zu einer Methode passt und ein anderes Projekt besser zu einer anderen. Es spricht absolut nichts dagegen, deine eigene Methode zu entwickeln oder mit einer gängigen Methode zu beginnen und sie dann nach deinem Geschmack abzuändern. Wenn du dich also fragst, wo du anfangen sollst, wenn du dich für deine eigene Methode entscheidest, ist es am besten, wenn du dir ein paar der bekanntesten Methoden ansiehst und herausfindest, was zu deinem Projekt passt und was nicht.
OOCSS-Ansatz
Das folgende Snippet zeigt den objektorientierten CSS-Ansatz zur Erstellung eines HTML-Toggles.
<div
class=
"toggle simple"
>
<div
class=
"toggle-control open"
>
<h1
class=
"toggle-title"
>
Title 1</h1>
</div>
<div
class=
"toggle-details open"
>
...</div>
...</div>
Die beiden Hauptprinzipien von OOCSS sind die Trennung von Struktur und Skin sowie die Trennung von Container und Inhalt.
Die Trennung von Struktur und Skin bedeutet, visuelle Merkmale so zu definieren, dass sie wiederverwendet werden können. Das einfache Element toggle
aus dem vorangegangenen Snippet ist klein und in vielen verschiedenen Situationen wiederverwendbar. Es kann mit verschiedenen Skins dargestellt werden, die sein Erscheinungsbild verändern. Der aktuelle Skin "einfach" hat vielleicht quadratische Ecken, aber der Skin "komplex" hat vielleicht abgerundete Ecken und einen Schlagschatten.
Die Trennung von Container und Inhalt bedeutet, dass die Position nicht mehr als Stilmerkmal verwendet wird. Anstatt Tags innerhalb eines Containers zu stylen, solltest du wiederverwendbare Klassen wie toggle-title
erstellen, die die gewünschte Textbehandlung unabhängig davon anwenden, auf welchem Element sie verwendet wird. Auf diese Weise kannst du ein H1 wie das Standard-H1 aussehen lassen, wenn keine Klasse auf es angewendet wird.
Dieser Ansatz ist sehr nützlich, wenn du deinen Entwicklern einen großen Satz von Komponenten zur Verfügung stellen willst, die sie mischen und anpassen können, um ihre UIs zu erstellen. Ein gutes Beispiel für diesen Ansatz ist Bootstrap, ein System voller kleiner Objekte, die mit verschiedenen Skins verziert sind. Das Ziel von Bootstrap ist es, ein komplettes System zu schaffen, mit dem ein Entwickler jede beliebige Benutzeroberfläche erstellen kann, die er braucht.
SMACSS-Ansatz
Die gleiche Umschaltkomponente, die in Scalable and Modular Architecture for CSS geschrieben wurde, würde wie folgt aussehen:
<div
class=
"toggle toggle-simple"
>
<div
class=
"toggle-control is-active"
>
<h2
class=
"toggle-title"
>
Title 1</h2>
</div>
<div
class=
"toggle-details is-active"
>
...</div>
...</dl>
Obwohl SMACSS viele Ähnlichkeiten mit OOCSS aufweist, unterscheidet sich der Ansatz dadurch, dass er das System der Stile in fünf spezifische Kategorien unterteilt:
- Basis
- Wie das Markup ohne die Anwendung von Klassen aussehen würde
- Layout
- Aufteilung der Seiten in Regionen
- Modul
- Die modularen, wiederverwendbaren Teile deines Designs
- Staat
- Beschreibt die Art und Weise, wie Module oder Layouts in bestimmten Zuständen oder Kontexten aussehen
- Thema
- Eine optionale visuelle Ebene, die es dir ermöglicht, verschiedene Themen auszutauschen
Im vorangegangenen Beispiel sehen wir eine Kombination aus Modulstilen (toggle
, toggle-title
, toggle-details
), Submodulen (toggle-simple
) und Zuständen (is-active
). Es gibt viele Gemeinsamkeiten zwischen OOCSS und SMACSS, wenn es darum geht, kleine modulare Funktionseinheiten zu erstellen. Beide legen alle Stile auf eine Root-Klasse fest und wenden dann Änderungen über einen Skin (OOCSS) oder ein Submodul (SMACSS) an. Die wichtigsten Unterschiede zwischen den beiden Ansätzen (abgesehen von der unterschiedlichen Strukturierung des Codes bei SMACSS) sind die Verwendung von Skins anstelle von Submodulen und das is
als Präfix für die Zustandsklassen.
BEM-Ansatz
schließlich ist die gleiche Toggle-Komponente in der Syntax des Block-Element-Modifiers geschrieben.
<div
class=
"toggle toggle--simple"
>
<div
class=
"toggle__control toggle__control--active"
>
<h2
class=
"toggle__title"
>
Title 1</h2>
</div>
<div
class=
"toggle__details toggle__details--active"
>
...</div>
...</dl>
BEM ist die dritte Methode, die wir uns ansehen werden, und liegt auf der anderen Seite des Spektrums von SMACSS. BEM ist einfach eine Konvention zur Benennung von Klassen. Sie schreibt dir nicht die Struktur deines CSS vor, sondern schlägt lediglich vor, dass jedes Element mit einer Klasse benannt wird, die Folgendes beschreibt:
- Block
- Der Name der übergeordneten Komponente
- Element
- Der Name des Elements innerhalb des Blocks
- Modifikator
- Jeder mit dem Block oder Element verbundene Modifikator
BEM verwendet eine sehr knappe Konvention für die Erstellung dieser Klassenstrings, die ziemlich lang werden können. Elemente werden nach einem doppelten Unterstrich eingefügt (z. B. toggle__details
) und Modifikatoren nach einem doppelten Bindestrich (toggle__details--active
). Dadurch wird deutlich, dass details
ein Element und active
ein Modifikator ist. Die Verwendung eines doppelten Gedankenstrichs bedeutet auch, dass dein Blockname news-feed
lauten kann, ohne dass feed
mit einem Modifikator verwechselt wird.
Der Vorteil dieses Ansatzes gegenüber OOCSS oder SMACSS ist, dass jede Klasse vollständig beschreibt, was sie leistet. Es gibt keine open
oder is-active
Klassen. Diese Klassen machen zwar in ihrem Kontext (in einem toggle
Element) Sinn, aber außerhalb dieses Kontexts haben wir keine Ahnung, was open
oder is-active
bedeutet. Ein BEM-Ansatz mag zwar redundant oder übermäßig langatmig erscheinen, aber wenn wir eine Klasse toggle__details--active
sehen, wissen wir genau, was sie bedeutet: Das Element details
innerhalb des Blocks toggle
ist aktiv.
Die Wahl des Richtigen für dich
Am Ende kommt es nur darauf an, eine Lösung zu finden, die für dich funktioniert. Entscheide dich nicht für eine Konvention, weil sie beliebt ist oder weil ein anderes Team sie verwendet. Alle drei Ansätze geben dir sehr ähnliche Werkzeuge an die Hand und lassen sich auf sehr ähnliche Weise in ein Planungssystem integrieren.
Wie ich in Kapitel 7 erläutern werde, haben wir uns bei Red Hat für eine Mischung aus SMACSS und BEM entschieden. Habt also keine Angst zu experimentieren, Ideen zu kombinieren oder euch etwas völlig Neues einfallen zu lassen! Du musst nur den Stand der Technik kennen, in der Lage sein zu erklären, warum dein Ansatz die Herausforderungen deines Projekts lösen wird, und ein Team haben, das bereit ist, sich für einen einzigen, einheitlichen Ansatz einzusetzen. Wenn du dich für OOSMABEM entscheidest, wünsche ich dir viel Erfolg! Ich freue mich darauf, darüber zu lesen.
Get Frontend-Architektur für Designsysteme now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.