Webkomponenten sind benutzerdefinierte Elemente, Shadow DOM und HTML-Vorlagen: die Plattformprimitive für wiederverwendbare, frameworkunabhängige Komponenten, deren Markup und Stile vom Dokument gekapselt sind. Sie können sie für Designsysteme, einbettbare Widgets und eigenständige Benutzeroberflächen verwenden.
Die folgenden Regeln sind übergreifend. Rufen Sie den spezifischen Leitfaden für die jeweilige Aufgabe ab. Jeder Leitfaden enthält den erforderlichen Kontext und dokumentiert die APIs und Fallstricke, die für die Aufgabe spezifisch sind.
Entscheiden, ob Sie überhaupt ein benutzerdefiniertes Element benötigen
Verwenden Sie ein benutzerdefiniertes Element nur, wenn Sie ein gekapseltes Verhalten, einen Lebenszyklus oder eine wiederverwendbare JS-basierte API für ein Tag benötigen. Erstellen Sie in folgenden Fällen keine:
- Ein einfaches HTML-Element mit CSS reicht aus. Ein gestaltetes
<button>- oder<details>-Element ist barrierefreier und günstiger als eine Neuimplementierung. - Das Ding dient nur der Darstellung und hat kein Verhalten. Eine Klasse für ein natives Element ist leistungsfähiger und kann mit
@layeroder@scopeeingeschränkt werden. - Sie benötigen nur die Stilbeschränkung auf dem Server ohne Interaktivität. Ein beschränktes Stylesheet oder ein deklaratives Shadow-DOM (ohne Upgrade) kann ausreichen.
Das Neuerstellen nativer Semantik (ein benutzerdefiniertes <my-button>, das Fokus, Aktivierung und ARIA neu erstellt) ist der häufigste Missbrauch: Sie erhalten die Barrierefreiheit der Plattform nur dann kostenlos, wenn Sie das native Element wiederverwenden. Wenn ein Wrapper im <my-button>-Stil erforderlich ist – normalerweise für gemeinsame Styling-Hooks, zusätzliches Markup oder Verhalten, das ein natives Element nicht ausdrücken kann –, sollten Sie das echte <button>-Element umschließen, anstatt es neu zu implementieren. Informationen zum Weiterleiten von Stilen an ein internes natives Element finden Sie unter Webkomponenten gestalten.
Die Frage ist, ob Sie einen gekapselten Zustand, eine skriptfähige API oder ein Verhalten benötigen, das das native Element nicht ausdrücken kann – nicht nur ein anderes Aussehen. Ein Like-/Favoriten-Umschalter ist ein gutes Beispiel: Er enthält einen gesperrten Domainstatus, stellt diesen Status als Styling-Hook für die umgebende Seite bereit und sendet ein eigenes change-Ereignis. All das bietet ein gestyltes <button> nicht. Eine Schaltfläche, die nur beim Drücken die Farbe ändert, reicht nicht aus. Verwenden Sie <button> und CSS. Wenn Sie ein benutzerdefiniertes Element erstellen, modellieren Sie seinen Status mit einem benutzerdefinierten Status oder Attribut (siehe Benutzerdefinierte Elemente).
Tool an die Codebasis anpassen
Ob etwas ein benutzerdefiniertes Element sein sollte, hängt davon ab, was im Projekt bereits verwendet wird. Agenten neigen dazu, zu stark zu abstrahieren und zu schnell nach einem neuen Element zu greifen. Im Zweifelsfall ist die einfachere Option (natives Element, CSS, eine Servervorlage) in der Regel die richtige. Sie können später zu einem benutzerdefinierten Element wechseln, wenn die Wiederverwendung oder das Verhalten dies tatsächlich erfordert. Heuristik, in grober Priorität:
- Die Codebasis hat bereits ein Komponentenmodell (React, Vue, Svelte usw.). Verwenden Sie standardmäßig die Komponente dieses Frameworks für die In-App-UI. Verwenden Sie ein benutzerdefiniertes Element nur, wenn das Element das Framework verlassen oder überdauern muss: ein Designsystem-Primitiv, das von mehreren Anwendungen oder Frameworks gemeinsam genutzt wird, ein Widget, das in Markup eingefügt wird, das nicht vom Framework gesteuert wird, oder eine Ausgabe, die als einfaches HTML funktionieren muss (E‑Mail, CMS, SSR-Fragmente). Richten Sie kein paralleles Komponentensystem für gewöhnliche In-App-Ansichten ein.
- Keine Tools, hauptsächlich statisches Markup. Verwenden Sie einfaches HTML + CSS oder eine Servervorlage bzw. ein Teil-Snippet. Ein benutzerdefiniertes Element ist nur dann sinnvoll, wenn Sie wirklich ein Verhalten, einen Lebenszyklus oder eine JS-gestützte API für das Tag benötigen – nicht nur, um Markup zu teilen, was ein Server-Include oder
<template>ohne JS erledigt. - Generator für statische Websites mit interaktiven Bereichen. Rendern Sie das Markup statisch und reservieren Sie das benutzerdefinierte Element für den interaktiven Teil. Bei „viel Markup, wenig Interaktivität“ sollten Sie das Markup im Light DOM (oder Declarative Shadow DOM) belassen und es durch ein kleines benutzerdefiniertes Element schrittweise verbessern lassen, anstatt alles über JS neu zu rendern.
- Stattdessen könntest du ein integriertes Element weiter ausbauen. Es ist in der Regel besser, ein natives Element so weit wie möglich zu nutzen (CSS, einige Listener, gelegentlich ein Pseudoelement), als ein benutzerdefiniertes Element zu verwenden, da Sie so die Barrierefreiheit und das Verhalten der Plattform kostenlos erhalten. Wechseln Sie zu einem benutzerdefinierten Element, wenn die Problemumgehungen nicht mehr wartbar sind – wenn Sie gegen das integrierte Verhalten des Elements kämpfen oder die Erstellungserfahrung schlechter wird, als es eine saubere Komponenten-API wäre. Dass ein integriertes Element ein wenig JS benötigt, ist an sich kein Grund, es zu ersetzen.
Übergreifende Regeln
Diese gelten für alle nachfolgenden Anleitungen:
- ERFORDERLICH: Der Tag-Name muss in Kebab-Case-Schreibweise mit mindestens einem Bindestrich angegeben werden (z.B.
my-element). Dadurch werden benutzerdefinierte Elemente von aktuellen und zukünftigen integrierten Elementen unterschieden. - ERFORDERLICH: Rufen Sie zuerst
super()im Konstruktor auf, bevor Siethisverwenden. Wenn Sie es nach einer Anweisung aufrufen, diethisliest oder schreibt, wird ein Fehler ausgegeben. - OBLIGATORISCH: Verwenden Sie semantische Elemente (
<button>,<nav>,<ul>) in Shadow-Vorlagen. Die meisten Fehler bei der Barrierefreiheit im Internet in Shadow-Bäumen stammen von nicht semantischem Markup. Ein Slot ändert nicht die Rolle des projizierten Elements. - OBLIGATORISCH: ARIA-
id-Referenzen (aria-labelledby,aria-describedby,aria-controls) dürfen keine Schattengrenze überschreiten. Das referenzierende Element und dieid, auf die es verweist, müssen sich im selben Baum befinden. - Verwenden Sie
attachShadow({ mode: 'open' }), damit Entwicklertools, Seitenskripts und Server-Hydration aufelement.shadowRootzugreifen können. Verwenden Sie'closed'nur, wenn Sie die interne Struktur vor Seitenskripts verbergen müssen. - Vermeiden Sie angepasste integrierte Elemente (
extends HTMLButtonElementmitis="…"). Safari hat sich dauerhaft geweigert, sie zu unterstützen.
Matrix der Anwendungsfälle
Identifizieren Sie die Aufgabe und rufen Sie die entsprechende Anleitung ab:
| Wenn Sie… | Abrufen |
|---|---|
| Neues semantisches Tag mit Verhalten und Lebenszyklus definieren | Benutzerdefinierte Elemente |
| Stile und Markup auf eine Komponente beschränken und Nutzerinhalte in diese Komponente projizieren | Shadow DOM, Templates & Slots |
| Shadow-Tree einer Komponente auf dem Server (SSR) ohne Flash of Unstyled Content (FOUC) rendern | Benutzerdefinierte Elemente vorab rendern |
Stile über die Schattengrenze hinweg (Theming, ::part, benutzerdefinierte Eigenschaften) |
Webkomponenten gestalten |
Wert eines benutzerdefinierten Steuerelements an ein umgebendes <form> senden |
Form-Associated Custom Elements |
| Benutzerdefiniertes Element barrierefrei machen (Rollen, Fokus, ARIA über Shadow-Roots hinweg) | Barrierefreie Webkomponenten |