🔹 Layout-card - Take control of where your cards end up

@CDRX2, this seems to have solved my problem! I switched over to using the layout-card instead of grid-layout and was able to remove all of the margins and/or padding! Not sure what the differences and/or limitations of the layout-card vs grid-layout are, but for now, I have the control I was looking for. Thanks, @CDRX2!

As seen without any gaps between the cards.

1 Like

For those coming after me, here is my complete yaml and a comparison of my three different attempts:

Vanilla HA (left), using custom:grid-layout (middle), using custom:mod-card & custom:layout-card (right)

yaml using custom:mod-card and custom:layout-card

views:
  - title: Home
    path: new-main
    panel: true
    theme: mobile
    icon: mdi:home
    badges: []
    cards:
      - type: custom:mod-card
        card_mod:
          style:
            layout-card$:
              grid-layout$: |
                :host {
                  padding: 0px !important;
                }
                #root {
                  margin: 2px !important;
                }
                #root > * {
                  margin: 2px !important;
                }
        card:
          type: custom:layout-card
          layout_type: grid
          layout_options:
            grid-template-columns: 1fr 1fr 2fr 1fr 1fr
            grid-template-rows: auto
            grid-template-areas: |
              "l1 l2 c1 r1 r2"
              "l3 l4 c1 r3 r4"
              "m1 m1 m1 m1 m1"
              "b1 b1 b2 b3 b3"
              "b4 b4 b5 b6 b6"
          cards:
          - type: custom:button-card
            name: L1
            show_name: false
            icon: mdi:eye-circle
            size: 70%
            aspect_ratio: 1/1
            style:
              ha-icon:
                $: |
                  ha-svg-icon {
                    color: rgb(0, 255, 255);
                    animation: wobbling 1s cubic-bezier(0.5,0,0.5,1) infinite alternate;
                  }
                  @keyframes wobbling {
                    0% {
                      transform: rotate(-45deg) perspective(100px) translate3d(0,0,-80px);
                    }
                    50% {
                      transform: rotate(+0deg) perspective(100px) translate3d(0,0,-10px);
                    }
                    100% {
                      transform: rotate(+45deg) perspective(100px) translate3d(0,0,-80px);
                    }
                  }
              .: |
                ha-card { 
                  border: solid 0px var(--primary-color);
                }
          - type: custom:button-card
            name: L2
            view_layout:
              grid-area: l2
            show_name: false
            icon: mdi:flash
            size: 70%
            aspect_ratio: 1/1
          - type: custom:button-card
            name: L3
            view_layout:
              grid-area: l3
            show_name: false
            icon: mdi:map
            size: 70%
            aspect_ratio: 1/1
            style: 'ha-card { height: 100%; }'
          - type: custom:button-card
            name: L4
            view_layout:
              grid-area: l4
            show_name: false
            icon: mdi:battery-50
            size: 70%
            aspect_ratio: 1/1
            style: 'ha-card { height: 100%; }'
          - type: custom:button-card
            name: C1
            view_layout:
              grid-area: c1
            show_name: false
            icon: mdi:camera
            size: 70%
            style: 'ha-card { height: 100%; }'
          - type: custom:button-card
            name: R1
            view_layout:
              grid-area: r1
            show_name: false
            icon: mdi:string-lights
            size: 70%
            aspect_ratio: 1/1
            style: 'ha-card { height: 100%; }'
          - type: custom:button-card
            name: R2
            view_layout:
              grid-area: r2
            show_name: false
            icon: mdi:cat
            size: 70%
            aspect_ratio: 1/1
            style: 'ha-card { height: 100%; }'
          - type: custom:button-card
            name: R3
            view_layout:
              grid-area: r3
            show_name: false
            icon: mdi:thermostat
            size: 70%
            aspect_ratio: 1/1
            style: 'ha-card { height: 100%; }'
          - type: custom:button-card
            name: R4
            view_layout:
              grid-area: r4
            show_name: false
            icon: mdi:sprinkler-variant
            size: 70%
            aspect_ratio: 1/1
            style: 'ha-card { height: 100%; }'
          - type: custom:weather-card
            entity: weather.benjamin_2
            current: true
            details: false
            forecast: true
            number_of_forecasts: '5'
            hourly_forecast: false
            view_layout:
              grid-area: m1
            style: |
              div.precipitation_probability {display: none;} 
              div.precipitation {display: none;}  
          - type: custom:button-card
            name: B1
            view_layout:
              grid-area: b1
            aspect_ratio: 1/1
            show_name: false
            icon: mdi:sofa-outline
            size: 70%
            style: 'ha-card { height: 100%; }'
          - type: custom:button-card
            name: B2
            view_layout:
              grid-area: b2
            aspect_ratio: 1/1
            show_name: false
            icon: mdi:bed-outline
            size: 70%
            style: 'ha-card { height: 100%; }'
          - type: custom:button-card
            name: B3
            view_layout:
              grid-area: b3
            aspect_ratio: 1/1
            show_name: false
            icon: mdi:desk
            size: 70%
            style: 'ha-card { height: 100%; }'
          - type: custom:button-card
            name: B4
            view_layout:
              grid-area: b4
            aspect_ratio: 1/1
            show_name: false
            icon: mdi:garage
            size: 70%
            style: 'ha-card { height: 100%; }'
          - type: custom:button-card
            name: B5
            view_layout:
              grid-area: b5
            aspect_ratio: 1/1
            show_name: false
            icon: mdi:fridge
            size: 70%
            style: 'ha-card { height: 100%; }'
          - type: custom:button-card
            name: B6
            view_layout:
              grid-area: b6
            aspect_ratio: 1/1
            show_name: false
            icon: mdi:desk
            size: 70%
            style: 'ha-card { height: 100%; }'
