A different take on designing a Lovelace UI

I’ve been using this layout for around a year – we all love it in this household, so thank you very much.

I decided to update some code today and I’ve run into an issue which is over my head. For context I use several Fire Tabs around the house which have a landscape screen width of 1280px.

Previously I’d setup and new breakpoint for this screen and everything worked un-touched for said year.

Here’s what the custom breakpoint looks like:

        #landscape
        "(max-width: 1200px)":
          grid-gap: var(--custom-layout-card-padding)
          grid-template-columns: repeat(4, 1fr) 0
          grid-template-rows: 0 repeat(1, fit-content(100%)) 0fr
          grid-template-areas: |
            "sidebar  .        .      .          ."
            "sidebar  lounge  studio  bedroom ."
            "sidebar  other  media  home       ."
            "sidebar  footer  footer  footer     ."
            "sidebar  .        .      .          .";

Now, I’ve run into a problem with the new footer which isn’t displaying at that size. It looks something like this and they’ve falling way below the bottom the cards:

Ideally the buttons would be sitting neatly under the ‘Other Media Home’ buttons. I cannot figure out why they’re dropping to that specific level in the view-port.

If any one has any suggestions for bringing them back up for this specific break-point/screen size I’d really appreciate it.

Currently focussing on this section of code in the themes.yaml

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

        grid-layout$hui-horizontal-stack-card:
          $: |
            #root {
              flex-wrap: wrap;
              justify-content: center;
              overflow: visible;
              margin-top: 1vh;
              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;
              }
            }

      card-mod-card: |
        .header .card-header {
          letter-spacing: 0.005em;
          font-size: 1.6em;
          font-weight: 500;
          padding: 1em 0 0 1.38em;
          line-height: initial;
          cursor: default;
        }

        .content .card-content {
          padding: var(--card-content-padding);
          letter-spacing: normal;
        }

However it’s highly likely I’m in the wrong place!

Thanks again,

EDIT: I’m guessing it’s something related to the hui-grid-card which is forcing the footer down?

i made a little watched / unwatched overlay for the card :slightly_smiling_face:

watched_overlay
watched_overlay1

media_widget

2 Likes

Very neat! Can you share the code for the Klima popup? Or better yet - your github fork? :slight_smile:

@Mattias_Persson

hi

i tried to add the icons that have the color of the light, but i had two problems, first when i shut the light off its still black, and he doenst change the icon color based of the light color. perhaps you can look at it?

type: custom:button-card
template: light_circle
show_icon: false

styles:
   .........
