A different take on designing a Lovelace UI

Good afternoon!

Since the 2024.7 HA update, my sidebar icons no longer color themselves correctly and the align-items: flex-end no longer seems to pin them to the bottom of the view. The only fix has been to remain at 2024.6.4. I was hoping someone would be able to quickly identify what needs to be changed, I am struggling with the page inspector in Chrome to determine exactly what needs to be modified. No matter how I try and reformat it, all 6 icons are picking up the modifications from “last-of-type”, where as pre 2024.7 they would all work with first-of, last-of, and nth-of selectors respectively. Appreciate any insight you have or pointing me in the right direction to figure it out myself, I am usually pretty good at figuring things out but this has stumped me for months.

This is all based off the older version of Mattias_Persson 's theme, with the sidebar icons, before he switched to the bottom bar. Here is everything from that themes.yaml up to that point for context.


    ####################################################
    #                                                  #
    #                     CARD-MOD                     #
    #                                                  #
    ####################################################

      card-mod-theme: tablet

      card-mod-root: |
        #view {
          background: url('/local/background.png') !important;
          background-size: cover !important;
        }

      card-mod-view-yaml: |
        .: |
          hui-view {
            background: none !important;
          }

        #################################################
        #                                               #
        #              GRID CARD HEADINGS               #
        #                                               #
        #################################################

        grid-layout$:
          hui-grid-card:
            $: |
              /* default */
              h1 {
                font-size: 2.4vw !important;
                line-height: 0 !important;
                font-weight: 500 !important;
                color: rgba(255, 255, 255, 0.8) !important;
                padding: 1vw 0 2vw 0 !important;
                letter-spacing: 0.006vw !important;
              }
              /* portrait */
              @media screen and (max-width: 1200px) {
                h1 {
                  font-size: 3.3vw !important;
                  line-height: 1.1vw !important;
                }
              }
              /* phone */
              @media screen and (max-width: 800px) {
                h1 {
                  font-size: 5.5vw !important;
                  line-height: 6vw !important;
                  margin: 2vw 0 0 0 !important;
                }
              }

        #################################################
        #                                               #
        #               SWIPE CARD MARGIN               #
        #                                               #
        #################################################

            $swipe-card$:
              .: |
                div {
                  height: 100%;
                }
              hui-horizontal-stack-card$:
                .: |
                  hui-conditional-card {
                    margin: 1px !important;
                  }

        #################################################
        #                                               #
        #            SIDEBAR VERTICAL-STACK             #
        #                                               #
        #################################################

        grid-layout$hui-vertical-stack-card$: |
          #root {
            background-color: rgba(0, 0, 0, 0.06);
            border-radius: 0;
            border-right: 0.1vw solid rgba(58, 69, 73, 0.2);
            min-height: 100vh;
          }
          /* phone */
          @media screen and (max-width: 800px) {
            #root {
              background-color: rgba(0,0,0,0);
              border-right: none;
              min-height: 100%;
              margin-left: -0.8%;
              margin-bottom: -6%;
            }
          }

        #################################################
        #                                               #
        #             SIDEBAR LAUNDRY TIMER             #
        #                                               #
        #################################################

        grid-layout$hui-vertical-stack-card$hui-conditional-card:
          .: |
            hui-conditional-card {
              display: flex;
              justify-content: center;
            }
            /* phone */
            @media screen and (max-width: 800px) {
              hui-conditional-card > button-card {
                zoom: 250%;
                width: 100%;
                filter: contrast(85%);
              }
            }

        #################################################
        #                                               #
        #             SIDEBAR BOTTOM ICONS              #
        #                                               #
        #################################################

        grid-layout$hui-vertical-stack-card$hui-grid-card:
          .: |
            hui-grid-card {
              display: flex;
              flex: auto;
              align-items: flex-end;
            }

            /* phone */
            @media screen and (max-width: 800px) {
              hui-grid-card {
                position: absolute;
                right: 5.5vw;
              }
            }

          $: |
            #root {
              width: 100%;
              padding: 0 13% 25% 13%;
            }

            /* phone */
            @media screen and (max-width: 800px) {
              #root {
                padding: 0 0 0 55%;
              }
            }

         #conditional color - Camera
          $hui-button-card:first-of-type$: |
            {% if is_state('sensor.template_recent_outside_motion', 'True') %}

              ha-card, ha-state-icon {
                color: rgb(35, 78 ,106) !important;
                opacity: 1 !important;
                animation: update 1.5s ease-out infinite;
              }

              ha-card:hover {
                filter: brightness(130%);
                animation-play-state: paused;
              }

            {% endif %}

              @keyframes update {
                0% {
                  transform: scale(1);
                }
                40% {
                  transform: scale(1.08);
                }
                50% {
                  transform: scale(0.98);
                }
                55% {
                  transform: scale(1.02);
                }
                60% {
                  transform: scale(0.98);
                }
                100% {
                  transform: scale(1);
                }
              }



         #conditional color - Music
          $hui-button-card:nth-of-type(2)$: |
            {% if is_state('media_player.main_floor', 'playing') %}

              ha-card, ha-state-icon {
                color: rgb(35, 78 ,106) !important;
                opacity: 1 !important;
                animation: update 1.5s ease-out infinite;
              }

              ha-card:hover {
                filter: brightness(130%);
                animation-play-state: paused;
              }

            {% endif %}

              @keyframes update {
                0% {
                  transform: scale(1);
                }
                40% {
                  transform: scale(1.08);
                }
                50% {
                  transform: scale(0.98);
                }
                55% {
                  transform: scale(1.02);
                }
                60% {
                  transform: scale(0.98);
                }
                100% {
                  transform: scale(1);
                }
              }


         #conditional color - Devices
          $hui-button-card:nth-of-type(3)$: |
            {% if is_state('binary_sensor.template_upstairs_vaccuum_color', 'dummyvaluetoturnthisoff') %}

              ha-card, ha-state-icon {
                color: rgb(35, 78 ,106) !important;
                opacity: 1 !important;
                animation: update 1.5s ease-out infinite;
              }

              ha-card:hover {
                filter: brightness(130%);
                animation-play-state: paused;
              }

            {% endif %}

              @keyframes update {
                0% {
                  transform: scale(1);
                }
                40% {
                  transform: scale(1.08);
                }
                50% {
                  transform: scale(0.98);
                }
                55% {
                  transform: scale(1.02);
                }
                60% {
                  transform: scale(0.98);
                }
                100% {
                  transform: scale(1);
                }
              }

         #conditional color - Weather
          $hui-button-card:nth-of-type(4)$: |
            {% if is_state('binary_sensor.template_upstairs_vaccuum_color', 'dummyvaluetoturnthisoff') %}

              ha-card, ha-state-icon {
                color: rgb(35, 78 ,106) !important;
                opacity: 1 !important;
                animation: update 1.5s ease-out infinite;
              }

              ha-card:hover {
                filter: brightness(130%);
                animation-play-state: paused;
              }

            {% endif %}

              @keyframes update {
                0% {
                  transform: scale(1);
                }
                40% {
                  transform: scale(1.08);
                }
                50% {
                  transform: scale(0.98);
                }
                55% {
                  transform: scale(1.02);
                }
                60% {
                  transform: scale(0.98);
                }
                100% {
                  transform: scale(1);
                }
              }


         #conditional color - Network Info
          $hui-button-card:nth-of-type(5)$: |
            #{% if not is_state('sensor.template_hass_version', 'Latest') %}
            {% if is_state('sensor.template_hass_version', 'Latest') %}


              ha-card, ha-state-icon {
                color: rgb(35, 78 ,106) !important;
                opacity: 1 !important;
                animation: update 1.5s ease-out infinite;
              }

              ha-card:hover {
                filter: brightness(130%);
                animation-play-state: paused;
              }

            {% endif %}

              @keyframes update {
                0% {
                  transform: scale(1);
                }
                40% {
                  transform: scale(1.08);
                }
                50% {
                  transform: scale(0.98);
                }
                55% {
                  transform: scale(1.02);
                }
                60% {
                  transform: scale(0.98);
                }
                100% {
                  transform: scale(1);
                }
              }


         #conditional color - Vaccuum
          $hui-button-card:last-of-type$: |
            {% if is_state('binary_sensor.template_mainfloor_vaccuum_color', 'on') or is_state('binary_sensor.template_upstairs_vaccuum_color', 'on') %}

              ha-card, ha-state-icon {
                color: rgb(35, 78 ,106) !important;
                opacity: 1 !important;
                animation: update 1.5s ease-out infinite;
              }

              ha-card:hover {
                filter: brightness(130%);
                animation-play-state: paused;
              }

            {% endif %}

              @keyframes update {
                0% {
                  transform: scale(1);
                }
                40% {
                  transform: scale(1.08);
                }
                50% {
                  transform: scale(0.98);
                }
                55% {
                  transform: scale(1.02);
                }
                60% {
                  transform: scale(0.98);
                }
                100% {
                  transform: scale(1);
                }
              }

      card-mod-card: |
        .header .card-header {
          letter-spacing: 0.005em;
          font-size: 1.6em;
          font-weight: 500;
          padding: 1em 0 0 1.68em;
          line-height: initial;
          cursor: default;
        }

        .content .card-content {
          padding: var(--card-content-padding);
        }

