A different take on designing a Lovelace UI

Hi all, quick question: the styling of my sidebar is not always applied (see pictures below). It looks like a random issue, sometimes it is loaded. Sometimes it is not. Looking in the CSS I notice that the car-mod is not loaded when the sidebar is not styled. So I suspect the card-mod?

I do load it in the recommended mode (configuration.yaml):

frontend:
  extra_module_url:
    /hacsfiles/lovelace-card-mod/card-mod.js

Example of correctly styled sidebar:

Example of incorrectly styled sidebar:

Anyone else facing this random loading of sidebar styling? Can anyone pinpoint me in the right direction, already tried a lot of thing without any success.

Thanks in advance!
Wouter

the sidebar should be custom:button-card for this exact reason. it’s faster and you can write both js and jinja templates

there’s a history of issues styling markdown card Markdown and themes - style doesn't stick · Issue #130 · thomasloven/lovelace-card-mod · GitHub

1 Like

is it possible to add extra theme, if i want to use a different theme and a dashboard on my mobile.

when i try to move theme.yaml in themes folder and rename it to tablet and change frontend in config.yaml

frontend: 
  themes: !include_dir_named themes
template: !include sidebar.yaml

it complains about the

extra_module_url:

  - /hacsfiles/lovelace-card-mod/card-mod.js

When i want to cast this dashboard to my nest hub mini i receive the message.
Set Theme to tablet in profile.

On my laptop everything is fine also try to set the theme default for all instances.
call service → Home Assistant Frontend: Set Theme
Set the dark theme from dropdown, to Dark
execute

But still the same message on the nest hub

you have to choose tablet theme in your profile settings theme

hello

perhaps some can help, its a little bit complicated but i try to explain, i have the standard light card with a extra card for my icon from my input_boolean.

When i now click the icon the light changes to ON and the boolean to ON.
the states are

Boolean = from OFF to ON
Light = from OFF to ON

or when i turn of the light

Boolean = from ON to OFF
Light = from ON to OFF

this here is the standard behaviour

gif

than everything works fine, BUT when my Motion sensor triggers the light the boolean doesnt change the states are now for example

Boolean = OFF
Light = from OFF to ON
or
from ON to OFF

than the icon with the motion sensor still stands in the circle with the brightness.

Unbenannt

my theory is that this happened because the state and card of the light changes from say ON to OFF but the second card with the icon remains unchanged since the boolean does not change its status

maybe someone of the pros here has an idea?
here is my card code

- type: custom:button-card
                    triggers_update:
                      - input_boolean.helper_wled_kueche_unterschraenke
                    show_state: false
                    tap_action:
                      !include popup/ledstripe_kitchen_top.yaml
                    entity: light.wled_kitchen_top_2
                    name: Oben
                    template:
                      - light
                      - icon_ledstripe  
                    variables:
                      helper_always_on: input_boolean.helper_wled_kueche_oberschraenke
                    custom_fields:
                      bm:
                        card:
                          type: custom:button-card
                          variables:
                            original_entity: light.wled_kitchen_top_2
                          hold_action: 
                            action: none
                          tap_action: 
                            action: call-service
                            service: light.toggle
                            service_data:
                              entity_id: light.wled_kitchen_top_2
                          triggers_update:
                            - input_boolean.helper_wled_kueche_oberschraenke
                          entity: input_boolean.helper_wled_kueche_oberschraenke
                          layout: vertical
                          name: []
                          state:
                            - value: 'on'
                              icon: mdi:motion-sensor-off
                              styles:
                                card:
                                  - margin: 45% -20% 0 0
                                  - border: 0px
                                icon: 
                                  - margin: 45% -20% 0 0
                                  - color: gray
                                  - width: 45%
                                  
                            - value: 'off'
                              icon: mdi:motion-sensor
                              styles:
                                card:
                                  - margin: 8% -20% 0 0
                                icon:
                                  #- display: none
                                   - margin: 8% -20% 0 0
                                   - width: 45%
                          
                      helper_circle: |
                        [[[
                          if (variables.state === 'off' ) {
                            let input = variables.helper_always_on,
                              radius = 9,
                              circumference = radius * 2 * Math.PI;
                            return `
                              <svg viewBox="0 0 50 50">
                                <style>
                                  circle {
                                    transform: rotate(-90deg);
                                    transform-origin: 50% 50%;
                                    stroke-dasharray: ${circumference};
                                    stroke-dashoffset: ${circumference * 2};
                                  }
                                  tspan {
                                    font-size: 10px;
                                  }
                                </style>
                                <circle cx="25" cy="25" r="${radius}" fill="rgba(255,255,255,0.04)" stroke="#313638" stroke-width="0.6"  />
                                <text x="50%" y="52%" fill="#8d8e90" font-size="12" text-anchor="middle" alignment-baseline="middle" dominant-baseline="middle"><tspan font-size="10"></tspan></text>
                              </svg>
                            `;
                          }
                          
                        
                         
                        ]]]