variables:

  state_on: >
        [[[ return ['on', 'home', 'cool', 'fan_only', 'playing'].indexOf(entity === undefined || entity.state) !== -1; ]]]
  timeout: >
        [[[ return entity === undefined || Date.now() - Date.parse(entity.last_changed); ]]]
  extra_styles: >
      [[[
        if (entity) {
          let hs = entity.attributes.hs_color === undefined,
            h = hs || entity.attributes.hs_color[0],
            s = hs || entity.attributes.hs_color[1],
            l_min = 28,
            l_max = 48,
            l_calc = entity.attributes.brightness / 2.54 * (l_max - l_min) / 100 + l_min;
          var light_color = entity.attributes.color_mode === 'color_temp' || entity.attributes.color_mode === 'brightness'
            ? `hsl(204, 58%, ${l_calc}%);`
            : `hsl(${h}, ${s}%, ${l_calc}%);`;
        }
        return `
          svg {
            --light-color:
            ${ variables.state_on && entity.attributes.brightness !== undefined
                ? light_color
                : variables.state_on && entity.attributes.brightness === undefined
                  ? '#3182b7;'
                  : '#9da0a2;'
            }
          }
entity: light.led_stripe_kueche
custom_fields:
  icon: >
    [[[
      let state = variables.state_on && variables.timeout < 2000 ? 'on' : null;
      return `
        <svg viewBox="0 0 50 50">
          <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>
          <path fill="#9da0a2" d="M27.4 47.3h-4.9s-.7.1-.7.8.4.9.7.9h4.9c.3 0 .7-.1.7-.9s-.7-.8-.7-.8zm3.3-2.9H19.3s-.8 0-.8.8.6.9.8.9h11.5c.2 0 .8-.1.8-.9-.1-.8-.9-.8-.9-.8zm0-3H19.3s-.8 0-.8.8.6.9.8.9h11.5c.2 0 .8-.1.8-.9-.1-.8-.9-.8-.9-.8zm0-2.9H19.3s-.8 0-.8.8.6.9.8.9h11.5c.2 0 .8-.1.8-.9s-.9-.8-.9-.8zm5.2-23.2c-3.3-5.3-7-5.6-10.9-5.6-3.8 0-8.4.4-10.9 5.6-.1.1-.1.3.1.7.4.8 3.3 7.2 3.2 18.8 0 1.1-.1 1.6 0 1.7 0 .1 0 .7 1.1.7h13c1 0 1-.5 1.1-.7v-1.7c-.1-11.6 2.8-18 3.2-18.8.1-.4.1-.5.1-.7"/>
          <path class="${state}" fill="var(--light-color)" d="M14.1 15.3c3.4-.3 7-.4 10.9-.4 3.8 0 7.5.2 10.9.4.4-.4.7-.8.9-1.1C39 8.5 38.9 6.5 38.9 6c-.2-4.4-8.4-5-12.1-5h0-3.4c-3.7 0-12 .5-12.1 5 0 .5-.1 2.5 2.1 8.2 0 .3.3.8.7 1.1z"/>
        </svg>
      `;
    ]]]
  ring: |
    [[[ if (entity.state == 'on' && entity.attributes.brightness) {
      const brightness = Math.round(entity.attributes.brightness/2.55);
      const radius = 20.5; const circumference = radius * 2 * Math.PI;
      return `<svg viewBox="0 0 50 50"><circle cx="25" cy="25" r="${radius}"
      stroke=gray stroke-width="2" fill="none"
      style="transform: rotate(-90deg); transform-origin: 50% 50%;
      stroke-dasharray: ${circumference};
      stroke-dashoffset: ${circumference - brightness / 100 * circumference};" />
      <text x="50%" y="56%" fill=gray font-size="16" font-weight= "bold"
      text-anchor="middle" alignment-baseline="middle">
      ${brightness}<tspan font-size="10">&#176;</tspan></text></svg>`;} 
    ]]]
name: LED Streifen
show_state: true

my light has hs attributes

something is missing here, but i dont know what, has it to do with “fill=“var(–light-color)”” under extra_styles?

Bit of a weird request if anyone has the understanding,

I broke the media icon location on this card (since corrected) by creating an icon with the following code:

icon_speaker:
  styles:
    custom_fields:
      icon:
        - width: 73%
        - margin-left: -9%
  custom_fields:
    icon_break: >

With this second instance of icon renamed to icon_break I get this result:

image

I happen to really like the size and location, and have been trying to understand how I could make this work correctly. I know nothing of these things but figure maybe a z-index thing? No idea where I’d put that tho…

Anyone have any clues for me to figure this out?

use you the complete code from matthias?
perhaps your grid layout or position of the title.

post the complete styles part.

Z-Index is for the layout priority to display something.

I am using the complete code, everything working perfectly.

To clarify, the image is how I would like the icon on the conditional media card to display.
I just can’t figure out how to get it there.
The code pasted is me just removing the icon template by renaming it, by coincidence it puts the icon in a spot I like.

I can see the positioning is set universally using a grid layout. Being universal, I can’t go modifying it’s position there, so I’m stuck for ideas.

You can use position: absolute to place the icon wherever

https://developer.mozilla.org/en-US/docs/Web/CSS/position

1 Like

I can have a look if you share your ui-lovelace.yaml

extra_styles should not be put in variables and you haven’t closed the template code block

I don’t know, see open issues. RomRider maintains button-card

1 Like

This code places the blur outside the button-card container (so it doesn’t get the 3d tilt effect), so there’s no need to adjust z-index

@Mattias_Persson

i tried now a fresh install and your code, i know that i must change all entitys and more but why is the design so different from that you posted above?

Don’t skip steps!

1 Like

Kind of you, thanks. Here’s the ui-lovelace.yaml

button_card_templates: !include button_card_templates.yaml

kiosk_mode:
  hide_header: true

views:
  - type: custom:grid-layout
    path: 0
    layout:
      #default
      margin: 0
      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  lounge  studio  bedroom   ."
        "sidebar  other     media     home   ."
        "sidebar  footer      footer  footer  ."

      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  ."
            ".  lounge  bedroom   ."
            ".  studio      other   ."
            ".  media       home    ."
            ".  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  lounge  bedroom  ."
        #     "sidebar  studio      other  ."
        #     "sidebar  media       home   ."
        #     "sidebar  footer      footer  ."
        #     "sidebar  .           .       ."

        #landscape
        "(max-width: 1200px)":
          grid-gap: var(--custom-layout-card-padding)
          grid-template-columns: repeat(4, 1fr) 0
          grid-template-rows: 0 repeat(1, fit-content(100%)) 0fr
          grid-template-areas: |
            "sidebar  .        .      .          ."
            "sidebar  lounge  studio  bedroom ."
            "sidebar  other  media  home       ."
            "sidebar  footer  footer  footer     ."
            "sidebar  .        .      .          ."; 

    cards:
      
      - type: custom:button-card #extra_styles fix
        styles: {card: [display: none]}

      #################################################
      #                                               #
      #                    SIDEBAR                    #
      #                                               #
      #################################################

      - type: vertical-stack
        view_layout:
          grid-area: sidebar
        cards:
          - type: custom:button-card
            entity: sensor.template_sidebar
            template: sidebar

          - type: conditional
            conditions:
              - entity: input_boolean.laundry_display
                state: 'on'
            card:
              type: custom:button-card
              entity: timer.laundry
              template: laundry

          # - type: grid
          #   cards:
          #     - type: button
          #       icon: mdi:home-lightbulb-outline
          #       tap_action:
          #         !include popup/lighting_popup.yaml
          #       hold_action:
          #         action: none

          #     - type: button
          #       icon: mdi:information-outline
          #       tap_action:
          #         !include popup/sidebar_information.yaml
          #       hold_action:
          #         action: none

          #     - type: button
          #       icon: mdi:arrow-up-bold-circle-outline
          #       tap_action:
          #         !include popup/sidebar_update.yaml
          #       hold_action:
          #         action: call-service
          #         service: homeassistant.update_entity
          #         service_data:
          #           entity_id: sensor.hacs

      #################################################
      #                                               #
      #                  lounge                   #
      #                                               #
      #################################################

      - type: grid
        title: Living Room
        view_layout:
          grid-area: lounge
        columns: 2
        cards:
          - type: custom:button-card
            entity: light.rgb_lounge 
            #entity: light.living_room_lights #lounge group
            name: Lighting
            template:
              - light
              - icon_hue

          - type: custom:button-card
            entity: climate.wiser_main_thermostat
            name: Climate
            tap_action: !include popup/bedroom_klimat.yaml
            template:
              - base
              - icon_climate
              - climate
            variables:
              circle_input: >
                [[[
                  if (entity) {
                    return entity.state == 'current_temperature' ?
                      entity.attributes.temperature :
                      entity.attributes.current_temperature;
                  }
                ]]]

          - type: custom:button-card
            entity: light.osram_orange_lamp
            name: Orange Lamp
            template:
              - base
              - icon_hue

          # - type: custom:button-card
          #   entity: #binary_sensor.motion_hall_occupancy
          #   name: Motion
          #   template:
          #     - base
          #     - circle
          #   variables:
          #     circle_input: >
          #       [[[ return states['sensor.hall_motion_xmins'].state; ]]]

          - type: custom:button-card
            entity: media_player.living_room_tv_2
            name: "TV"
            hold_action: !include popup/bedroom_tv2.yaml
            template:
              - base
              - icon_tv
              - loader


      #################################################
      #                                               #
      #                    STUDIO                     #
      #                                               #
      #################################################

      - type: grid
        title: Office
        view_layout:
          grid-area: studio
        columns: 2
        cards:

          - type: custom:button-card
            entity: light.office_lights
            name: Lighting
            tap_action: !include popup/office_lighting.yaml
            template:
              - light
              - icon_hue

          - type: custom:button-card
            entity: climate.wiser_main_thermostat
            name: Climate
            tap_action: !include popup/bedroom_office.yaml
            template:
              - base
              - icon_climate
              - climate
            variables:
              circle_input: >
                [[[ return states['sensor.temp_office_temperature'].state; ]]]


          - type: custom:button-card
            entity: binary_sensor.macbook_pro_active
            name: Computer
            hold_action: !include popup/studio_dator.yaml
            template:
              - base
              - icon_imac
              - loader
              - lock

          - type: custom:button-card
            entity: switch.amplifier
            name: Hi-Fi
            hold_action: !include popup/studio_monitorer.yaml
            template:
              - base
              - icon_monitors
              - circle
              - loader
            variables:
              circle_input: >
                [[[ return states['sensor.office_hifi_volume2'].state; ]]]


      #################################################
      #                                               #
      #                    bedroom                     #
      #                                               #
      #################################################

      - type: grid
        title: Bedroom
        view_layout:
          grid-area: bedroom
        columns: 2
        cards:
          - type: custom:button-card
            entity: light.rgb_bedroom
            name: Lighting
            template:
              - light
              - icon_hue

          - type: custom:button-card
            entity: climate.wiser_main_thermostat
            name: Climate
            tap_action: !include popup/climate_bedroom.yaml
            template:
              - base
              - icon_climate
              - climate
            variables:
              circle_input: >
                [[[ return states['sensor.temp_bedroom_temperature'].state; ]]]

          - type: custom:button-card
            name: Radio
            entity: input_boolean.bedroom_radio
            tap_action: !include popup/bedroom_radio.yaml
            template:
              - base
              - icon_monitors

          - type: custom:button-card
            entity: media_player.samsung_tv
            name: Bed
            hold_action: !include popup/bedroom_tv2.yaml
            template:
              - base
              - icon_tv
              - loader

      #################################################
      #                                               #
      #                     MEDIA                     #
      #                                               #
      #################################################


      - type: grid
        title: Media
        view_layout:
          grid-area: media
        columns: 1
        cards:

          - type: custom:swipe-card
            start_card: 1
            parameters:
              roundLengths: true
              effect: coverflow
              speed: 650
              spaceBetween: 20
              threshold: 7
              coverflowEffect:
                rotate: 80
                depth: 300
            cards:

              - type: grid
                columns: 2
                cards: 

                  - type: custom:button-card
                    entity: media_player.office
                    name: Office
                    template:
                      - media
                      - icon_monitors

                  - type: custom:button-card
                    name: Kitchen
                    tap_action: !include popup/kitchen_radio_full.yaml
                    entity: media_player.kitchen_stereo
                    template:
                      - base
                      - icon_monitors
                    #- circle

                  - type: custom:button-card
                    entity: media_player.spotify
                    name: Spotify
                    template:
                      - media
                      - icon_spotify

                  - type: custom:button-card
                    entity: switch.playstation_5
                    name: Playstation
                    hold_action: !include popup/lounge_playstation.yaml
                    template:
                      - base
                      - icon_ps5
                      - loader

              - type: horizontal-stack
                cards:
                  - type: conditional
                    conditions:
                      - entity: input_select.conditional_media
                        state: Last downloaded
                    card:
                      type: custom:button-card
                      entity: sensor.plex_recently_added
                      tap_action:
                        action: none
                      template:
                        - conditional_media
                        - recently_downloaded
                        - icon_plex

      #################################################
      #                                               #
      #                    other                     #
      #                                               #
      #################################################

      - type: grid
        title: Other
        view_layout:
          grid-area: other
        columns: 2
        cards:
          - type: custom:button-card
            entity: input_boolean.all_lights
            name: All Lights
            tap_action: !include popup/lighting_popup.yaml #popup/all_lighting.yaml
            template:
              - base
              - icon_hue

          - type: custom:button-card
            entity: light.kitchen_lights #group
            name: Kitchen
            template:
              - light
              - icon_hue

          - type: custom:button-card
            entity: camera.office_camera
            name: Cameras
            tap_action: !include popup/cameras.yaml
            template:
              - base
              - icon_camera

          - type: custom:button-card
            entity: fan.studio_wemo
            name: AC
            hold_action: !include popup/other_lighting.yaml
            template:
              - base
              - icon_fan2
              - loader

      #################################################
      #                                               #
      #                     home                     #
      #                                               #
      #################################################

      - type: grid
        title: Home
        view_layout:
          grid-area: home
        columns: 2
        cards:
          - type: custom:button-card
            entity: person.tom
            name: Tom
            tap_action: !include popup/home_tom.yaml
            hold_action:
              action: none
            template: person

          - type: custom:button-card
            entity: person.charlotte
            name: Charlotte
            tap_action: !include popup/home_charlotte.yaml
            hold_action:
              action: none
            template: person

          - type: custom:button-card
            entity: script.home_leave
            name: Leave Home
            hold_action:
              action: none
            template:
              - base
              - icon_away

          - type: custom:button-card
            entity: script.home_arrive
            name: Home
            hold_action:
              action: none
            template:
              - base
              - icon_home

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

      - type: horizontal-stack
        view_layout:
          grid-area: footer
        cards:

          - type: custom:button-card
            name: >
              <ha-icon icon="mdi:cog"></ha-icon> Info
            tap_action:
              !include popup/sidebar_information.yaml
            template: footer

          - type: custom:button-card
            name: >
              <ha-icon icon="mdi:home-lightbulb-outline"></ha-icon> Lights
            tap_action:
              !include popup/lighting_popup.yaml
            template: footer

          - type: custom:button-card
            name: >
              <ha-icon icon="mdi:home-lightbulb-outline"></ha-icon> Test
            tap_action:
              !include popup/popups/test_popup.yaml
            template: footer

          - type: custom:button-card
            name: >
              <ha-icon icon="mdi:home-lightbulb-outline"></ha-icon> Test
            tap_action:
              !include popup/popups/test_popup.yaml
            template: footer

          - type: custom:button-card
            name: >
              <ha-icon icon="mdi:home-lightbulb-outline"></ha-icon> Test
            tap_action:
              !include popup/popups/test_popup.yaml
            template: footer

          - type: custom:button-card
            name: >
              <ha-icon icon="mdi:home-lightbulb-outline"></ha-icon> Test
            tap_action:
              !include popup/popups/test_popup.yaml
            template: footer


          # - type: grid
          #   cards:
          #     - type: button
          #       icon: mdi:home-lightbulb-outline
          #       tap_action:
          #         !include popup/lighting_popup.yaml
          #       hold_action:
          #         action: none

          #     - type: button
          #       icon: mdi:information-outline
          #       tap_action:
          #         !include popup/sidebar_information.yaml
          #       hold_action:
          #         action: none

          #     - type: button
          #       icon: mdi:arrow-up-bold-circle-outline
          #       tap_action:
          #         !include popup/sidebar_update.yaml
          #       hold_action:
          #         action: call-service
          #         service: homeassistant.update_entity
          #         service_data:
          #           entity_id: sensor.hacs

Looks good here

Have you had a look in the browser console?

recently_downloaded was recently merged into conditional_media

1 Like

damn, that was indeed the culprit – thanks so much. I owe you a beer, that’s for sure

@Mattias_Persson

maybe it’s a bit naïve, but now always has to edit the ui-lovelace.yaml in notepadd++? so you don’t see his changes at all?

or gives a edior with preview what i do?

Anyone has an idea what just happened? The alignment of the full home assistant environment is going nuts!

Or is this an april fools day joke from the Home Assistant team?

2 Likes