With this I get them to stay within the screen, but not the offset on the rows.
Now every button is aligned, but I want some of them to occupy more rows in the grid.
play with the card settings
styles:
card:
- margin-top: -40px
- padding: 8px
- height: 80%
- border-radius: 36px 36px 6px 36px
Thank you for the help!
That did the trick for now ![]()
I have a horizontal layout card where each column is rather “min width” to fit the content inside. The problem I’m facing is that the content is aligned to the center of the layout-card HTML object.
- type: custom:layout-card
layout_type: custom:horizontal-layout
layout:
max_width: initial
place-items: start
card_margin: 10px
cards: ...
This is generated:
I need to position the elements to the start (left) of the horizontal-layout HTML object, with the CSS object place-items: start.
There are 2 options:
- To add the
place-items: startto thehorizontal-layoutelement - Or add the
justify-content: startto the#columnsdiv, which has, by default in the card, the value set tocenter
I tried with card_mod even, but I can’t make it work, because it is not a ha-card. Any advice?
I love you mate.
If you ever come to Italy one day, send me a message and I’ll buy you a beer (or even two).
By(t)e
Is it possible to set the with depended on the screen size of a device?
I tried with show, but can not get it to work.
For my older android i would like to have a smaller dashboard and for my newer/langer screen a bigger size.
(ps. examle shows twice the same width, ofc thats wrong/for test.)
But they have bothe the same size?
Here is what I’m using to experiment. You’ll have to upload the images to
/homeassistant/www/car.
card_margin if needed isn’t working. Known bug
this works:
card_mod:
style: |
:host {
--masonry-view-card-margin: 0px;
}
This is the code for a complete view
views:
- path: default_view
title: Home
type: panel
cards:
- type: custom:layout-card
layout_type: custom:grid-layout
layout:
gap: 0
padding: 0
card_margin: 0
grid-template-columns: repeat(6, 91.5px)
grid-template-rows: auto
grid-template-areas: |
"plaats1 plaats2 plaats3 plaats4 plaats5 plaats6"
"plaats7 plaats8 plaats9 plaats10 plaats11 plaats12"
mediaquery:
'(max-width: 415px)':
gap: 0
margin: 0
padding: 0
card_margin: 0px
grid-template-columns: repeat(1, 100%)
grid-template-rows: auto
grid-template-areas: |
"plaats1"
"plaats2"
"plaats3"
"plaats4"
"plaats5"
"plaats6"
'(max-width: 920px)':
gap: 0
margin: 0
padding: 0
card_margin: 0px
grid-template-columns: repeat(3, 91.5px)
grid-template-rows: auto
grid-template-areas: |
"plaats1 plaats2 plaats3"
"plaats4 plaats5 plaats6"
"plaats7 plaats8 plaats9"
'(max-width: 1400px)':
gap: 0
margin: 0
padding: 0
card_margin: 0px
grid-template-columns: repeat(4, 91.5px)
grid-template-rows: auto
grid-template-areas: |
"plaats1 plaats2 plaats3 plaats4"
"plaats5 plaats6 plaats7 plaats8"
"plaats9 plaats10 plaats11 plaats12"
'(max-width: 2100px)':
gap: 0
margin: 0
padding: 0
card_margin: 0px
grid-template-columns: repeat(6, 91.5px)
grid-template-rows: auto
grid-template-areas: >
"plaats1 plaats2 plaats3 plaats4 plaats5 plaats6" "plaats7
plaats8 plaats9 plaats10 plaats11 plaats12"
cards:
- type: markdown
content: |
<div style="line-height: 1; margin: 0; padding: 0;">1</div>
view_layout:
grid-area: plaats1
card_mod:
style: |
:host {
--masonry-view-card-margin: 0px;
}
ha-card {
height: 100% !important;
width: 100% !important;
border: 6px solid black !important;
display: flex !important;
justify-content: center !important;
align-items: center !important;
padding: 0 !important;
margin: 0 !important;
font-size: 36px !important;
font-weight: bold !important;
overflow: hidden !important;
card_margin: 0px
}
- type: markdown
content: |
<div style="line-height: 1; margin: 0; padding: 0;">2</div>
view_layout:
grid-area: plaats2
card_mod:
style: |
:host {
--masonry-view-card-margin: 0px;
}
ha-card {
height: 113.3px !important;
width: 91.5px !important;
border: 6px solid black !important;
display: flex !important;
justify-content: center !important;
align-items: center !important;
padding: 0 !important;
margin: 0 !important;
font-size: 36px !important;
font-weight: bold !important;
overflow: hidden !important;
card_margin: 0px
}
- type: markdown
content: |
<div style="line-height: 1; margin: 0; padding: 0;">3</div>
view_layout:
grid-area: plaats3
card_mod:
style: |
:host {
--masonry-view-card-margin: 0px;
}
ha-card {
height: 113.3px !important;
width: 91.5px !important;
border: 6px solid black !important;
display: flex !important;
justify-content: center !important;
align-items: center !important;
padding: 0 !important;
margin: 0 !important;
font-size: 36px !important;
font-weight: bold !important;
overflow: hidden !important;
card_margin: 0px
}
- type: markdown
content: |
<div style="line-height: 1; margin: 0; padding: 0;">4</div>
view_layout:
grid-area: plaats4
card_mod:
style: |
:host {
--masonry-view-card-margin: 0px;
}
ha-card {
height: 113.3px !important;
width: 91.5px !important;
border: 6px solid black !important;
display: flex !important;
justify-content: center !important;
align-items: center !important;
padding: 0 !important;
margin: 0 !important;
font-size: 36px !important;
font-weight: bold !important;
overflow: hidden !important;
card_margin: 0px
}
- type: markdown
content: |
<div style="line-height: 1; margin: 0; padding: 0;">5</div>
view_layout:
grid-area: plaats5
card_mod:
style: |
:host {
--masonry-view-card-margin: 0px;
}
ha-card {
height: 113.3px !important;
width: 91.5px !important;
border: 6px solid black !important;
display: flex !important;
justify-content: center !important;
align-items: center !important;
padding: 0 !important;
margin: 0 !important;
font-size: 36px !important;
font-weight: bold !important;
overflow: hidden !important;
card_margin: 0px
}
- type: markdown
content: |
<div style="line-height: 1; margin: 0; padding: 0;">6</div>
view_layout:
grid-area: plaats6
card_mod:
style: |
:host {
--masonry-view-card-margin: 0px;
}
ha-card {
height: 113.3px !important;
width: 91.5px !important;
border: 6px solid black !important;
display: flex !important;
justify-content: center !important;
align-items: center !important;
padding: 0 !important;
margin: 0 !important;
font-size: 36px !important;
font-weight: bold !important;
overflow: hidden !important;
card_margin: 0px
}
- type: picture
image: /local/car/aimg7.png
view_layout:
grid-area: plaats7
card_mod:
style: |
ha-card {
width: 91.5px;
height: 113.3px;
padding: 0;
border: none;
border-radius: 0;
}
:host {
--masonry-view-card-margin: 0px;
}
ha-card img {
display: block; /* <-- ADD THIS LINE */
width: 100%;
height: 100%;
object-fit: fill;
}
- type: picture
image: /local/car/aimg8.png
view_layout:
grid-area: plaats8
card_mod:
style: |
ha-card {
width: 91.5px;
height: 113.3px;
padding: 0;
border: none;
border-radius: 0;
}
:host {
--masonry-view-card-margin: 0px;
}
ha-card img {
display: block; /* <-- ADD THIS LINE */
width: 100%;
height: 100%;
object-fit: fill;
}
- type: picture
image: /local/car/aimg9.png
view_layout:
grid-area: plaats9
card_mod:
style: |
ha-card {
width: 91.5px;
height: 113.3px;
padding: 0;
border: none;
border-radius: 0;
}
:host {
--masonry-view-card-margin: 0px;
}
ha-card img {
display: block; /* <-- ADD THIS LINE */
width: 100%;
height: 100%;
object-fit: fill;
}
- type: picture
image: /local/car/aimg10.png
view_layout:
grid-area: plaats10
card_mod:
style: |
ha-card {
width: 91.5px;
height: 113.3px;
padding: 0;
border: none;
border-radius: 0;
}
:host {
--masonry-view-card-margin: 0px;
}
ha-card img {
display: block; /* <-- ADD THIS LINE */
width: 100%;
height: 100%;
object-fit: fill;
}
- type: picture
image: /local/car/aimg11.png
view_layout:
grid-area: plaats11
card_mod:
style: |
ha-card {
width: 91.5px;
height: 113.3px;
padding: 0;
border: none;
border-radius: 0;
}
:host {
--masonry-view-card-margin: 0px;
}
ha-card img {
display: block; /* <-- ADD THIS LINE */
width: 100%;
height: 100%;
object-fit: fill;
}
- type: picture
image: /local/car/aimg12.png
view_layout:
grid-area: plaats12
card_mod:
style: |
ha-card {
width: 91.5px;
height: 113.3px;
padding: 0;
border: none;
border-radius: 0;
}
:host {
--masonry-view-card-margin: 0px;
}
ha-card img {
display: block; /* <-- ADD THIS LINE */
width: 100%;
height: 100%;
object-fit: fill;
}






