🔹 Card-mod - Super-charge your themes!

I do not think it will work.
In short:
this will work:

ha-card.some_class some_element { ... }

this will not:

ha-card.some_class some_element $:
  some_element { ... }

Are you sure your idea works?

I know only one method of defining a “classy shadowRoot element” - using variables.

Try it. It works.

Here is a theme I’m building at the moment:

test:
  card-mod-theme: test
  card-mod-card-yaml: |
    .: |
      @keyframes fadeIn {
        0% { opacity: 0; }
        100% { opacity: 1; }
      }

      ha-card {
        height: 60px !important;
        width: 180px !important;
        animation: fadeIn 1s ease;
        border-radius: 15px;
        border: none;
        background:
          {% if is_state(config.entity, 'on') %} white
          {% else %} rgba(0,0,0,0.5) {% endif %}
      }

      ha-card.automation {
        --tile-color:
          {% if is_state(config.entity, 'on') %} lightgrey !important
          {% else %} grey !important {% endif %};
        --mdc-ripple-color: lightgrey !important;
      }

      ha-card.light {
      }

      ha-card.switch {
        --tile-color:
          {% if is_state(config.entity, 'on') %} palegreen !important
          {% else %} grey !important {% endif %};
        --mdc-ripple-color: palegreen !important;
      }

      ha-card.water {
        --tile-color:
          {% if is_state(config.entity, 'on') %} deepskyblue !important
          {% else %} grey !important {% endif %};
        --mdc-ripple-color: deepskyblue !important;
      }

      .icon-container {
        position: absolute !important;
        margin-left: -20px !important;
        margin-top: -20px !important;
        left: 30px !important;
        top: 50% !important;
      }

      ha-card.automation ha-tile-icon {
        --tile-icon-color:
          {% if is_state(config.entity, 'on') %} white
          {% else %} lightgrey {% endif %} !important
      }

      ha-card.light ha-tile-icon {
        --tile-icon-color:
          {% if is_state(config.entity, 'on') %} white
          {% else %} gold {% endif %} !important
      }

      ha-card.switch ha-tile-icon {
        --tile-icon-color:
          {% if is_state(config.entity, 'on') %} white
          {% else %} palegreen {% endif %} !important
      }

      ha-card.water ha-tile-icon {
        --tile-icon-color:
          {% if is_state(config.entity, 'on') %} white
          {% else %} deepskyblue {% endif %} !important
      }

      ha-tile-info {
        position: absolute !important;
        padding: 0% !important;
        margin-top: -20px;
        left: 60px;
        top: 50%;
        max-width: 110px
      }
    ha-card.automation>div.tile>div.content>div.icon-container>ha-tile-icon$: |
      div.shape::before {
        opacity: 0
        }
      div.shape {
        background-color:
          {% if is_state(config.entity, 'on') %} lightgrey
          {% else %} rgba(0,0,0,0.2) {% endif %};
        }
    ha-card.light>div.tile>div.content>div.icon-container>ha-tile-icon$: |
      div.shape::before {
        opacity: 0
        }
      div.shape {
        background-color:
          {% if is_state(config.entity, 'on') %} var(--tile-color)
          {% else %} rgba(0,0,0,0.2) {% endif %};
        }
    ha-card.switch>div.tile>div.content>div.icon-container>ha-tile-icon$: |
      div.shape::before {
        opacity: 0
        }
      div.shape {
        background-color:
          {% if is_state(config.entity, 'on') %} palegreen
          {% else %} rgba(0,0,0,0.2) {% endif %};
        }
    ha-card.water>div.tile>div.content>div.icon-container>ha-tile-icon$: |
      div.shape::before {
        opacity: 0
        }
      div.shape {
        background-color:
          {% if is_state(config.entity, 'on') %} deepskyblue
          {% else %} rgba(0,0,0,0.2) {% endif %};
        }
        
    ha-tile-info$: |
      .primary {
        color:
          {% if is_state(config.entity, 'on') %} black
          {% else %} white {% endif %};
        }
        
      .secondary {
        color:
          {% if is_state(config.entity, 'on') %} grey
          {% else %} lightgrey {% endif %};
        font-size: 14px
        }

In my dashboard I simply set the theme and then use tile cards, each with an assigned class. The themed shadowDOM elements (in this case, the div.shape bits for colouring the icon) work correctly showing different colours for different classes.

Great that you posted here your real code - but could you demonstrate it with a short solution? (which may be reproduced)

Sure. Here are three cards (using the visual editor):

type: tile
entity: light.sofa_lights
card_mod:
  class: light
type: tile
entity: switch.ensuite_towel_rail
card_mod:
  class: switch
type: tile
entity: switch.irrigation
card_mod:
  class: water

Page theme set to ‘test’.
Result:

Unfortunately this way may be used in some cases only - like “ha-card → element_1 → $ → element_2” but not in “ha-card → for each element_1 → …”.
Compare these:

    ha-card.class_2>.card-content>div>:first-child $ hui-generic-entity-row $: |
          state-badge {
            color: cyan;
          }

    ha-card.class_2>.card-content div>:first-child $ hui-generic-entity-row $: |
      state-badge {
        color: cyan;
      }

There is only ONE difference - a “whitespace” is used instead of “>” - which in THIS particular case must be same, but a style with “>” does work, with “whitespace” does not.
That particular case was about synthetic situation “Entities card with many rows, change icon color for each row”.

Even this simplest does not work (styling a header for Glance card), so I cannot understand why that “>” style works:

    ha-card.class_1 $: |
      .card-header {
        color: cyan !important;
        font-size: 40px !important;
        letter-spacing: 10px !important;
      }

Also, please check if your method works with OMITTED elements in the path - i.e. when you do not have to write down ALL consecutive elements in the path.
I was sure till now that using variables is the only working way. You demonstrated an alternative - but it has some limitations…

Yeah, it won’t work for theming multiple element 1s, you’d have to specify for each. I don’t use entity cards with multiple rows so can see how that would be a pain.

Have tried omitting elements in the path and it doesn’t work - you have to do the whole path.

Can you give me an example of you using class-defined variables to style a shadowDOM element?

A bit later I’ll post kind of tutorial dedicated to this staff.
Gonna refer your experience too.

Using classes:

The simplest example:

Theme:

  card-mod-card-yaml: |
    $: |
      ha-card.class_0 {
        border: solid 1px #ff0000;
        background-color: lightblue;
      }

Cards:

  - type: vertical-stack
    cards:
      - type: history-graph
        title: class_0
        entities:
          - sun.sun
        card_mod:
          class: class_0

      - type: history-graph
        title: title
        entities:
          - sun.sun

      - type: entities
        title: class_0
        entities:
          - sun.sun
        card_mod:
          class: class_0

      - type: entities
        title: title
        entities:
          - sun.sun


If needed to style elements inside shadowRoot - a possible way is defining variables.
Consider this Entities card:

type: entities
entities:
  - sun.sun
  - sensor.processor_use
  - input_number.test_number
  - input_select.test_value
  - input_boolean.test_boolean

изображение
and you want to set colors for icons differently for odd & even rows.
Theme (only related styling, no “class_0” code):

  card-mod-card-yaml: |
    .card-content:
      div:nth-child(n):
        :first-child $ hui-generic-entity-row $: |
          state-badge {
            color: var(--class_2-color-odd,orange);
          }
      div:nth-child(2n+1):
        :first-child $ hui-generic-entity-row $: |
          state-badge {
            color: var(--class_2-color-even,lightgreen);
          }
    .: |
      ha-card.class_2 {
        --class_2-color-odd: red;
        --class_2-color-even: cyan;
        background-color: yellow;
      }

where “orange” & “lightgreen” may be replaced by a default (for now!!!) “–paper-item-icon-color”.
Here some variables are defined in some “classy” ha-card → then used inside some internal shadowRoot. Be aware of defining default values for cards of different classes.
Cards:

  - type: vertical-stack
    cards:
      - &ref_entities_diff_domains
        type: entities
        entities:
          - sun.sun
          - sensor.processor_use
          - input_number.test_number
          - input_select.test_value
          - input_boolean.test_boolean

      - <<: *ref_entities_diff_domains
        card_mod:
          class: class_2

      - <<: *ref_entities_diff_domains
        card_mod:
          class: class_0


where the 3rd card has applied “class_0” styles (described above).


One more example - styling a card’s header.
Due to some recent changes the “.card-header” element is located inside shadowRoot for some cards (like “history-graph”, “glance”). So, the theme must define some “classy” style for “.card-header” inside shadowRoot and directly inside ha-card.
Theme:

  card-mod-card-yaml: |
    $: |
      .card-header {
        color: var(--class_1-color,var(--ha-card-header-color,--primary-text-color)) !important;
        font-size: var(--class_1-font-size,var(--ha-card-header-font-size, 24px)) !important;
        letter-spacing: var(--class_1-letter-spacing) !important;
      }
    .: |
      ha-card.class_1 .card-header {
        color: var(--class_1-color);
        font-size: var(--class_1-font-size);
        letter-spacing: var(--class_1-letter-spacing);
      }
      ha-card.class_1 {
        border: solid 1px #ff0000;
        background-color: lightgrey;
        --class_1-color: magenta;
        --class_1-font-size: 10px;
        --class_1-letter-spacing: 10px;
      }

Here all described cards together:

code
theme: Backend-selected
path: classy-shadowroot
badges: []
title: classy shadowroot
cards:

  - type: vertical-stack
    cards:
      - type: history-graph
        title: class_0
        entities:
          - sun.sun
        card_mod:
          class: class_0
      - type: history-graph
        title: title
        entities:
          - sun.sun
      - type: entities
        title: class_0
        entities:
          - sun.sun
        card_mod:
          class: class_0
      - type: entities
        title: title
        entities:
          - sun.sun

  - type: vertical-stack
    cards:
      - &ref_entities_diff_domains
        type: entities
        entities:
          - sun.sun
          - sensor.processor_use
          - input_number.test_number
          - input_select.test_value
          - input_boolean.test_boolean
      - <<: *ref_entities_diff_domains
        card_mod:
          class: class_2
      - <<: *ref_entities_diff_domains
        card_mod:
          class: class_0

  ############################################################

  - type: vertical-stack
    cards:
      - type: history-graph
        title: class_1
        entities:
          - sun.sun
        card_mod:
          class: class_1

      - type: history-graph
        title: title
        entities:
          - sun.sun

  ############################################################

  - type: vertical-stack
    cards:
      - type: entity
        entity: sun.sun
        name: class_1
        card_mod:
          class: class_1

      - type: entity
        entity: sun.sun

  ############################################################

  - type: vertical-stack
    cards:
      - type: glance
        title: class_1
        <<: &ref_glance_card
          entities:
            - entity: sun.sun
          show_name: true
          show_icon: true
          show_state: true
        card_mod:
          class: class_1

      - type: glance
        title: title
        <<: *ref_glance_card

  ############################################################

  - type: vertical-stack
    cards:
      - type: entities
        entities:
          - entity: sun.sun
        title: class_1
        card_mod:
          class: class_1

      - type: entities
        entities:
          - entity: sun.sun
        title: title

  - type: vertical-stack
    cards:
      - type: entities
        title: class_1
        card_mod:
          class: class_1
        entities:
          - type: custom:hui-element
            <<: &ref_hui_element
              card_type: entities
              entities:
                - entity: sun.sun
            title: title
          - type: custom:hui-element
            <<: *ref_hui_element
            title: class_1
            card_mod:
              class: class_1
      - type: entities
        entities:
          - entity: sun.sun
        title: title

What I tried and failed:

    ha-card.class_1 $: |
      .card-header {
        color: cyan !important;
        font-size: 40px !important;
        letter-spacing: 10px !important;
      }
    ha-card.class_2 .card-content:
      div:nth-child(n):
        :first-child $ hui-generic-entity-row $: |
          state-badge {
            color: cyan;
          }
      div:nth-child(2n+1):
        :first-child $ hui-generic-entity-row $: |
          state-badge {
            color: red;
          }

And check an alternative solution provided by @ajoyce (theme, test cards):
Here a path should be defined as

    ha-card.light>div.tile>div.content>div.icon-container>ha-tile-icon$: |
      div.shape::before {
        opacity: 0
        }

Unfortunately this method has some limitations (here, here) - and anyway it could be a better solution in some cases than using variables.

talking about classes, I just noticed this difference beween Safari and Chrome

in my main theme I set ha-card-border-radius: 0px

and Safari listens to that everywhere, also in the footer buttons, because of:

  card-mod-card-yaml: |
    hui-buttons-header-footer $ hui-buttons-base $ : |
      .ha-scrollbar {
        justify-content: space-evenly;
        height: 50px;
        align-content: center;
        margin: -8px 0px 0px 0px;
        --ha-chip-background-color: var(--primary-color);
        --ha-chip-text-color: var(--card-background-color);
        --paper-item-icon-active-color: red;
        --secondary-text-color: var(--card-background-color);
      }

    hui-buttons-header-footer $ hui-buttons-base $ .ha-scrollbar ha-chip:
      $: |
        .mdc-chip {
          border: 1px solid green;
          border-radius: var(--ha-card-border-radius);
        }

However, Chrome does:

and, upon checking the inspector I see

Scherm­afbeelding 2023-03-18 om 13.10.10
which apparently is not applying the card-mod…
changing that manually to
Scherm­afbeelding 2023-03-18 om 13.10.16

indeed results in

in Chrome now also…

Is my class not correct? Why the difference between Safari and Chrome…?

I also tested with:

      $: |
        .mdc-chip {
          border: 1px solid green;
          border-radius: var(--ha-card-border-radius) !important;
        }

but the makes no difference

card-mod theme for chip buttons

  card-mod-row-yaml: |
    hui-buttons-base $: |
      ha-assist-chip {
        border: 1px solid red;
        border-radius: 4px;
      }
before 2023.12, for a footer
  card-mod-card-yaml: |
    hui-buttons-header-footer $ hui-buttons-base $ .ha-scrollbar ha-chip:
      $: |
        .mdc-chip {
          border: 1px solid red !important;
          border-radius: 4px !important;
        }
