Inline-Styles kein Bad Practice mehr?

Wir befolgen und verbreiten oft universelle Regeln in der Softwareentwicklung. "Verwende niemals Inline-Styles" ist eine davon. Dies stammt jedoch aus einer Zeit, in der HTML statisch war und Inline-Styles daher nur durch Hinzufügen von !important überschrieben werden konnten. In modernerer Entwicklung ist HTML jedoch nicht mehr statisch, sondern das Ergebnis dynamischer Frameworks (Frontend und Backend gleichermaßen), die diesen Hauptnachteil von Inline-Styles beseitigen. Auf diese Weise können wir Vorteile wie Colocation von Struktur und Layout, Lesbarkeit und leichteres Verständnis nutzen, indem wir diese einst verpönte Funktion von HTML nutzen.

Vor ein paar Tagen habe ich ein Gespräch zwischen zwei Entwicklern belauscht. Der Junior der beiden schrieb ein Stück Code, das wohl so oder so ähnlich aussah:

<form style="max-width: 500px;">
  <div style="display: flex; flex-direction: column;">
    <label for="contact-form__name">Name</label>
    <input type="text" id="contact-form__name" name="name">
  </div>
</form>

Der andere Entwickler gab dieses Feedback: "Das sieht schon gut aus, aber Du solltest niemals Inline-Styles in HTML verwenden. Verschiebe diese Styles immer in CSS-Dateien." Der Junior reagierte: "Das wusste ich nicht, danke! Warum sollten wir das so machen?" "Das ist Best Practice", war die Antwort des anderen Entwicklers. Das war das Ende dieses Gesprächs. Ich begann, darüber nachzudenken.

Nur ein Entwickler der dunklen Seite kennt nichts als Extreme

Wir Entwickler mögen es, Regeln zu haben. Besonders die absoluten: Benutze niemals eval, benutze nicht == und verwende niemals Inline-Styles. Diese Regeln geben uns klare Richtlinien und machen uns das Leben leichter. Keine Diskussion, kein Kontext erforderlich, einfach und klar. Am besten konfigurieren wir einen Linter der verhindert, dass wir diese Patterns jemals in unserem Code verwenden. Das kann zu einem Problem werden, wenn wir vergessen, warum es diese "Regeln" gibt oder was die Beweggründe hinter ihrer Enstehung waren. Aus diesem Grund möchte ich heute einen genaueren Blick auf Inline-Styles werfen.

Vorteile von Inline-Styles

Zunächst möchte ich auf die Vorteile eingehen, die wir durch Inline-Styles gewinnen können. Wir brauchen schließlich keine Technologie einzusetzen, wenn andere strikt überlegen sind.

Colocation

Ein Prinzip in der Software-Entwicklung ist Colocation: Code, der sich zusammen ändert, sollte so nah wie möglich beieinander sein. Das macht es uns leichter, alle erforderlichen Änderungen für eine neue Funktion oder eine neue Anforderung zu überblicken. Stellen wir uns eine Codebase vor, in der eine HTML-Datei oder Komponente bearbeitet werden soll, die 5 oder 6 Ebenen tief in der Verzeichnisstruktur versteck ist.

In einem traditionellen Entwicklungs-Workflow hätten wir eine globale oder eine danebenliegende CSS-Datei, die den Inhalt der HTML-Datei formattiert. Je größer der Abstand zwischen diesen Dateien ist, desto wahrscheinlicher ist es, dass wir vergessen, alle Klassen, die in der HTML-Datei verwendet wurden, zu ändern oder zu entfernen. Wenn sich die CSS-Datei direkt neben der HTML-Datei befindet, öffnen wir sie nebeneinander und können leicht vergleichen.

- HTML
  - layouts
    - head
      - navigation
        - links
          - links.html
- CSS
  - layouts
    - head
      - navigation
        - links
          - links.css

Im anderen Extrem würden wir die Verzeichnisstruktur spiegeln, damit die CSS-Dateien genauso angeordnet sind, wie die HTML-Files. In diesem Fall müssten wir die gleichen 5 oder 6 Ordner parallel öffnen, um an die entsprechende CSS-Datei zu gelangen. Ich würde wahrscheinlich vergessen, diese Datei zu überprüfen, wenn ich nur eine kleine Änderung an der HTML-Datei vornehmen müsste.

In unserem Beispiel von vorhin wird die Colocation auf die Spitze getrieben: Alle Strukturen und Styles befinden sich direkt nebeneinander in derselben Datei. Jedes Element hat seine eigenen Styles, die direkt an es selbst angehängt sind. Es ist absolut unmöglich, Styles für Elemente zu vergessen, die nicht mehr verwendet werden, und nahezu unmöglich, die Styles nicht anzusehen, wenn man nur um einige Elemente bewegen oder bearbeiten muss. Dies hat den zusätzlichen Vorteil, dass man diesen Code nehmen, ihn in eine völlig andere Datei verschieben oder kopieren kann und er genau gleich funktioniert, da es keine externen Abhängigkeiten gibt.

