TeamDesk Knowledge Base & Support

   Home      FAQ      Forum      Idea Exchange      Ask a Question      My Stuff      Help   
  
Adding Persistant Top Banner in New UI (v3) Without Breaking Menus CSS
Here’s your same text in plain formatting only, no Markdown:

---

Wanted a persistent, site-wide “Sandbox” banner in the TeamDesk v3 UI that:

* Stays visible on every page
* Doesn’t block or overlap the Setup, Support, or other header menus
* Survives navigation without needing a full page reload

What didn’t work:

* Injecting the banner above or overlaying the header caused menu links to be unclickable.
* Adding the banner inside unrelated DOM elements broke header layout.

What worked:

* Inserting the banner into the native v3 banner slot:

<li id="td-topmenu-alert" class="v3-header-nav__banner"></li>

* Using a small JS script to build and insert the banner dynamically and reinsert if the header reflows.

* Styling in CSS so the banner appears as a compact pill (or optional full-width stripe).

JS (UTF-8 clean, drop-in ready):

(function () {
"use strict";
const BANNER\_ID = "druther-global-banner";
const messageHTML = `<strong>Sandbox Notice:</strong> You are working in <em>LPDB8 Sandbox - John Doe</em>. Changes here do not affect production.`;

function ready(fn) {
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", fn);
else fn();
}
function findBannerContainer() { return document.getElementById("td-topmenu-alert"); }
function buildBannerNode() {
const w = document.createElement("div");
w\.id = BANNER\_ID;
w\.className = "td-banner td-banner--info";
w\.innerHTML = `<div class="td-banner__content">${messageHTML}</div>`;
return w;
}
function ensureBanner() {
const slot = findBannerContainer();
if (!slot || slot.querySelector("#" + BANNER\_ID)) return;
slot.prepend(buildBannerNode());
}
function bootstrap() {
ensureBanner();
const mo = new MutationObserver(() => ensureBanner());
const h = document.getElementById("td-header");
if (h) mo.observe(h, { childList: true, subtree: true });
}
ready(bootstrap);
})();

Why this works:

* Uses the slot TeamDesk already reserves for top-of-page notices.
* Avoids messing with absolute positioning or z-indexes.
* Survives SPA-like partial reloads because it re-injects on DOM changes.

---

If you want, I can also make a **forum-friendly compact + full-width CSS toggle block** so people can copy both styles in one go. That would make your post even more complete.


ID
2119
Category
Customization
Author

L. C. Parker
Date Created
8/8/2025 5:33:46 PM
Date Updated
8/9/2025 11:02:22 AM
Comments
L. C. Parker 8/8/2025 5:35:08 PM
dbscript-v3.js
---
/* Relies on #td-topmenu-alert.v3-header-nav__banner existing in the header.
*/

(function () {
"use strict";

// ---- CONFIG ----
const BANNER_ID = "lcparker37-global-banner";
const DISMISS_KEY = "lcparker37.globalBanner.dismissed"; // change to rotate message
const messageHTML = `
<strong>Sandbox Notice:</strong>
You are working in <em>LPDB8 Sandbox - John Doe</em>. Changes here do not affect production.
`;
const showClose = false; // set to true if you want a close (dismiss) button
const bannerRole = "status"; // 'status' or 'alert'

// ---- UTIL ----
function ready(fn) {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", fn);
} else {
fn();
}
}

function findBannerContainer() {
return document.getElementById("td-topmenu-alert");
}

function alreadyDismissed() {
try { return localStorage.getItem(DISMISS_KEY) === "1"; } catch (_) { return false; }
}

function markDismissed() {
try { localStorage.setItem(DISMISS_KEY, "1"); } catch (_) {}
}

function buildBannerNode() {
const wrapper = document.createElement("div");
wrapper.id = BANNER_ID;
wrapper.className = "td-banner td-banner--info"; // styled in dbstyles-v3.css
wrapper.setAttribute("role", bannerRole);
wrapper.setAttribute("aria-live", "polite");

const content = document.createElement("div");
content.className = "td-banner__content";
content.innerHTML = messageHTML;

wrapper.appendChild(content);

if (showClose) {
const btn = document.createElement("button");
btn.type = "button";
btn.className = "td-banner__close";
btn.setAttribute("aria-label", "Dismiss banner");
btn.textContent = "×";
btn.addEventListener("click", () => {
markDismissed();
wrapper.remove();
});
wrapper.appendChild(btn);
}

return wrapper;
}

function ensureBanner() {
const slot = findBannerContainer();
if (!slot) return;
if (showClose && alreadyDismissed()) return;
if (slot.querySelector("#" + BANNER_ID)) return;
slot.prepend(buildBannerNode());
}

function bootstrap() {
ensureBanner();
const header = document.getElementById("td-header");
if (!header) return;
const mo = new MutationObserver(() => ensureBanner());
mo.observe(header, { childList: true, subtree: true });
}

ready(bootstrap);
})();
L. C. Parker 8/8/2025 5:35:27 PM
dbstyles-v3.css
---
/* Put the banner on its own row under the floated left/right menus */
#td-topmenu-alert {
clear: both;
overflow: auto; /* new BFC; isolates from floats */
display: block;
margin: 6px 0 0; /* matches the screenshots’ spacing */
padding: 0;
text-align: center; /* centers the inner inline-block */
}

/* The banner “pill†itself */
#td-topmenu-alert .td-banner {
display: inline-block; /* enables centering */
background: #ffe9e9;
color: #5a0b0b;
border: 1px solid #ffbcbc;
border-radius: 8px;
padding: 10px 14px;
font-weight: 700;
line-height: 1.35;
text-align: left; /* readable copy */
vertical-align: top;
max-width: min(1100px, calc(100% - 32px)); /* tidy on wide screens */
box-shadow: 0 0 0 1px rgba(0,0,0,0.03) inset; /* subtle definition */
}

/* Links inside */
#td-topmenu-alert .td-banner__content a {
color: #7a0f0f;
text-decoration: underline;
}

/* Optional dismiss (if you enable it in JS later) */
#td-topmenu-alert .td-banner__close {
margin-left: 8px;
border: 0; background: transparent;
font-size: 18px; cursor: pointer; color: inherit;
}
#td-topmenu-alert .td-banner__close:hover,
#td-topmenu-alert .td-banner__close:focus { opacity: .8; }

/* Hide in print */
@media print { #td-topmenu-alert { display: none !important; } }
L. C. Parker 8/8/2025 5:37:22 PM
Complements of ChatGPT 5
Patricio Bustos 8/9/2025 11:02:22 AM
Tks for sharing :)
Feedback
Back to Search Results