1 Like

I can confirm I see the correct border-radius on iOS app also.

I’ve just checked Firefox latest browser, and that also does not apply the mod, and shows rounded corners…

even if I use

border-radius: 0px !important;

they dont apply border-radius

note they DO apply the border, so the path to the element is correct

Chrome does have some other issues, in showing the view tab icons correctly…

where both Safari and Firefox (and the iOS app) correctly show this:

but that was not the reason to post this…

Create a test theme and copy / paste my code - if it works?

uhm, not sure what you mean?

I have the exact same code? (besides the border-radius obviously )

I mean - with only this style (just in case).
My code exactly.

Wait…

    hui-buttons-header-footer $ hui-buttons-base $ .ha-scrollbar ha-chip:
      $: |
        .mdc-chip {
          border: 1px solid green;
          border-radius: var(--ha-card-border-radius) !important;
        }

this does work after all…
After the edit and addition of the !important, I had refreshed the view, in stead of reloading the theme…

doing so now shows me the squared corners!

So, Safari does not require the !important, nor does the iOS App (which essentially is a Safari browser), but Chrome and Firefox do…

since we’re on this button footer modding…

it’s all about these

    footer:

      type: buttons
      entities:
        - entity: input_boolean.loop_sound_bite
          name: Loop Soundbite
        - entity: script.play_sound_bite
          name: Speel soundbite
          icon: mdi:play

and, I really want to change the icon based in state of the config.entity.

when I used custom:button-card, I used:

              icon: >
                [[[ return (entity.state === 'on') ? 'mdi:stop' : 'mdi:play' ]]]

so I tried to set that mod on host

        card_mod:
          style: |
            :host {
              --card-mod-icon:
                {% if states(config.entity) == 'on' %} mdi:stop
                {% else %} mdi:play
                {% endif %};

but see no change. Is there some undocumented option to change those entity (script) icons in a footer button?

Cannot be used here.

type: entities
title: footer
entities:
  - input_boolean.test_boolean
footer:
  type: buttons
  entities:
    - entity: sun.sun
      show_name: true
      show_icon: true
    - entity: sun.sun
      show_name: true
      show_icon: true
    - entity: input_boolean.test_boolean
      show_name: true
      show_icon: true
    - entity: sun.sun
      show_name: true
      show_icon: true
card_mod:
  style:
    hui-buttons-header-footer $ hui-buttons-base $ .ha-scrollbar:
      ha-chip:
        $: |
          .mdc-chip {
            border: 1px solid red !important;
            border-radius: 4px !important;
          }
        .: |
          ha-chip:nth-child(3) {
            {% if is_state('input_boolean.test_boolean','on') %}
            --ha-chip-background-color: lightgreen;
            {% else %}
            --ha-chip-background-color: cyan;
            {% endif %}
          }

изображение

Actually, this is a bit an off-topic here, not exactly related to themeing.

indeed, moving to card-mod please

if you use thumbing like:

DONT UPDATE TO 2023.4 beta, because it will render

o and, btw, on button-card, see: Lovelace: Button card - #6829 by Mariusthvdb

Editing the header bar to semi transparent is failing now, too. Even with the new 3.2.1 update.

  card-mod-root: |
    app-header {
      background: var(--ha-card-background) !important;
    }
    app-toolbar {
      background: transparent !important;
    }

This worked for ages now. So I hope it’s correct ^^

Ah and this for more understanding of my doing:

  ha-card-background: "rgba(0, 0, 0, 0.5)"

the new card-mod did fix the main issue probably, but the mods on the card-mod-sidebar-yaml: no longer work.

update

well, they do card-mod-sidebar-yaml no longer works · Issue #264 · thomasloven/lovelace-card-mod · GitHub

but things changed in HA see:

so, for the regular notification badge, we need to add a default ,248px like:

      a[data-panel='ui-familie'] paper-icon-item:after {
              content: "{{states('sensor.family_home')}}";
              left: calc(var(--app-drawer-width,248px) - 44px);
              position: absolute;
              min-width: 20px;
              box-sizing: border-box;
              border-radius: 50%;
              font-weight: 400;
              background-color: {{states('sensor.presence_color')}}; /*var(--accent-color);*/
              line-height: 20px;
              text-align: center;
              padding: 0px 6px;
              color: {{'saddlebrown' if states('sensor.presence_color') == 'gold' else 'ivory'}};
              /*var(--text-accent-color, var(--text-primary-color));*/
              font-size: 14px;
      }

and that brings back the notifications:

however, collapsing the menu changes css to a fixed 26px as you can see in the Inspector screenshot, and I’ve not found a way yet to mimic that in the mod. Or find a conditional on the menu being collapsed or not