2 Likes

Hi,

Were you able to solve this? I have the exact same problem.

Update:
It is possible to do this by using the built-in ‘vertical-stack’ card. Just create 1 row only and inside each column put one ‘vertical-stack’ card. Inside the card place your other cards.

Update 2:
For some cards which fill the entire row height it might be needed to install card-mod and add the following styling:

style: | 
          ha-card {
            height: auto !important;
          }
1 Like

Thanks for the update!

I haven’t figured it out yet. Looks like your approach could be a good temporary solution. I opened an issue on the github repo, maybe the developer might chime in at some point. Here’s the link:

I’ve read this thread up and down in circles, but was not able to find an answer to my question:

Is it in non-grid-layout, e.g. horizontal, possible to set the width of the columns in % together width the number of columns or at least depending on mediaquery?

Currently only

      card:
        type: custom:layout-card
        layout_type: horizontal
        layout:
          width: 30

is working and the unit is always px.

But I would like to have either

      card:
        type: custom:layout-card
        layout_type: horizontal
        layout:
          width: 30px

with a defined number of columns (like in grid)

or at least

      card:
        type: custom:layout-card
        layout_type: horizontal
        layout:
          width: 30
          mediaquery:
            "(min-width: 1112px)":
              width: 300
            "(min-width: 812px)":
              width: 100

But currently nothing is working. :cry:

Somehow I cannot get card to work after recent updates :frowning:
I want to have header across entire top row and bottom row split between graph and statistics areas.
Here is the code using explicit placement of cards within grid:

type: custom:layout-card
layout_type: grid
layout:
  grid-template-columns: 70% 30%
  grid-template-rows: 40% 60%
cards:
  - type: markdown
    grid-column: 1/3
    grid-row: 1/2
    content: HEADER
  - type: markdown
    grid-column: 1/2
    grid-row: 2/3
    content: GRAPH
  - type: markdown
    grid-column: 2/3
    grid-row: 2/3
    content: STATS

And here I tried to use named areas:

type: custom:layout-card
layout_type: grid
layout:
  grid-template-columns: 70% 30%
  grid-template-rows: 40% 60%
  grid-template-areas: |
    "header header"
    "graph  stats"
cards:
  - type: markdown
    grid-area: header
    content: HEADER
  - type: markdown
    grid-area: graph
    content: GRAPH
  - type: markdown
    grid-area: stats
    content: STATS

In both cases I have the same result, as if card used horizontal layout, placing cards horizontaly one by one, as they apperar in config and then overflowing to second row. Grid is created properly with right columns and rows width/height. Only placementof cards within is not workiing as intended:
Screenshot 2021-09-11 at 2.05.01

What am I doing wrong?

EDIT: I found the solution, I was using grid_area wrong… to properly assign area to card following code should be used:

type: custom:layout-card
layout_type: grid
layout:
  grid-template-columns: 70% 30%
  grid-template-rows: 40% 60%
  grid-template-areas: |
    "header header"
    "graph  stats"
cards:
  - type: markdown
    view_layout:
      grid-area: header
    content: HEADER
  - type: markdown
    view_layout:
      grid-area: graph
    content: GRAPH
  - type: markdown
    view_layout:
      grid-area: stats
    content: STATS