Before 2024.7, the 6 icons would stick to the bottom of the screen, but now they move with the sidebar text. And they should all be white, except the 6th icon which is active because the Vacuum is going. Instead all 6 get the modifications for card 6, if that makes sense.

Thank you for any pointers!

Hi All

First time poster here, slowly building a dash for myself but getting stuck at certain places. Looking to see if someone on here can help me out with an issue i’m having with custom button card. What i am trying to do is to have a custom button card upon tap use browser mod and then display another set of button cards. I am able to do this but for some reason the pop up has this space on the right hand side that i cannot seem to remove. I have been trying for quite sometime and have had no success. Just wondering if someone here can help out.

My code is below

type: custom:button-card
template:
  - base
entity: light.study_led_lightstrip
name: TEST
icon: mdi:test-tube
size: 80%
show_state: true
aspect_ratio: 1/1
tap_action:
  action: fire-dom-event
  browser_mod:
    service: browser_mod.popup
    data:
      title: Test Title
      content:
        type: custom:layout-card
        layout_type: custom:grid-layout
        layout:
          margin: 0 0 0 0  # Remove margin
          gap: 0  # Remove gap between items
          grid-template-columns: repeat(2, 1fr)
          grid-template-rows: repeat(auto-fill, minmax(100px, 1fr))
          mediaquery:
            '(max-width: 800px)':
              grid-template-columns: 100%  
        cards:
          - type: custom:auto-entities
            filter:
              include:
                - entity_id: light.study_led_lightstrip
                  options:
                    type: custom:button-card
                    name: Study
                    icon: mdi:lightbulb
                    size: 100%  
                    show_state: true
                    show_label: false
                    label: |
                      [[[
                        return states['fan.deck_fan'].attributes.percentage + '%';
                      ]]]
                    aspect_ratio: 1/1
                    padding: 0 0 0 0  # Set padding to 0 to eliminate gaps
                    state:
                      - value: 'on'
                        styles:
                          card:
                            - height: 100px
                            - width: 100%  # Set width to 100% to fill the grid cell
                            - padding: 0  # Set padding to 0
                        icon:
                          - color: rgb(0, 175, 0)
                    template:
                      - base
                - entity_id: light.study_led_lightstrip
                  options:
                    type: custom:button-card
                    name: Study
                    icon: mdi:lightbulb
                    size: 100%  
                    show_state: true
                    show_label: false
                    label: |
                      [[[
                        return states['fan.deck_fan'].attributes.percentage + '%';
                      ]]]
                    aspect_ratio: 1/1
                    padding: 0 0 0 0  # Set padding to 0 to eliminate gaps
                    state:
                      - value: 'on'
                        styles:
                          card:
                            - height: 100px
                            - width: 100%  
                            - padding: 0  
                        icon:
                          - color: rgb(0, 175, 0)
                    template:
                      - base
                - entity_id: light.study_led_lightstrip
                  options:
                    type: custom:button-card
                    name: Study
                    icon: mdi:lightbulb
                    size: 100%  
                    show_state: true
                    show_label: false
                    label: |
                      [[[
                        return states['fan.deck_fan'].attributes.percentage + '%';
                      ]]]
                    aspect_ratio: 1/1
                    padding: 0 0 0 0  
                    state:
                      - value: 'on'
                        styles:
                          card:
                            - height: 100px
                            - width: 100%  
                            - padding: 0  
                        icon:
                          - color: rgb(0, 175, 0)
                    template:
                      - base
                - entity_id: light.study_led_lightstrip
                  options:
                    type: custom:button-card
                    name: Study
                    icon: mdi:lightbulb
                    size: 100%  
                    show_state: true
                    show_label: false
                    label: |
                      [[[
                        return states['fan.deck_fan'].attributes.percentage + '%';
                      ]]]
                    aspect_ratio: 1/1
                    padding: 0 0 0 0  
                    state:
                      - value: 'on'
                        styles:
                          card:
                            - height: 100px
                            - width: 100%  
                            - padding: 0
                        icon:
                          - color: rgb(0, 175, 0)
                    template:
                      - base                      
            card:
              type: grid
              columns: 2
            card_param: cards

