Lovelace: Button card

I was unaware you ccould create css classes. Nice

ok, as said I hadn’t yet set styles :wink:

so I need this to hover on the icon, since that’s the only thing showing on my buttons in the shortcut menu. please let me post what I tried:

using the adapted button_template, with the extra_styles @KTibow posted above:

button_shortcut_menu:
  variables:
    dashboard: >
      [[[ return window.location.pathname.split('/')[1] ]]]
    view: >
      [[[ return window.location.pathname.split('/').slice(-1) ]]]
  size: 25px
  styles:
    icon:
      - color: var(--secondary-text-color)
    card:
      - background: >
          [[[ return variables.view == variables.path
              ? 'var(--secondary-background-color)' : 'var(--card-background-color)';
          ]]]
  extra_styles: |
    /* Tooltip container */
    .tooltip {
      position: relative;
      display: inline-block;
      border-bottom: 1px dotted black; /* If you want dots under the hoverable text */
    }

    /* Tooltip text */
    .tooltip .tooltiptext {
      visibility: hidden;
      width: 120px;
      background-color: black;
      color: #fff;
      text-align: center;
      padding: 5px 0;
      border-radius: 6px;

      /* Position the tooltip text - see examples below! */
      position: absolute;
      z-index: 1;
    }

    /* Show the tooltip text when you mouse over the tooltip container */
    .tooltip:hover .tooltiptext {
      visibility: visible;
    }

and the menu button itself:

      - type: custom:button-card
        template: button_shortcut_menu
        icon: >
          [[[
          return `<div class="tooltip">
                  <ha-icon>icon='mdi:calendar'></ha-icon>
                  <span class="tooltiptext">Calendar</span>
                  </div>`
          ]]]
        tap_action:
          action: navigate
          navigation_path: /lovelace/tijd_agenda
        variables:
          path: tijd_agenda
        styles:
          icon:
            - color: >
                [[[ return states['sensor.trash_color'].state ]]]

is this getting anywhere close to what it should be? Must be wrong somewhere because I cant see an icon anymore, nor a tooltip…

trying the html in the button code for now, if this will work, Ill try to move it to the template and need to find a way to have the icon set in the button config (maybe yet another variable?) and feed it to the template

