button-card icon indicating copy to clipboard operation
button-card copied to clipboard

( ? ) Using extra_styles or styles (NOT card_mod) to modify some shadow-roots css elements?

Open pickonedev opened this issue 11 months ago • 3 comments

Hi!

I have this card:

type: custom:button-card
custom_fields:
  first:
    card:
      type: weather-forecast
      show_current: true
      show_forecast: true
      entity: weather.outside_weather_station
      forecast_type: daily
  second:
    card:
      type: custom:hourly-weather
      entity: weather.tomorrow_io_home_daily
      num_segments: 24
      hide_minutes: true
      name: null
      icons: true
      forecast_type: hourly
  third:
    card:
      type: weather-forecast
      show_current: false
      show_forecast: true
      entity: weather.tomorrow_io_home_daily
      forecast_type: daily
      forecast_slots: 6
  forth:
    card:
      type: custom:mini-graph-card
      hours_to_show: 24
      points_per_hour: 60
      animate: true
      hour24: true
      state_map:
        - value: "off"
          label: "Off"
        - value: "on"
          label: "On"
      show:
        labels: true
        name: false
        icon: false
      line_width: 0.3
      smoothing: false
      entities:
        - entity: weather.outside_weather_station
          attribute: temperature
          show_legend: true
          name: Temperature
          y_axis: primary
        - entity: weather.outside_weather_station
          attribute: humidity
          show_legend: true
          name: Humidity
          show_state: true
          y_axis: secondary
      tap_action:
        action: none
styles:
  card:
    - padding: 0px 10px 0px 10px
  custom_fields:
    first:
      - padding: 10px 0px 0px 0px
    second:
      - padding: 10px 0px 0px 0px
    third:
      - padding: 10px 0px 0px 0px
    forth:
      - padding: 10px 0px 10px 0px
  grid:
    - grid-template-areas: "'first' 'second' 'third' 'forth'"
    - grid-template-columns: 1fr
card_mod:
  style: 
    "hui-weather-forecast-card$": |
      ha-card{background: none !important}

And all I want is to use extra_styles, which is amazingly far better than card_mod, regarding the rendering speed, instead card_mod. There is any solution to modify what's inside a shadow-root, with extra_styles or even with styles from custom button card? Please, no card_mod solution, I am starting to hate that addon, because of one and only reason :-D

Thanks in advance!

pickonedev avatar Mar 20 '25 16:03 pickonedev

