This topic is locked

Modern responsive Navbar with Dark/Light Theme for PHPRunner projects

9/3/2025 1:06:41 PM
PHPRunner Tips and Tricks
P
ppradhan@live.com author

Hi,
I've compilled 'custom_css' and 'custom_functions.js' codes that instantly adds Theme Switcher (dark/light) Icon and generates responsive Navbar with Menus for your existing PHPRunner projects. It is straight forward:

  1. copy codes from 'custom_css' to your custom css in the Style Editor.
  2. copy codes from 'custom_functions' to your Custom_functions.js in the event section.
    Enjoy..... (you can customize as per your requirement).

==== CUSTOM_CSS ===


/* ===========================================
THEME + NAV (Bootstrap 3)
=========================================== */

/* Sticky navbar offset (kept) */

/* Fill the mobile safe-area above the fixed navbar with the navbar theme */
@supports (padding-top: env(safe-area-inset-top)) {
/* Push navbar content below the status bar area */
.r-topheader {
padding-top: env(safe-area-inset-top);
}
/* Paint the status bar area using the same navbar background */
.r-topheader::before {
content: "";
position: fixed;
top: 0; left: 0; right: 0;
height: env(safe-area-inset-top);
background: var(--bg-navbar); /* uses your theme vars */
z-index: 1031; /* just above the navbar (1030) */
pointer-events: none;
}
}

/* Optional: when gradients look too “busy” up there, use the solid tone */
@media (max-width: 767px) {
.r-topheader::before { background: var(--bg-navbar-solid); }
}

@media (max-width: 767px) {
body { padding-top: 60px !important; }
}

.r-topheader {
position: fixed !important;
top: 0 !important; left: 0 !important; right: 0 !important;
z-index: 1030 !important;
}

/* Light variables (kept) */
:root {
--bg-primary:#fff; --bg-secondary:#f8f9fa;
--bg-navbar:linear-gradient(135deg, blue 0%, red 100%);
--bg-navbar-solid:#2E86C1; --bg-dropdown:#fff;
--text-primary:#333; --text-secondary:#666; --text-muted:#999; --text-navbar:#fff;
--border-color:#e3e6ea; --border-navbar:rgba(255,255,255,.2);
--link-color:#2E86C1; --link-hover:#1B4F72; --navbar-brand-color:#fff;
--btn-default-bg:#fff; --btn-default-border:#ccc; --btn-primary-bg:#2E86C1; --btn-secondary-bg:#E74C3C;
--shadow-light:rgba(0,0,0,.1); --shadow-navbar:rgba(46,134,193,.3);
--bg-input: white; --text-input: black;
}