I’m trying to place a 2-button row under a 380px-wide status block. With unequal columns (e.g., 280px and 100px) using custom:layout-card, the left edge of the row shifts inward compared to the status block above. With equal columns the alignment is fine. This happens both with Tile and with Mushroom template cards.
The goal is simple: 380px container, same 4px L/R padding as the status block, and a 2-column row (wide “Start” + narrow “Off”) that aligns exactly left with the status block.
type: custom:layout-card
layout_type: grid
layout:
grid-template-columns: 280px 100px
grid-gap: 0
cards:
- type: tile
entity: switch.tapw_timeprogram_bms_forced_qube2
name: Tapwater starten
icon: mdi:hot-tub
vertical: false
tap_action:
action: call-service
service: switch.turn_on
target:
entity_id: switch.tapw_timeprogram_bms_forced_qube2
card_mod:
style:
ha-tile-info$: |
.secondary { display: none !important; }
- type: tile
entity: switch.tapw_timeprogram_bms_forced_qube2
name: Uit
icon: mdi:stop
vertical: false
tap_action:
action: call-service
service: switch.turn_off
target:
entity_id: switch.tapw_timeprogram_bms_forced_qube2
card_mod:
style:
ha-tile-info$: |
.secondary { display: none !important; }
Play with negative margins in layout block:
layout:
margin: "-4px -4px -4px -4px"
grid-template-columns: 280px 100px
grid-gap: 0
Can you share the entire card code to include the top card?
As mention, a negative left margin may solve your issue
type: custom:layout-card
layout_type: grid
layout:
grid-template-columns: 40% 60%
margin: 0px 0px 0px -14px
cards:
- type: tile
entity: switch.xxxx
name: Tapwater starten
icon: mdi:hot-tub
- type: tile
entity: switch.xxxxxx
name: Tapwater starten
icon: mdi:hot-tub
- margin: 1px 2px 3px 4px
- top margin is 1px
- right margin is 2px
- bottom margin is 3px
- left margin is 4px
Big thanks.
I started with margin. left -4px did the trick for the left side. Unfortunately, the right side started to shift left which I could not solve with margin option.
grid-template-columns: did the trick, I increased the second column by 8px. Although the total value exceeds the 380, the two items are now aligned.
layout:
margin: 0px 0px 0px -4px
grid-template-columns: 280px 108px
grid-gap: 0
Glad it worked out and happy to assist!
This happened because you specified fixed widths in pixels. This could be prevented by:
grid-template-columns: 280px auto
Or
grid-template-columns: 70% 30%
Tried both option and with both options there is a small indent on the right side.
I might have understood if I had to add 4px (so 104px), but I don’t understand is why I have to add 8px if width is 380px, margin -4. Might be that the status block is 384?
Code status block
type: custom:mod-card
card_mod:
style: |
ha-card { width: 380px; margin: 0; }
card:
type: vertical-stack
cards:
- type: custom:button-card
name: Warm water
show_state: false
show_icon: false
tap_action:
action: navigate
navigation_path: /huis/0
styles:
grid:
- grid-template-areas: "'home n gear' 'info info info'"
- grid-template-columns: 40px 1fr 40px
- grid-template-rows: auto 1fr
card:
- padding: 4px
- height: 180px
- width: 380px
- overflow: hidden
name:
- font-size: 14px
- font-weight: bold
- text-align: left
- padding-bottom: 4px
custom_fields:
home:
- display: flex
- align-items: center
- justify-content: center
gear:
- display: flex
- align-items: center
- justify-content: center
info:
- padding: 4px
custom_fields:
I have created this status block. Intent is to go to home view, if you click on home icon and to settings view if you click on gear.
Enclosed code works great in my browser. If I click exactly on gear the settings view opens clicking somewhere else is going back to home.
Unfortunately, this is not working on the app. My guess is that the gear and home areas are overlapping and it’s luck that this works in browser.
I would like to create 3 areas (left, middle and right). Left and right slightly bigger than icon and rest is middle. In this case I will link left and middle both to the home view. Three columns will give me flexibility for the future.
Tried a lot but was not successful. Can someone provide a tip on how to define the 3 areas
Thanks.
type: custom:mod-card
card_mod:
style: |
ha-card { width: 380px; margin: 0; }
card:
type: vertical-stack
cards:
- type: custom:button-card
name: Warm water
show_state: false
show_icon: false
tap_action:
action: navigate
navigation_path: /huis/0
styles:
grid:
- grid-template-areas: "'home n gear' 'info info info'"
- grid-template-columns: 40px 1fr 40px
- grid-template-rows: auto 1fr
card:
- padding: 4px
- height: 180px
- width: 380px
- overflow: hidden
name:
- font-size: 14px
- font-weight: bold
- text-align: left
- padding-bottom: 4px
custom_fields:
home:
- display: flex
- align-items: center
- justify-content: center
gear:
- display: flex
- align-items: center
- justify-content: center
info:
- padding: 4px
custom_fields:
home: >
<ha-icon icon="mdi:home" style="color: var(--primary-color); width:
38px; height: 38px;"></ha-icon>
gear: |
[[[
// Alarm-badge op het tandwiel + eigen navigatie (geen bubbels)
const anyAlarm =
states['binary_sensor.glbal_qube2']?.state === 'on' ||
states['binary_sensor.al_maxtime_antileg_active_qube2']?.state === 'on' ||
states['binary_sensor.al_maxtime_dhw_active_qube2']?.state === 'on';
const alarmDot = anyAlarm
? '<span style="position:absolute; top:-2px; right:-2px; width:10px; height:10px; background:var(--error-color); border-radius:50%; box-shadow:0 0 0 2px white;"></span>'
: '';
return `
<div style="position:relative; width:38px; height:38px; display:flex; align-items:center; justify-content:center; cursor:pointer;"
onclick="event.stopPropagation(); window.history.pushState(null,'','/huis/wp2-instelling'); window.dispatchEvent(new Event('location-changed'));">
<ha-icon icon="mdi:cog" style="width:22px; height:22px; color: var(--primary-text-color);"></ha-icon>
${alarmDot}
</div>
`;
]]]
info: |
[[[
// ===== Helpers =====
const safeNum = (eid,d=1)=>{const s=states[eid]?.state;if(!s||s==='unknown'||s==='unavailable')return null;const v=parseFloat(s);return isNaN(v)?null:parseFloat(v.toFixed(d));};
const safeTxt = (eid)=>{const s=states[eid]?.state;return (s&&s!=='unknown'&&s!=='unavailable')?s:'';};
// ===== Entities =====
const statusCode = parseInt(states['sensor.unitstatus_qube2']?.state ?? 'NaN'); // 0,1,6,8,9,15,16,17,22
const serviceTxt = safeTxt('sensor.qube_driewegklep_dhw_cv_status_2'); // 'CV' of 'SWW'
const tDHWT = safeNum('sensor.dhw_temp_qube2', 1);
// Alarm => rood randje om de info-zone
const anyAlarm =
states['binary_sensor.glbal_qube2']?.state === 'on' ||
states['binary_sensor.al_maxtime_antileg_active_qube2']?.state === 'on' ||
states['binary_sensor.al_maxtime_dhw_active_qube2']?.state === 'on';
const alarmStyle = anyAlarm ? 'box-shadow: inset 0 0 0 2px var(--error-color); border-radius: 8px;' : '';
// ===== Kleur per status =====
const colorByStatus = {
0:'#000000', 1:'#1e88e5', 6:'#e53935',
8:'#43a047', 9:'#43a047',
15:'#9e9e9e', 16:'#9e9e9e',
17:'#f9a825', 22:'#43a047'
};
const iconColor = colorByStatus[statusCode] ?? '#607d8b';
// ===== Icoonkeuze volgens tabel =====
const pickIcon = (code, service)=>{
if ([0,1,6].includes(code)) return 'mdi:hot-tub'; // altijd SWW
if (code === 22) return 'mdi:hot-tub'; // Heating DHW
return (service === 'CV') ? 'mdi:radiator' : 'mdi:hot-tub';
};
const statusIcon = pickIcon(statusCode, serviceTxt);
// ===== Gauge (klok) =====
const SCALE_MIN = 20, SCALE_MAX = 70;
const START_DEG = -120, END_DEG = 120;
const CX = 60, CY = 60, R = 46;
const clamp=(v,min,max)=>Math.max(min,Math.min(max,v));
const mapTempToDeg=(T)=>{ if(T==null)return START_DEG; const p=(clamp(T,SCALE_MIN,SCALE_MAX)-SCALE_MIN)/(SCALE_MAX-SCALE_MIN); return START_DEG + p*(END_DEG-START_DEG); };
const polarToXY=(cx,cy,r,deg)=>{ const rad=(deg-90)*Math.PI/180; return [cx+r*Math.cos(rad), cy+r*Math.sin(rad)]; };
const arcPath=(cx,cy,r,d1,d2)=>{ const [x1,y1]=polarToXY(cx,cy,r,d1); const [x2,y2]=polarToXY(cx,cy,r,d2); const la=(Math.abs(d2-d1)>180)?1:0; const sw=d2>d1?1:0; return `M ${x1.toFixed(1)} ${y1.toFixed(1)} A ${r} ${r} 0 ${la} ${sw} ${x2.toFixed(1)} ${y2.toFixed(1)}`; };
// Zones (vast)
const A1s=START_DEG, A1e=mapTempToDeg(40); // geel
const A2s=A1e, A2e=mapTempToDeg(55); // groen
const A3s=A2e, A3e=mapTempToDeg(62); // donkergroen
const A4s=A3e, A4e=END_DEG; // rood
const needleDeg = mapTempToDeg(tDHWT ?? SCALE_MIN);
const [nx, ny] = polarToXY(CX, CY, R-6, needleDeg);
const C_Y='#f9a825', C_G='#43a047', C_DG='#1b5e20', C_R='#e53935', C_RING='#e0e0e0';
// Linker status icoon (groot, verticaal gecentreerd)
const colStatus = `
<div style="flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; height:120px;">
<ha-icon icon="${statusIcon}" style="color:${iconColor}; width:64px; height:64px;"></ha-icon>
</div>
`;
// Gauge in het midden + temperatuur dichter tegen de gauge
const dhwText = (tDHWT!=null) ? `${tDHWT.toFixed(1)} °C` : '...';
const colDHW = `
<div style="text-align:center; flex:1;">
<svg width="120" height="120" viewBox="0 0 120 120">
<path d="${arcPath(CX,CY,R, START_DEG, END_DEG)}" stroke="${C_RING}" stroke-width="10" fill="none" />
<path d="${arcPath(CX,CY,R, A1s, A1e)}" stroke="${C_Y}" stroke-width="10" fill="none" />
<path d="${arcPath(CX,CY,R, A2s, A2e)}" stroke="${C_G}" stroke-width="10" fill="none" />
<path d="${arcPath(CX,CY,R, A3s, A3e)}" stroke="${C_DG}" stroke-width="10" fill="none" />
<path d="${arcPath(CX,CY,R, A4s, A4e)}" stroke="${C_R}" stroke-width="10" fill="none" />
${[20,30,40,50,60,70].map(v=>{
const deg=mapTempToDeg(v); const [tx,ty]=polarToXY(CX,CY,R+8,deg);
return `<text x='${tx.toFixed(1)}' y='${ty.toFixed(1)}' text-anchor='middle' alignment-baseline='middle' font-size='10' fill='#555'>${v}</text>`;
}).join('')}
<circle cx="${CX}" cy="${CY}" r="4" fill="#555"/>
<line x1="${CX}" y1="${CY}" x2="${nx.toFixed(1)}" y2="${ny.toFixed(1)}" stroke="#111" stroke-width="3" stroke-linecap="round"/>
</svg>
<div style="margin-top:-16px; line-height:1; font-size:16px; font-weight:600;">${dhwText}</div>
</div>
`;
// Wrapper met alarmstijl
return `
<div style="display:flex; align-items:center; justify-content:space-between; width:100%; ${alarmStyle}">
<div style="display:flex; gap:8px; flex:1;">
${colStatus}
${colDHW}
</div>
</div>
`;
]]]
Layout Card adds the following margin by default: 0px 4px 0px 4px
See under the heading Layouts here: GitHub - thomasloven/lovelace-layout-card: 🔹 Get more control over the placement of lovelace cards.
We can suppress this standard margin as follows:
margin: 0px -4px 0px -4px
That should make my suggestions for grid-template-columns work. You may need to readjust the margin values a little.
Tested both options with
margin: 0px -4px 0px -4px
And indeed they both give the expected result!.
Thanks
You’re welcome. I’m glad it works.
1 down, still 1 to go. I spend quite some hours on these topics, so your support is highly appreciated.
Any feedback regarding my second question (sorry for asking)
This is a somewhat more complex code and question. However, there are also experts here for custom:button-card. You might want to ask your question again here:
But one suggestion would be to “embed” additional custom:button cards nested as custom_fields. This allows you to assign different tap_actions to all three. This is explained here:




