Styling elements in Picture elements card: a small tutorial

Conditional styles:

Styles can be set dynamically dependingly on some conditions (like some entity’s state) with the help of "card-mod":


type: vertical-stack
cards:
  - type: entities
    entities:
      - entity: input_boolean.test_boolean
  - type: picture-elements
    card_mod:
      style: |
        ha-card {
          {% if is_state('input_boolean.test_boolean','on') %}
          --my-state-color: magenta;
          --my-transform-style: translate(-50%,-50%) rotate(-0.25turn);
          {% else %}
          --my-state-color: cyan;
          --my-transform-style: translate(-50%,-50%)
          {% endif %}
        }
    elements:
      - type: state-badge
        entity: sensor.cleargrass_1_co2
        style:
          top: 8%
          left: 20%
          '--label-badge-background-color': var(--my-state-color)
          transform: var(--my-transform-style)
      - type: state-icon
        entity: sensor.cleargrass_1_co2
        style:
          top: 28%
          left: 20%
          '--paper-item-icon-color': var(--my-state-color)
          transform: var(--my-transform-style)
      - type: icon
        icon: mdi:car
        style:
          top: 38%
          left: 20%
          color: var(--my-state-color)
          transform: var(--my-transform-style)
      - type: state-label
        entity: sensor.cleargrass_1_co2
        style:
          top: 52%
          left: 20%
          color: var(--my-state-color)
          transform: var(--my-transform-style)
      - type: service-button
        title: Command
        service: homeassistant.update_entity
        service_data:
          entity_id: sun.sun
        style:
          top: 70%
          left: 20%
          '--mdc-theme-primary': var(--my-state-color)
          transform: var(--my-transform-style)
      - type: image
        image: /local/images/small_pic.jpg
        style:
          top: 90%
          left: 20%
          transform: var(--my-transform-style)
    image: /local/images/white.jpg

Important note: this method CANNOT be used to define dynamically other options like “image”, “entity” etc.

Also: there are 2 ways:

  1. Declare a variable on a picture-elements card’s level - then this variable may be used for a few elements.
  2. Declare a variable on some element’s level - to use this variable for this element only.

One more example.

1 Like

Changing Z-order for elements:

By default elements have a Z-index in accordance to the order they are defined in a card.
The example below has one “image” element, some “state-label” elements, some “icon” elements.
Some elements are overlapped by the “image” element by default.
The "z-index" property may be used to change a Z-index:
image

type: picture-elements
card_mod:
  style: |
    ha-card {
      height: 150px !important;
    }
elements:
  - type: icon
    icon: mdi:car
    style:
      top: 10%
      left: 10%
  - type: icon
    icon: mdi:car
    style:
      top: 10%
      left: 40%
      z-index: 999
  - type: state-label
    entity: sun.sun
    style:
      top: 35%
      left: 20%
  - type: state-label
    entity: sun.sun
    style:
      top: 40%
      left: 20%
      z-index: 999
  - type: image
    image: /local/images/pink_mask.png
    style:
      top: 0%
      left: 0%
  - type: icon
    icon: mdi:car
    style:
      top: 10%
      left: 20%
  - type: state-label
    entity: sun.sun
    style:
      top: 30%
      left: 20%
image: /local/images/blue.jpg
1 Like

Styling custom cards inside the Picture elements card:

Example 1 - toggle-entity row:
image

type: picture-elements
card_mod:
  style: |
    ha-card {
      height: 250px !important;
    }