The result looks like this
Button card

Hi,

I´m not quite sure if this can help you, but you may can take a look on a similar approach over a pop up integration. When I press these “Draußen” button the pop up with additional four lights occur. Normaly transparent background but for better comparsion with grey background.

bg2

action: fire-dom-event
browser_mod:
  service: browser_mod.popup
  data:
    timeout: 22000
    title: Draußen
    style: >
      --popup-background-color: transparent;
    content:
      type: custom:mod-card
      card_mod:
        style:
          layout-card:
            $grid-layout:
              $:
                .: |
                  #root {
                    margin: 15px !important;
                  }

      card:
        type: custom:layout-card
        layout_type: custom:grid-layout
        layout:
          #grid-template-columns: 15vw
          grid-template-columns: 21vw
          #grid-template-rows: 20vw 20vw
          grid-template-areas: |
            "entrance entrance entrance"
          mediaquery:
            #phone
            "(max-width: 800px)":
              grid-template-columns: 20px 80vw 10px
              #grid-template-rows: 1fr
              grid-template-areas: |
                ". entrance ."
            #tablet
            "(max-width: 2000px)":
              grid-template-columns: 21vw
              #grid-template-rows: 1fr
              grid-template-areas: |
                "entrance entrance entrance"

        cards:

      #################################################
      #                                               #
      #                 entrance-ROOM                   #
      #                                               #
      #################################################

         - type: grid
           #title: entrance-room
           view_layout:
             grid-area: entrance
           columns: 1
           cards:

              - type: grid
                columns: 2
                cards:

                  - type: custom:button-card
                    entity: light.haustur
                    name: Haustür
                    double_tap_action:
                      action: block
                    styles:
                      custom_fields:
                        timer:
                          - top: 118px
                    custom_fields:
                      icon: >
                        <ha-icon icon="phu:wall-lantern"
                        style="color:var(--button-card-light-color);"></ha-icon>
                    variables:
                      timer: timer.draussen
                    template:
                      - light
                      - timer

                  - type: custom:button-card
                    entity: light.terrasse_draussen
                    name: Terrasse
                    double_tap_action:
                      action: block
                    template:
                      - light
                      - icon_porch

                  - type: custom:button-card
                    entity: light.garage
                    name: Garage
                    double_tap_action:
                      action: block
                    styles:
                      custom_fields:
                        timer:
                          - top: 118px
                    custom_fields:
                      icon: >
                        <ha-icon icon="phu:rooms-carport"
                        style="color:var(--button-card-light-color);"></ha-icon>
                    variables:
                      timer: timer.draussen
                    template:
                      - light
                      - timer

                  - type: custom:button-card
                    entity: light.garage_seite
                    name: Garage Seite
                    double_tap_action:
                      action: block
                    template:
                      - light
                      - icon_porch

@zs2766 兄弟,请问能分享一下这个空调的程式码吗? 如果可以的话十分感激。

Thanks for this info. Your approach is similar to mine. I looked at your code and tried to use it for my dashboard and got it working however the popped up icon sizes are too big for my dashboard. I tried looking at how to reduce the size and tried a few things but could not figure it out. Can you help with that by any chance ?

Hi,

the onyl idea I have is to add: “columns: 4”. Then the icon are shrinked.

        cards:

      #################################################
      #                                               #
      #                 entrance-ROOM                   #
      #                                               #
      #################################################

         - type: grid
           #title: entrance-room
           view_layout:
             grid-area: entrance
           columns: 4
           cards:

                  - type: custom:button-card
                    entity: light.haustur
                    name: Haustür


If i do that on mine i still get the same space left on the side. Do you mind sharing your code on github by any chance so that i can look at it and see if i can work something out ? Maybe even incorporate some of what your doing

@VietNgoc I noticed your dash uses something like this as well. Any chance you could look at this and tell me where i might be going wrong ?

size: 100% I think this option affects the icons. Try removing or adjusting the size?

Does anyone else still use the Markdown card like Mattias used for displaying the number of devices that are currently in an ‘on’ state? I’ve used it for some time on my dashboard, inspired by Mattias’, and always have a template error in my log when HA restarts. I’m sure it’s due to some state/s not being available when the template is evaluated, but i’m not sure what I would have to change to stop this happening? My code is:

              - type: markdown
                content: |-
                  {% set lights = [
                    states.light.lounge_background_lights,
                    states.light.lounge_bay_window_lights,
                    states.light.lounge_ceiling_light,
                    states.light.lounge_table_lamp,
                    states.switch.kitchen_light,
                    states.light.office_ceiling_lights,
                    states.light.toilet_light,
                    states.light.loft_light,
                    states.light.desk_light
                  ] %}

                  {% set devices = [
                    states.switch.tumble_dryer,
                    states.media_player.rx_v675_main,
                    states.media_player.lounge_tv
                  ] %}

                  {% set lights_on = lights | selectattr('state','eq','on') | list %}
                  {% set lights_name = lights | selectattr('state','eq','on') | map(attribute='name') | join(', ') %}

                  {% set devices_on = devices | selectattr('state','search','(on|playing)') | list %}
                  {% set devices_name = devices_on | map(attribute='name') | join(', ') %}

                  {% if (lights_on | length == 0) and (devices_on | length == 2) %}
                    {{ devices_name | replace(', ', ' and ') }} are on

                  {% elif (lights_on | length == 0) and (devices_on | length > 0) %}
                    {{ devices_name | regex_replace(',([^,]*)$', ' and\\1') }} is on

                  {% elif (lights_on | length == 1) and (devices_on | length == 1) %}
                    {{ lights_name }} and {{devices_name }} are on

                  {% elif (lights_on | length == 1) and (devices_on | length > 1) %}
                    {{ lights_name }}, {{ devices_name | regex_replace(',([^,]*)$',' and\\1') }} are on

                  {% elif (lights_on | length > 1) and (devices_on | length == 1) %}
                    {{ lights_on | length }} lights and {{ devices_name }} are on

                  {% elif (lights_on | length > 1) and (devices_on | length > 1) %}
                    {{ lights_on | length }} lights, {{ devices_name | regex_replace(',([^,]*)$',' and\\1') }} are on

                  {% elif (lights_on | length == 1) and (devices_on | length == 0) %}
                    {{ lights_name }} is on

                  {% elif (lights_on | length > 1) and (devices_on | length == 0) %}
                    {{ lights_on | length }} lights are on

                  {% else %}
                    <font color='#6a7377'>Everything is turned off</font>
                  {% endif %}