2 Likes

If I want to use horizontal mode, but want to have the column width exactly as in standard mode, how to do this?

If I do not use an option, the column width is sometimes less than default (I have then 3 instead of 2 columns). With e.g. width: 450 is looks o.k. but then 450 is set on 300px devices as well and is wrong.

I’m having the hardest time finding the right config, so I am hoping someone would be able to point me in the right direction;

I have a grid, 12x7 that I can place individual cards in - but I am trying to get some to span multiple cells eg. 2x3 with no luck.

current yaml:
views:

  • title: Home
    path: Home
    panel: true
    icon: mdi:home
    badges: []
    cards:
    • type: custom:mod-card
      card_mod:
      style:
      layout-card$:
      grid-layout$: |
      :host {
      padding: 0px !important;
      }
      #root {
      margin: 2px !important;
      }
      #root > * {
      margin: 2px !important;
      }
      card:
      type: custom:layout-card
      layout_type: grid
      layout_options:
      grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr fr 1fr 1fr
      grid-template-rows: 1fr 1fr 1fr 1fr 1fr 1fr
      grid-template-areas: |
      “r1c1 r1c2 r1c3 r1c4 r1c5 r1c6 r1c7 r1c8 r1c9 r1c10 r1c11 r1c12”
      “r2c1 r2c2 r2c3 r2c4 r2c5 r2c6 r2c7 r2c8 r2c9 r2c10 r2c11 r2c12”
      cards:
      - type: custom:button-card
      name: L2
      view_layout:
      grid-area: r2c5
      show_name: false
      icon: mdi:flash
      aspect_ratio: 1/1

how can i get this card to take up 2 column’s worth of cells??

I am having a similar issue configuring my layout. I am not sure what I am missing.

I have created a layout based on the example on the github page.
This is what mine looks like:

Now that I have a layout with a sidebar, header, footer and main sections, I want to start populating it with cards. I will be using mostly custom button cards to make it look like this:

This is my code:

title: Estate Dashboard
name: estate-dashboard
swipe_nav:
  wrap: true
  animate: swipe
sidebar:
  title: null
  clock: false
  digitalClock: true
  digitalClockWithSeconds: true
  twelveHourVersion: false
  period: false
  date: true
  hideTopMenu: false
  hideHassSidebar: false
  showTopMenuOnMobile: true
  dateformat: dddd, DD MMMM YYYY
  width:
    desktop: 15
    mobile: 15
    tablet: 20
  breakpoints:
    mobile: 768
    tablet: 1024
  template: |
    <li>
      {% if now().hour  < 5 %} Good night {{'\U0001F634'}}
      {% elif now().hour < 12 %} Good morning {{'\u2615\uFE0F'}}
      {% elif now().hour < 18 %} Good afternoon {{'\U0001F44B\U0001F3FB'}}
      {% else %} Good evening {{'\U0001F44B\U0001F3FB'}}{% endif %}{{user}}
    </li>
  style: |
    :host {
        --sidebar-background: #FFF;
        --sidebar-text-color: #000;
        --face-color: #FFF;
        --face-border-color: #FFF;
        --clock-hands-color: #000;
        --clock-seconds-hand-color: #FF4B3E;
        --clock-middle-background: #FFF;
        --clock-middle-border: #000;
        box-shadow: inset -11px 0px 6px 1px #1d1f1f;
    }
    #customSidebar {
       z-index: 9999!important;
     }
    .sidebarMenu li {
        line-height: 35px!important;
     }
    .sidebarMenu li ha-icon {

     }
    .sidebarMenu li.active {
        background-color: #2C2E30!important;
        border-radius: 40px!important;
        font-weight: bold!important;
     }
    .sidebarMenu li.active ha-icon {

     }
    .digitalClock {
        padding-bottom: 5px;
        padding-top: 15px
    }
    .digitalClock {
        font-size: 50px !important;
        font-weight: 500!important;
        text-align: center;
    }
    .date {
        padding-bottom: 10px;
        font-size: 25px;
        font-weight: 300;
        text-align: center;
    }
  sidebarMenu:
    - action: navigate
      active: true
      name: MAIN
      icon: mdi:bullseye
      navigation_path: /estate-dashboard/main
    - action: navigate
      active: true
      name: HOUSE
      icon: mdi:home-outline
      navigation_path: /estate-dashboard/house
    - action: navigate
      active: true
      name: GALLERY
      icon: mdi:car-multiple
      navigation_path: /estate-dashboard/gallery
    - action: navigate
      active: true
      name: GUEST HOUSE
      icon: mdi:home-floor-g
      navigation_path: /estate-dashboard/guest-house
    - action: navigate
      active: true
      name: CABANA
      icon: mdi:pool
      navigation_path: /estate-dashboard/cabana
    - action: navigate
      active: true
      name: GROUNDS
      icon: mdi:home-group
      navigation_path: /estate-dashboard/grounds
  bottomCard:
    type: horizontal-stack
    cardOptions:
      cards:
        - type: custom:button-card
          color_type: card
          color: rgb(255, 255, 255)
          icon: mdi:shield-home
          tap_action:
            action: navigate
            navigation_path: /estate-dashboard/security
        - type: custom:button-card
          color_type: card
          color: rgb(255, 255, 255)
          tap_action:
            action: navigate
            navigation_path: /estate-dashboard/settings
          icon: mdi:cog-sync-outline
    cardStyle: |
      :host {
        width: 100%;
        background-color:#FFF;
      }