Nobody used shadow-root with button-card? :-(

pickonedev avatar Mar 22 '25 21:03 pickonedev

@pickonedev I use quite intesive extra styles in config, but I'm quite confused by your question. Do you want to use extra styles for custom_fields item? Like for example in your case

custom_fields:
  first:
    card:
      type: weather-forecast

using extra_styles for weather-forecast ? If so, I don't think you can. You can only use extra_styles for the button-card level itself.

You can take a look at my config for inspiration.

https://github.com/ngocjohn/hass-config/blob/main/config/dashboards/templates/button_card_templates/tpl_base.yaml#L169

ngocjohn avatar Mar 22 '25 21:03 ngocjohn

Well... because I cannot add extra_styles to custom_fields, I want to use extra_styles on the parent card, trying to modify those custom_fields, but I meet shadow-root on the way :-D

Wait a sec, can javascript be used on extra_styles as well? I thought only jinja template can be used there... If so, how can I use something similar to this document.querySelector('#first').shadowRoot.querySelector('hui-weather-forecast-card').setAttribute('style', 'background: none'); I think it could help me change the background inside shadow-root....

Edit: I think I managed how to do this

extra_styles: |
  [[[
    setTimeout(() => {
      this.shadowRoot.querySelector('#first').querySelector('hui-weather-forecast-card')?.shadowRoot.querySelector('ha-card')?.style.setProperty('background', 'none', 'important');
      this.shadowRoot.querySelector('#second').querySelector('hourly-weather')?.shadowRoot.querySelector('ha-card')?.style.setProperty('background', 'none', 'important');
      this.shadowRoot.querySelector('#third').querySelector('hui-weather-forecast-card')?.shadowRoot.querySelector('ha-card')?.style.setProperty('background', 'none', 'important');
      this.shadowRoot.querySelector('#forth').querySelector('mini-graph-card')?.shadowRoot.querySelector('ha-card')?.style.setProperty('background', 'none', 'important');
    }, 50);
  ]]]

Seems that I need to apply the timeout, or else it will not work, at least for hourly-weather (which seems to be slower to render)

If you have any other idea how to achieve this in other way, more optimized or better, please share it with me. Thanks!

pickonedev avatar Mar 23 '25 08:03 pickonedev

Seems that I need to apply the timeout, or else it will not work, at least for hourly-weather (which seems to be slower to render)

I understand you wish to not use card-mod, but such complexity of when the shadowRoot is available, how to style etc, is why card-mod exists.

As for a non card-mod type option, as the elements are lit elements you could use the updateComplete promise to take the guess work out of when the element is available. You would have to test whether you need to chain at each level.

dcapslock avatar Aug 15 '25 03:08 dcapslock

The difference between card_mod and extra_styles (of custom:button-card), is that extra_styles apply the style instantly, even if you have a very slow device, the style will be applied instantly... card_mod instead, if you have a slower device, the style will be applyed after everything is loaded... Because of this, I tried to use extra_styles, but... yes, I cannot avoid card_mod in every situation... Can you please tell me how to use updateComplete? With a sample, please!

Thanks!

Edit: Even with extra_styles, I have done something like this, for another card, in order to trick it not to show the transition between styles

extra_styles: |
  [[[
    this.style.opacity = '0';
    
    setTimeout(() => {
      this.style.opacity = '1';
      this.style.transition = 'opacity 0.5s ease';
    }, 300);

  ]]]

pickonedev avatar Aug 15 '25 07:08 pickonedev

Try this to get the idea. A bit of duplicate code as the updateComplete promise does not resolve to any parameter which means in the arrow function you need to query the elemnet tree again. You may be able to refactor or tidy up but this shows the idea of updateComplete and how that means you don't have to guess any timeouts.

There is no need to wait for ha-card to be complete as hui-weather-forecast-card being complete will mean ha-card is available, unless it has errored in which case there will be hui-error-card.

type: custom:button-card
styles:
  card:
    - background: red
  grid:
    - grid-template-areas: "'first'"
custom_fields:
  first:
    card:
      type: weather-forecast
      show_current: true
      show_forecast: true
      entity: weather.carlingford
      forecast_type: daily
extra_styles: |
  [[[
    this.updateComplete.then(() => {
      this.shadowRoot.querySelector("#first").querySelector('hui-weather-forecast-card').updateComplete.then(() => {
        this.shadowRoot.querySelector("#first").querySelector('hui-weather-forecast-card').shadowRoot.querySelector('ha-card')?.style.setProperty('background', 'none', 'important');     
      })
    })
  ]]]

https://github.com/user-attachments/assets/cd5d3cb8-7648-4a9b-9c24-76e375fe09f1

dcapslock avatar Aug 15 '25 12:08 dcapslock

Try this to get the idea. A bit of duplicate code as the updateComplete promise does not resolve to any parameter which means in the arrow function you need to query the elemnet tree again. You may be able to refactor or tidy up but this shows the idea of updateComplete and how that means you don't have to guess any timeouts.

There is no need to wait for ha-card to be complete as hui-weather-forecast-card being complete will mean ha-card is available, unless it has errored in which case there will be hui-error-card.

type: custom:button-card styles: card: - background: red grid: - grid-template-areas: "'first'" custom_fields: first: card: type: weather-forecast show_current: true show_forecast: true entity: weather.carlingford forecast_type: daily extra_styles: | [[[ this.updateComplete.then(() => { this.shadowRoot.querySelector("#first").querySelector('hui-weather-forecast-card').updateComplete.then(() => { this.shadowRoot.querySelector("#first").querySelector('hui-weather-forecast-card').shadowRoot.querySelector('ha-card')?.style.setProperty('background', 'none', 'important');
}) }) ]]]

Screen.Recording.2025-08-15.at.10.47.37.pm.mov

I am trying to do this to a filter card inside of custom_fields and I think I am missing something...

extra_styles: |
  [[[
    this.updateComplete.then(() => {
      this.shadowRoot.querySelector("#cards").querySelector('hui-entity-filter-card').updateComplete.then(() => {
        this.shadowRoot.querySelector("#cards").querySelector('hui-entity-filter-card').shadowRoot.querySelector("hui-entities-card").shadowRoot.querySelector('ha-card')?.style.setProperty('background', 'red', 'important');     
      })
    })
  ]]]

This is not working, any idea why? The name of custom_field is 'cards'

pickonedev avatar Aug 28 '25 11:08 pickonedev

Not sure. You may need to wait for hui-entities-card to be ready.

dcapslock avatar Aug 28 '25 12:08 dcapslock