and the error that logs on restart of HA:

homeassistant.exceptions.TemplateError: UndefinedError: 'None' has no attribute 'state'

As soon as HA finishes loading, the card displays correctly on my dashboard, it just logs that error every time there is a restart.

Create what you have in content as a template sensor itself, then use its state in markdown.
something like this… you can then use it for other templates using individual attributes

2024-11-04 20.20.05

any template using this syntax is prone to errors/issues. they should be avoided in all situations. only use them in the developer tools template engine to see what the state of that particular entity_id consists of.

it’s documented and advised not to use, instead use states(entity_id) and state_attr(entity_id,attribute)

considering what you’re doing why not use the label to select your devices (add a label to the lights you’re interested in, and use label_entities('your_label')

eg:

 {{label_entities('kritiek')|select('is_state','off')|list|count}}

if you require the name you need to expand, but thats also easy:

            {% set kritiek = label_entities('kritiek')|select('is_state','off')
               |expand|map(attribute='name')|list %}
            {% set count = kritiek|count %}
            {%- if count == 0 -%} Ok, alle kritieke schakelaars staan aan
            {%- elif count == 1 -%}{{count}} Kritieke schakelaar staat uit: {{kritiek[0]}}
            {%- elif count == 2 -%}
              {{count}} Kritieke schakelaars staan uit: {{kritiek[0]}} en {{kritiek[1]}}
            {% else %}
              {{count}} Kritieke schakelaars staan uit: {{kritiek[:-1]|join(', ')}}, en {{kritiek[-1]}}
            {% endif %}

or

      data:
        message: >
          {% set lights_on = label_entities('binnenlamp')
             |select('is_state','on')|expand|map(attribute='name')|list|sort %}
          {% if lights_on|length == 0 %} Geen lampen meer aan, welterusten...
          {% elif lights_on|length == 1 %} De {{lights_on[0]}} staat aan.
          {% elif lights_on|length == 2 %}
            De {{lights_on[0]}} en {{lights_on[1]}} lampen staan aan.
          {% else %}
            De {{lights_on[:-1]|join(', ')}}, en {{lights_on[-1]}} lampen staan aan.
          {% endif %}

Yes that only changes the icon size in the card but not the card itself. How have you done it on your dash ? I noticed your lounge button pops up 5 new button cards in 3 columns and they are the right size. That is exactly what i am trying to achieve

This is my yaml for popup…

Thank you, I forgot that the original code had the entire sidebar as a template sensor. I moved away from that and created my own sidebar area. Thank you for showing the animation of the template editor, using the attributes, that’s helpful :+1:

Ah thank you, yes using a label for these entities makes a lot more sense. I’ll work through and do that in HA, and update my markdown card to reflect this, and expand for the light/device names to achieve the same as what I have now, but without the template errors during startup :slight_smile:

1 Like