Skip to content

Instantly share code, notes, and snippets.

@enginefeeder101
Created December 23, 2025 19:57
Show Gist options
  • Select an option

  • Save enginefeeder101/af4e0d50932d915f07c61179df555291 to your computer and use it in GitHub Desktop.

Select an option

Save enginefeeder101/af4e0d50932d915f07c61179df555291 to your computer and use it in GitHub Desktop.
Home Assistent Floating Back Button Helper
/**
* Home Assistent Floating Back Button Helper
*
* Place this file in `/www`
* Add the file as a Dashboard resource `config/lovelace/dashboards`
* 1. Click on `Resources` in the top right menu
* 2. Enter `/local/nav-back-helper.js?v=0.0.4`
*/
(() => {
const VERSION = "0.0.4";
const BUTTON_ID = "nav-back-helper";
const POLL_INTERVAL = 500;
let floatingButton = null;
let pollHandle = null;
/**
* Utility: deep query through shadow roots
*/
function qsDeep(selectors) {
let node = document;
for (let i = 0; i < selectors.length; i++) {
const selector = selectors[i];
node = node.querySelector(selector);
if (!node) return null;
if (i < selectors.length - 1 && node.shadowRoot) {
node = node.shadowRoot;
}
}
return node;
}
/**
* Wait for Home Assistant to be ready
*/
function waitForHass() {
return new Promise((resolve) => {
if (window.hassConnection) {
window.hassConnection.then(resolve);
} else {
window.addEventListener("hass-connection-ready", resolve, { once: true });
}
});
}
/**
* Find native HA back button
*/
function getBackButton() {
return qsDeep([
"home-assistant",
"home-assistant-main",
"ha-panel-lovelace",
"hui-root",
"ha-icon-button-arrow-prev",
]);
}
/**
* Create floating back button
*/
function createFloatingBackButton() {
const btn = document.createElement("div");
btn.id = BUTTON_ID;
btn.textContent = "⬅";
Object.assign(btn.style, {
position: "fixed",
bottom: "2rem",
right: "2rem",
width: "3.5rem",
height: "3.5rem",
lineHeight: "3.5rem",
textAlign: "center",
fontSize: "1.8rem",
fontWeight: "1000",
borderRadius: "50%",
background: "var(--card-background-color, #fff)",
color: "var(--primary-text-color, #000)",
cursor: "pointer",
boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
zIndex: "9999",
userSelect: "none",
transition: "background 0.2s, color 0.2s, transform 0.1s",
});
btn.addEventListener("mouseenter", () => {
btn.style.background = "var(--primary-color, #03a9f4)";
btn.style.color = "var(--sl-color-primary-600, #fff)";
});
btn.addEventListener("mouseleave", () => {
btn.style.background = "var(--card-background-color, #fff)";
btn.style.color = "var(--primary-text-color, #000)";
});
btn.addEventListener("mousedown", () => {
btn.style.transform = "scale(0.95)";
});
btn.addEventListener("mouseup", () => {
btn.style.transform = "scale(1)";
});
btn.addEventListener("click", () => {
const backButton = getBackButton();
backButton?.click();
});
return btn;
}
/**
* Ensure correct button state
*/
function syncButton() {
const backButton = getBackButton();
const shouldShow = backButton && (!backButton.checkVisibility || backButton.checkVisibility() === false);
if (shouldShow) {
if (!floatingButton) {
floatingButton = createFloatingBackButton();
document.body.appendChild(floatingButton);
}
} else {
if (floatingButton) {
floatingButton.remove();
floatingButton = null;
}
}
}
/**
* Observe HA DOM changes
*/
async function init() {
await waitForHass();
syncButton();
pollHandle = setInterval(syncButton, POLL_INTERVAL);
console.info(
`%c${BUTTON_ID.toUpperCase()}\n%cVersion: ${VERSION}`,
"color: #03a9f4; font-weight: bold;",
""
);
}
init();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment