DOM-Navigation
Home Assistant nutzt intensiv Shadow DOM. Dadurch lassen sich Komponenten wie <ha-card> oder <ha-icon> gut wiederverwenden. Für CSS-Styling bedeutet es aber, dass Elemente in einem #shadow-root nicht einfach von außen gestylt werden können.
Wenn du Karten im Element-Inspector des Browsers untersuchst, siehst du oft Einträge wie #shadow-root (open). Elemente darin erben nicht automatisch Styles aus dem äußeren DOM.
Um Elemente innerhalb eines #shadow-root zu stylen, muss style: als Dictionary geschrieben werden, nicht nur als String. Jeder Dictionary-Schlüssel wählt ein oder mehrere Elemente über eine angepasste querySelector()-Suche aus. Der Wert wird dann in diese Elemente injiziert.
TIP
Die angepasste querySelector()-Funktion ersetzt ein Dollarzeichen $ durch einen #shadow-root im Selektor.
Der Prozess ist rekursiv. Der Wert eines Eintrags kann also wieder ein Dictionary sein. Ein Schlüssel . wählt das aktuelle Element.
Beispiel: Markdown-Karte
Ziel: Alle Überschriften dritter Ebene ### in einer Markdown-Karte violett färben und den Kartenhintergrund ändern.
type: markdown
content: |-
# Example
## A teal markdown card where h3 tags are purple
### Like thisDas <ha-card>-Element ist die Basis. Von dort muss ein Shadow Root innerhalb von <ha-markdown> durchquert werden. Der Selektor ist:
ha-markdown $:Für den Hintergrund soll direkt auf das Basiselement geschrieben werden:
.:Das vollständige UIX-Styling:
uix:
style:
ha-markdown $: |
h3 {
color: purple;
}
.: |
ha-card {
background: teal;
}
Eine Selektorkette sucht Schritt für Schritt nach Elementen, getrennt durch Leerzeichen oder $. Für jeden Zwischenschritt wird nur der erste Treffer gewählt. Beim letzten Selektor einer Dictionary-Ebene werden dagegen alle passenden Elemente gewählt.
Ketten, die mit $ enden, sind ein Komfortfall: Sie wählen die Shadow Roots aller passenden Elemente.
Verkettungsbeispiel
Dieser Pfad wählt div-Elemente im ersten Marker einer Map-Karte:
ha-map $ ha-entity-marker $ div: |Dieser Pfad wählt div-Elemente in allen Map-Markern, weil die Suche bei ha-entity-marker $ aufgeteilt wird:
ha-map $ ha-entity-marker $:
div: |Lade-Reihenfolge
Home Assistant optimiert die Lade-Reihenfolge stark. Es ist nicht garantiert, dass zum Zeitpunkt der UIX-Suche alle Elemente bereits existieren. Wenn etwas nur manchmal funktioniert, teile die Kette in mehrere Ebenen auf:
ha-map $:
ha-entity-marker $:
div: |So kann UIX später ab dem stabileren Zwischenpunkt erneut suchen.
Express Search Selector $$
Für tief verschachtelte Elemente, vor allem Card Features und More-info Controls, wäre ein vollständiger Pfad durch alle Shadow Roots sehr lang. Der Express-Search-Selektor $$ ist eine Kurzform für eine rekursive, Shadow-DOM-durchdringende Suche durch alle Nachfahren des aktuellen Kontexts.
A $$ B $entspricht sinngemäß:
A $ <zwischen-1> $ <zwischen-2> $ ... B $Die Zwischenstationen werden automatisch gefunden.
TIP
$$ ist ein Selektor für die Pfadmitte. Er muss zwischen zwei Selektorschritten stehen und darf nicht am Anfang eines Pfads stehen.
Card Features: vorher und nachher
Ausführlicher Pfad:
uix:
style:
hui-card-features $:
hui-card-feature $:
hui-humidifier-toggle-card-feature $:
ha-control-select $: |
.container {
opacity: 0.8;
}Mit $$:
uix:
style:
"hui-card-features $$ ha-control-select $": |
.container {
opacity: 0.8;
}Mehrere Card-Feature-Typen
uix:
style:
"hui-card-features $$ ha-control-number-buttons $": |
#input::before {
background: red;
}
"hui-card-features $$ ha-control-select-menu $": |
.select-anchor {
--control-select-menu-background-color: red !important;
}
.select-anchor:hover {
--control-select-menu-background-color: purple !important;
}Performance
Da $$ das gesamte Shadow-DOM-Teilbaumgebiet durchsucht, ist es langsamer als ein expliziter Pfad. Für performancekritische Fälle ist ein genauer Pfad besser.
Lade-Reihenfolge und Retries
Das Retry-Verhalten durch Aufteilen von Ketten gilt pro Dictionary-Eintrag. Bei $$ wird die gesamte Tiefensuche als Einheit wiederholt. Wenn das Ziel sehr spät lädt, ist ein explizit aufgeteilter Pfad stabiler:
uix:
style:
hui-card-features $:
"hui-card-feature $$ ha-control-select $": |
.container { opacity: 0.8; }Host/Element-Pfad-Auswahl
Ein Pfad kann mit einem &-Host/Element als erstem Schritt beginnen. Dieser Schritt filtert das ursprüngliche Element, auf das UIX angewendet wird, bevor Traversal stattfindet.
- Ist das Start-Element ein ShadowRoot, wird gegen den Host dieses Shadow Roots geprüft.
- Ist das Start-Element ein normales Element, wird gegen dieses Element geprüft.
Das wird vor allem in Themes genutzt, um Pfade nur dann anzuwenden, wenn Host oder Element eine bestimmte Klasse, ID, ein Attribut oder eine Property besitzen.
Die Prüfung erfolgt direkt gegen Parent-/Host-Properties, nicht über die CSS-Selector-Engine. Unterstützt werden:
| Token | Prüfung |
|---|---|
tagname | element.localName === 'tagname' |
.classname | element.classList.contains('classname') |
#id | element.id === 'id' |
[attr] | element.hasAttribute('attr') |
[attr=val] | exakter Attributwert |
[attr^=val] | Wert beginnt mit |
[attr$=val] | Wert endet mit |
[attr*=val] | Wert enthält |
[attr~=val] | Whitespace-getrenntes Wort |
[attr|=val] | Wert ist gleich oder beginnt als --Subtag |
{.prop} | element.prop ist nicht null oder undefined |
{.prop=val} | String(element.prop) === val |
{.prop^=val} | String-Wert beginnt mit val |
{.prop$=val} | String-Wert endet mit val |
{.prop*=val} | String-Wert enthält val |
{.prop~=val} | Whitespace-getrenntes Wort im String-Wert |
{.prop|=val} | Wert ist gleich oder beginnt als --Subtag |
Tokens können kombiniert werden, zum Beispiel &ha-dialog.my-class[data-type="video"]. Alle Tokens müssen passen. Leerzeichen außerhalb von Attributklammern und Property-Klammern teilen den Pfad und sind in einem &-Selektor nicht unterstützt. Leerzeichen und $ innerhalb von [...] und {...} gelten als normale Zeichen.
Property-Selektoren navigieren echte JavaScript-Properties über dot-separierte Pfade mit optionalem Chaining, zum Beispiel {.notification.notification_id='1234567'}. Ganze Zahlen werden bei Arrays als Indizes behandelt, etwa {.items.0.name}.
Klassenbasierte Selektoren können zur Lesbarkeit geklammert werden: &(.my-class) entspricht &.my-class.
Dialog stylen
Nur den Inhalt eines Dialogs vom Typ type-hui-dialog-web-browser-play-media stylen:
uix-dialog-yaml: |
"&(.type-hui-dialog-web-browser-play-media) $ ha-dialog-header $": |
section.header-content {
display: none;
}Badge stylen
uix-badge-yaml: |
.: |
:host(.type-power-total) {
--ha-card-border-width: 3px;
--ha-card-border-color: red;
}
"&.type-power-total ha-badge $": |
.badge {
border-style: double !important;
}Attribut-Selektoren mit $= und Punkten
uix-entity-marker-yaml: |
"&[entity-id$='dev']": |
:host {
--uix-image: /local/media/person_grey.png;
}
div.marker {
border-color: red !important;
border-width: 5px;
}uix-entity-marker-yaml: |
"&[entity-id='person.dev']": |
:host {
--uix-image: /local/media/person_grey.png;
}
div.marker {
border-color: red !important;
border-width: 5px;
}Property-Selektoren {.prop}
uix:
style:
"&{.notification}":
".": |
ha-card { opacity: 0.5; }
"&{.notification.notification_id='1234567'}":
".": |
ha-card { border: 2px solid red; }
"&{.type^=light}":
".": |
ha-card { background: yellow; }
"&{.items.0.name='foo'}":
".": |
ha-card { background: teal; }Alle Operatoren wie bei Attribut-Selektoren werden unterstützt: =, ~=, ^=, $=, *=, |=.
DOM-Inspektionshelfer
UIX liefert drei Browser-Konsolenhelfer mit, um gültige Style-Pfade, Forge-Spark-Pfade und die UIX-Hierarchie zur Laufzeit zu verstehen. Öffne die DevTools, wähle ein Element im Elements-Panel aus, sodass es $0 ist, und rufe einen der Helfer auf.
uix_tree($0): allgemeiner Helfer
Zeigt alles, was UIX über den Bereich um das ausgewählte Element weiß.
| Bereich | Inhalt |
|---|---|
| Closest UIX Parent | Nächstes übergeordnetes Element mit nicht-kindlichem uix-node, inklusive Template-Variablen und UIX-Typ. |
| Active UIX Children | Pfade, die aktuell als Kinder des UIX-Parents gestylt werden, inklusive aufgelöster DOM-Elemente. |
| Available YAML Selectors | Alle erreichbaren YAML-Style-Schlüssel innerhalb des Shadow-DOM-Teilbaums des UIX-Parents. |
uix_tree($0)uix_style_path($0): spezifischer Style-Helfer
Zeigt den genauen UIX-Pfad zum ausgewählten Element und erzeugt ein YAML-Grundgerüst.
| Bereich | Inhalt |
|---|---|
| Closest UIX Parent | Wie bei uix_tree. |
| UIX Path to Target | Exakter Pfad mit $ für Shadow-Root-Wechsel vom UIX-Parent zu $0. |
| CSS Target | Tagname, ID, Klassen und vorgeschlagener CSS-Selektor. |
| Boilerplate UIX YAML | Einfügbares Karten-YAML, wenn der Typ direkt über uix: gestylt werden kann. |
| Boilerplate Theme YAML | Einfügbares Theme-YAML; bei Theme-only-Typen ist dies das einzige Grundgerüst. |
uix_style_path($0)uix_path($0) ist ein Kurz-Alias für uix_style_path($0).
uix_forge_path($0): Forge-Helfer
Zeigt den Pfad vom nächsten uix-forge-Element zum ausgewählten Element. Der Pfad kann als Wert für for, before oder after in einer Forge-Spark-Konfiguration genutzt werden.
| Bereich | Inhalt |
|---|---|
| Closest UIX Forge Parent | Nächstes übergeordnetes uix-forge-Element. |
| Forge Path to Target | Selektorpfad mit $ für Shadow-Root-Wechsel vom erzeugten Element zu $0. |
| Boilerplate Spark YAML | Einfügbares Spark-YAML mit dem gefundenen Pfad. |
uix_forge_path($0)WARNING
Wenn du ein Spark-Element desselben Typs hinzufügst, zum Beispiel ein Tile-Icon before ha-tile-icon, achte besonders auf die Dokumentation dieses Sparks. Der Pfad muss spezifisch genug sein, damit UIX bei Updates nicht das Spark-Element selbst auswählt.
Tipps für stabile DOM-Pfade
- Beginne mit dem nächsten stabilen Home-Assistant-Element, nicht mit tiefen zufälligen Klassen.
- Nutze
$, wenn du bewusst in einen Shadow Root wechselst. - Vermeide automatisch erzeugte Klassen oder IDs, wenn sie sich bei Updates ändern können.
- Prüfe Custom Cards nach Updates erneut, wenn interne Markup-Strukturen geändert wurden.
- Nutze
uix_forge_path($0)für Spark-Ziele, weil Spark-Pfade vom Forge-Element aus gedacht sind.
Unterschied zwischen Style-Pfad und Forge-Pfad
| Helfer | Gedacht für | Ergebnis |
|---|---|---|
uix_style_path($0) | normales UIX-Styling | YAML-Grundgerüst für uix: oder Theme-Variablen |
uix_forge_path($0) | Spark-Konfiguration | Pfad für for, before oder after |
uix_tree($0) | Übersicht und Fehlersuche | Eltern, Kinder und verfügbare Selektoren |
Ein Style-Pfad und ein Forge-Pfad können ähnlich aussehen, starten aber in unterschiedlichen Kontexten. Verwende deshalb immer den Helfer, der zur geplanten Konfiguration passt.
Wenn ein Selektor nichts findet
- Prüfe, ob das Element erst später gerendert wird.
- Prüfe, ob ein Shadow Root mit
$übersprungen werden muss. - Prüfe, ob der Pfad vom richtigen UIX-Parent startet.
- Teste eine einfache sichtbare Regel wie
outline. - Erzeuge den Pfad erneut mit dem passenden Browser-Helfer.
uix:
style:
"hui-tile-card $ ha-tile-icon":
".": |
outline: 2px solid red;Wenn diese Regel wirkt, ist der Pfad korrekt und du kannst das eigentliche Styling einsetzen.
