Skip to content

Instantly share code, notes, and snippets.

@Mariusthvdb
Last active September 3, 2026 09:14
Show Gist options
  • Select an option

  • Save Mariusthvdb/ecdd6ac6776d501b09e859888b01a9c6 to your computer and use it in GitHub Desktop.

Select an option

Save Mariusthvdb/ecdd6ac6776d501b09e859888b01a9c6 to your computer and use it in GitHub Desktop.
UIX themes
##########################################################################################
# themes_uix.yaml updated 2026.09.03
# @mariusthvdb
# https://gist.github.com/Mariusthvdb
# - Enables individual color customization for view icons.
# - Provides an optional replacement for the default three-dots options menu.
# - Supports configuration of left-side menu icons, including animations, color schemes,
# and notification badges.
# - Allows definition of custom background images or colors to override the default
# theme background.
# - so much more, and growing per release of UIX...
# versions:
# HA 0S 18.2
# HA 2026.9.0
##########################################################################################
# Uix 8.2.0-beta.11
# https://github.com/Lint-Free-Technology/uix
# https://uix-guides.lf.technology
# https://uix.lf.technology/using/themes/
# Auto-entities 2.7.0-beta.1
# https://github.com/Lint-Free-Technology/lovelace-auto-entities
# Template-entity-row 3.0.0
# https://github.com/Lint-Free-Technology/lovelace-template-entity-row
# Fold-entity-row 4.0.0
# https://github.com/Lint-Free-Technology/lovelace-fold-entity-row
##########################################################################################
# Kiosk-mode 14.1.0
# https://github.com/NemesisRE/kiosk-mode
# Custom-sidebar 17.1.0
# https://github.com/elchininet/custom-sidebar
# credits to all mentioned repo code-owners and maintainers without whom none of this
# would be possible, special thanks to @dcapslock for former card-mod fixes and
# development of auto-entities and fold-entity-row and the card-mod replacement Uix
# of course to @thomasloven for having initiated many of this, and whose magic cards have
# been an essential part of my configs for a long time
############################## Links ###################################################
# UIX
# https://community.home-assistant.io/t/ui-extension-add-css-styles-to-almost-any-part-of-the-home-assistant-ui/
# https://community.home-assistant.io/t/uix-forge-template-cards-badges-rows-picture-elements-sections-and-add-sparks/
# Card-mod
# https://community.home-assistant.io/t/card-mod-add-css-styles-to-any-lovelace-card
# https://community.home-assistant.io/t/card-mod-super-charge-your-themes
##########################################################################################
theme-mods:
# https://uix-guides.lf.technology/elements/2026/03/02/css-vars-entities.html
# set custom css vars in the highest element in the Frontend hierarchy
# use them in any styling lower in the hierarchy
uix-top-app-bar-fixed: |
header.top-app-bar {
background: var(--header-background);
--app-header-text-color: var(--app-header-text-color);
}
/* set color of actions to primary to standout from gradient */
section#actions {
color: {{'var(--primary-color)' if licht_thema() else
'var(--card-background-color)'}};
}
uix-drawer: |
:host {
--wa-panel-border-radius: 0px;
{% set isDark = is_state('sun.sun','below_horizon') %}
{# set global variables #}
--darkslateblue-if-dark: {{ 'darkslateblue' if isDark else 'red' }};
--slategrey-if-dark: {{ 'slategrey' if isDark else 'green' }};
--yellow-if-not-dark: {{ 'yellow' if not isDark else 'pink' }};
--orange-if-not-dark: {{ 'orange' if not isDark else 'purple' }};
{% set path = panel.viewUrlPath %}
{% if path == 'plafond_spots' %}
--uix-view-background-image: /local/images/areas/plafond.jpg;
{% endif %}
}
# https://uix.lf.technology/using/themes/#macros
# set for use in this themes_uix file, but also for generic UIX styling anywhere
# see https://uix.lf.technology/using/icons/#full-theme-example
uix-macros-yaml: |
luchtvochtigheid_binnen_base:
params:
- entity_id
template: >
{%- set entityString = entity_id.replace('.','_') -%}
{%- set lvb = states(entity_id)|int(-5) if entity_id|has_value else None %}
--uix-icon-for-{{ entityString }}:
{%- if lvb is none %} mdi:water-alert-outline
{%- elif lvb > 70 %} mdi:water-percent-alert
{%- elif lvb > 50 %} mdi:water-percent
{%- else %} mdi:water-percent-alert
{%- endif %};
--uix-icon-color-for-{{ entityString }}:
{%- if lvb is none %} var(--disabled-color)
{%- elif lvb > 80 %} var(--critical-color)
{%- elif lvb > 70 %} var(--alert-color)
{%- elif lvb > 60 %} var(--warning-color)
{%- elif lvb > 50 %} var(--success-color)
{%- elif lvb > 40 %} var(--alert-color)
{%- else %} var(--critical-color)
{%- endif %};
luchtvochtigheid_binnen:
template: >
{% for entity in label_entities('luchtvochtigheid_binnen') %}
{{ luchtvochtigheid_binnen_base(entity) }}
{% endfor %}
power_color_base:
params:
- entity_id
template: >
{%- set entityString = entity_id.replace('.','_') -%}
--uix-icon-color-for-{{ entityString }}:
{%- set state = states(entity_id)|int(-5) if entity_id|has_value else None %}
{%- if state is none %} var(--disabled-color)
{%- elif state == 0 %} var(--no-power-color)
{%- elif state < 500 %} var(--warning-color)
{%- elif state < 1000 %} var(--info-color)
{%- elif state < 2500 %} var(--success-color)
{%- elif state < 4000 %} var(--alert-color)
{%- elif state < 6000 %} var(--error-color)
{%- else %} var(--critical-color)
{%- endif %};
power_color:
template: >
{% for entity in label_entities('power') %}
{{ power_color_base(entity) }}
{% endfor %}
temp_icon_color_apparaat_base:
params:
- entity_id
template: >
{%- set entityString = entity_id.replace('.','_') -%}
{%- set state = states(entity_id)|float(-5) if entity_id|has_value else None %}
--uix-icon-for-{{ entityString }}:
{%- if state is none -%} mdi:thermometer-off
{%- elif state < 25 -%} cli:thermometer-empty
{%- elif state < 45 -%} mdi:thermometer-low
{%- elif state < 55 -%} mdi:thermometer
{%- elif state < 70 -%} mdi:thermometer-high
{%- elif state < 85 -%} cli:thermometer-full
{%- else -%} mdi:thermometer-alert
{%- endif %};
--uix-icon-color-for-{{ entityString }}:
{%- if state is none %} var(--disabled-color)
{%- elif state < 25 %} var(--info-color)
{%- elif state < 45 %} var(--success-color)
{%- elif state < 55 %} var(--warning-color)
{%- elif state < 70 %} var(--error-color)
{%- elif state < 85 %} var(--alert-color)
{%- else %} var(--critical-color)
{%- endif %};
temp_icon_color_apparaat:
template: >
{% for entity in label_entities('apparaat_temperatuur') %}
{{ temp_icon_color_apparaat_base(entity) }}
{% endfor %}
temp_icon_color_binnen_base:
params:
- entity_id
template: >
{%- set entityString = entity_id.replace('.','_') -%}
--uix-icon-for-{{ entityString }}: mdi:home-thermometer;
--uix-icon-color-for-{{ entityString }}:
{%- set state = states(entity_id)|float(-5) if entity_id|has_value else None %}
{%- if state is none %} var(--state-unavailable-color)
{%- elif state < 10 %} var(--critical-low-color)
{%- elif state < 17 %} var(--info-color)
{%- elif state < 25 %} var(--success-color)
{%- elif state < 30 %} var(--warning-color)
{%- elif state < 35 %} var(--alert-color)
{%- else %} var(--critical-color)
{%- endif %};
temp_icon_color_binnen:
template: >
{% for entity in label_entities('binnen_temperatuur') %}
{{ temp_icon_color_binnen_base(entity) }}
{% endfor %}
temp_icon_color_buiten_base:
params:
- entity_id
template: >
{%- set entityString = entity_id.replace('.','_') -%}
--uix-icon-for-{{ entityString }}: mdi:sun-thermometer;
--uix-icon-color-for-{{ entityString }}:
{%- set state = states(entity_id)|float(-5) if entity_id|has_value else None %}
{%- if state is none %} var(--state-unavailable-color)
{%- elif state < -20 %} black
{%- elif state < -15 %} navy
{%- elif state < -10 %} darkblue
{%- elif state < -5 %} mediumblue
{%- elif state < 0 %} blue
{%- elif state < 5 %} dodgerblue
{%- elif state < 10 %} lightblue
{%- elif state < 15 %} turquoise
{%- elif state < 20 %} green
{%- elif state < 25 %} darkgreen
{%- elif state < 30 %} orange
{%- elif state < 35 %} crimson
{%- else %} firebrick
{%- endif %};
temp_icon_color_buiten:
template: >
{% for entity in label_entities('buiten_temperatuur') %}
{{ temp_icon_color_buiten_base(entity) }}
{% endfor %}
donker_thema:
returns: true
template: "{% do returns(is_state('binary_sensor.donker_thema','on')) %}"
licht_thema:
returns: true
template: "{% do returns(is_state('binary_sensor.donker_thema','off')) %}"
feest:
returns: true
template: "{% do returns(is_state('binary_sensor.feest_alerts','on')) %}"
kerst:
returns: true
template: "{% do returns(is_state('input_select.theme','Kerst')) %}"
lek:
returns: true
template: "{% do returns(is_state('binary_sensor.watermeter_leak_detected','on')) %}"
meteo:
returns: true
template: "{% do returns(is_state('binary_sensor.meteoalarm_brabant','on')) %}"
netto:
returns: true
template: "{% do returns(is_state('binary_sensor.netto_stroom_teruglevering','off')) %}"
pomp:
returns: true
template: "{% do returns(is_state('switch.vijverpompen','on')) %}"
vent:
returns: true
template: "{% do returns(is_state('input_boolean.ventilate','on')) %}"
animations:
template: |
@keyframes blink {
50% { opacity: 0.2; }
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes pulse {
0% { opacity: 0.8; transform: scale(0.2); }
80% { opacity: 0; transform: scale(1.2); }
100% { opacity: 0; transform: scale(2.2); }
}
@keyframes glow {
0% { box-shadow: 0 0 0px var(--warning-color); }
50% { box-shadow: 0 0 4px 2px var(--success-color); }
100% { box-shadow: 0 0 0px var(--warning-color); }
}
@keyframes rotate {
0% {transform: rotate(0);}
100% {transform: rotate(3.142rad);}
}
@keyframes rotation {
0% {transform: rotate(0deg);}
100% {transform: rotate(360deg);}
}
header_style:
template: |
background: var(--header-background);
color: var(--text-color-off);
font-size: 20px;
font-weight: 400;
header_style_important:
template: |
background: var(--header-background);
color: var(--text-color-off) !important;
font-size: 20px !important;
font-weight: 400 !important;
header_spacing:
template: |
padding: var(--ha-space-1) var(--ha-space-3);
margin: 0 0 var(--ha-space-4) 0;
header_spacing_important:
template: |
padding: var(--ha-space-1) var(--ha-space-3) !important;
margin: 0 0 var(--ha-space-4) 0;
uix-badge: |
{{animations()}}
:host {
--ha-card-border-width: 2px ;
--ha-card-box-shadow: var(--box-shadow);
}
:host(.type-power-total) {
--ha-card-border-color: darkblue;
}
:host(.type-power-total) ha-badge {
--badge-color: darkblue;
}
:host(.type-power-total) ha-svg-icon {
animation: {{'1.5s pulse infinite linear' if netto() else 'none'}};
}
:host(.type-gas-total) {
--ha-card-border-color: var(--energy-gas-color);
}
:host(.type-gas-total) ha-svg-icon {
{% set gas = states('sensor.gas_per_kwartier')|int(0) > 0 %}
animation: {{'1.5s pulse infinite linear' if gas else 'none'}};
}
:host(.type-water-total) {
--ha-card-border-color: var(--energy-water-color);
}
:host(.type-water-total) ha-svg-icon {
{% set water = states('sensor.watermeter_usage_last_minute')|int(0) > 0 %}
animation: {{'1.5s pulse infinite linear' if water else 'none'}};
}
uix-config-yaml: |
cloud-account $ cloud-account-overview $: |
/* this hides the thank you card */
ha-card:has(div.thank-you-header) {
display: none;
}
ha-panel-tools $ tools-state $: |
tools-state-renderer {
overflow: clip; /* as table can scroll both x and y, you can only get sticky if you clip both which $
}
ha-panel-tools $ tools-state $ tools-state-renderer $: |
/* Set selector to div.entities div.filters to stick search row */
div.entities div.filters {
/* div.entities div.row:nth-of-type(2) { */
position: sticky;
top: 0; /* Maybe 1px or 2px if you want a slight gap at the top */
z-index: 9; /* show above search boxes */
/*background: var(--table-row-background-color);*/
}
/*div.entities lit-virtualizer {
overflow: auto; /* scroll the actual data rows but header stays fixed */
}*/
ha-panel-tools $:
tools-yaml-config $ ha-card $: |
:host {
--ha-card-box-shadow: var(--box-shadow);
}
:host .card-header {
{{header_style()}}
{{header_spacing()}}
}
tools-yaml-config $ ha-card[header^=YAML]:
.: |
.card-content {
position: sticky;
top: -4px;
z-index: 1;
background: var(--card-background-color);
}
$: |
:host slot {
display: block;
/* apply to card-content to not leave a gap for scrolling items to bleed through */
padding-top: 0px;
height: 600px;
overflow: scroll;
color: var(--warning-color);
font-weight: 600;
}
.: |
/* explicitly set these vars from main theme for light team (has reversed colors */
/*{% if donker_thema() %}
partial-panel-resolver > :first-child {
--app-header-background-color: var(--header-background-color) !important;
--app-header-text-color: var(--app-header-text-color) !important;
}
{% endif %}*/
:host {
{{ luchtvochtigheid_binnen() }}
{{ temp_icon_color_apparaat() }}
{{ temp_icon_color_binnen() }}
{{ temp_icon_color_buiten() }}
{{ power_color() }}
/*--mdc-text-field-fill-color: whitesmoke;*/
--mdc-select-fill-color: red;
--code-editor-gutter-color: rgb(from var(--primary-color) r g b / 0.2);
}
uix-profile-yaml: |
"* $":
hass-tabs-subpage $: |
.container {
background: var(--lovelace-background);
}
.toolbar-content {
background: var(--header-background);
--primary-color: green;
}
.bottom-bar {
background: var(--header-background) !important;
}
# set a left margin on all type section labels (also in folds) and align with
# fold-entity-row label margin (set in theme)
# reduce margin-top above divider to not make section above show taller than
# fold-entity-row height apply styling to the fold-entity-row icon, which now supports
# ripple (LFT edition)
uix-row-yaml: |
.: |
:host(.type-section) .divider {
margin-top: 0px;
background-color: var(--primary-color)
}
/* stock type: section */
:host(.type-section) .label {
margin-left: var(--section-label-margin-left);
color: var(--success-color);
font-style: italic;
}
/* stock type: section inside fold-entity-row */
:host(.type-fold-entity-row-section:not([role="switch"])) .label {
text-align: center;
color: var(--info-color);
font-style: italic;
}
:host(.type-fold-entity-row-section:not([role="switch"])) .divider {
background: var(--warning-color);
}
ha-icon-button $: |
/* defaults to color of icon, with opacity as per vars below */
mwc-icon-button {
--mdc-ripple-color: var(--primary-color);
--mdc-ripple-hover-opacity: 0.2; /* defaults 0.04 */
--mdc-ripple-focus-opacity: 0.6; /* defaults 0.12 */
}
# uix-glance: |
# :host {
# transition: none !important;
# }
##########################################################################################
# make badges mask image less obtrusive
# decrease gap in header, between markdowns and badges
##########################################################################################
uix-view-yaml: |
:first-child $:
hui-view-header:
$: |
/* reduce gap between badges and section below */
.layout {
gap: 8px !important;
}
/* make the overlay at the border of the scroller less obtrusive */
.badges.scroll {
mask-image: linear-gradient(
90deg,
transparent 0%,
black 1.5%,
black 98.5%,
transparent 100%
) !important;
}
/* move badges to the left, remove bigger padding from default
in uix-root-yaml with ha-tab-padding-x */
.badges-scroll hui-view-badges {
--badge-padding: 4px !important;
}
hui-sidebar-view $:
.type-energy-distribution,.type-energy-sankey $:
ha-card $: |
.card-header {
background: var(--header-background);
color: var(--text-color-off) !important;
font-size: 20px !important;
padding: 4px 12px !important;
margin: 0 0 16px 0 !important;
}
.type-energy-sources-table $: |
.card-header {
{{header_style()}}
padding: 0px 12px;
display: flex;
align-items: center;
}
.type-energy-devices-graph $: |
.card-header {
header_style()}}
padding: 0px 12px !important;
margin: 0 0 8px 0px;
}
.: |
/* force full width on cards, that show like regular cards and not panel
this is for the manual views with strategy config (like stock energy) */
:host {
{% set panelUrlPath = panel.panelUrlPath if 'panelUrlPath' in panel else '' %}
{% if panelUrlPath == 'ui-utiliteiten' %}
--ha-view-sections-column-max-width: 100%;
{% endif %}
}
{% set seizoen = states('sensor.season_astronomical') %}
{% set extension = '_donker' if donker_thema() else '' %}
{% set path = panel.viewUrlPath %}
hui-view {
{%- if path == 'welkom' -%}
{%- if is_state('input_boolean.home_mode_party','on') -%}
background: repeat url('/local/modes/party.png');
{%- endif -%}
{%- elif path in ['weer','buienradar','weer_cams'] -%}
{%- if is_state('input_boolean.weer_view_background','on') -%}
background:
repeat url({{state_attr('camera.buienradar','entity_picture')}}) fixed;
{%- endif -%}
{%- elif path in ['feest','familie_overzicht','kalender'] -%}
{%- if feest() -%} background: repeat url('/local/images/balloons.png') fixed;
{%- endif -%}
{%- elif path == 'tijd' -%}
background: repeat url('/local/season/{{seizoen}}_2.png') fixed;
{%- elif path in ['onderweg','proximity'] -%}
background:
center/cover no-repeat url('/local/wallpapers/map{{extension}}.png') fixed;
{%- elif path == 'xbox' -%}
background:
center/cover no-repeat url('/local/xbox/xbox-animated-background.gif') fixed;
{%- endif -%}
}
/* set on views that dont have <hui-root> or top bar */
.header {
background: {%- if kerst() -%} url('/local/wallpapers/snow.mp4')
{%- elif feest() -%} url('/local/images/balloons.png')
{%- elif donker_thema() -%} var(--lovelace-background)
{%- else -%} var(--header-background)
{%- endif -%};
color: var(--app-header-text-color);
}
##########################################################################################
# instead of using the theme option on a view, set a generic template on the hui-view
# in uix-view-yaml, or even in uix-view-background
# This will be more efficient, as the modifications are less intrusive than reloading
# the full themes.
##########################################################################################
uix-view-background: |
:host {
{% set path = panel.viewUrlPath %}
{%- if path == 'plafond_spots' -%}
filter: {{'grayscale()' if is_state('light.plafond','off') else 'none'}};
{%- endif -%}
}
##########################################################################################
# to align notification badges on their right side (with the core notification use
# left: calc(var(--app-drawer-width,248px) - 22px);
# transform: translateX(-100%);
# -32px aligns with the middle of the core notification badge, and around that the
# translateX(-50%) centers.
# no more use of complex calculations based on string length in notification.
##########################################################################################
##########################################################################################
# most of the sidebar settings are now in custom plugin custom-sidebar
# https://github.com/elchininet/custom-sidebar
# config https://gist.github.com/Mariusthvdb/e41957dc6f66d9764e0480030c9bd386
# below animations are additional, because not possible in custom-sidebar
# https://community.home-assistant.io/t/card-mod-super-charge-your-themes/212176/1236
##########################################################################################
uix-sidebar: |
{{ animations() }}
ha-list-item-button.configuration {
{{'display: none !important;' if is_state('input_boolean.hide_sidebar_configuration','on')}}
}
ha-list-item-button.notifications {
{{'display: none;' if is_state('input_boolean.hide_sidebar_notifications','on')}}
}
ha-list-item-button.user {
{{'display: none;' if is_state('input_boolean.hide_sidebar_user','on')}}
}
div.panels-list ha-list-nav.after-spacer {
{{'display: none;' if is_state('group.hide_sidebar_after_spacer','on')}}
}
{%- set marquee = states('sensor.marquee_alerts')|int(0) %}
{% set style_badge_not_expanded %}
position: absolute;
box-sizing: border-box;
min-width: 18px;
max-height: 18px;
border-radius: 24px;
font-weight: 400;
line-height: 20px;
text-align: center;
left: 26px;
font-size: 10px;
top: 8px;
padding: 0px 2px;
transform: translateX(-50%);
{% endset %}
/* notification badge on collapsed menu icon */
:host(:not([expanded])) .menu ha-icon-button:after {
content: "{{- marquee if marquee > 0 }}";
{{style_badge_not_expanded}}
color: ivory;
background: var(--alert-color);
}
{% set beweging_buiten = is_state('binary_sensor.camera_beweging_buiten','on') %}
{% set core = is_state('update.home_assistant_core_update','on') %}
{% set noti = states('sensor.config_notifications')|int(0) > 0 %}
{% set klimaat = is_state('binary_sensor.klimaat_woonkamer_alert','on') %}
ha-list-item-button#sidebar-panel-ui-data {
animation: {{'blink 2.5s ease infinite' if lek() else 'none'}};
/* --uix-icon: mdi:help */ /* just a note to remember how to set an icon */
}
ha-list-item-button#sidebar-panel-ui-data ha-icon {
animation: {{'spin 3s infinite linear' if lek() else 'none'}};
}
ha-list-item-button#sidebar-panel-huis-klimaat ha-icon {
animation: {{'spin 3s infinite linear' if klimaat or vent() else 'none'}};
}
ha-list-item-button#sidebar-panel-ui-familie ha-icon {
animation: {{'blink 2s ease infinite,spin 3s infinite linear' if feest() else 'none'}};
}
/* different animation when not expanded because of spinning badge */
:host(:not([expanded])) ha-list-item-button#sidebar-panel-ui-familie ha-icon {
animation: {{'blink 2.5s ease infinite' if feest() else 'none'}};
}
ha-list-item-button#sidebar-panel-ui-instellingen ha-icon {
/*animation: {{'blink 2s ease infinite' if vent() else 'none'}};*/
}
ha-list-item-button#sidebar-panel-ui-develop ha-icon {
animation: {{'blink 2s ease infinite' if core or noti else 'none'}};
}
ha-list-item-button#sidebar-panel-ui-cctv ha-icon {
animation: {{'blink 2.5s ease infinite' if beweging_buiten else 'none'}};
}
##########################################################################################
# create 'header' like effect on the energy-distribution.
# combine with headers on all other energy cards below
uix-grid-section-yaml: |
.type-energy-distribution,.type-power-sankey,.type-energy-sankey,.type-water-sankey $ ha-card $: |
.card-header {
background: var(--header-background);
color: var(--text-color-off) !important;
font-size: 20px !important;
padding: 4px 12px !important;
margin: 0 0 16px 0 !important;
}
.type-energy-sources-table $: |
.card-header {
{{header_style()}}
padding: 0px 12px;
}
.mdc-data-table {
margin: -16px 0 0 0;
}
uix-card-yaml: |
hui-energy-period-selector $: |
section.date-range ha-spinner {
display: none;
}
hui-buttons-header-footer:
$: |
.divider {
margin: 8px 0 0 0 !important;
}
hui-buttons-header-footer $ hui-buttons-base:
$: |
.ha-scrollbar {
justify-content: space-evenly;
height: 50px;
align-content: center;
padding: 0 !important;
}
hui-energy-graph-chip:
$: |
.chip {
color: var(--header-chip-text-color);
}
.: |
/* create headers like on all other cards in config, combine with grid-yaml above.
still requires text-color adjustment on the total-energy chip in header.
add box-shadow to all cards */
{% set panelUrlPath = panel.panelUrlPath if 'panelUrlPath' in panel else '' %}
{% if panelUrlPath in ['energy','ui-utiliteiten'] %}
:host .card-header {
{{header_style()}}
{{header_spacing()}}
}
:host ha-card {
box-shadow: var(--box-shadow);
}
{% endif %}
/* also create header on date-picker, a bit too tall because ha-card still remains */
:host(.type-energy-date-selection) ha-card {
background: var(--header-background) !important;
}
:host(.type-energy-date-selection) .card-content {
{{header_style()}}
height: var(--header-height) !important;
padding: 0px 12px;
}
/* create classes to be set in card uix stylings using class: key */
:host(.class-heading) .container {
background: var(--header-background);
--ha-heading-card-title-color: var(--text-color-off);
--ha-heading-card-title-font-size: 20px;
--ha-heading-card-title-font-weight: 400;
height: var(--header-height);
padding: 0 12px;
box-shadow: var(--ha-card-box-shadow);
}
:host(.class-heading-alert) .container {
background: var(--alert-background);
--ha-heading-card-title-color: var(--alert-text-color);
--ha-heading-card-title-font-size: 20px;
--ha-heading-card-title-font-weight: 400;
height: var(--header-height);
padding: 0 12px;
}
:host(.class-heading-no-color) .container {
--ha-heading-card-title-font-size: 20px;
--ha-heading-card-title-font-weight: 400;
height: var(--header-height);
padding: 0 12px;
box-shadow: var(--ha-card-box-shadow);
}
:host(.class-header-icon) .card-header .icon {
padding-left: 0;
padding-right: 4px;
}
:host(.class-header-margin) .card-header,
.class-header-margin .card-header {
{{header_style()}}
padding: 4px 16px 4px 12px;
margin: 0 0 16px 0px;
}
:host(.class-header-margin-no-color) .card-header {
font-weight: 400;
font-size: 20px;
padding: 4px 16px 4px 12px;
margin: 0 0 16px 0px;
}
:host(.class-header-no-margin) .card-header {
{{header_style()}}
padding: 4px 16px 4px 12px;
margin: 0 0 8px 0px; /* for fold-entity-row */
}
:host(.class-header-no-margin-no-color) .card-header {
font-weight: 400;
font-size: 20px;
padding: 4px 16px 4px 12px;
margin: 0 0 8px 0px; /* for fold-entity-row */
}
:host(.class-scroll-card-content) .card-content {
max-height: {{states('input_number.max_scroll_hoogte')|int(0)}}px;
overflow-y: scroll;
overflow-x: none;
}
:host(.class-box-shadow) ha-card {
box-shadow: var(--box-shadow);
}
:host(.class-shortcut-style-as-header) ha-card {
--tile-color: var(--text-color-off);
--ha-tile-info-primary-color: var(--text-color-off);
--ha-tile-info-primary-font-size: 20px;
--ha-tile-info-primary-font-weight: 400;
background: var(--header-background);
box-shadow: var(--box-shadow) !important;
--row-height: var(--header-height);
}
ha-card {
{{'background: repeat url("/local/season/kerst_smurfen.png");' if kerst()}}
overflow:
{{'hidden' if is_state('input_boolean.hide_card_overflow','on') else 'unset'}};
}
{{ "ha-card::before
{
content: '';
background: top / contain no-repeat url('/local/season/snow.png');
height: 16%;
position: absolute;
left: -1%;
width: 102%;
top: -30px;
}"
if kerst()}}
##########################################################################################
# Set the animation of the currently selected tab icon, color and icon itself are
# set below the .: section to prevent background animation
# https://community.home-assistant.io/t/card-mod-super-charge-your-themes/212176/1094
# https://community.home-assistant.io/t/card-mod-add-css-styles-to-any-lovelace-card/120744/5174
# https://github.com/thomasloven/lovelace-card-mod/issues/268
# https://community.home-assistant.io/t/2025-5-two-million-strong-and-getting-better/886760/217?u=mariusthvdb
# https://community.home-assistant.io/t/2025-7-thats-the-question/907169/45?u=mariusthvdb
##########################################################################################
# remove action buttons, except the download button in Energy panels, where
# Kiosk-mode doesnt apply. Also new Home panel
uix-root-yaml: |
.header: |
{{animations()}}
.action-items #button-0 {
{{'display: none;' if is_state('input_boolean.hide_add_to_home_assistant','on')}}
}
.action-items #button-1 {
{{'display: none;' if is_state('input_boolean.hide_search','on')}}
}
.action-items #button-2 {
{{'display: none;' if is_state('input_boolean.hide_assistant','on')}}
}
.action-items #button-3 {
{{'display: none;' if is_state('input_boolean.hide_energy_download_button','on')}}
color: {{'var(--app-header-text-color)' if donker_thema() else
'var(--primary-color)'}};
}
.toolbar {
padding-left: 1px !important;
}
.header ha-tab-group$: |
wa-icon[name='chevron-left'], wa-icon[name='chevron-right'] {
{{'display: none;' if is_state('input_boolean.toon_chevrons','off')}}
}
/* Optionally set tab padding per device width. Only mobile. */
@media (orientation: portrait) and (max-width: 569px) {
:host {
--ha-tab-padding-start: 0.8rem !important;
--ha-tab-padding-end: 0.8rem !important;
}
}
@media (min-width: 570px) {
:host {
--ha-tab-padding-start: 1.2rem !important;
--ha-tab-padding-end: 1.2rem !important;
}
}
ha-tab-group-tab[aria-label='Ventilatoren'] ha-icon$: |
/* animations must be set here on ha-svg-icon, because under .: sets on whole tab
browser requirement to have key frames in light dom. so repeat for all */
ha-svg-icon {
transition: opacity 0.5s;
animation: {{'spin 1s infinite linear' if vent() else 'none'}};
}
ha-tab-group-tab[aria-label='Vijver'] ha-icon$: |
ha-svg-icon {
animation: {{'spin 3s infinite linear' if pomp() else 'none'}};
}
ha-tab-group-tab[aria-label='Weer'] ha-icon$: |
ha-svg-icon {
animation: {{'blink 2s ease infinite' if meteo() else 'none'}};
}
ha-tab-group-tab[aria-label='Updates'] ha-icon$: |
{% set hacs = states('sensor.hacs_updates_count')|int(0) > 0 %}
{% set ha = states('sensor.home_assistant_updates_count')|int(0) > 0 %}
{% set repairs = states('sensor.repairs_count')|int(0) > 0 %}
{%- set update = hacs or ha %}
ha-svg-icon {
animation: {{'blink 2s infinite linear' if update else
'spin 2s infinite linear' if repairs else 'none'}};
}
ha-tab-group-tab[aria-label='Kalender'] ha-icon$: |
ha-svg-icon {
animation: {{'blink 1s infinite linear' if feest() else 'none'}};
}
ha-tab-group-tab[aria-label='Feest'] ha-icon$: |
ha-svg-icon {
animation: {{'spin 3s infinite linear' if feest() else 'none'}};
}
ha-tab-group-tab[aria-label='Water'] ha-icon$: |
ha-svg-icon {
animation: {{'spin 3s infinite linear' if lek() else 'none'}};
}
ha-tab-group-tab[aria-label='Samenvatting'] ha-icon$: |
ha-svg-icon {
{% set kritiek = states('sensor.hubs_samenvatting')|int(-1) > 0 or
states('sensor.kritieke_schakelaars_samenvatting')|int(-1) > 0 %}
animation: {{'pulse 2s ease 0s infinite normal' if kritiek else 'none'}};
}
.: |
/* an intentional trade-off: return to YAML duplication/overhead in dashboards to
reduce runtime lag, rather than using macros everywhere, (here in root) which
causes noticeable dashboard performance issues. */
:host {
{{ luchtvochtigheid_binnen() }}
{{ temp_icon_color_apparaat() }}
{{ temp_icon_color_binnen() }}
{{ temp_icon_color_buiten() }}
{{ power_color() }}
}
/* center the view tabs when in potrait mode to make 1 column visually */
@media (orientation: portrait) or (max-width: 850px) {
.toolbar ha-tab-group::part(tabs) {
justify-content: center;
}
}
{% set alerts = states('sensor.alerts_notifying')|int(0) > 0 %}
{% set motion = is_state('binary_sensor.motion_sensors_all','on') %}
{% set symbol = states('sensor.buienradar_symbol') %}
{% set temp = states('sensor.ws_5500_outdoor_temperature')|default(-100) %}
{% set template = is_state('input_boolean.menu_options_template','on') %}
{% set wekker = is_state('binary_sensor.wekker_voor_morgen','on') %}
{% set verwarming = is_state('binary_sensor.opentherm_heating','on') or
is_state('binary_sensor.mill_heater_aan','on') %}
/* Set the three-dots transparency, to allow replacement template text */
ha-dropdown:nth-of-type(2) ha-icon-button {
{{'color: transparent;' if template}}
{{'pointer-events: none;' if template}}
display: inline-flex; /* required in Safari to center the text, Chrome ok without */
cursor: context-menu; /* set here, because doesnt work in the ::after pseudo-element */
}
/* Optionally set a replacement template text */
ha-dropdown:nth-of-type(2) ha-icon-button::after {
content: "{%- set sym = symbol|truncate(21,False,'') -%}
{%- if template -%} {{- sym}} | {{temp}} °C
{%- endif -%}";
color: {{'var(--app-header-text-color)' if donker_thema() else 'var(--primary-color)'}};
font-size: 20px;
pointer-events: auto;
align-self: center;
}
ha-tab-group-tab:hover {
--icon-primary-color: var(--primary-color);
background: var(--lovelace-background);
}
/* Set the color of the currently selected tab,
Set size of the currently selected tab icon */
ha-tab-group-tab[aria-selected=true] {
--ha-tab-active-text-color: oklch(from var(--primary-color) calc(l + .5) c h);
border: 1px solid var(--primary-color);
--mdc-icon-size: {{states('input_number.active_icon_size')|int(24)}}px;
height: calc(var(--header-height) - 2px);
display: flex;
}
/* This hides the help button when in edit mode */
a.menu-link[target='_blank'] {
{% if is_state('input_boolean.hide_edit_mode_help','on') %}
visibility: hidden;
margin-right: -30px;
{% endif %}
}
/* Dynamically set the color and icons of individual tabs */
ha-tab-group-tab[aria-label='Wekker instellingen'] {
color: var(--{{'alert' if wekker else 'success'}}-color);
}
ha-tab-group-tab[aria-label='Welkom'] {
{{'--uix-icon: mdi:home-alert' if alerts }};
color: var(--{{'alert' if alerts else 'success'}}-color);
}
ha-tab-group-tab[aria-label='Samenvatting'] {
{%- if states('sensor.hubs_samenvatting')|int(-1) > 0 or
states('sensor.kritieke_schakelaars_samenvatting')|int(-1) > 0 %}
--uix-icon: mdi:alert;
color: var(--alert-color);
{%- endif %}
}
ha-tab-group-tab[aria-label='Beweging'] {
{{'--uix-icon: mdi:motion-sensor;' if motion}}
{{'color: var(--alert-color);' if motion}}
}
ha-tab-group-tab[aria-label='Alarm'] {
--uix-icon: {{states('sensor.alarm_panel_icon')|default('mdi:help')}};
color: {{states('sensor.alarm_panel_icon_color')|default('pink')}};
}
ha-tab-group-tab[aria-label='Verwarming'] {
--uix-icon: mdi:{{'radiator' if verwarming else 'radiator-off'}};
{{'color: var(--alert-color);' if verwarming}}
}
ha-tab-group-tab[aria-label='Updates'] {
{% set hacs = states('sensor.hacs_updates_count')|int(0) > 0 %}
{% set ha = states('sensor.home_assistant_updates_count')|int(0) > 0 %}
{% set repairs = states('sensor.repairs_count')|int(0) > 0 %}
{%- set update = hacs or ha %}
--uix-icon: mdi:{{'package-up' if update else
'settings' if repairs else 'package'}};
color: {{ 'var(--error-color)' if repairs else
'var(--ha-color)' if ha else
'var(--warning-color)' if hacs }}
}
ha-tab-group-tab[aria-label='Kalender'] {
{{'--uix-icon: mdi:party-popper;' if feest()}}
color: {{'var(--warning-color)' if feest() else
states('sensor.afval_kleur') if states('sensor.afval_kleur') != 'gray'
else 'var(--alert-color)' if wekker}};
}
ha-tab-group-tab[aria-label='Familie overzicht'] {
color: {{states('sensor.presence_color')|default('pink')}};
}
ha-tab-group-tab[aria-label='Feest'] {
{{'color: var(--warning-color);' if feest()}}
}
ha-tab-group-tab[aria-label='Klimaat'] {
color: {{states('sensor.inside_temperature_color_name')|default('pink')}};
}
ha-tab-group-tab[aria-label='Rolluiken'] {
--uix-icon: mdi:window-shutter{{'-alert' if
not has_value('cover.slaapkamers')
else '-open' if is_state('cover.slaapkamers','open')}};
}
ha-tab-group-tab[aria-label='Verlichting'] {
{% set lampen = states('sensor.lampen_samenvatting')|int(-1) %}
--uix-icon: mdi:lightbulb-group{{'-off' if lampen == 0 else ''}};
{{'color: var(--active-color);' if lampen > 0 }}
}
ha-tab-group-tab[aria-label='Weer'] {
--uix-icon: {{states('sensor.meteoalarm_icon')
if meteo() else states('sensor.weather_icon')}};
color: {{'var(--alert-color)' if meteo() else
states('sensor.temperature_color_name')}};
}
ha-tab-group-tab[aria-label='Energie'] {
color: {{'brown' if netto() else 'var(--power-color)'}};
}
ha-tab-group-tab[aria-label='Zonne-energie'] {
{{'color: var(--power-color);' if not netto() }}
}
ha-tab-group-tab[aria-label='Water'] {
{{'--uix-icon: mdi:pipe-leak;' if lek()}}
color: {%- if lek() %} var(--alert-color)
{%- elif is_state('binary_sensor.watermeter_update_available','on') %}
blue
{%- endif %};
}
ha-tab-group-tab[aria-label='Ventilatoren'] {
{{'color: var(--alert-color);' if vent()}}
}
ha-tab-group-tab[aria-label='Vijver'] {
{{'color: lightseagreen;' if pomp()}}
}
ha-tab-group-tab[aria-label='Tijd'] {
--uix-icon: {{state_attr('sensor.hour_icon','icon')}};
color: {{'var(--state-sun-below_horizon-color)'
if is_state('binary_sensor.donker_buiten','on') else
'deepskyblue'}};
}
ha-tab-group-tab[aria-label='Home Assistant'] {
color: var(--ha-color);
}
##########################################################################################
# modify the more-info dialog, breadcrumbs and set a blur
##########################################################################################
uix-more-info-yaml: |
ha-dialog-header: |
/* adding this, because Kiosk-mode does not apply to /config dashboards */
.breadcrumb {
{% if is_state('input_boolean.hide_dialog_header_breadcrumb_navigation','on') %}
display: none !important
{% endif %};
}
"ha-more-info-details $": |
div.content {
display: flex;
flex-direction: column-reverse;
}
ha-grouped-list *:not(:first-child) {
border-top: none;
}
div.content ha-grouped-list:nth-of-type(1) {
margin-top: var(--ha-space-6);
}
div.content ha-grouped-list:nth-of-type(3) {
margin-top: 0px;
}
.uix-forge-more-info-details-content ha-more-info-details $: |
div.content {
padding: var(--ha-space-0);
}
div.card-content {
padding: var(--ha-space-1);
}
ha-more-info-info $: |
div.content {
padding: var(--ha-space-4);
}
ha-more-info-info $ more-info-content $:
more-info-script $ ha-control-button $: |
.button {
border-radius: var(--border-radius-square) !important;
}
more-info-input_boolean,more-info-switch,more-info-siren $:
ha-state-control-toggle $: |
ha-control-switch {
--control-switch-border-radius: var(--border-radius-square) !important;
--control-switch-thickness: var(--more-info-control-thickness) !important;
}
more-info-fan $:
ha-control-select-menu $: |
.select-anchor {
border-radius: var(--border-radius-square) !important;
}
ha-state-control-fan-speed $: |
ha-control-slider {
--control-slider-border-radius: var(--border-radius-square) !important;
--control-slider-thickness: var(--more-info-control-thickness) !important;
}
ha-state-control-fan-speed $ ha-control-slider $: |
.slider .slider-track-bar::after {
border-radius: var(--border-radius-square) !important;
}
.tooltip {
border-radius: var(--border-radius-square) !important;
background: var(--card-background-color) !important;
}
more-info-alarm_control_panel $:
ha-state-control-alarm_control_panel-modes $: |
ha-control-select {
--control-select-border-radius: var(--border-radius-square) !important;
--control-select-thickness: var(--more-info-control-thickness) !important;
}
more-info-cover $:
ha-state-control-cover-position $: |
ha-control-slider {
--control-slider-border-radius: var(--border-radius-square) !important;
--control-slider-thickness: var(--more-info-control-thickness) !important;
}
ha-state-control-cover-position $ ha-control-slider $: |
.slider .slider-track-bar::after {
border-radius: var(--border-radius-square) !important;
}
.tooltip {
border-radius: var(--border-radius-square) !important;
background: var(--card-background-color) !important;
}
more-info-light $:
ha-state-control-light-brightness $: |
ha-control-slider {
--control-slider-border-radius: var(--border-radius-square) !important;
--control-slider-thickness: var(--more-info-control-thickness) !important;
}
ha-state-control-light-brightness $ ha-control-slider $: |
.slider .slider-track-bar::after {
border-radius: var(--border-radius-square) !important;
}
.tooltip {
border-radius: var(--border-radius-square) !important;
background: var(--card-background-color) !important;
}
light-color-rgb-picker $: |
.native-color-picker {
color: var(--primary-color) !important;
}
light-color-temp-picker $: |
ha-control-slider {
--control-slider-border-radius: var(--border-radius-square) !important;
--control-slider-thickness: var(--more-info-control-thickness) !important;
}
light-color-temp-picker $ ha-control-slider $: |
.slider {
--handle-size: 4px;
}
.slider .slider-track-cursor {
--cursor-size: 14px !important;
}
.slider .slider-track-cursor::after {
border-radius: var(--border-radius-square) !important;
}
.tooltip {
border-radius: var(--border-radius-square) !important;
background: var(--card-background-color) !important;
}
ha-control-select-menu $: |
.select-anchor {
border-radius: var(--border-radius-square) !important;
}
.: |
:host {
{{ luchtvochtigheid_binnen() }}
{{ temp_icon_color_apparaat() }}
{{ temp_icon_color_binnen() }}
{{ temp_icon_color_buiten() }}
{{ power_color() }}
}
.uix-forge-more-info-details-wrap {
border-top: 1px solid var(--divider-color);
padding: var(--ha-space-2) var(--ha-space-4);
}
.uix-forge-more-info-details-title {
font-size: var(--ha-font-size-m);
font-weight: var(--ha-font-weight-medium);
}
# styling to make the dropdown more-info look exactly like the fold-entity-row
##########################################################################################
# modify ha-dialog, ha-md-dialog, ha-wa-dialog or ha-drawer
# uix theme variable is uix-dialog and class applied to dialog element is the
# parent element with any ha- stripped off, then prefix with type (similar to cards).
# e.g. hui-dialog-create-card will have class type-dialog-create-card.
##########################################################################################
uix-dialog-yaml: |
$: |
:host(.type-dialog-box) wa-dialog::part(dialog)::backdrop {
backdrop-filter: blur(17px);
}
ha-dialog-header $: |
.header-navigation-icon {
color: var(--app-header-text-color);
}
.header-bar {
height: var(--header-height);
}
.: |
.dialog-title.form {
color: var(--app-header-text-color);
}
{% set titel = params.title if 'title' in params else '' %}
{% set ha = titel == 'Home Assistant herstarten?' %}
{% set sy = titel == 'Systeem herstarten?' %}
{% set vm = titel == 'Home Assistant herstarten in veilige modus?' %}
{% set af = titel == 'Systeem afsluiten?' %}
{% set kleur =
'rgb(255, 213, 0)' if ha else
'var(--error-color)' if sy else
'var(--warning-color)' if vm else
'black' if af else
'var(--header-background)'
%}
.title {
font-weight: 400;
font-size: 20px;
color:
{% if ha %} black
{% elif sy %} white
{% elif vm %} white
{% elif af %} white
{% else %} var(--app-header-text-color)
{% endif %};
}
ha-dialog-header {
background:
{% if ha or sy or vm or af %}
linear-gradient(90deg, {{kleur}} 0%, var(--primary-color) 100%)
{% else %} var(--header-background)
{% endif %};
margin: 0 0 16px 0px !important;
}
uix-toast-yaml: |
.: |
{% if "UIX has been updated" in params.message %}
ha-button {
--wa-color-on-normal: green;
}
{% endif %}
$: |
{{ animations() }}
{% if "UIX has been updated" in params.message %}
div.toast {
background: var(--alert-color);
color: white;
animation: glow ease 2s infinite;
box-shadow: var(--box-shadow);
border: 3px white double;
}
{% elif "annotatedyaml.loader" in params.message %}
div.toast {
background: var(--warning-color);
color: black;
animation: glow ease 2s infinite;
box-shadow: var(--box-shadow);
border: 3px var(--success-color) double;
}
{% endif %}
# You can test various dialogs by running the following in Browser console…
#
# UIX update
#
# document.querySelector("home-assistant").dispatchEvent(new CustomEvent("hass-notification", { detail: { message: "UIX has been updated", duration: -1, dismissable: true }, bubbles: true, composed: true }));
# Dialog Box - replace title with which dialog box you want to test.
#
# window.cardHelpers.showAlertDialog(document.querySelector("home-assistant"), { title: "Home Assistant herstarten?", text: "Some text"})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment