2006 veröffentlichten Tim Berners-Lee und Noah Mendelsohn ein W3C TAG Finding namens «The Rule of Least Power». Die Kernidee ist verblüffend einfach: Wähle bei der Entwicklung einer Lösung die am wenigsten mächtige Sprache oder Technologie, die für die Aufgabe geeignet ist.
Es geht nicht darum, primitiv zu sein. Es geht um Hebelwirkung. Je weniger mächtig das Werkzeug, desto mehr kann die Plattform für dich tun — Barrierefreiheit, Performance, Resilienz und Portabilität sind eingebaut. In dem Moment, in dem du zu JavaScript greifst, um etwas zu tun, was HTML bereits kann, übernimmst du Verantwortung, die der Browser kostenlos übernommen hätte.
Nach 16 Jahren Frontend-Entwicklung habe ich festgestellt, dass dieses Prinzip einer der durchgängig wertvollsten Leitfäden in meiner Arbeit ist. Es ist der Grund, warum ich hinterfrage, wenn Teams nach einer Datepicker-Library, einer Modal-Komponente oder einem Formularvalidierungs-Framework greifen — wenn der Browser das alles bereits mitbringt.
Die Power-Leiter
Denke an Frontend-Technologien als eine Leiter zunehmender Mächtigkeit — und zunehmender Kosten:
HTML → CSS → JavaScript → Framework (React, Vue) → Library → Zusätzliches Framework/Meta-Framework.
Jede Stufe, die du aufsteigst, bringt: Bundle-Grösse, Wartungsaufwand, Abstraktionslecks, potenzielle Bugs und Lernaufwand. Die Regel besagt: Bleibe so weit unten auf der Leiter, wie es das Problem erlaubt. Wenn HTML es kann, hör dort auf. Wenn du Styling-Logik brauchst, CSS. Wenn du Interaktivität brauchst, Vanilla JS. Greife erst zu React, wenn du Komponentenzustand und Reaktivität brauchst. Füge erst eine Library hinzu, wenn die eingebauten APIs wirklich nicht ausreichen.
Die meisten Teams klettern standardmässig an die Spitze der Leiter und schauen nie nach unten. Sie installieren eine Form-Library, bevor sie ein natives Formular ausprobieren. Sie fügen eine Modal-Komponente hinzu, bevor sie <dialog> entdecken. Sie schreiben JavaScript für Dinge, die CSS seit Jahren beherrscht.
Formularvalidierung: Das am meisten überengineerte Pattern in React
Hier zahlt sich die Regel am meisten aus. Ich habe unzählige React-Codebases gesehen mit Controlled Forms, useState für jedes Feld, eigenen Validierungsfunktionen, Error-State-Management — Hunderte von Codezeilen, die replizieren, was der Browser nativ tut.
HTML-Formularvalidierung ist unglaublich mächtig: required, minLength, maxLength, pattern (volle Regex), type="email", type="url", type="number" mit min/max/step. Der Browser validiert, zeigt barrierefreie Fehlermeldungen, verhindert das Absenden und gibt Fehler an Screenreader weiter. Alles kostenlos.
Kombiniere das mit Reacts Uncontrolled-Form-Pattern (useRef + FormData) und du bekommst das Beste aus beiden Welten: Reacts Komponentenmodell mit der nativen Validierung des Browsers. Null Re-Renders bei der Eingabe, null Validierungszustand zu verwalten, null Bugs durch State-DOM-Synchronisierung.
// ✅ The "least power" way — HTML does the work
function ContactForm({ onSubmit }: { onSubmit: (data: FormData) => void }) {
const formRef = useRef<HTMLFormElement>(null);
return (
<form
ref={formRef}
onSubmit={(e) => { e.preventDefault(); onSubmit(new FormData(formRef.current!)); }}
>
<input name="name" required minLength={2} />
<input name="email" type="email" required />
<button type="submit">Send</button>
</form>
);
}
// Zero state. Zero re-renders. The browser validates for you.
// Works without JavaScript. Accessible by default.Vergleiche das mit dem typischen «React-Weg»:
// ❌ The "React way" — 40 lines for a validated form
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [errors, setErrors] = useState<Record<string, string>>({});
const validate = () => {
const errs: Record<string, string> = {};
if (!name.trim()) errs.name = 'Required';
if (!/^[^@]+@[^@]+\.[^@]+$/.test(email)) errs.email = 'Invalid';
setErrors(errs);
return Object.keys(errs).length === 0;
};
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
if (!validate()) return;
submit({ name, email });
};
// Plus: controlled inputs, error rendering, re-renders on every keystroke…CSS kann mehr als du denkst
Du musst eine Validierungsfehlermeldung nur anzeigen, wenn ein Feld ungültig ist? Schreib kein useState + Conditional Render. CSS-Pseudoklassen wie :invalid, :valid, :placeholder-shown und :user-invalid können das. Kombiniere sie mit dem Adjacent-Sibling-Selektor (+) und du hast Validierungsfeedback ohne JavaScript.
Das :not(:placeholder-shown):invalid-Pattern ist besonders mächtig — es zeigt den Fehler erst, nachdem der Benutzer begonnen hat zu tippen, und vermeidet das rote Aufblitzen beim Seitenaufruf. Das ist eine häufige UX-Anforderung, die Teams mit JavaScript-State lösen. CSS macht es in einer Zeile.
/* Show validation errors with zero JavaScript */
input:not(:placeholder-shown):invalid {
border-color: #d32f2f;
}
/* Show the error message next to an invalid field */
input:not(:placeholder-shown):invalid + .error-hint {
display: block;
}
/* The HTML — that's it */
/* <input type="email" required placeholder="you@example.com" /> */
/* <span class="error-hint">Please enter a valid email</span> */
.error-hint {
display: none;
color: #d32f2f;
font-size: 0.85rem;
}Das <dialog>-Element: Ersetze deine Modal-Library
Das HTML <dialog>-Element bietet: modale und nicht-modale Modi, einen eingebauten Backdrop (stylebar mit ::backdrop), automatisches Focus-Trapping, Escape zum Schliessen, das inert-Attribut für Hintergrundinhalte und einen Rückgabewert über <form method="dialog">.
Das ist alles, was deine Modal-Library tut — nur dass es null Bytes gross ist, null Abhängigkeiten hat, standardmässig barrierefrei ist und bereits im Browser steckt. Jeder moderne Browser unterstützt es. Ich habe 3 verschiedene Modal-Libraries in Produktionscodebases durch <dialog> ersetzt.
<!-- ❌ npm install some-modal-library -->
<!-- Bundle size: +12KB gzipped, new API to learn, z-index wars -->
<!-- ✅ HTML <dialog> — built in, accessible, zero dependencies -->
<dialog id="confirm">
<h2>Are you sure?</h2>
<form method="dialog">
<button value="cancel">Cancel</button>
<button value="confirm">Confirm</button>
</form>
</dialog>
<script>
// Show it
document.getElementById('confirm').showModal();
// Esc to close: free. Focus trap: free. Backdrop: free.
// method="dialog" closes it and gives you the return value.
</script>requestSubmit(), pattern und native Date-Inputs
Drei weitere Beispiele der Regel in Aktion:
requestSubmit() ist die DOM-API, die die meisten React-Entwickler nicht kennen. Anders als form.submit(), das die Validierung umgeht, löst requestSubmit() die native Constraint-Validierung des Browsers aus. Wenn das Formular ungültig ist, zeigt der Browser seine eingebaute Fehler-UI. Wenn gültig, feuert er das Submit-Event. Eine Codezeile ersetzt eine eigene Validierungsfunktion.
Das HTML-pattern-Attribut akzeptiert eine vollständige JavaScript-Regex. Musst du ein Telefonnummernformat validieren? Einen Produktcode? Eine Postleitzahl? pattern macht es deklarativ, mit barrierefreien Fehlermeldungen. Keine Library nötig.
HTML-Date-Inputs sind umstritten, weil sie in verschiedenen Browsern unterschiedlich aussehen. Aber das ist eigentlich ein Feature — auf Mobile lösen sie den nativen Datepicker aus (Scrollrad auf iOS, Kalender auf Android), der jedem JavaScript-Datepicker weit überlegen ist. Ich diskutiere aktiv mit UI/UX-Teams, um das Design um den nativen Input herum zu gestalten, anstatt ihn zu ersetzen. Allein die Mobile-Erfahrung rechtfertigt es, und auf dem Desktop sind die kleinen visuellen Unterschiede ein akzeptabler Kompromiss für null Bundle-Grösse und perfekte Barrierefreiheit.
<!-- HTML pattern attribute — regex validation, no JS needed -->
<input
type="text"
name="phone"
pattern="[0-9+\s]{7,15}"
title="Phone number (digits, spaces, +)"
required
/>
<!-- requestSubmit() — trigger native validation from JS -->
<script>
// Unlike form.submit(), requestSubmit() runs validation first
document.getElementById('myForm').requestSubmit();
// If invalid → shows native error tooltips
// If valid → fires the submit event
</script>
<!-- HTML date input — no datepicker library needed -->
<input type="date" name="birthday" min="1920-01-01" max="2026-01-01" />
<!-- Native on every browser. Better UX on mobile (native date wheel). -->Web Components: Wenn du Wiederverwendbarkeit ohne Framework brauchst
Manchmal brauchst du eine wiederverwendbare Komponente, die frameworkübergreifend funktioniert — oder ganz ohne Framework. Hier glänzen Web Components. Bei AXA habe ich eine unternehmensweite Komponentenbibliothek mit Web Components gebaut, weil die Teams verschiedene Frameworks nutzten. Die Komponenten funktionierten überall: React-Apps, reine HTML-Seiten, CMS-Templates.
Web Components sitzen perfekt auf der Power-Leiter: mächtiger als HTML/CSS allein, aber weniger mächtig (und weniger kostspielig) als eine framework-spezifische Komponentenbibliothek. Sie nutzen das native Komponentenmodell der Plattform — Shadow DOM, Custom Elements, Slots — statt ein virtuelles vom Framework.
Die Rule of Least Power bedeutet nicht «benutze nie mächtige Tools». Sie bedeutet: Benutze sie nicht, wenn ein einfacheres Tool die Aufgabe erfüllt. Web Components sind die richtige Antwort, wenn du frameworkübergreifende Wiederverwendbarkeit brauchst. React ist die richtige Antwort, wenn du komplexes State-Management brauchst. Eine npm-Library ist die richtige Antwort, wenn die native API wirklich nicht ausreicht. Der Schlüssel liegt darin, bewusst nach ihnen zu greifen, nicht standardmässig.
Wie du das morgen anwendest
Bevor du eine Abhängigkeit hinzufügst oder eine eigene Lösung schreibst, gehe die Leiter hoch: Kann HTML das? (required, pattern, type, <dialog>, <details>, <datalist>). Kann CSS das? (:invalid, :has(), scroll-snap, Container Queries). Kann eine native DOM-API das? (requestSubmit, FormData, Constraint Validation API, Intersection Observer). Kann Vanilla JS das ohne Library?
Nur wenn du bei jeder Stufe «nein» geantwortet hast, solltest du zur nächsten Ebene greifen. Das verlangsamt dich nicht — es beschleunigt dich. Weniger Code zu schreiben, weniger zu warten, weniger Bugs, bessere Performance und eingebaute Barrierefreiheit.
Das W3C hat uns das vor zwanzig Jahren gesagt. Der Browser wird seitdem jedes Jahr mächtiger. Das meiste, was du heute per npm installierst, bietet die Plattform bereits. Du musst nur nach unten schauen, bevor du nach oben kletterst.