if anyone would have a minute to help me a bit further here it would be much appreciated. Much of my troubles is caused by not being able to show the icon using the code herein I fear (copied from the docs. Or is that only allowed in custom_fields, and not the main pre-defined ones?:

        icon: >
          [[[
          return `<div class="tooltip">
                  <ha-icon>icon='mdi:calendar'></ha-icon>
                  <span class="tooltiptext">Calendar</span>
                  </div>`
          ]]]

if I could start getting that right, next step (the tooltip) would probably be easier. If possible at all of course.
thanks of you would!

Mind making a compiled button-card for testing (manually add the template in to your setup)? Thanks!

I already tried that, but nothing shows…

  - type: custom:button-card
    icon: >
      [[[
        return `<ha-icon icon='mdi:calendar'></ha-icon>`
      ]]]
    tap_action:
      action: navigate
      navigation_path: /lovelace/tijd_agenda

shows a button, tapping it works, but no icon…

This should work:

  - type: custom:button-card
    icon: >
      [[[
        return `mdi:calendar`
      ]]]
    tap_action:
      action: navigate
      navigation_path: /lovelace/tijd_agenda

yes it does! and, using your extra_styles in my button template, how should I next add Petro’s template around that icon?

        icon: >
          [[[
          return `<div class="tooltip">mdi:calendar
                  <span class="tooltiptext">Calendar</span>
                  </div>`
          ]]]

? obviously not…

I don’t think it’s possible to override the HTML of the icon. Gonna come back to this later. I’d recommend adding a custom field which is a markdown card, and disabling the icon.

thanks, appreciate your efforts. Maybe Romrider could provide us (me) with some certainty. If not possible yet, might be a fine FR, adding tooltip on a button with an icon only.

will test with name to be sure the tooltip itself is correct.

using:

  - type: custom:button-card
    template: button_shortcut_menu
    name: >

      [[[
         return `<div class="tooltip">Cal
                <span class="tooltiptext">Calendar</span>
                 </div>`
      ]]]

the name shows fine, even with the dots of the extra_styles, but no tooltip on hover:

this seems to be the only reference by the master himself, stating it should be possible, so that’s hopeful.
no example anywhere though…

tried a little hack:

    name: >
        `<div class='tooltip'><ha-icon icon='mdi:calendar'</ha-icon>
          <span class='tooltiptext'>Tooltip text</span>
        </div`

showing the icon, but still no tooltip. giving up for now.

HI Guys,

can one help me for this:

grafik

thank you

What do you need help with? Is there a bug? Do you want to add something?

i want to integrate that and would like to have the code.

The Code from Pedro works only for lovelace gen :slight_smile:?

# lovelace_gen

{% set color = color|default('var(--paper-item-icon-active-color)') %}

type: custom:button-card
aspect_ratio: 1/1
entity: {{ entity }}
size: 60%
name: {{ name }}
show_state: true
show_name: true
show_label: false
styles:
  icon:
  - height: auto
  img_cell:
  - justify-content: start
  - align-items: start
  grid:
  - grid-template-areas: '"i info" "n n" "s s" "l l"'
  - grid-template-columns: 1fr 35%
  - grid-template-rows: 1fr 0.min-content min-content min-content
  - position: relative
  card:
    - border-radius: 15px
    - padding: 10px
  name:
  - justify-self: start
  - align-self: end
  - font-weight: bold
  - font-family: Helvetica 
  - font-size: 12px
  - text-align: start
  - background-image: linear-gradient(to right, white 0%, white 80%, rgba(0,0,0,0))
  - -webkit-background-clip: text
  - -webkit-text-fill-color: transparent
  - position: relative
  - display: inline-block
  - width: 100%
  - align-content: start
  - text-align: start
  - text-overflow: unset
  state:
  - justify-self: start
  - align-self: end
  - font-weight: bold
  - font-family: Helvetica 
  - font-size: 12px
  - text-align: start
  custom_fields:
    info:
    - align-self: start
custom_fields:
  info: >
      [[[
        function capitalizeFirstLetter(string) {
          return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
        }
        
        const length = 41;
        const width = 3;

        if (entity.state === 'on' && entity.attributes.brightness) {
          const radius = length / 2;
          const brightness = Math.round(entity.attributes.brightness / 2.54);
          const circumference = radius * 2 * Math.PI;
          return `
            <svg viewBox="0 0 50 50">
              <circle cx="25" cy="25" r="${radius}" fill="none" stroke="var(--paper-item-icon-color)" opacity="0.5" stroke-width="${width}" />
              <circle style="
                  transform: rotate(-90deg);
                  transform-origin: 50% 50%;
                  stroke-dasharray: ${circumference};
                  stroke-dashoffset: ${circumference - brightness / 100 * circumference};
                "
                id="c_brightness" cx="25" cy="25" r="${radius}" stroke="var(--paper-item-icon-active-color)" stroke-width="${width}" fill="none" stroke-linecap="round" />
              <text x="50%" y="54%" fill="var(--primary-text-color)" font-size="14" text-anchor="middle" alignment-baseline="middle">${brightness}<tspan font-size="10">%</tspan>
              </text>
            </svg>
            `;
          }
        else if (entity.state === 'on' && entity.attributes.speed && entity.attributes.speed_list)  {
          const text = capitalizeFirstLetter(entity.attributes.speed);
          const gap = 5;
          const edge = (50 - length) / 2;
          const y = 50 - edge;
          var items = entity.attributes.speed_list;
          if (items.indexOf('off') !== -1){
            items.splice(items.indexOf('off'), 1);
          }
          const current = items.indexOf(entity.attributes.speed)
          var i;
          var x1 = edge;
          var ret = `<svg  viewBox="0 0 50 50">`;
          var l = (length - gap * (items.length - 1)) / items.length;
          for (i = 0; i < items.length; i++) {
            var x2 = x1 + l;
            var color = (i <= current) ? "var(--paper-item-icon-active-color)" : "var(--paper-item-icon-color)";
            var opacity = (i <= current) ? "1.0" : "0.5";
            ret += `<line x1="${x1}" y1="${y}" x2="${x2}" y2="${y}" stroke="${color}" stroke-width="${width}" opacity="${opacity}" stroke-linecap="round" />`;
            x1 = x2 + gap;
          }
          ret += `<text x="50%" y="54%" fill="var(--primary-text-color)" font-size="14" text-anchor="middle" alignment-baseline="middle">${text}</text></svg>`
          return ret;
        }
      ]]]
state:
- value: 'on'
  styles:
    card:
    - opacity: 1.0
    icon:
    - color: |
        [[[
          var [domain, object_id] = entity.entity_id.split('.');
          if (domain === "light")
            return 'var(--button-card-light-color)';
          return '{{ color }}';
        ]]]
    name:
      - color: white
    state:
      - color: gray
    lock:
      - color: white 
    label:
      - color: gray
- value: 'off'
  styles:
    card:
    - opacity: 0.5
    icon:
    - color: var(--paper-item-icon-color)
    name:
    - color: var(--primary-text-color)
    state:
    - color: var(--primary-text-color)
    label:
    - color: var(--primary-text-color)
    lock:
    - color: var(--paper-item-icon-color)
- value: "unavailable"
  styles:
    card:
    - opacity: 0.2
    icon:
    - color: var(--paper-item-icon-color)')
    name:
    - color: var(--primary-text-color)
    state:
    - color: hsl(0, 100%, 50%)
    label:
    - color: var(--primary-text-color)
    lock:
    - color: var(--paper-item-icon-color)
tap_action:
  action: toggle
  haptic: light
hold_action:
  action: more-info
  haptic: heavy
# lovelace_gen

{% set color = color|default('var(--paper-item-icon-active-color)') %}

type: custom:button-card
aspect_ratio: 1/1
entity: {{ entity }}
size: 60%
name: {{ name }}
show_state: true
show_name: true
show_label: false
styles:
  icon:
  - height: auto
  img_cell:
  - justify-content: start
  - align-items: start
  grid:
  - grid-template-areas: '"i info" "n n" "s s" "l l"'
  - grid-template-columns: 1fr 35%
  - grid-template-rows: 1fr 0.min-content min-content min-content
  - position: relative
  card:
    - border-radius: 15px
    - padding: 10px
  name:
  - justify-self: start
  - align-self: end
  - font-weight: bold
  - font-family: Helvetica 
  - font-size: 12px
  - text-align: start
  - background-image: linear-gradient(to right, white 0%, white 80%, rgba(0,0,0,0))
  - -webkit-background-clip: text
  - -webkit-text-fill-color: transparent
  - position: relative
  - display: inline-block
  - width: 100%
  - align-content: start
  - text-align: start
  - text-overflow: unset
  state:
  - justify-self: start
  - align-self: end
  - font-weight: bold
  - font-family: Helvetica 
  - font-size: 12px
  - text-align: start
  custom_fields:
    info:
    - align-self: start
custom_fields:
  info: >
      [[[
        function capitalizeFirstLetter(string) {
          return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
        }
        
        const length = 41;
        const width = 3;

        if (entity.state === 'on' && entity.attributes.brightness) {
          const radius = length / 2;
          const brightness = Math.round(entity.attributes.brightness / 2.54);
          const circumference = radius * 2 * Math.PI;
          return `
            <svg viewBox="0 0 50 50">
              <circle cx="25" cy="25" r="${radius}" fill="none" stroke="var(--paper-item-icon-color)" opacity="0.5" stroke-width="${width}" />
              <circle style="
                  transform: rotate(-90deg);
                  transform-origin: 50% 50%;
                  stroke-dasharray: ${circumference};
                  stroke-dashoffset: ${circumference - brightness / 100 * circumference};
                "
                id="c_brightness" cx="25" cy="25" r="${radius}" stroke="var(--paper-item-icon-active-color)" stroke-width="${width}" fill="none" stroke-linecap="round" />
              <text x="50%" y="54%" fill="var(--primary-text-color)" font-size="14" text-anchor="middle" alignment-baseline="middle">${brightness}<tspan font-size="10">%</tspan>
              </text>
            </svg>
            `;
          }
        else if (entity.state === 'on' && entity.attributes.speed && entity.attributes.speed_list)  {
          const text = capitalizeFirstLetter(entity.attributes.speed);
          const gap = 5;
          const edge = (50 - length) / 2;
          const y = 50 - edge;
          var items = entity.attributes.speed_list;
          if (items.indexOf('off') !== -1){
            items.splice(items.indexOf('off'), 1);
          }
          const current = items.indexOf(entity.attributes.speed)
          var i;
          var x1 = edge;
          var ret = `<svg  viewBox="0 0 50 50">`;
          var l = (length - gap * (items.length - 1)) / items.length;
          for (i = 0; i < items.length; i++) {
            var x2 = x1 + l;
            var color = (i <= current) ? "var(--paper-item-icon-active-color)" : "var(--paper-item-icon-color)";
            var opacity = (i <= current) ? "1.0" : "0.5";
            ret += `<line x1="${x1}" y1="${y}" x2="${x2}" y2="${y}" stroke="${color}" stroke-width="${width}" opacity="${opacity}" stroke-linecap="round" />`;
            x1 = x2 + gap;
          }
          ret += `<text x="50%" y="54%" fill="var(--primary-text-color)" font-size="14" text-anchor="middle" alignment-baseline="middle">${text}</text></svg>`
          return ret;
        }
      ]]]
state:
- value: 'on'
  styles:
    card:
    - opacity: 1.0
    icon:
    - color: |
        [[[
          var [domain, object_id] = entity.entity_id.split('.');
          if (domain === "light")
            return 'var(--button-card-light-color)';
          return '{{ color }}';
        ]]]
    name:
      - color: white
    state:
      - color: gray
    lock:
      - color: white 
    label:
      - color: gray
- value: 'off'
  styles:
    card:
    - opacity: 0.5
    icon:
    - color: var(--paper-item-icon-color)
    name:
    - color: var(--primary-text-color)
    state:
    - color: var(--primary-text-color)
    label:
    - color: var(--primary-text-color)
    lock:
    - color: var(--paper-item-icon-color)
- value: "unavailable"
  styles:
    card:
    - opacity: 0.2
    icon:
    - color: var(--paper-item-icon-color)')
    name:
    - color: var(--primary-text-color)
    state:
    - color: hsl(0, 100%, 50%)
    label:
    - color: var(--primary-text-color)
    lock:
    - color: var(--paper-item-icon-color)
tap_action:
  action: toggle
  haptic: light
hold_action:
  action: more-info
  haptic: heavy

hi, see if someone can help me, so far I have this working:

      hold_action:
        action: url
        url_path: >  
          [[[ return states['[[entity]]'].attributes.map_link ]]]

now I am trying to achieve the same with the sensor of the ha application, in the template editor it works correctly:

{%  set geo = state_attr('sensor.redmi_note_7_geocoded_location', 'Location').split('[')[1].split(']')[0] %}
  https://www.google.com/maps/search/?api=1&basemap=roadmap&layer=traffic&query={{geo}}

the problem is that I am not able to add it correctly to the configuration, I have tried:

      hold_action:
        action: url
        url_path: >
          [[[
            {%  set geo = state_attr('sensor.redmi_note_7_geocoded_location', 'Location').split('[')[1].split(']')[0] %}
            return https://www.google.com/maps/search/?api=1&basemap=roadmap&layer=traffic&query={{geo}}
          ]]]

and

      hold_action:
        action: url
        url_path: >
          [[[
            {%  set geo = state_attr('sensor.redmi_note_7_geocoded_location', 'Location').split('[')[1].split(']')[0] %}
            return "https://www.google.com/maps/search/?api=1&basemap=roadmap&layer=traffic&query={{geo}}"
          ]]]

I would appreciate your help, greetings

JS doesn’t support Jinja2, don’t try to mix them. Try out

      hold_action:
        action: url
        url_path: >
          [[[
            return 'https://www.google.com/maps/search/?api=1&basemap=roadmap&layer=traffic&query=' + states['sensor.redmi_note_7_geocoded_location'].attributes.location.split('[')[1].split(']')[0]
          ]]]
1 Like

Thanks a lot, its working

Hi,

I have this :

color: auto
color_type: icon
entity: input_select.chauffage_salon
icon: 'mdi:clock-outline'
name: Matin
show_icon: true
show_label: false
show_last_changed: false
show_name: true
show_state: false
styles:
  card:
    - height: 100px
    - width: 355px
    - margin: 0px 0px 0px 3px
    - box-shadow: none
    - background: 'rgba(0,0,0,0)'
    - border: 2px solid var(--primary-color)
  custom_fields:
    start_time:
      - z-index: 100
      - background-color: 'rgba(0, 0, 0, 0)'
      - position: absolute
      - left: 115px
      - font-size: 15px
      - line-height: 20px
      - transform: scale(0.9)
    end_time:
      - z-index: 100
      - background-color: 'rgba(0, 0, 0, 0)'
      - position: absolute
      - left: 225px
      - font-size: 15px
      - line-height: 20px
      - transform: scale(0.9)
  grid:
    - position: relative
  icon:
    - color: var(--primary-color)
    - transform: scale(0.4) rotate(0.8turn)
    - position: absolute
    - left: '-50px'
  name:
    - position: absolute
    - bottom: 23px
    - left: 45px
    - font-size: 18px
    - color: black
tap_action:
  action: none
custom_fields:
  start_time:
    card:
      entity: input_datetime.heating_time_confort_morning_start
      hide:
        name: true
      hour_step: 1
      layout:
        align_controls: center
        name: inside
      link_values: true
      minute_step: 5
      name: ''
      style:
        .: |
          ha-card {
            width: 30%;
            background-color: rgba(0,0,0,0);
            box-shadow: none;
          }
        .time-picker-row:
          .time-picker-content:
            .: |
              .time-separator {
                display: none;
              }
            time-unit:
              $: |
                .time-unit {
                  padding: 2px !important;
                }
                .time-input {
                  border: 2px solid var(--primary-color) !important;
                  background-color: rgba(0,0,0,0) !important;
                  color: black !important;
                  border-radius: 5px;
                }     
                .time-picker-icon {
                  color: var(--primary-color) !important;
                }
      type: 'custom:time-picker-card'
  end_time:
    card:
      entity: input_datetime.heating_time_confort_morning_end
      hide:
        name: true
      hour_step: 1
      layout:
        align_controls: center
        name: inside
      link_values: true
      minute_step: 5
      name: ''
      style:
        .: |
          ha-card {
            width: 30%;
            box-shadow: none;
            background-color: rgba(0,0,0,0);
          }
        .time-picker-row:
          .time-picker-content:
            .: |
              .time-separator {
                display: none;
              }
            time-unit:
              $: |
                .time-unit {
                  padding: 2px !important;
                }
                .time-input {
                  border: 2px solid var(--primary-color) !important;
                  background-color: rgba(0,0,0,0) !important;
                  color: black !important;
                  border-radius: 5px;
                }     
                .time-picker-icon {
                  color: var(--primary-color) !important;
                }
      type: 'custom:time-picker-card'
type: 'custom:button-card'

and i got :
image

I have a problem.
None of the embedded cards have focus.
I have tried playing with the z-index css but it doesn’t work.

Any ideas ?

I had already asked a question of this type and the correction was the css z-index.

But for this case, it does not work.

Thanks in advance.

I answer to myself if one day someone has the problem :

The focus problem is because I had set action to none.

So I put the action on script, without parameters (so no call):

I also reviewed the positions of the z-index css :

color: auto
color_type: icon
entity: input_select.chauffage_salon
icon: 'mdi:clock-outline'
name: Matin
show_icon: true
show_label: false
show_last_changed: false
show_name: true
show_state: false
styles:
  card:
    - height: 100px
    - width: 355px
    - margin: 0px 0px 0px 3px
    - box-shadow: none
    - background: 'rgba(0,0,0,0)'
    - border: 2px solid var(--primary-color)
  custom_fields:
    start_time:
      - z-index: 100
      - background-color: 'rgba(0, 0, 0, 0)'
      - position: absolute
      - left: 115px
      - font-size: 15px
      - line-height: 20px
      - transform: scale(0.9)
    end_time:
      - z-index: 100
      - background-color: 'rgba(0, 0, 0, 0)'
      - position: absolute
      - left: 225px
      - font-size: 15px
      - line-height: 20px
      - transform: scale(0.9)
  grid:
    - position: relative
  icon:
    - color: var(--primary-color)
    - transform: scale(0.4) rotate(0.8turn)
    - position: absolute
    - left: '-50px'
    - z-index: 10
  name:
    - position: absolute
    - bottom: 23px
    - left: 45px
    - font-size: 18px
    - color: black
tap_action:
  action: call-service
custom_fields:
  start_time:
    card:
      entity: input_datetime.heating_time_confort_morning_start
      hide:
        name: true
      hour_step: 1
      layout:
        align_controls: center
        name: inside
      link_values: true
      minute_step: 5
      name: ''
      style:
        .: |
          ha-card {
            width: 30%;
            background-color: rgba(0,0,0,0);
            box-shadow: none;
          }
        .time-picker-row:
          .time-picker-content:
            .: |
              .time-separator {
                display: none;
              }
            time-unit:
              $: |
                .time-unit {
                  padding: 2px !important;
                }
                .time-input {
                  border: 2px solid var(--primary-color) !important;
                  background-color: rgba(0,0,0,0) !important;
                  color: black !important;
                  border-radius: 5px;
                }     
                .time-picker-icon {
                  color: var(--primary-color) !important;
                }
      type: 'custom:time-picker-card'
  end_time:
    card:
      entity: input_datetime.heating_time_confort_morning_end
      hide:
        name: true
      hour_step: 1
      layout:
        align_controls: center
        name: inside
      link_values: true
      minute_step: 5
      name: ''
      style:
        .: |
          ha-card {
            width: 30%;
            box-shadow: none;
            background-color: rgba(0,0,0,0);
          }
        .time-picker-row:
          .time-picker-content:
            .: |
              .time-separator {
                display: none;
              }
            time-unit:
              $: |
                .time-unit {
                  padding: 2px !important;
                }
                .time-input {
                  border: 2px solid var(--primary-color) !important;
                  background-color: rgba(0,0,0,0) !important;
                  color: black !important;
                  border-radius: 5px;
                }     
                .time-picker-icon {
                  color: var(--primary-color) !important;
                }
      type: 'custom:time-picker-card'
type: 'custom:button-card'

ezgif.com-video-to-gif

I love this card ! :heart:

6 Likes

It’s work on pc (chrome) but not in app or mobile

Screenshot post? What do you mean by it’s not working? If it’s the time picker or card-mod, raise an issue over there.