elements:
  - type: custom:hui-element
    row_type: toggle-entity
    entity: input_boolean.test_boolean
    name: Some toggle (default style)
    style:
      top: 0%
      left: 0%
      transform: translate(0,0)
  - type: custom:hui-element
    row_type: toggle-entity
    entity: input_boolean.test_boolean
    name: Some toggle
    style:
      top: 14%
      left: 0%
      transform: translate(0,0)
      width: 250px
      '--paper-item-icon-color': magenta
      color: cyan
  - type: custom:hui-element
    row_type: toggle-entity
    entity: input_boolean.test_boolean
    name: Some toggle
    style:
      top: 21%
      left: 0%
      transform: translate(0,0)
      width: 250px
      '--paper-item-icon-color': green
      color: orange
  - type: custom:hui-element
    row_type: toggle-entity
    entity: input_boolean.test_boolean
    name: Some toggle
    style:
      top: 28%
      left: 0%
      transform: translate(0,0)
      width: 250px
      '--paper-item-icon-color': red
      color: magenta
      '--switch-unchecked-track-color': lightgreen
      '--switch-checked-track-color': cyan
      '--switch-unchecked-button-color': red
      '--switch-checked-button-color': green
image: /local/images/white.jpg

Example 2 - Glance card:
image

type: picture-elements
card_mod:
  style: |
    ha-card {
      height: 450px !important;
    }
elements:
  - type: custom:hui-element
    card_type: glance
    entities:
      - entity: sensor.cleargrass_1_co2
      - entity: sensor.cleargrass_1_co2
    name: Some toggle (default style)
    style:
      top: 10%
      left: 10%
      transform: translate(0,0)
      '--paper-item-icon-color': red
      '--ha-card-background': rgba(0,100,0,0.8)
      '--ha-card-border-radius': 14px
  - type: custom:hui-element
    card_type: glance
    entities:
      - entity: sensor.cleargrass_1_co2
      - entity: sensor.cleargrass_1_co2
    name: Some toggle (default style)
    style:
      top: 60%
      left: 10%
      transform: translate(0,0)
      '--paper-item-icon-color': red
      '--ha-card-background': rgba(0,0,0,0)
      background-image: url("/local/images/blue_low_2.jpg")
      background-size: 100% 100%
image: /local/images/pink.jpg

Example 3 - Entities card:

type: picture-elements
card_mod:
  style: |
    ha-card {
      height: 250px !important;
    }
elements:
  - type: custom:hui-element
    card_type: entities
    entities:
      - entity: input_boolean.test_boolean
        name: Some toogle
      - entity: input_number.test_number
        name: Some silder
    style:
      top: 5%
      left: 5%
      transform: translate(0,0)
      '--paper-item-icon-color': red
      '--ha-card-background': rgba(0,0,255,0.1)
      '--ha-card-border-radius': 14px
      width: 400px
      --primary-text-color: orange
image: /local/images/white.jpg
3 Likes

Issues about positioning & scaling elements

When creating a floorplan, people sometimes facing an issue “my floorplan is malformed on iPad/iPhone/etc”.
In short - you may spend lot of time to position your elements (lightbulbs, furniture, equipment, doors, smoke detectors, …) on a floorplan - i.e. by setting “top / left / scale” properties; and then on another device your results look differently.
On GitHub there are plenty of issues about the same problem. People consider this as a bug - which is wrong.

A similar case - a floorplan look differently when a view is in “panel: true” mode or with a right sidebar. Reason is same - size of a card is different in these cases.

There is a difference between icons/badges/labels and images in part of scaling.
Consider an example below.

Create an image larger than your viewport (for instance, for my 1920x1200 screen I created a 3000x3000 image) with a similar “chess” pattern:
image
So, the image must have clear marks for 33%, 66% in horizontal & vertical directions.

Create a new view in Panel mode and add this card:

type: picture-elements
elements:
  - type: state-icon
    entity: sun.sun
    icon: mdi:white-balance-sunny
    style:
      top: 33.3333%
      left: 33.3333%
      transform: translate(-50%,-50%) scale(2,2)
  - type: icon
    icon: mdi:white-balance-sunny
    style:
      top: 33.3333%
      left: 66.6666%
  - type: state-badge
    entity: sun.sun
    icon: mdi:white-balance-sunny
    style:
      top: 66.6666%
      left: 33.3333%
  - type: state-label
    entity: sun.sun
    icon: mdi:white-balance-sunny
    style:
      top: 66.6666%
      left: 66.6666%
image: /local/images/test/9colors_low.jpg

image