#  Werte fuer circle zeile 677    <text x="50%" y="52%" fill="#8d8e90" font-size="12" text-anchor="middle" alignment-baseline="middle" dominant-baseline="middle">${states[variables.helper_always_on].state}<tspan font-size="10"></tspan></text>
                               
                    styles:
                     custom_fields:
                        helper_circle:
                          - position: absolute
                          - width: 100%
                          - height: 100%
                          - margin: -31% -30% 0 0
                          - display: initial
                          - opacity: 1
                          - justify-self: end     
                              
                        bm:
                          - position: absolute
                          - width: 50%
                          - height: 50%
                          - display: initial
                          - opacity: 1
                          - justify-self: end 

@Mattias_Persson perhaps?

1 Like

that is complicated,

Can you explain why you need the input boolean at all?
Why not use the on off state of the light to control the position of the motion-sensor icon?

The other option is to use an automation to keep the boolean and light state the same, or have the automation that is triggered by the Motion sensor also toggle the input boolean. however, if the input boolean is only for the icon position I would remove it

hello @masoncrawford1994,

yes of course, i need the input_boolean for the option to set my motion sensor automation off or dont turn my light off.
For example, the code i posted is for my kitchen Wled Stripe under my kitchen, the stripe is motion controlled by a motion sensor, when i now want to have the light ON and not got switched off by the motion sensor i need this helper (input_boolean).

but sometimes when guests are here or i want to cook for a longer time i want the light always on. And thats what the helper does.

thats why i cant toogle the boolean with the Motion sensor Automation.

hmm there must be a way to solve the whole thing, right? At least I hope so
somehow link the status of the button card with the icon with the entity of the light or something

when i use the light entity as a variable in the icon button card? and than add a condition that when
light entity changed to ON && Boolean == OFF ?

i tried something like this

- value: 'off'
                              icon: mdi:motion-sensor
                              styles:
                                card: 
                                  
                                  - margin: >
                                        [[[
                                          if (variables.state === 'off') 
                                            {
                                              return `8% -20% 0 0`;   
                                            }
                                          if (variables.state === 'off' && variables.original_entity === 'on') 
                                            {
                                              return `45% -20% 0 0`;   
                                            } 
                                        ]]]
                                  
                                icon:
                                  #- display: none
                                   - margin: >
                                        [[[
                                          if (variables.state === 'off') 
                                            {
                                              return `8% -20% 0 0`;   
                                            } 
                                          if (variables.state === 'off' && variables.original_entity === 'on') 
                                            {
                                              return `45% -20% 0 0`;   
                                            } 
                                        ]]]
                                   - width: 45%
                                   - color: gray
                                  
                     

here the variable for the if condition : variables: original_entity: light.wled_kitchen_top_2
but this wont work, when the light turns on and the boolean is still OFF he makes nothing. because the boolean changes not the state

perhaps over a animation? he must only slide the icon to the bottom, but when there is no boolean trigger it wont work or?

This is essentially what I’ve done, but I renamed this theme to button-dashboard.yaml

in configuration.yaml

lovelace:
  mode: yaml
  resources: !include resources.yaml
  dashboards:
    button-dashboard:
      mode: yaml
      filename: button-dashboard.yaml
      title: Button Dashboard
      icon: mdi:tools
      show_in_sidebar: false

frontend:
  themes: !include_dir_merge_named themes
  extra_module_url:
    - /hacsfiles/lovelace-card-mod/card-mod.js

template: !include sidebar.yaml

in themes.yaml comment out or delete these lines

#extra_module_url:
#  - /hacsfiles/lovelace-card-mod/card-mod.js

#themes:

perhaps you @Mattias_Persson have a idea?

im trying to recreate what you have in my test environment so I can have a go at helping you out,

one thing im confused by is line 3 of the code you provided, you have input_boolean.helper_wled_kueche_unterschraenke set to triggers_update for what looks to be the button for oberschraenke not unterschraenke

is that just an error? if not, why is this the case?

I think I got it, this is the main changes.

        icon: > 
          [[[
            return states[variables.helper_always_on].state === "off" ? "mdi:motion-sensor-off" : "mdi:motion-sensor"
          ]]]
        styles:
          card:
            - margin: > 
                [[[
                  return variables.state === 'on' ? '45% -20% 0 0' : ' 8% -20% 0 0'
                ]]]
            - border: 0px
          icon: 
            - margin: > 
                [[[
                  return variables.state === 'on' ? '45% -20% 0 0' : ' 8% -20% 0 0'
                ]]]
            - color: gray
            - width: 45% 

this is the fix applied to the code you provided.

- type: custom:button-card
  triggers_update:
    - input_boolean.helper_wled_kueche_unterschraenke
  show_state: false
  tap_action:
    !include popup/ledstripe_kitchen_top.yaml
  entity: light.wled_kitchen_top_2
  name: Oben
  template:
    - light
    - icon_ledstripe  
  variables:
    helper_always_on: input_boolean.helper_wled_kueche_oberschraenke
  custom_fields:
    bm:
      card:
        type: custom:button-card
        variables:
          original_entity: light.wled_kitchen_top_2
        hold_action: 
          action: none
        tap_action: 
          action: call-service
          service: light.toggle
          service_data:
            entity_id: light.wled_kitchen_top_2
        triggers_update:
          - input_boolean.helper_wled_kueche_oberschraenke
        entity: input_boolean.helper_wled_kueche_oberschraenke
        layout: vertical
        name: []
        icon: > 
          [[[
            return states[variables.helper_always_on].state === "off" ? "mdi:motion-sensor-off" : "mdi:motion-sensor"
          ]]]
        styles:
          card:
            - margin: > 
                [[[
                  return variables.state === 'on' ? '45% -20% 0 0' : ' 8% -20% 0 0'
                ]]]
            - border: 0px
          icon: 
            - margin: > 
                [[[
                  return variables.state === 'on' ? '45% -20% 0 0' : ' 8% -20% 0 0'
                ]]]
            - color: gray
            - width: 45%  
    helper_circle: |
      [[[
        if (variables.state === 'off' ) {
          let input = variables.helper_always_on,
            radius = 9,
            circumference = radius * 2 * Math.PI;
          return `
            <svg viewBox="0 0 50 50">
              <style>
                circle {
                  transform: rotate(-90deg);
                  transform-origin: 50% 50%;
                  stroke-dasharray: ${circumference};
                  stroke-dashoffset: ${circumference * 2};
                }
                tspan {
                  font-size: 10px;
                }
              </style>
              <circle cx="25" cy="25" r="${radius}" fill="rgba(255,255,255,0.04)" stroke="#313638" stroke-width="0.6"  />
              <text x="50%" y="52%" fill="#8d8e90" font-size="12" text-anchor="middle" alignment-baseline="middle" dominant-baseline="middle"><tspan font-size="10"></tspan></text>
            </svg>
          `;
        }
      ]]]
#  Werte fuer circle zeile 677    <text x="50%" y="52%" fill="#8d8e90" font-size="12" text-anchor="middle" alignment-baseline="middle" dominant-baseline="middle">${states[variables.helper_always_on].state}<tspan font-size="10"></tspan></text>
              
  styles:
    custom_fields:
      helper_circle:
        - position: absolute
        - width: 100%
        - height: 100%
        - margin: -31% -30% 0 0
        - display: initial
        - opacity: 1
        - justify-self: end     
      bm:
        - position: absolute
        - width: 50%
        - height: 50%
        - display: initial
        - opacity: 1
        - justify-self: end