views:
  - title: MAIN
    path: main
    theme: Caule Black Orange
    icon: mdi:bullseye
    badges:
      - entity: person.haz
  - title: HOUSE
    path: house
    theme: Caule Black Orange
    icon: mdi:home-outline
    type: custom:grid-layout
    layout:
      grid-template-columns: 10% fr fr fr
      grid-template-rows: min-content min-content min-content
      grid-template-areas: |
        "sidebar header header header"
        "sidebar main main main"
        "sidebar footer footer footer"
      mediaquery:
        '(max-width: 600px)':
          grid-template-columns: 100%
          grid-template-rows: min-content min-content min-content min-content
          grid-template-areas: |
            "header"
            "sidebar"
            "main"
            "footer"
        '(max-width: 800px)':
          grid-template-columns: 10% 90%
          grid-template-rows: min-content min-content min-content
          grid-template-areas: |
            "sidebar header"
            "sidebar main"
            "sidebar footer"
    cards:
      - type: vertical-stack
        cards:
          - type: custom:simple-weather-card
            entity: weather.home_austin
            card_mod:
              style: |
                ha-card {
                  padding: 0px;
                }
            name: ' '
            backdrop:
              day: var(--primary-color)
              night: '#40445a'
            primary_info:
              - extrema
              - humidity
              - wind_speed
              - wind_bearing
            secondary_info:
              - pressure
              - precipitation_probability
              - precipitation
        layout:
          grid-area: header
      - type: custom:button-card
        template: container
        color: '#EDE7B0'
        name: Guest Bedroom
        custom_fields:
          buttons:
            card:
              type: horizontal-stack
              cards:
                - entity: switch.ge_14291_in_wall_smart_switch_switch_2
                  name: Kitchen
                  template: standard
                  icon: mdi:wall-sconce-flat
                  type: custom:button-card
                - entity: light.ge_14294_in_wall_smart_dimmer_level_10
                  name: Table
                  template: standard
                  icon: mdi:ceiling-light
                  type: custom:button-card
                - entity: light.ge_14294_in_wall_smart_dimmer_level_7
                  name: Dining
                  template: standard
                  icon: mdi:ceiling-light
                  type: custom:button-card
                - entity: switch.patio_light
                  template: standard
                  name: Patio
                  icon: mdi:outdoor-lamp
                  type: custom:button-card

The main question I have is the following:
How do I move the map or the custom button card from the bottom of the dashboard which is where the layout places them ignoring the grid above it, to a grid area?
for example, I want the custom:simple-weather-card to be in the grid area sidebar.
I want to move the custom:button-card to the header grid area.

Any thoughts?

1 Like

If you want to use named areas, then the same name should be used for all grid cells that you want your content to span across. So in your case areas should be named:

“r1c1 r1c1 r1c1 r1c4 r1c5 r1c6 r1c7 r1c8 r1c9 r1c10 r1c11 r1c12”
“r1c1 r1c1 r1c1 r2c4 r2c5 r2c6 r2c7 r2c8 r2c9 r2c10 r2c11 r2c12”

In this example content placed in r1c1 will span across first 3 cells horizontally and 2 cells vertically.

I tried that, too - it blows up the button to be square, not rectangular… and also messes up the other cell sizes. I am running HAOS 6.2; core-2021.9.4 - maybe something is broken?

If it helps, I am using a custom button card in the cell that I am trying to get a 2x3 layout for.

Thanks,
Dan

Well, as far as I recall button card has its own proportion management options, perhaps this conflicts with what layout card tries to do. Lets test it with any other standard lovelace card. If result will be the same then it is layout card, if it will fit properly the button card is the issue.

Found the solution. The post from @mirekmal fixed it for me too

The structure is set, now I can start populating it with my cards!

From what I can tell, and I have to test it more, the mediaquery works too.

- title: GALLERY
    path: gallery
    theme: Caule Black Orange
    icon: mdi:car-multiple
    panel: true
    cards:
      - type: custom:layout-card
        layout_type: custom:grid-layout
        layout:
          grid-template-columns: 10% fr fr fr
          grid-template-rows: min-content min-content min-content
          grid-template-areas: |
            "header header header header"
            "sidebar main main main"
            "sidebar main main main"
          mediaquery:
            '(max-width: 600px)':
              grid-template-columns: 100%
              grid-template-rows: min-content min-content min-content min-content
              grid-template-areas: |
                "header"
                "sidebar"
                "main"
            '(max-width: 800px)':
              grid-template-columns: 10% 90%
              grid-template-rows: min-content min-content min-content
              grid-template-areas: |
                "sidebar header"
                "sidebar main"
                "sidebar main"
        cards:
          - type: custom:simple-weather-card
            entity: weather.home_austin
            card_mod:
              style: |
                ha-card {
                  padding: 0px;
                }
            name: ' '
            backdrop:
              day: var(--primary-color)
              night: var(--disabled-color)
            primary_info:
              - extrema
              - humidity
              - wind_speed
              - wind_bearing
            secondary_info:
              - pressure
              - precipitation_probability
              - precipitation
            view_layout:
              grid-area: header
            content: HEADER
          - type: markdown
            view_layout:
              grid-area: sidebar
            content: GRAPH
          - type: markdown
            view_layout:
              grid-area: main
            content: STATS

I will update if I learn something new as I work through this.

Hi, it’s nice to see everyone exited about layout-card! Would like to join the party.
Could anyone point me at some documentation regarding how to use this?
For example, what does gridrows and gridcols 1/3 2/3 etc. mean?
Is there any write up about this topic?
Anyhow, question is - where does one start?

I found some old youtube video referencing to usage readme… is it relevant?
image

Hi,
The grid properties are all based on CSS. You’ll find some infos here or by googling CSS grids, that ought to get you started :wink:

1 Like

To add to this:

2 Likes

Thanks for replies! Yeah, have to learn css a bit. Sometimes I confuse haas specific configurations with css code parts.

Guys, I’m trying this layout card on Core but cannot find the “Layout Options” box to enter the width & cols.

Hello, I have a hard time trying to write correct yaml for the layout card. Grid option in combination with “children” elements seems hard to get to work. Here is my grid:

Please, Can someone help me write the yaml for the layout card.
Here is the html and css.

<div class="container">
  <div class="card1"></div>
  <div class="card2">
    <div class="card2\.1"></div>
    <div class="card2\.2"></div>
  </div>
  <div class="card3">
    <div class="card3\.1"></div>
    <div class="card3\.2"></div>
  </div>
  <div class="card4"></div>
</div>
.container {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  grid-auto-columns: 1fr;
  grid-auto-rows: 1fr;
  gap: 10px 10px;
  grid-auto-flow: row;
  grid-template-areas:
    "card1 card2"
    "card3 card4";
}

.card1 { grid-area: card1; }

.card2 {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 0.5fr 0.5fr;
  gap: 10px 0px;
  grid-auto-flow: row;
  grid-template-areas:
    "card2\.1 card2\.1 card2\.1"
    "card2\.2 card2\.2 card2\.2";
  grid-area: card2;
}

.card2\.1 { grid-area: card2\.1; }

.card2\.2 { grid-area: card2\.2; }

.card3 {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 0.5fr 0.5fr;
  gap: 10px 0px;
  grid-auto-flow: row;
  grid-template-areas:
    "card3\.1 card3\.1 card3\.1"
    "card3\.2 card3\.2 card3\.2";
  grid-area: card3;
}

.card3\.1 { grid-area: card3\.1; }

.card3\.2 { grid-area: card3\.2; }

.card4 { grid-area: card4; }

1 Like

you have an awesome setup. I am struggling to arrange multicolumn card like yours. could you share your yaml please? thanks.