/* Dark variables (kept) */
body.dark-theme {
--bg-primary:#1a1a1a; --bg-secondary:#2d2d2d;
--bg-navbar:linear-gradient(135deg,#1B4F72 0%,#C0392B 100%);
--bg-navbar-solid:#1B4F72; --bg-dropdown:#3a3a3a;
--text-primary:#fff; --text-secondary:#ccc; --text-muted:#999; --text-navbar:#fff;
--border-color:#444; --border-navbar:rgba(255,255,255,.15);
--link-color:#5DADE2; --link-hover:#85C1E9; --navbar-brand-color:#fff;
--btn-default-bg:#3a3a3a; --btn-default-border:#555; --btn-primary-bg:#1B4F72; --btn-secondary-bg:#C0392B;
--shadow-light:rgba(255,255,255,.1); --shadow-navbar:rgba(27,79,114,.4);
--bg-input: transparent; --text-input: lightgray;
}

/* Surfaces & navbar */
.r-body,.r-content,.r-data-block { background:var(--bg-primary) !important; color:var(--text-primary) !important; }

.navbar-default{
background:var(--bg-navbar) !important; border:0 !important; border-radius:0 !important;
margin-bottom:0 !important; min-height:60px !important; width:100% !important;
box-shadow:0 2px 10px var(--shadow-navbar);
}
.navbar-default .container-fluid,.navbar-default .container{
width:100% !important; max-width:none !important; padding:0 15px !important;
}

.navbar-default .navbar-brand{
color:var(--navbar-brand-color) !important; font-weight:700 !important; font-size:20px !important;
text-shadow:1px 1px 2px rgba(0,0,0,.3) !important;
-webkit-transition:transform .3s ease; transition:transform .3s ease;
}
.navbar-default .navbar-brand:hover,
.navbar-default .navbar-brand:focus{ color:#fff !important; -webkit-transform:scale(1.05); transform:scale(1.05); }

/* Toggle: keep bars for a11y, add MENU label */
.navbar-default .navbar-toggle{
border:2px solid var(--border-navbar) !important; background:rgba(255,255,255,.1) !important; border-radius:6px !important;
}
.navbar-default .navbar-toggle .icon-bar{ background:var(--text-navbar) !important; }
.navbar-default .navbar-toggle:hover,
.navbar-default .navbar-toggle:focus{ background:rgba(255,255,255,.2) !important; border-color:#fff !important; }
.navbar-toggle{ padding:6px 10px !important; position:relative; }
.navbar-toggle:after{
content:'MENU'; position:relative; margin-left:8px; color:var(--text-navbar); font-weight:700; font-size:14px;
}

/* Menu links */
.r-menu.nav.navbar-nav>li>a{ color:var(--text-navbar) !important; font-weight:500 !important; }
.r-menu.nav.navbar-nav>li>a:hover,
.r-menu.nav.navbar-nav>li>a:focus{ color:#fff !important; background:rgba(255,255,255,.15) !important; }
.r-menu.nav.navbar-nav>li.active>a{ color:#fff !important; background:rgba(255,255,255,.2) !important; }

/* Dropdowns */
.dropdown-menu{
background:var(--bg-dropdown) !important; border:1px solid var(--border-color) !important; box-shadow:0 8px 24px rgba(0,0,0,.15) !important;
}
.dropdown-menu>li>a{ color:var(--text-primary) !important; }
.dropdown-menu>li>a:hover,
.dropdown-menu>li>a:focus{ color:var(--link-hover) !important; background:var(--bg-secondary) !important; }

/* Theme switcher button */
.theme-switcher-btn{ margin-top:1px; }
#dynamic_theme_switcher{
position:relative !important; min-width:45px !important; height:38px !important; padding:8px 12px !important;
border-radius:20px !important; background:var(--btn-default-bg) !important; border:2px solid var(--border-navbar) !important;
box-shadow:0 2px 8px var(--shadow-light) !important; cursor:pointer !important; margin:5px 8px !important;
display:inline-flex !important; align-items:center; justify-content:center;
}
#dynamic_theme_switcher:hover{ -webkit-transform:translateY(-2px) scale(1.05); transform:translateY(-2px) scale(1.05); }
#dynamic_theme_switcher .theme-icon{ font-size:18px !important; }
#dynamic_theme_switcher .theme-icon:before{ content:"☀️" !important; }
body:not(.dark-theme) #dynamic_theme_switcher{
background:linear-gradient(45deg,#fff,#f0f0f0) !important; border:2px solid rgba(255,255,255,.3) !important; box-shadow:0 2px 8px rgba(255,255,255,.2) !important;
}
body.dark-theme #dynamic_theme_switcher .theme-icon:before{
content:"
S
salus2 9/4/2025

Thanks for putting this together and sharing, I'd like to give it a try. I see the "custom_css" section, where is the "custom_functions" section?

P
ppradhan@live.com author 9/4/2025

@salus2,
I've pasted full codes for custom_css and custom_fuctions. But viewing this page on large screen seems to have truncated codes. Please view this page in your mobile - you can see full code there. Sorry but, this is something our boss @'PHPRUNNER - Xlinesoft' should fix our view page here (like: codes could be collapsed/expanded in the view page).
If you are facing issue let me know, and I will have to split them in 3-4 parts here.

S
salus2 9/4/2025

Thanks for that info. Maybe you are bumping into a form post length limit? Please seperate into sections if possible.

P
ppradhan@live.com author 9/5/2025

/ =========== CUSTOM_CSS ====================
THEME + NAV (Bootstrap 3.x) - PHPRunner App
===========================================
/
/ Sticky navbar offset (kept) /

/ Fill the mobile safe-area above the fixed navbar with the navbar theme /
@supports (padding-top: env(safe-area-inset-top)) {
/ Push navbar content below the status bar area /
.r-topheader { padding-top: env(safe-area-inset-top); }
/ Paint the status bar area using the same navbar background /
.r-topheader::before { content: ""; position: fixed; top: 0; left: 0; right: 0; height: env(safe-area-inset-top); background: var(--bg-navbar); / uses your theme vars /
z-index: 1031; / just above the navbar (1030) / pointer-events: none; }
}

/ Optional: when gradients look too “busy” up there, use the solid tone /
@media (max-width: 767px) { .r-topheader::before { background: var(--bg-navbar-solid); } }
@media (max-width: 767px) { body { padding-top: 60px !important; } }

.r-topheader { position: fixed !important; top: 0 !important; left: 0 !important; right: 0 !important; z-index: 1030 !important; }

/ Light variables (kept) /
:root {
--bg-primary:#fff; --bg-secondary:#f8f9fa;
--bg-navbar:linear-gradient(135deg, blue 0%, red 100%);
--bg-navbar-solid:#2E86C1; --bg-dropdown:#fff;
--text-primary:#333; --text-secondary:#666; --text-muted:#999; --text-navbar:#fff;
--border-color:#e3e6ea; --border-navbar:rgba(255,255,255,.2);
--link-color:#2E86C1; --link-hover:#1B4F72; --navbar-brand-color:#fff;
--btn-default-bg:#fff; --btn-default-border:#ccc; --btn-primary-bg:#2E86C1; --btn-secondary-bg:#E74C3C;
--shadow-light:rgba(0,0,0,.1); --shadow-navbar:rgba(46,134,193,.3);
--bg-input: white; --text-input: black;
}

/ Dark variables (kept) /
body.dark-theme {
--bg-primary:#1a1a1a; --bg-secondary:#2d2d2d;
--bg-navbar:linear-gradient(135deg,#1B4F72 0%,#C0392B 100%);
--bg-navbar-solid:#1B4F72; --bg-dropdown:#3a3a3a;
--text-primary:#fff; --text-secondary:#ccc; --text-muted:#999; --text-navbar:#fff;
--border-color:#444; --border-navbar:rgba(255,255,255,.15);
--link-color:#5DADE2; --link-hover:#85C1E9; --navbar-brand-color:#fff;
--btn-default-bg:#3a3a3a; --btn-default-border:#555; --btn-primary-bg:#1B4F72; --btn-secondary-bg:#C0392B;
--shadow-light:rgba(255,255,255,.1); --shadow-navbar:rgba(27,79,114,.4);
--bg-input: transparent; --text-input: lightgray;
}

/ Surfaces & navbar /
.r-body,.r-content,.r-data-block { background:var(--bg-primary) !important; color:var(--text-primary) !important; }

.navbar-default{ background:var(--bg-navbar) !important; border:0 !important; border-radius:0 !important; margin-bottom:0 !important; min-height:60px !important; width:100% !important; box-shadow:0 2px 10px var(--shadow-navbar); }
.navbar-default .container-fluid,.navbar-default .container{ width:100% !important; max-width:none !important; padding:0 15px !important; }

.navbar-default .navbar-brand{ color:var(--navbar-brand-color) !important; font-weight:700 !important; font-size:20px !important;
text-shadow:1px 1px 2px rgba(0,0,0,.3) !important; -webkit-transition:transform .3s ease; transition:transform .3s ease; }
.navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus{ color:#fff !important; -webkit-transform:scale(1.05); transform:scale(1.05); }

/ Toggle: keep bars for a11y, add MENU label /
.navbar-default .navbar-toggle{ border:2px solid var(--border-navbar) !important; background:rgba(255,255,255,.1) !important; border-radius:6px !important; }
.navbar-default .navbar-toggle .icon-bar{ background:var(--text-navbar) !important; }
.navbar-default .navbar-toggle:hover,
.navbar-default .navbar-toggle:focus{ background:rgba(255,255,255,.2) !important; border-color:#fff !important; }
.navbar-toggle{ padding:6px 10px !important; position:relative; }
.navbar-toggle:after{ content:'MENU'; position:relative; margin-left:8px; color:var(--text-navbar); font-weight:700; font-size:14px; }

/ Menu links /
.r-menu.nav.navbar-nav>li>a{ color:var(--text-navbar) !important; font-weight:500 !important; }
.r-menu.nav.navbar-nav>li>a:hover,
.r-menu.nav.navbar-nav>li>a:focus{ color:#fff !important; background:rgba(255,255,255,.15) !important; }
.r-menu.nav.navbar-nav>li.active>a{ color:#fff !important; background:rgba(255,255,255,.2) !important; }

/ Dropdowns /
.dropdown-menu{ background:var(--bg-dropdown) !important; border:1px solid var(--border-color) !important; box-shadow:0 8px 24px rgba(0,0,0,.15) !important; }
.dropdown-menu>li>a{ color:var(--text-primary) !important; }
.dropdown-menu>li>a:hover,
.dropdown-menu>li>a:focus{ color:var(--link-hover) !important; background:var(--bg-secondary) !important; }

/ Theme switcher button /
.theme-switcher-btn{ margin-top:1px; }

dynamic_theme_switcher{

position:relative !important; min-width:45px !important; height:38px !important; padding:8px 12px !important;
border-radius:20px !important; background:var(--btn-default-bg) !important; border:2px solid var(--border-navbar) !important;
box-shadow:0 2px 8px var(--shadow-light) !important; cursor:pointer !important; margin:5px 8px !important;
display:inline-flex !important; align-items:center; justify-content:center;
}

dynamic_theme_switcher:hover{ -webkit-transform:translateY(-2px) scale(1.05); transform:translateY(-2px) scale(1.05); }

dynamic_theme_switcher .theme-icon{ font-size:18px !important; }

dynamic_theme_switcher .theme-icon:before{ content:"☀️" !important; }

body:not(.dark-theme) #dynamic_theme_switcher{
background:linear-gradient(45deg,#fff,#f0f0f0) !important; border:2px solid rgba(255,255,255,.3) !important; box-shadow:0 2px 8px rgba(255,255,255,.2) !important;
}
body.dark-theme #dynamic_theme_switcher .theme-icon:before{ content:"

P
ppradhan@live.com author 9/5/2025

...continue of custom_css

/ Collapsed menu styling /
@media (max-width:767px){
.navbar-nav { margin: 7.5px 0px !important; }
.navbar-header{ position:relative !important; width:100% !important; height:60px; }
.navbar-header .navbar-brand{ position:absolute !important; left:0; top:50% !important; -webkit-transform:translateY(-50%); transform:translateY(-50%); padding-left:15px !important; }
.navbar-header .navbar-brand:hover{ -webkit-transform:translateY(-50%) scale(1.05); transform:translateY(-50%) scale(1.05); }

.navbar-collapse{ background:var(--bg-navbar) !important; border-top:1px solid var(--border-navbar) !important; margin-top:1px !important; padding:0 !important; }

mobile_nav_tools{ display:flex; justify-content:space-between; align-items:center; padding:8px 15px; margin-top:10px; border-top:1px solid var(--border-navbar); }

.mobile-nav-left{ display:flex; align-items:center; gap:10px; }

mobile_nav_tools>span,.mobile-nav-left>span{ padding:0 !important; margin:0 !important; border:0 !important; float:none !important; display:flex !important; align-items:center !important; }

mobile_nav_tools #dynamic_theme_switcher{ margin:0 !important; width:42px !important; height:38px !important; -webkit-transform:none !important; transform:none !important; }

tbody { background-color: var(--bg-primary) !important; }
.r-flexgrid.r-flexgrid.r-flexgrid.r-flexgrid > tbody > tr:not(.rnr-hiddenelem ):not([data-hidden]):not([data-media-hidden]).r-gridrow {
border: 1px solid var(--bg-primary) !important; border-radius: 10px; padding: 5px 10px;
background-color: var(--bg-secondary) !important; background-image: none !important;
}

body:not(.dark-theme) .navbar-collapse .r-menu a{ color:#e8e8e8 !important; text-shadow:1px 1px 1px rgba(0,0,0,.5) !important; }
body:not(.dark-theme) .navbar-collapse .r-menu a:hover,
body:not(.dark-theme) .navbar-collapse .r-menu li.active>a{ color:#fff !important; background:rgba(255,255,255,.1) !important; }
body:not(.dark-theme) .navbar-collapse .dropdown-menu{ background:rgba(0,0,0,.2) !important; }
body:not(.dark-theme) .navbar-collapse .dropdown-menu a{ color: var(--text-primary) !important; }
}

/ Dark overrides for your custom snippets (kept) /
body.dark-theme .stats-grid .panel{ background:var(--bg-secondary) !important; border-color:var(--border-color) !important; }
body.dark-theme .stats-grid .panel-value{ color:var(--text-primary) !important; }
body.dark-theme .stats-grid .small-label{ color:var(--text-secondary) !important; }
body.dark-theme .country-stats-card,
body.dark-theme .country-stats-header{ background:var(--bg-secondary) !important; border-color:var(--border-color) !important; color:var(--text-primary) !important; }
body.dark-theme .country-name,
body.dark-theme .visa-count{ color:var(--text-secondary) !important; background:var(--border-color) !important; }
body.dark-theme .country-item:first-child .visa-count{ background:linear-gradient(135deg,#2ecc71,#27ae60) !important; color:#fff !important; }
body.dark-theme .country-stats-header:hover{ background:var(--bg-primary) !important; }
body.dark-theme .dept-dropdown-wrapper .mydropdown-menu{ background:var(--bg-dropdown) !important; }
body.dark-theme .dept-dropdown-wrapper .mydropdown-menu li a{ color:var(--text-primary) !important; background:var(--bg-secondary) !important; }
body.dark-theme .dept-dropdown-wrapper .mydropdown-menu li a:hover{ background:var(--border-color) !important; color:var(--link-hover) !important; }
body.dark-theme .offcanvas,
body.dark-theme .off-canvas{ background:var(--bg-secondary) !important; }
body.dark-theme .offcanvas h2,
body.dark-theme .off-canvas h2{ color:var(--link-color) !important; }
body.dark-theme .offcanvas .close-btn,
body.dark-theme .off-canvas .close-btn{ color:var(--text-secondary) !important; }
body.dark-theme .offcanvas .minimal-table,
body.dark-theme .off-canvas .report-table{ color:var(--text-primary) !important; }
body.dark-theme .offcanvas .minimal-table th,
body.dark-theme .off-canvas .report-table th,
body.dark-theme .offcanvas .minimal-table td,
body.dark-theme .off-canvas .report-table td{ border-color:var(--border-color) !important; }
body.dark-theme .offcanvas .minimal-table tfoot tr,
body.dark-theme .off-canvas .report-table tfoot tr{ background:var(--bg-primary) !important; }
body.dark-theme .modal .modal-content{ background:var(--bg-secondary) !important; }
body.dark-theme .modal .modal-header h2{ color:var(--text-primary) !important; }
body.dark-theme .modal .close-btn1{ color:var(--text-secondary) !important; }

body { background-color: var(--bg-primary) !important; }

.greeting, .weather-section, .date-section, [data-itemtype="text"] , [data-itemid="add_header"],
[data-itemid="edit_header"], [data-itemid="view_header"], .panel-title { color: var(--text-secondary) !important; }
.breadcrumb { background-color: var(--bg-secondary) !important; color: var(--text-secondary) !important; }

.r-topbar-page, .r-gridrow, .form-section, td,
[data-itemid^="text"] p, [data-itemid^="text"] p span, [data-itemid^="text"] span { background-color: var(--bg-primary) !important; color: var(--text-primary) !important; }

input, select, textarea { background-color: var(--bg-input) !important; color: var(--text-input) !important; }
.panel-heading { background-color: var(--bg-secondary) !important; }
.panel-title::after { content: " ⇓"; font-weight: bold; }
.modal { top: 50px !important; }
.modal-header, .modal-footer { border-color: var(--bg-secondary) !important; }
.r-controls, .r-sideimage { background-color: var(--bg-primary) !important; } / login page /

/ Custom Selectors here /
.off-canvas, #offcanvasMenu { z-index: 1050 !important;}
.r-noti-header, .r-noti-h-text, .r-noti-close { background-color: var(--bg-primary) !important; color: var(--text-primary) !important; }
.r-noti-messages, .r-noti-message { background-color: var(--bg-secondary) !important; color: var(--text-secondary) !important; }

/ Fallback button (no inline styles in JS) /
.theme-fallback{ position:fixed; top:10px; right:80px; z-index:9999; }

P
ppradhan@live.com author 9/5/2025

/* ==========CUSTOM_FUNCTIONS.JS ==============
DARK/LIGHT THEME (Bootstrap 3 / ES5)

  • Mobile safe-area & address bar theming
    =========================================== */

var ThemeManager = { buttonId: 'dynamic_theme_switcher', initialized: false, originalParents: {}, _resizeTimer: null, _nav: null,

init: function () { if (this.initialized) return;
this._nav = document.querySelector('.navbar-default');
var savedTheme = this.getTheme();
this.applyTheme(savedTheme);

// NEW: make the phone's status/address bar + safe-area match the navbar
this.injectSafeAreaStyles(); // injects a small CSS block once
this.setThemeColor(); // sets <meta name="theme-color"> from CSS var

this.createThemeButton(); this.bindEvents(); this.updateButtonText(savedTheme);

// Debounced resize handler
var self = this;
window.addEventListener('resize', function () { clearTimeout(self._resizeTimer); self._resizeTimer = setTimeout(function () { self.handleResize(); }, 120); });
this.handleResize();

// Mobile submenu toggles (ES5-safe)
this.setupSubmenuToggle();

this.initialized = true;

},

// ES5 helper: closest ancestor with a matching id
_closestById: function (el, id) { while (el && el !== document && el.nodeType === 1) { if (el.id === id) return el; el = el.parentNode; } return null; },

// Setup manual toggle for top-level dropdowns on mobile
setupSubmenuToggle: function () { var container = document.querySelector('.navbar-collapse');
if (!container) return;

var links = container.querySelectorAll('.r-menu.nav.navbar-nav > li.dropdown > a');
for (var i = 0; i < links.length; i++) {
var link = links[i];

// Use attribute instead of dataset (ES5-safe)
if (link.getAttribute('data-custom-submenu-toggle') === 'true') continue;

link.addEventListener('click', function (e) {
if (window.innerWidth >= 768) return; // Desktop: let Bootstrap handle it

e.preventDefault();
e.stopPropagation();

var parentLi = this.parentNode;
var wasOpen = parentLi.classList ? parentLi.classList.contains('open') : /(^|\s)open(\s|$)/.test(parentLi.className);

// Close others
var allLis = container.querySelectorAll('.r-menu.nav.navbar-nav > li.dropdown');
for (var j = 0; j < allLis.length; j++) {
if (allLis[j].classList) allLis[j].classList.remove('open');
else allLis[j].className = allLis[j].className.replace(/(^|\s)open(\s|$)/g, ' ');
}

// Toggle current
if (!wasOpen) { if (parentLi.classList) parentLi.classList.add('open');
else parentLi.className += ' open'; }
});

link.setAttribute('data-custom-submenu-toggle', 'true');
}

},

createThemeButton: function () {
if (document.getElementById(this.buttonId)) return;

var loc = this.findButtonContainer();
if (loc && loc.container) {
var container = document.createElement('span');
container.className = 'navbar-form r-align-right navbar-collapse theme-switcher-container';
container.setAttribute('data-itemid', 'theme_switcher');

var button = document.createElement('a');
button.id = this.buttonId;
button.className = 'btn btn-default btn-sm navbar-btn theme-switcher-btn';
button.href = 'javascript:void(0)';
button.innerHTML = '<span class="theme-icon"></span>';
button.setAttribute('role', 'switch');
button.setAttribute('aria-label', 'Toggle Dark/Light Theme');
button.setAttribute('title', 'Toggle Dark/Light Theme');

container.appendChild(button);
if (loc.insertBefore) loc.container.insertBefore(container, loc.insertBefore);
else loc.container.appendChild(container);
} else {
this.createFallbackButton();
}

},

handleResize: function () { var isMobile = window.innerWidth < 768; if (isMobile) this.groupMobileNavbarItems(); else this.ungroupMobileNavbarItems(); },

groupMobileNavbarItems: function () {
var collapsibleMenu = document.querySelector('.navbar-collapse[data-itemid="menu"]');
if (!collapsibleMenu || document.getElementById('mobile_nav_tools')) return;

var nav = this._nav || document.querySelector('.navbar-default');
if (!nav) return;

var themeSwitcher = nav.querySelector('[data-itemid="theme_switcher"]');
var notifications = nav.querySelector('[data-itemid="notifications"]');
var userButton = nav.querySelector('[data-itemid="username_button"]');
if (!(themeSwitcher && notifications && userButton)) return;

if (!this.originalParents.themeSwitcher) this.originalParents.themeSwitcher = themeSwitcher.parentNode;
if (!this.originalParents.notifications) this.originalParents.notifications = notifications.parentNode;
if (!this.originalParents.userButton) this.originalParents.userButton = userButton.parentNode;

var toolsGroup = document.createElement('div');
toolsGroup.id = 'mobile_nav_tools';

var leftItems = document.createElement('div');
leftItems.className = 'mobile-nav-left';
leftItems.appendChild(themeSwitcher);
leftItems.appendChild(notifications);

toolsGroup.appendChild(leftItems);
toolsGroup.appendChild(userButton);
collapsibleMenu.appendChild(toolsGroup);

},

ungroupMobileNavbarItems: function () {
var toolsGroup = document.getElementById('mobile_nav_tools');
if (!toolsGroup) return;

var themeSwitcher = toolsGroup.querySelector('[data-itemid="theme_switcher"]');
var notifications = toolsGroup.querySelector('[data-itemid="notifications"]');
var userButton = toolsGroup.querySelector('[data-itemid="username_button"]');
var nav = this.originalParents.themeSwitcher || this._nav || document.querySelector('.navbar-default');
if (nav) {
if (themeSwitcher) nav.appendChild(themeSwitcher);
if (notifications) nav.appendChild(notifications);
if (userButton) nav.appendChild(userButton);
}
if (toolsGroup.parentNode) toolsGroup.parentNode.removeChild(toolsGroup);

},

findButtonContainer: function () {
var nav = this._nav || document.querySelector('.navbar-default');
if (!nav) return null;

var notif = nav.querySelector('[data-itemid="notifications"]');
if (notif && notif.parentNode) return { container: notif.parentNode, insertBefore: notif };

var userBtn = nav.querySelector('[data-itemid="username_button"]');
if (userBtn && userBtn.parentNode) return { container: userBtn.parentNode, insertBefore: userBtn };

return { container: nav, insertBefore: null };

},

createFallbackButton: function () {
var fb = document.createElement('div');
fb.className = 'theme-fallback';
var btn = document.createElement('a');
btn.id = this.buttonId;
btn.className = 'btn btn-default btn-sm theme-switcher-btn';
btn.href = 'javascript:void(0)';
btn.innerHTML = '<span class="theme-icon"></span>';
btn.title = 'Toggle Theme';
fb.appendChild(btn);
document.body.appendChild(fb);
},

getTheme: function () {
try {
if (window.localStorage) {
var t = localStorage.getItem('phprunner_theme');
if (t) return t;
}
} catch (e) {}
return this.getCookie('phprunner_theme') || 'light';
},

saveTheme: function (theme) {
try {
if (window.localStorage) localStorage.setItem('phprunner_theme', theme);
} catch (e) {}
this.setCookie('phprunner_theme', theme, 365);
},

applyTheme: function (theme) {
var isDark = theme === 'dark';
if (isDark) document.body.classList.add('dark-theme');
else document.body.classList.remove('dark-theme');

// Scoped transition class (smooth flip, avoids global *)
document.body.classList.add('theme-transition');
setTimeout(function () {
document.body.classList.remove('theme-transition');
}, 350);

},

toggleTheme: function () {
var isDark = document.body.classList.contains('dark-theme');
var next = isDark ? 'light' : 'dark';
this.applyTheme(next);
this.saveTheme(next);

// NEW: keep address-bar color aligned with theme
this.setThemeColor();

this.updateButtonText(next);
this.showThemeChangeEffect();

},

updateButtonText: function (theme) {
var btn = document.getElementById(this.buttonId);
if (!btn) return;
var txt = (theme === 'dark') ? 'Switch to Light Mode' : 'Switch to Dark Mode';
btn.title = txt;
btn.setAttribute('aria-label', txt);
btn.setAttribute('aria-checked', theme === 'dark' ? 'true' : 'false');
},

bindEvents: function () {
var self = this;
if (document.body._hasThemeSwitcherListener) return;

document.body.addEventListener('click', function (e) {
var button = self._closestById(e.target, self.buttonId);
if (button) {
e.preventDefault();
self.toggleTheme();
}
});
document.body._hasThemeSwitcherListener = true;

},

showThemeChangeEffect: function () {
var btn = document.getElementById(this.buttonId);
if (!btn) return;
btn.classList.add('theme-changing');
setTimeout(function () { btn.classList.remove('theme-changing'); }, 600);
},

setCookie: function (name, value, days) {
var expires = '';
if (days) {
var d = new Date();
d.setTime(d.getTime() + (days 24 60 60 1000));
expires = '; expires=' + d.toUTCString();
}
document.cookie = name + '=' + (value || '') + expires + '; path=/';
},

getCookie: function (name) {
var nameEQ = name + '=';
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === ' ') c = c.substring(1);
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length);
}
return null;
},

/ ===================== NEW: Safe-area + address bar ===================== /

// Injects a small CSS block that paints the safe-area (status bar/notch) above
// the fixed navbar using your theme variables. Runs once.
injectSafeAreaStyles: function () {
if (document.getElementById('tm-safearea-style')) return;

// Note: Uses your existing CSS variables --bg-navbar and --bg-navbar-solid
var css =
'@supports (padding-top: env(safe-area-inset-top)) {' +
' .r-topheader{padding-top: env(safe-area-inset-top);}' +
' .r-topheader::before{content:"";position:fixed;top:0;left:0;right:0;height: env(safe-area-inset-top);' +
' background: var(--bg-navbar);z-index:1031;pointer-events:none;}' +
'}' +
'@media (max-width:767px){.r-topheader::before{background: var(--bg-navbar-solid);}}';

var style = document.createElement('style');
style.type = 'text/css';
style.id = 'tm-safearea-style';
if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); }
var head = document.getElementsByTagName('head')[0] || document.documentElement;
head.appendChild(style);

},

// Ensures <meta name="theme-color"> exists and sets it to the solid navbar color.
// This tints the mobile browser address bar to match your theme (Android/Chrome etc.).
ensureThemeColorMeta: function () {
var m = document.querySelector('meta[name="theme-color"]');
if (!m) { m = document.createElement('meta'); m.setAttribute('name', 'theme-color');
(document.head || document.getElementsByTagName('head')[0]).appendChild(m);
}
return m;
},

setThemeColor: function () {
var m = this.ensureThemeColorMeta();
// Read your solid navbar color from CSS variable (light/dark aware)
var solid = '#2E86C1';
try {
var cs = window.getComputedStyle ? window.getComputedStyle(document.body) : null;
var v = cs ? cs.getPropertyValue('--bg-navbar-solid') : null;
if (v) solid = v.trim() || solid;
} catch (e) {}
m.setAttribute('content', solid);
}
};

function initThemeManager() { // Small delay so PHPRunner DOM pieces are available setTimeout(function () { ThemeManager.init(); }, 200); }

if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initThemeManager); } else { initThemeManager(); }

window.ThemeManager = ThemeManager;

G
Grdimitris 9/5/2025

The body.dark-theme #dynamic_theme_switcher .theme-icon:before{
content:" is incomplete

P
ppradhan@live.com author 9/5/2025

I'd pasted full code but for some reason it truncated. Below is the missing code, but make sure to delete duplicates because I've copied little more so that you guys can figure out the missing part and have them compiled.

missing in above ..... (part of custom_css)

body:not(.dark-theme) #dynamic_theme_switcher{
background:linear-gradient(45deg,#fff,#f0f0f0) !important; border:2px solid rgba(255,255,255,.3) !important; box-shadow:0 2px 8px rgba(255,255,255,.2) !important;
}
body.dark-theme #dynamic_theme_switcher .theme-icon:before{
content:"

G
Grdimitris 9/5/2025

Still missing. Copy and some next lines to be in middle

fhumanes 9/5/2025

Hello @ppradhan@live.com,

When I tried to put code in the forum, if it was a bit extensive, it was impossible for me because when you enter the content it is not appreciated how it will be seen.

All I can offer is to load it on my website, as a link to a file or as a publication (what you consider best), so that there is no problem or loss of information in the code.

My blog is https://fhumanes.com/

Greetings,
fernando

P
ppradhan@live.com author 9/6/2025

Sorry guys, copying code here in the blog truncates and has issues. So, I hosted them in the server itself.
Simply click these links, copy and paste them at 'custom css' - style, and 'custom_functions.js' - event. You get new Navbarbar that is responsive and attractive.
You can customize as per your requirements.
Links:
custom_css: https://ppradhan.com.np/aaa/custom_css.css
custom_functions: https://ppradhan.com.np/aaa/custom_functions.js

S
salus2 9/6/2025

Cool! Here is an example...

https://calculationforms.com/ClassicModels/products_list.php

Thanks for sharing!