1 Like

I saw you made this. Mind sharing?

yeah that’s the solution. Many Thanks
I ate hours there, I don’t understand enough javascript, I would never have thought of that

return variables.state === 'on' ? '45% -20% 0 0' : ' 8% -20% 0 0'

Thank you Thank you

of course, but its not animated only with color when the state is ON, its my Made in Shade
this is my “disgraced icon” it’s a bit ugly and deserves some love again when i have time

icon_pool:
    styles:
      custom_fields:
        icon:
          - width: 72%
    custom_fields:
      icon: >
        [[[
          
          let color;
          if (variables.state === 'on' && variables.timeout < 2000) {
            color = 'var(--primary-color)';
          } 
          if (variables.state === 'off' && variables.timeout < 2000) {
            color = '#a0a0a0';
          }
          if (variables.state === 'on' && variables.timeout > 2000) {
            color = 'var(--primary-color)';
          }
          if (variables.state === 'off' && variables.timeout > 2000) {
            color = '#a0a0a0';
          }
          let state = variables.state ? 'on' : null;
          return `
                <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
                 viewBox="0 0 480 480" style="enable-background:new 0 0 480 480;" xml:space="preserve">
                <path class="${state}" fill="${color}" d="M447.216,504.163h-64.784c-11.542,0-20.898-9.356-20.898-20.898V326.588h106.58v156.677
                C468.114,494.807,458.758,504.163,447.216,504.163z"/>
          
                <polygon fill="${color}" points="409.078,415.115 336.98,415.115 336.98,381.678 388.18,381.678 	"/>
                <rect x="190.694" y="33.959" fill="${color}" width="73.143" height="125.388"/>
                <polygon fill="${color}" points="91.429,504.163 60.082,504.163 60.082,478.041 101.878,478.041 	"/>
                <polygon fill="${color}" points="300.408,504.163 269.061,504.163 258.612,478.041 300.408,478.041 	"/>
                <path class="${state}" fill="${color}" d="M339.592,232.49H20.898c-7.214,0-13.061,5.847-13.061,13.061l0,0
                    c0,7.214,5.847,13.061,13.061,13.061h318.694c7.214,0,13.061-5.847,13.061-13.061l0,0
                    C352.653,238.337,346.806,232.49,339.592,232.49z"/>
           
                <path class="${state}" fill="${color}" d="M316.082,478.041H44.408c-11.542,0-20.898-9.356-20.898-20.898V258.612H336.98v198.531
                C336.98,468.685,327.624,478.041,316.082,478.041z"/>
           
                <path class="${state}" fill="${color}" d="M336.98,455.053v2.09c0,11.546-9.352,20.898-20.898,20.898H44.408
                    c-11.546,0-20.898-9.352-20.898-20.898v-2.09H336.98z"/>
                <path class="${state}" fill="${color}" d="M125.461,258.612v219.429H44.408c-11.546,0-20.898-9.352-20.898-20.898V258.612H125.461z"/>
           
                <path class="${state}" fill="${color}" d="M316.082,159.347H44.408c-11.542,0-20.898,9.356-20.898,20.898v52.245H336.98v-52.245
                C336.98,168.703,327.624,159.347,316.082,159.347z"/>
           
                <circle style="fill:#FFFFFF;" cx="227.265" cy="326.531" r="20.898"/>
                <circle style="fill:#FFFFFF;" cx="227.265" cy="410.122" r="20.898"/>
            
                <rect x="175.02" y="7.837" fill="${color}" width="104.49" height="26.122"/>
                <path class="${state}" d="M423.217,422.952l-30.694-49.11h-47.707v-108.08c8.999-2.329,15.673-10.494,15.673-20.21c0-5.583-2.173-10.831-6.119-14.778
                c-2.682-2.682-5.973-4.522-9.555-5.441v-45.087c0-15.845-12.891-28.735-28.735-28.735h-44.408v-45.704H256v45.704h-20.898v-43.637
                l82.934-49.96l-8.088-13.426l-38.275,23.057V41.796h15.673V0H167.184v41.796h15.673V151.51H44.408
                c-15.844,0-28.735,12.89-28.735,28.735v45.096C6.675,227.67,0,235.835,0,245.551c0,5.583,2.173,10.831,6.119,14.778
                c2.682,2.682,5.973,4.522,9.555,5.441v191.373c0,15.845,12.891,28.735,28.735,28.735h7.837V512h44.49l10.449-26.122h146.123
                L263.755,512h44.49v-26.122h7.837c15.844,0,28.735-12.89,28.735-28.735v-34.191H423.217z M383.836,389.515l11.102,17.763h-50.122
                v-17.763H383.836z M316.082,167.184c7.203,0,13.061,5.859,13.061,13.061v44.408h-80.98v15.673h91.429
                c1.397,0,2.709,0.543,3.695,1.529c0.986,0.986,1.53,2.299,1.53,3.696c0,2.881-2.344,5.224-5.224,5.224h-91.429v15.673h80.98v180.767
                H133.298V266.449h86.131v32.442c-12.046,3.42-20.898,14.512-20.898,27.64c0,15.845,12.891,28.735,28.735,28.735
                S256,342.375,256,326.531c0-13.128-8.852-24.219-20.898-27.64V167.184H316.082z M31.347,266.449h86.277v180.767H31.347V266.449z
                 M227.265,313.469c7.202,0,13.061,5.859,13.061,13.061s-5.859,13.061-13.061,13.061c-7.202,0-13.061-5.859-13.061-13.061
                S220.063,313.469,227.265,313.469z M182.857,15.673h88.816v10.449h-88.816V15.673z M198.531,41.796H256v35.19l-36.571,22.031v52.494
                h-20.898V41.796z M44.408,167.184h175.02v57.469H31.347v-44.408C31.347,173.042,37.206,167.184,44.408,167.184z M20.898,240.327
                h198.531v10.449H20.898c-1.397,0-2.709-0.543-3.695-1.529c-0.987-0.986-1.53-2.299-1.53-3.696
                C15.673,242.67,18.017,240.327,20.898,240.327z M86.123,496.327H67.918v-10.449h22.384L86.123,496.327z M292.571,496.327h-18.204
                l-4.18-10.449h22.384V496.327z M316.082,470.204H44.408c-5.138,0-9.58-2.989-11.711-7.314h295.096
                C325.661,467.215,321.219,470.204,316.082,470.204z"/>
                <path class="${state}" d="M227.265,381.388c-15.844,0-28.735,12.89-28.735,28.735c0,15.845,12.891,28.735,28.735,28.735S256,425.967,256,410.122
                C256,394.278,243.109,381.388,227.265,381.388z M227.265,423.184c-7.202,0-13.061-5.859-13.061-13.061s5.859-13.061,13.061-13.061
                c7.202,0,13.061,5.859,13.061,13.061S234.468,423.184,227.265,423.184z"/>
                <path class="${state}" d="M512,365.772h-36.049v-47.015H353.698v42.026h15.673V334.43h90.906v31.342v97.118v20.376
                c0,7.202-5.859,13.061-13.061,13.061h-64.784c-7.202,0-13.061-5.859-13.061-13.061v-47.25h-15.673v47.25
                c0,15.845,12.891,28.735,28.735,28.735h64.784c15.844,0,28.735-12.89,28.735-28.735V462.89H512V365.772z M496.327,447.216h-20.376
                v-65.771h20.376V447.216z"/>

             
              <style>
                @keyframes on {
                  0% {
                    transform: scale(0.85);
                  }
                  20% {
                    transform: scale(1.1);
                  }
                  40% {
                    transform: scale(0.95);
                  }
                  60% {
                    transform: scale(1.03);
                  }
                  80% {
                    transform: scale(0.97);
                  }
                  100% {
                    transform: scale(1);
                  }
                }
                .on {
                  animation: on 0.8s;
                  transform-origin: center;
                }
              </style>
                
            </svg>
          `;
        ]]]
 