Now resize the browser’s window - all elements are still placed on same places:

A small trick how to set positions ONCE - i.e. w/o endless “set position → save → reload page → check if it is OK → set position → …”:
– open your floorplan’s main image in Photoshop (or any other program with a similar functionality);
– place a mouse on a desired point;
– write down coordinates of this point in pixels (like “1234, 567”);
– calculate a relative coordinates like “left = COORD_X / IMAGE_WIDTH”; let it be “left = 1234 / 3000 = 41.13 %”.

Note that on a smaller viewport elements become BIGGER (respectively to the main image).
This may be solved by scaling elements differently on different viewports ("transform: translate(-50%,-50%) scale(diff_scale_value,diff_scale_value)").
Two ways may be used:
– the whole floorplan card is a decluttering-card - pass a scale value as a variable;
– use card-mod to set scales conditionally dependingly on a current viewport (use “mediaquery”).

Result - all tested elements (icons, badges, labels) are placed / scaled properly on different viewports.


Now let’s check images as elements.
Consider these facts:

  1. A main image fills all card’s area: first it fills the area in horizontal direction (since the card’s width is fixed), then it fills the area in vertical direction (according to the image’s aspect ratio).
  2. Assume that some “image” element is placed with "left: 33.333%, translate(0%,0%)" - i.e. the top left corner has coord_x = 33.333%. So there is 66.6667% space left on the right. If the image’s width is less than this space - then this image is placed in this space with a possible gap between the image & right border (unoccupied space). If the image’s width is bigger - then it occupies the whole space left w/o a gap (no unoccupied space left). This is described here.

Means - image elements have different sizes (respectively to the main image) dependingly on a current viewport.
Add this element to the test card (image size is 100px x 100px - less than the main image):

  - type: image
    image: /local/images/test/1color.jpg
    style:
      top: 33.3333%
      left: 66.6666%
      transform: translate(0%,0%)

and resize the browser’s window:

The only right solution seems to use the same size for the main image and image elements:
– all image elements have "left: 0%, top: 0%, translate(0%,0%)";
– all image elements are PNG files with transparent background.

Check this:

type: picture-elements
elements:
  - type: image
    image: /local/images/test/car.png
    style:
      top: 0%
      left: 0%
      transform: translate(0%,0%)
image: /local/images/test/garage.png


where the garage & car - are different images:

The same approach may be used to create “dark rooms” for a floorplan.

3 Likes

How to set a main image dynamically:

There 3 methods available to achieve this:

  1. Use card-mod to define background color for the card.
  2. Use “image” element with “state_image” option.
  3. Use “config-template-card” to define background color for the card.

Card-mod method:

Consider this card:

type: vertical-stack
cards:
  - type: entities
    entities:
      - input_select.test_tri_state
  - type: picture-elements
    image: /local/images/test/transparent.png
    elements:
      - type: state-label
        entity: input_select.test_tri_state
        style:
          top: 20%
          left: 20%
    card_mod:
      style: |
        ha-card {
          {% if is_state('input_select.test_tri_state','auto') %}
            {% set IMAGE = '/local/images/test/lightgreen_1.jpg' %}
          {% elif is_state('input_select.test_tri_state','on') %}
            {% set IMAGE = '/local/images/test/lightgreen_2.jpg' %}
          {% else %}
            {% set IMAGE = '/local/images/test/lightgreen_3.jpg' %}
          {% endif %}
          background-size: 100% 100%;
          background-image: url({{IMAGE}});
        }

The “input_select.test_tri_state” helper has three possible values - “auto, on, off”.
The “transparent.png” is a LARGE (I myself use a 4K image) transparent png-file - it must be larger than any of used images (lightgreen_1…3.jpg). If you need a particular size (not square) - define a CSS “height” property by card-mod.
Note that all 3 images (lightgreen_1…3.jpg) may have different dimensions - anyway they will be stretched; surely all images should be of the same size since they describe “one card in different conditions”.


"image" element method:

Currently only an internal “`image’” element can have a dynamically defined image (“state-image”). When using this way, a user need to enlarge the internal “image” element to cover whole card - which is not an easy task.
The best way is to use images of the SAME dimensions (1920x1080 on the example below):
– transparent (or ANY, anyway will be overlapped but I recommend using transparent to avoid visible transitions) image as the “main image” of the card;
– different corresponding images for different conditions.
Here there is a limitation: “each condition = a state of some entity”, i.e. it cannot be some complex condition. If you need a complex condition - either use a template sensor for this condition or use other methods described in this post.

type: vertical-stack
cards:
  - type: entities
    entities:
      - input_select.test_tri_state
  - type: picture-elements
    image: /local/images/test/transparent_1920_1080.png
    elements:
      - type: image
        entity: input_select.test_tri_state
        style:
          top: 0%
          left: 0%
          transform: translate(0,0)
        state_image:
          auto: /local/images/test/lightgreen_1.jpg
          'on': /local/images/test/lightgreen_2.jpg
          'off': /local/images/test/lightgreen_3.jpg
      - type: state-label
        entity: input_select.test_tri_state
        style:
          top: 20%
          left: 10%

image


"config-template-card" method:

Place the whole picture-elements card inside “config-template-card” and set the main image in a JS template dynamically:

type: vertical-stack
cards:
  - type: entities
    entities:
      - entity: input_boolean.test_boolean

  - type: custom:config-template-card
    variables:
      INPUT_BOOLEAN: states['input_boolean.test_boolean']
    entities:
      - ${INPUT_BOOLEAN.entity_id}
    card:
      type: picture-elements
      image: >-
        ${INPUT_BOOLEAN.state === "on" ?
        "/local/images/test/blue.jpg" :
        "/local/images/test/orange.jpg" }
      elements:
        - type: state-label
          entity: sun.sun
          style:
            top: 50px
            left: 100px

image

Here only the “‘input_boolean.test_boolean’” entity is defined as “monitored”, but IRL all entities used on the “picture-elements” card must be listed as “monitored”.
I do not recommend this method since it may cause flickering of the card (the whole card is redrawn if any of monitored entities changes).

3 Likes

A few words about actions:

Consider this example:

  - type: picture-elements
    image: /local/images/test/transparent_1920_1080.png
    elements:

      - type: image
        entity: zone.home
        style:
          top: 0%
          left: 0%
          transform: translate(0,0)
        image: /local/images/test/lightgreen_1.jpg

      - type: state-label
        entity: sun.sun
        style:
          top: 20%
          left: 20%

      - type: icon
        icon: mdi:car
        style:
          top: 20%
          left: 60%
        tap_action:
          action: navigate
          navigation_path : /developer-tools/yaml

      - type: state-icon
        entity: input_boolean.test_boolean
        style:
          top: 50%
          left: 10%

      - type: icon
        icon: mdi:link
        style:
          top: 50%
          left: 70%
        tap_action:
          action: url
          url_path : http://google.com

      - type: icon
        icon: mdi:circle
        style:
          top: 50%
          left: 90%
        tap_action:
          action: call-service
          service: input_boolean.toggle
          data:
            entity_id: input_boolean.test_boolean

      - type: service-button
        title: Toggle boolean
        style:
          top: 75%
          left: 30%
        service: input_boolean.toggle
        service_data:
          entity_id: input_boolean.test_boolean

Here each element has an associated action:
– the “image” element - used to “mimic” the card’s main image (it covers the whole card - described here & here); the main image itself cannot have an action associated;
– the “state-label” element has a default “more-info” action;
– two “icon” elements have “navigate” & “url” actions;
– another “icon” element uses “input_boolean.toggle” service call;
– the “state-icon” element has a default “more-info” action;
– the “service-button” element uses “input_boolean.toggle” service call.


Next, imagine that an additional layer should be added into the card (in case of a floorplan - could be a dark room mask). To prevent overlapping other elements, this layer must be added BELOW other elements (which may be overlapped otherwise):

  - type: picture-elements
    image: /local/images/test/transparent_1920_1080.png
    elements:
      - type: image
        entity: zone.home
        ....

      - type: image
        style:
          top: 0%
          left: 0%
          transform: translate(0,0)
          pointer-events: none
        image: /local/images/test/transparent_mask.png

      - type: state-label
        ...

      ...

image

Note a “pointer-events: none” style which makes this layer “transparent”, i.e. here an action for the 1st image element is executed.


Regarding “service-button”.
It is used to call a service - so it cannot be used to show “more-info”, navigate or follow url.
If you need to have a “service-button” to execute of of these actions - you may “mimic” this element by a “state-label” element:
image
Here “service-button” on the right, “state-label” on the left.
Add this element:

      - type: state-label
        entity: input_text.test_text
        style:
          top: 75%
          left: 60%
          color: var(--primary-color)
          text-transform: uppercase
          font-family: var(--mdc-typography-button-font-family,var(--mdc-typography-font-family,Roboto,sans-serif))
          font-weight: var(--mdc-typography-button-font-weight,500)
          letter-spacing: var(--mdc-typography-button-letter-spacing,0.0892857em)
          font-size: var(--mdc-typography-button-font-size,0.875rem)
        tap_action:
          ...

Here CSS values are same as used for the “service-button” element and may be changed in some HA version, so use this method on your own risk.

reserved post 16

reserved post 17

reserved post 18

reserved post 19

reserved post 20

1 Like

Awesome, i really appretiate your work here!:):

1 Like

Does anyonne know how to make the hitbox of the icons bigger and how to remove the shadow of the picturelement?
I want to get rid of the black line at the bottom.
image

type: picture-elements
elements:
  - type: icon
    icon: mdi:chevron-up
    tap_action:
      action: call-service
      service: script.fernseher_up
    style:
      top: 18%
      left: 50%
      transform: translate(-50%, -50%) scale(2, 2)

Great examples. How would you set the height of an image element based on a sensor value?
The use case is using overlays with transparent parts over the main image to show levels in tanks.

The element size is better to set dynamically by card-mod since it supports templates.

1 Like

Hi!

I’m building my floorplan with picture-elements card and I run into a problem: I cannot change the icon color of a switch when it is turned on.

My code is the following:

          - type: state-icon
            tap_action:
              action: toggle
            entity: light.terasz_szoba_lampa
            style:
              bottom: 33%
              right: 7%
              border-radius: 50%
              "--paper-item-icon-color": black
              "--paper-item-icon-active-color" : yellow
              text-align: center
              background-color: rgba(102, 102, 102, 0.2)

I’d like to have the “switched on” color yellor.

Any ideas? :slight_smile:

Useless here since there is no name displayed.

This works - you may check it if change “black” to “cyan” to be sure.

This does not work due to the light entity.
Check this:

type: picture-elements
elements:
  - type: state-icon
    entity: light.virtual_light_1
    tap_action: &ref_tap_action
      action: toggle
    style:
      bottom: 33%
      right: 7%
      <<: &ref_settings
        border-radius: 50%
        '--paper-item-icon-color': cyan
        '--paper-item-icon-active-color': red
        background-color: rgba(102, 102, 102, 0.2)
  - type: state-icon
    entity: input_boolean.test_boolean
    tap_action: *ref_tap_action
    style:
      bottom: 53%
      right: 7%
      <<: *ref_settings
image: /local/images/blue.jpg

изображение

It works for the input_boolean entity and does not for the light entity.
You need to “override” this by some way - kind of using “!important” in CSS.
Seems that card-mod may be the only solution (provided here).

Hi all,

Thanks for all these great ideas!

Is there any way to rotate an icon according a variable?
Let’s say, something like this:

 - type: icon
     icon: mdi:bike
     style:
       top: 38%
       right: 7%
       transform:
         rotate({{ states('sensor.mower_yaw')|int  }}deg)

It is to set an icon according the position of the mower.

Check this.
Specify a variable by card-mod, then use it.

It sound easy… but how I specify and use it?