Colocation macht es einfacher, Code zu ändern, zu verschieben oder zu löschen, da voneinander abhängende Dinge nebeneinander liegen.

Einfache CSS-Regeln

CSS-Utility-Bibliotheken wie Tailwind oder bis zu einem gewissen Grad Bootstrap folgen ebenfalls dem Colocation-Prinzip:

<div class="md:flex">
  <div class="md:flex-shrink-0">
    <img
      class="rounded-lg md:w-56"
      src="https://some.image.com"
      alt="Woman paying for a purchase"
    >
  </div>
  <div class="mt-4 md:mt-0 md:ml-6">
    <div class="uppercase tracking-wide text-sm text-indigo-600 font-bold">
      Marketing
    </div>
    <a
      href="#"
      class="block mt-1 text-lg leading-tight font-semibold text-gray-900 hover:underline">
        Finding customers for your new business
    </a>
    <p class="mt-2 text-gray-600">
      Getting a new business off the ground is a lot of hard work.
      Here are five ideas you can use to find your first customers.
    </p>
  </div>
</div>

Hier muss man nur all diese Utility-Klassen lernen und kann sie dann im HTML-Code einstreuen und erhält eine vollständig gestylte Anwendung, ohne eine CSS-Datei berühren zu müssen. Das einzige Problem ist, dass man diese völlig neue "Sprache" lernen muss. Ja, sie verwendet zum Teil das gleiche Vokabular wie CSS, fügt aber auch Abkürzungen (wie mt für margin-top) und andere Konventionen (wie Mediaqueries md für mittlere oder lg für große Bildschirme) hinzu.

Wenn man sich an Inline-Styles hält, macht man einen Kompromiss zwischen einer kürzeren Lernkurve und einer feineren Kontrollmöglichkeit mit Inline-Styles und knapper Syntax, mehr Funktionen (Hover-Styles oder Mediastyles sind mit Inline-Styles nicht möglich) und einem einheitlichen Designsystem mit den CSS-Utilities.

Dynamische Inline-Styles

Das Hauptproblem bei den Inline-Styles war immer die fehlende Möglichkeit, die definierten Styles zu überschreiben, da sie in der CSS-Cascade eine sehr hohe Priorität haben. Dies wird zu einem Problem, wenn man wiederverwendbare Komponenten mit Inline-Styles erstellt, da Benutzer dieser Komponente nicht in der Lage sind, die Styles je nach Kontext zu verändern. Dies war jedoch in Zeiten von größerer Bedeutung, in denen das geschriebene HTML statisch war. Web-Frameworks kehren diesen Nachteil gewissermaßen um. Werfen wir einen Blick auf die folgende React-Komponente (React ist hier nur beispielhaft verwendet, gleiche Konzepte wären mit Angular oder Vue möglich):

import { CSSProperties } from "react";
import theme from "./theme.ts";

function LoadingButton(props: {
  onClick: () => Promise<any>;
  styles?: CSSProperties;
}) {
  /**
   * Some logic here
   */

  return (
    <button style={{ background: theme.colors.background, ...props.styles }}>
      Load
    </button>
  );
}

In diesem Fall haben wir weiterhin Inline-Styles verwendet, ohne die Vorteile der CSS-Welt zu vernachlässigen: Wir können Variablen aus unserem importierten Theme verwenden und uns damit an einen konsistenten Styleguide halten. Darüber hinaus überschreiben wir unsere selbstdefinierten Styles mit den Styles aus den Props und erhalten daher unsere Erweiterbarkeit der Styles:

import theme from "./theme.ts";

function App() {
  return (
    <>
      <LoadingButton onClick={loadData} />
      <LoadingButton onClick={loadData} styles={{ fontSize: "2em" }} />
      <LoadingButton
        onClick={loadData}
        styles={{ background: theme.colors.danger }}
      />
    </>
  );
}

Durch die Kapselung von HTML in Komponenten oder Partials brauchen wir die Inline-Styles auch nicht zu wiederholen, wie es in statischen HTML-Dateien der Fall war. Ein weiterer Nachteil wurde behoben. Die einzigen noch fehlenden Merkmale von Inline-Styles sind Mediaqueries und Pseudo-Selektoren. Wenn diese also benötigt werden, muss man also weiterhin auf andere Ansätze zurückgreifen. (Zum Beispiel auf CSS-in-JS)

Fazit

Dieser Artikel sollte nicht davon überzeugen, im nächsten Projekt nur noch Inline-Styles zu verwenden. Er sollte vor Allem zum Nachdenken über Annahmen und Regeln anregen. Wenn Du das nächste Mal ein Stück Code reviewst und etwas findest, das Dir nicht gefällt, denk doch kurz über die Gründe sowie Vor- und Nachteile der gezeigten Lösung nach. Und um das wichtigste nicht zu vergessen: Teile diese Überlegungen beim Review mit deinen Kollegen, damit auch sie lernen, Regeln nicht blind zu befolgen.