1 Like

what is in your kids_climate then ?

I no longer use this approach for my ac so I can not provide the code, but it was the same AC popup that was provided by @Mattias_Persson with an added input select input to set the fan speed.

happy to help,

there might have been a way to get this working with the “low code” approach that you had taken, but as a developer I went straight to the code way and didn’t look into the low code approach.

this is what’s known as a ternary statement. its just a fancy inline if statement, you can read more in the javascript documentation.

with the same code, why is the result on my home assistant like this, please help

Capture.JPGddd

HI, maybe anyone can help, because obviously I´m to dumb to change the aspect ratio to fit it to a normal Full-HD screen.
I´ve already changed the size of the background.png and took a fresh browser to ensure the cache is clean.

The HTML-Tag seems to have the right size:

But the root element not :thinking:

@tommysusilo, welcome.

@Giblet has given a fantastic response below mine

This is a more recent graph card, the post you replied to is over a year old, lots of updates between now and then. A different take on designing a Lovelace UI - #4263 by karma

if you have any issues feel free to reach out.

@kamilk, I can’t tell what you are asking.
if you would like the layout to work on a larger screen then this is my solution

views:
  - type: custom:grid-layout
    path: 0
    layout:
      #default
      grid-gap: var(--custom-layout-card-padding)
      grid-template-columns: 0.7fr repeat(3, 1fr) 0.7fr
      grid-template-rows: 0 repeat(2, fit-content(100%)) 0fr
      grid-template-areas: |
        "sidebar  .           .       .       ."
        "sidebar  Living_Room  Rooms  Climate  footer"
        "sidebar  media       Home  People   footer"
        "sidebar   .       .   .  ."
      mediaquery: 
        #phone
        '(max-width: 800px)':
          grid-gap: calc(var(--custom-layout-card-padding) * 1.7)
          grid-template-columns: 0 repeat(2, 1fr) 0
          grid-template-rows: 0 repeat(5, fit-content(100%)) 0fr
          grid-template-areas: |
            ".  .           .        ."
            ".  sidebar     sidebar  ."
            ".  Living_Room  Climate   ."
            ".  Rooms      Home   ."
            ".  media       People    ."
            ".  footer      footer   ."
            ".  .           .        ."
        #portrait
        '(max-width: 1200px)':
          grid-gap: var(--custom-layout-card-padding)
          grid-template-columns: repeat(3, 1fr) 0
          grid-template-rows: 0 repeat(3, fit-content(100%)) 0fr
          grid-template-areas: |
            "sidebar  .           .       ."
            "sidebar  Living_Room  Climate  ."
            "sidebar  Rooms      Home  ."
            "sidebar  media       People   ."
            "sidebar  footer      footer  ."
            "sidebar  .           .       ."
        '(max-width: 1440px)':
          grid-gap: var(--custom-layout-card-padding)
          grid-template-columns: repeat(4, 1fr) 0
          grid-template-rows: 0 repeat(2, fit-content(100%)) 0fr
          grid-template-areas: |
            "sidebar  .           .       .      ."
            "sidebar  Living_Room  Rooms  Climate  ."
            "sidebar  media       Home  People   ."
            "sidebar  footer      footer  footer  ."
        theme: tablet

and I have added this to the team to stack the footer icons on larger screens


      #################################################
      #                                               #
      #                    FOOTER                     #
      #                                               #
      #################################################

      grid-layout$hui-horizontal-stack-card:
        $: |
          #root {
            flex-wrap: wrap;
            justify-content: center;
            overflow: visible;
            margin-top: -1.95vw !important;
            padding-bottom: 2.5em;
          }
          @media screen and (min-width: 1200px) {
            #root {
              flex-wrap: nowrap;
              justify-content: space-between;
              margin-top: -1.6vh;
              padding-bottom: 0;
            }
          }
          @media screen and (min-width: 1441px) {
            #root {
              flex-direction: column;
            }
          }
          /* phone */
          @media screen and (max-width: 800px) {
            #root {
              padding-top: 4vw !important;
            }
          }

posting YAML helps, the browser debug screenshots are fantastic but you should provide more context next time