Hi @Mariusthvdb Nice template for button card… ![]()
I recently created something similar for my custom card. so let me improve your template. I modified the function so that the background gradient width is in the range of the maximum entity current value, instead of the entire 100% card width. I created two versions, one with a color gradient and the other with color blocks. Maybe someone prefers this style more.

base card template:
ts_bar_card:
show_state: true
styles:
grid:
- grid-template-areas: '"i n s"'
- grid-template-columns: 50px 1fr
name:
- width: 100%
- color: white
- font-weight: 400
- font-size: 16px
- text-align: left
- z-index: 2
state:
- z-index: 2
icon:
- z-index: 2
card:
- height: 26px
- padding: 4px 8px
- font-size: 1em
- border-radius: 8px
- position: relative
- background: var(--secondary-background-color)
gradient
- type: custom:button-card
entity: input_number.test_max_value
name: Color Gradient
template:
- ts_bar_card
- color_gradient_bar
color_gradient_bar:
styles:
custom_fields:
bar:
- position: absolute
- top: 0
- left: 0
- bottom: 0
- width: 100%
- height: 100%
- border-radius: 8px
- z-index: 1
- transition: width 0.5s ease-out
- background: >
[[[
let currentLevel = parseFloat(entity.state),
maxValue = variables.max_value || 100;
function generateGradient() {
const colorThresholds = [
{ "value": 0, "color": "#00ff4c"},
{ "value": 20, "color": "#d49adf" },
{ "value": 40, "color": "#00d5ff"},
{ "value": 60, "color": "#850000"},
{"value": 80,"color": "#342678"},
{"value": 90,"color": "#7968cf"}
]
const sorted = colorThresholds.sort((a, b) => a.value - b.value);
const _level = Math.min(currentLevel, maxValue);
let stops = [];
let lastColor = sorted[0].color;
for (const { value, color } of sorted) {
if (value > _level) break;
const percentage = (value / _level) * 100;
stops.push(`${color} ${percentage}%`);
lastColor = color;
}
const normalizedLevel = 100;
const lastStop = stops[stops.length - 1];
if (!lastStop?.endsWith(`${normalizedLevel}%`)) {
stops.push(`${lastColor} ${normalizedLevel}%`);
}
return `linear-gradient(90deg, ${stops.join(', ')})`;
}
return generateGradient();
]]]
custom_fields:
bar: >
[[[
let currentLevel = parseFloat(entity.state);
setTimeout(() => {
let elt = this.shadowRoot,
card = elt.getElementById('card'),
container = elt.getElementById('container'),
bar = elt.getElementById('bar');
if (elt && card && container && bar) {
card.insertBefore(bar, container);
bar.style.width = currentLevel + '%';
}
}, 0);
return ' ';
]]]
color blocks
- type: custom:button-card
entity: input_number.test_max_value
name: Color Block
template:
- ts_bar_card
- color_block_bar
color_block_bar:
styles:
custom_fields:
bar:
- position: absolute
- top: 0
- left: 0
- bottom: 0
- width: 100%
- height: 100%
- border-radius: 8px
- z-index: 1
- transition: width 0.5s ease-out
- background: >
[[[
let currentLevel = parseFloat(entity.state),
maxValue = variables.max_value || 100;
function generateColorBlocks() {
const colorThresholds = [
{ "value": 0, "color": "#00ff4c"},
{ "value": 20, "color": "#d49adf" },
{ "value": 40, "color": "#00d5ff"},
{ "value": 60, "color": "#850000"},
{"value": 80,"color": "#342678"},
{"value": 90,"color": "#7968cf"}
]
const sorted = colorThresholds.sort((a, b) => a.value - b.value);
const cappedLevel = Math.min(currentLevel, maxValue);
let stops = [];
for (let i = 0; i < sorted.length - 1; i++) {
const current = sorted[i];
const next = sorted[i + 1];
if (current.value > cappedLevel) break;
const fromValue = (current.value / cappedLevel) * 100;
const toValue = (Math.min(next.value, cappedLevel) / cappedLevel) * 100;
stops.push(`${current.color} ${fromValue}%`, `${current.color} ${toValue}%`);
if (next.value >= cappedLevel) break;
}
let lastThreshold;
for (let i = sorted.length - 1; i >= 0; i--) {
if (sorted[i].value <= cappedLevel) {
lastThreshold = sorted[i];
break;
}
}
if (lastThreshold && lastThreshold.value < cappedLevel) {
const start = (lastThreshold.value / cappedLevel) * 100;
stops.push(`${lastThreshold.color} ${start}%`, `${lastThreshold.color} 100%`);
}
return `linear-gradient(90deg, ${stops.join(', ')})`;
}
return generateColorBlocks();
]]]
custom_fields:
bar: >
[[[
let currentLevel = parseFloat(entity.state);
setTimeout(() => {
let elt = this.shadowRoot,
card = elt.getElementById('card'),
container = elt.getElementById('container'),
bar = elt.getElementById('bar');
if (elt && card && container && bar) {
card.insertBefore(bar, container);
bar.style.width = currentLevel + '%';
}
}, 0);
return ' ';
]]]
bars in entities card

bars in entities card
- type: entities
entities:
- entity: input_number.test_max_value
- type: custom:button-card
entity: input_number.test_max_value
name: Color Gradient
template:
- ts_bar_card
- color_gradient_bar
- type: custom:button-card
entity: input_number.test_max_value
name: Color Block
template:
- ts_bar_card
- color_block_bar
state_color: true