
Boris Nauta
Certified NVC trainer, Mindfulness coach, Equanimity
/*prevent FOUC on mobile when using sidebar */
.dwc-mobile :is(.bricks-is-frontend.brx-header-left, .bricks-is-frontend.brx-header-right) #brx-header {
position: relative;
inline-size: 100%;
flex-direction: column;
}
.dwc-mobile .bricks-is-frontend:is(.brx-header-left, .brx-header-right) :is(#brx-content, #brx-footer) {
margin-inline-start: 0;
}
/*prevent FOUC on desktop when using sidebar */
.bricks-is-frontend:is(.brx-header-left, .brx-header-right):not(.show-nav) .dwc-nav-wrapper {
display: none;
}
/*=== sidebar css ===*/
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header {
flex-direction: column;
box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.7);
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]).no-scroll {
overflow: visible;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu.brxe-nav-nested .brx-nav-nested-items {
max-block-size: 100dvb;
padding-block-end: 12rem;
overscroll-behavior: contain;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nav-wrapper {
position: relative;
overflow: hidden;
block-size: 100%;
transform: translateX(0%);
visibility: visible;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) .dwc-nest-menu .brxe-toggle {
display: none !important;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu {
display: flex;
flex-direction: column;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-header > div {
display: flex;
grid-template-columns: 1fr;
block-size: 100%;
flex-direction: column;
justify-content: flex-start;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-menu-wrap {
inline-size: 100%;
display: grid;
grid-template-columns: 1fr;
block-size: 100%;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header :is(.brxe-code, .dwc-nest-menu-overlay) {
display: none;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-header {
inline-size: 100%;
padding-inline: 0;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu-top {
min-block-size: var(--top-offset);
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu:not( [data-hide-close-bar = 'true']) .brx-dropdown-content {
inset-block-start: calc(var(--top-offset) + 1px) !important;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu:not( [data-submenu-reveal = 'slide']) .brx-dropdown-content:not([data-submenu-reveal = 'slide'] *) {
inset-inline-start: 0;
inset-block-start: unset !important;
overflow: hidden;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu:not([data-submenu-reveal = 'slide']) .brxe-dropdown:not(.open.active) > .brx-dropdown-content > .brxe-dropdown:not([data-submenu-reveal = 'slide'] *){
visibility: hidden;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu.brxe-nav-nested.brx-open .brxe-dropdown > .brx-dropdown-content {
overscroll-behavior: contain;
min-inline-size: var(--mobile-menu-width);
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu .brxe-dropdown.open > .brx-submenu-toggle button:not([data-submenu-reveal = 'expand'] button) {
min-block-size: calc(var(--top-offset) - 1px);
inset-block-start: 0;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu .brxe-dropdown.open[data-submenu-reveal = 'slide'] > .brx-submenu-toggle button {
min-block-size: calc(var(--top-offset) - 1px);
inset-block-start: 0;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu .brxe-dropdown .brx-submenu-toggle button {
min-block-size: 0;
}
/* sidebar css ends*/
/*sidebar in builder*/
/*=== sidebar css ===*/
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header {
flex-direction: column;
box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.7);
--top-offset: 40px;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu.brxe-nav-nested .brx-nav-nested-items {
max-block-size: 100dvb;
padding-block-end: 12rem;
overscroll-behavior: contain;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nav-wrapper {
position: relative;
overflow: hidden;
block-size: 100%;
transform: translateX(0%);
visibility: visible;
}
body:is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-menu .brxe-toggle {
display: none !important;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu {
display: flex;
flex-direction: column;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-header > div {
display: flex;
grid-template-columns: 1fr;
block-size: 100%;
flex-direction: column;
justify-content: flex-start;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-menu-wrap {
inline-size: 100%;
display: grid;
grid-template-columns: 1fr;
block-size: 100%;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-header {
inline-size: 100%;
padding-inline: 0;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu-top {
min-block-size: var(--top-offset);
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu .brx-dropdown-content {
inset-block-start: calc(var(--top-offset) - 1px) !important;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu.brxe-nav-nested.brx-open .brxe-dropdown > .brx-dropdown-content {
overscroll-behavior: contain;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu .brxe-dropdown.open > .brx-submenu-toggle button {
min-block-size: calc(var(--top-offset) - 1px);
inset-block-start: 0;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu .brxe-dropdown .brx-submenu-toggle button {
min-block-size: 0;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-menu {
margin: 0 !important;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] .brx-nav-nested-items {
flex-direction: column !important;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-toggle--open.brxe-toggle {
display: flex !important;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-menu .brx-nav-nested-items {
position: relative !Important;
background: var(--mobile-menu-bg) !important;
align-items: stretch;
flex: 1;
}
:where(.brx-header-left, .brx-header-right)[data-builder-mode]:not(:has(.dwc-sidebar)) .dwc-nest-menu-top::before {
content: 'this space is the back text bar';
padding: 1rem;
background-color: gray;
color: white;
width: 100%;
text-transform: uppercase;
font-size: 12px;
font-weight: bold;
letter-spacing: 1px;
}
/* MENU CTA (LAST BUTTON) */
:is(.brx-header-left, .brx-header-right)[data-builder-window] [data-last-item-is-button="true"].dwc-nest-menu .brx-nav-nested-items > .menu-item:last-of-type {
padding-inline: var(--menu-item-inline-padding) !important;
padding-block: var(--menu-item-block-padding) !important;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-menu-top {
min-block-size: 80px !important;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-nav-items {
overflow-y: scroll;
}
:is(.brx-header-left, .brx-header-right)[data-builder-mode] .brx-dropdown-content {
min-inline-size: var(--mobile-menu-width);
position: static;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu.brxe-nav-nested .brx-nav-nested-items {
flex-wrap: nowrap;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu .brx-dropdown-content {
visibility: visible !important;
opacity: 1;
}
/*OVERLAY SIDEBAR*/
html:not(.dwc-mobile):has([data-overlay-sidebar=true]) {
--mobile-menu-bg: rgb(255 255 255 / 0%);
--menu-item-border: solid 1px rgb(255 255 255 / 50%);
}
html:not(.dwc-mobile):has([data-overlay-sidebar=true]) :is(.brx-header-left, .brx-header-right):not([data-builder-modee]) :is(main, footer){
margin: 0 !important
}
html:not(.dwc-mobile):has([data-overlay-sidebar=true]) :is(.brx-header-left, .brx-header-right):not([data-builder-modee]) :is(main, footer) :where(section):not(section>section) {
padding-inline-start: calc(var(--mobile-menu-width) + clamp(1.5rem, calc(0.625vw + 1.375rem), 1.875rem));
max-inline-size: 100%
}
html:not(.dwc-mobile):has([data-overlay-sidebar=true]) :is(.brx-header-left, .brx-header-right):not([data-builder-modee]) #brx-header {
border-radius: var(--overlay-sidebar-radius);
overflow: hidden;
background: var(--overlay-sidebar-bg);
box-shadow: var(--overlay-sidebar-shadow) !important;
inset: var(--overlay-sidebar-inset);
}
html:not(.dwc-mobile):has([data-overlay-sidebar=true]) :is(.brx-header-left, .brx-header-right):not([data-builder-modee]) .dwc-nest-header{
backdrop-filter: blur(13px);
background: transparent !important;
}
html:not(.dwc-mobile):has([data-overlay-sidebar=true]):not([data-builder-modee]) .brx-dropdown-content {
background-color: rgb(255 255 255 / 100%);
}
/*NO BRX-OPEN STYLES*/
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu.brxe-nav-nested .brx-nav-nested-items {
display: flex;
flex-wrap: nowrap;
flex-direction: column !important;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) .dwc-nest-menu.brxe-nav-nested .brxe-dropdown .brx-dropdown-content {
visibility: visible;
min-inline-size: var(--mobile-menu-width) !important;
}
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) .dwc-nest-menu[data-submenu-reveal="expand"] .brxe-dropdown.open>.brx-dropdown-content {
position: static;
}
class SidebarNavigation {
constructor(options = {}) {
// Basic configuration properties
this.config = {
minWidth: options.minWidth || MegaMenuCONFIG.minWidth, // Using external minWidth variable
menuSelector: options.menuSelector || '.dwc-nest-menu',
openClass: options.openClass || 'brx-open',
activeClasses: options.activeClasses || ['open', 'active'],
leftHeaderClass: options.leftHeaderClass || 'brx-header-left',
rightHeaderClass: options.rightHeaderClass || 'brx-header-right',
debounceDelay: options.debounceDelay || 100,
menuItemClickDelay: options.menuItemClickDelay || 300
};
// Set dependent selectors
const menuSelector = this.config.menuSelector;
this.config.submenuToggleSelector = options.submenuToggleSelector || `${menuSelector} .brx-submenu-toggle`;
this.config.dropdownSelector = options.dropdownSelector || `${menuSelector} .brxe-dropdown`;
this.config.dropdownContentSelector = options.dropdownContentSelector || `${menuSelector} .brx-dropdown-content`;
// State
this.previousHeaderClass = null;
this.dropdownClickHandlers = new Map();
this.menuHoverHandlers = null;
this.menuItemClickTimeout = null;
this.keyboardNavHandler = null;
this.cachedFocusableElements = null;
this.cachedElements = {
menuElement: null,
navElement: null,
dropdowns: null,
dropdownToggles: null,
menuItems: null
};
// Bind methods to this instance
this.handleResize = this.debounce(this.handleMenu.bind(this), this.config.debounceDelay);
this.handleOutsideClick = this.handleOutsideClick.bind(this);
}
// Initialize everything - called once
init() {
// Wait for DOM to be fully loaded before attaching events
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
this.initAfterDOMLoaded();
}, { once: true });
} else {
this.initAfterDOMLoaded();
}
return this;
}
// Separate initialization method to run after DOM is loaded
initAfterDOMLoaded() {
// Cache DOM elements once
this.cacheElements();
// Setup resize event with passive flag
window.addEventListener('resize', this.handleResize, { passive: true });
// Setup mutation observer for critical class changes only
this.setupMutationObserver();
// Initial setup based on current screen size
this.handleMenu();
// Cache focusable elements once if header class is present
if (this.hasHeaderClass()) {
this.cacheFocusableElements();
this.setupMenuFocusNavigation();
}
}
// Cache all required DOM elements upfront
cacheElements() {
this.cachedElements.menuElement = document.querySelector(this.config.menuSelector);
if (this.cachedElements.menuElement) {
this.cachedElements.navElement = this.cachedElements.menuElement.querySelector('.dwc-nest-nav-items');
this.cachedElements.dropdowns = Array.from(document.querySelectorAll(this.config.dropdownSelector));
this.cachedElements.dropdownToggles = Array.from(document.querySelectorAll(this.config.submenuToggleSelector));
this.cachedElements.menuItems = Array.from(document.querySelectorAll(`${this.config.menuSelector} .menu-item`));
}
}
// Set up a focused mutation observer only for dropdown state changes
setupMutationObserver() {
if (!this.cachedElements.dropdowns || this.cachedElements.dropdowns.length === 0) return;
const callback = (mutations) => {
for (let mutation of mutations) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
const target = mutation.target;
const prevClassList = mutation.oldValue ? mutation.oldValue.split(' ') : [];
const hadBothBefore = prevClassList.includes('open') && prevClassList.includes('active');
const hasBothNow = target.classList.contains('open') && target.classList.contains('active');
if (hadBothBefore !== hasBothNow) {
this.updateDropdownAccessibility();
break; // Only need to update once per batch
}
}
}
};
// Create observer with optimized options
this.classObserver = new MutationObserver(callback);
// Observe only the dropdown elements
this.cachedElements.dropdowns.forEach(dropdown => {
this.classObserver.observe(dropdown, {
attributes: true,
attributeFilter: ['class'],
attributeOldValue: true
});
});
}
// Cache focusable elements for keyboard navigation
cacheFocusableElements() {
if (!this.cachedElements.navElement) return;
// Get direct children of nav
const directChildren = Array.from(this.cachedElements.navElement.children);
// Find the first focusable element within each direct child
this.cachedFocusableElements = directChildren.map(child => {
// Check if the child itself is focusable
if (child.matches('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])')) {
return child;
}
// Otherwise, find the first focusable element within this child
return child.querySelector('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
}).filter(Boolean); // Remove null/undefined values
}
// Clean up all event listeners and observers
destroy() {
// Clean up the mutation observer
if (this.classObserver) {
this.classObserver.disconnect();
this.classObserver = null;
}
// Clean up resize listener
window.removeEventListener('resize', this.handleResize);
// Clean up click handlers
if (this.dropdownClickHandlers.size > 0) {
this.dropdownClickHandlers.forEach((handler, toggle) => {
toggle.removeEventListener('click', handler);
});
this.dropdownClickHandlers.clear();
}
// Clean up hover handlers
this.cleanupMenuHover();
// Clean up menu item click handlers
this.cleanupMenuItemClicks();
// Clean up outside click handler
document.removeEventListener('click', this.handleOutsideClick);
// Clean up keyboard navigation
if (this.keyboardNavHandler) {
document.removeEventListener('keydown', this.keyboardNavHandler);
this.keyboardNavHandler = null;
}
// Clear any pending timeouts
if (this.menuItemClickTimeout) {
clearTimeout(this.menuItemClickTimeout);
this.menuItemClickTimeout = null;
}
}
// Utility methods
hasHeaderClass() {
return document.body.classList.contains(this.config.leftHeaderClass) ||
document.body.classList.contains(this.config.rightHeaderClass);
}
debounce(func, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => func(...args), delay);
};
}
// Check if an element has all the required active classes
hasAllActiveClasses(element) {
return this.config.activeClasses.every(className => element.classList.contains(className));
}
// Toggle all active classes on an element
toggleActiveClasses(element) {
this.config.activeClasses.forEach(className => {
element.classList.toggle(className);
});
}
// Core functionality methods
handleMenu() {
if (!this.cachedElements.menuElement) return;
if (!this.hasHeaderClass() && !this.previousHeaderClass) return;
const screenWidth = window.innerWidth;
const isLargeScreen = screenWidth >= this.config.minWidth;
const menuElement = this.cachedElements.menuElement;
if (!isLargeScreen) {
// Save which class was present before removal
if (this.hasHeaderClass()) {
this.previousHeaderClass = document.body.classList.contains(this.config.leftHeaderClass)
? this.config.leftHeaderClass
: this.config.rightHeaderClass;
// Remove header classes
document.body.classList.remove(this.config.leftHeaderClass, this.config.rightHeaderClass);
menuElement.classList.remove(this.config.openClass);
// Reset accessibility attributes
this.resetAccessibilityAttributes();
}
// Clean up event handlers for mobile view
this.cleanupMenuHover();
this.cleanupMenuItemClicks();
this.cleanupDropdownHandlers();
document.removeEventListener('click', this.handleOutsideClick);
return;
}
// Large screen behavior
if (!this.hasHeaderClass() && this.previousHeaderClass) {
document.body.classList.add(this.previousHeaderClass);
}
if (this.hasHeaderClass()) {
if (!menuElement.classList.contains(this.config.openClass)) {
menuElement.classList.add(this.config.openClass);
}
// Setup elements for large screen view
this.setupMenuHover();
this.setupMenuItemClicks();
this.setupDropdownHandlers();
this.setupMenuFocusNavigation();
this.updateDropdownAccessibility();
// Ensure outside click handler is set up
document.removeEventListener('click', this.handleOutsideClick);
document.addEventListener('click', this.handleOutsideClick, { passive: false });
}
}
// Reset accessibility attributes when switching to mobile
resetAccessibilityAttributes() {
if (!this.cachedElements.dropdowns) return;
// Remove all inert attributes from dropdown contents
this.cachedElements.dropdowns.forEach(dropdown => {
const content = dropdown.querySelector(this.config.dropdownContentSelector);
if (content) {
content.removeAttribute('inert');
}
const button = dropdown.querySelector('button');
if (button) {
button.setAttribute('aria-expanded', 'false');
}
});
}
setupMenuFocusNavigation() {
// Only run if hasHeaderClass() is true and we have focusable elements
if (!this.hasHeaderClass() || !this.cachedFocusableElements || this.cachedFocusableElements.length === 0) {
return;
}
// Clean up previous handler if it exists
if (this.keyboardNavHandler) {
document.removeEventListener('keydown', this.keyboardNavHandler, true);
this.keyboardNavHandler = null;
}
const navMenu = this.cachedElements.menuElement;
const focusableElements = this.cachedFocusableElements;
const firstFocusableElement = focusableElements[0];
const lastFocusableElement = focusableElements[focusableElements.length - 1];
// Find adjacent focusable elements outside the menu (only once during setup)
const headerElement = navMenu.closest('header') || document.querySelector('header');
// Prepare variables to hold adjacent elements
let prevFocusableElement = null;
let nextFocusableElement = null;
let firstElementAfterHeader = null;
if (headerElement) {
// Get all focusable elements within the header - do this once and cache the result
const headerFocusables = Array.from(
headerElement.querySelectorAll('a:not([tabindex="-1"]), button:not([tabindex="-1"]), input:not([tabindex="-1"]), select:not([tabindex="-1"]), textarea:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])')
).filter(el => window.getComputedStyle(el).display !== 'none');
// Find the index of our first and last menu elements in one pass
const menuStartIndex = headerFocusables.indexOf(firstFocusableElement);
const menuEndIndex = headerFocusables.indexOf(lastFocusableElement);
// Cache the adjacent elements
if (menuStartIndex > 0) {
prevFocusableElement = headerFocusables[menuStartIndex - 1];
}
if (menuEndIndex !== -1 && menuEndIndex < headerFocusables.length - 1) {
nextFocusableElement = headerFocusables[menuEndIndex + 1];
}
// Pre-calculate the first element after header - but only if needed
if (!nextFocusableElement) {
// Use a more efficient selector that targets immediate children of body that aren't the header
const selector = 'body > *:not(header)';
const nonHeaderElements = document.querySelectorAll(selector);
// Only process if we have elements
if (nonHeaderElements.length > 0) {
// Create a function to find the first focusable element (used later if needed)
this.findFirstFocusableAfterHeader = () => {
for (const element of nonHeaderElements) {
const focusable = element.querySelector('a:not([tabindex="-1"]), button:not([tabindex="-1"]), input:not([tabindex="-1"]), select:not([tabindex="-1"]), textarea:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])');
if (focusable && window.getComputedStyle(focusable).display !== 'none') {
return focusable;
}
}
return null;
};
}
}
}
// Create keyboard navigation handler with closure over the cached elements
this.keyboardNavHandler = (e) => {
// Quick check for Tab key first
if (e.key !== 'Tab') return;
// Then check if focus is inside the menu
if (!navMenu.contains(document.activeElement)) return;
let targetElement = null;
// Handle tab navigation at boundaries only
if (!e.shiftKey && document.activeElement === lastFocusableElement) {
// Forward tab from last element
e.preventDefault();
e.stopPropagation();
if (nextFocusableElement) {
targetElement = nextFocusableElement;
} else if (this.findFirstFocusableAfterHeader) {
// Only search for elements after header if needed and not already found
firstElementAfterHeader = this.findFirstFocusableAfterHeader();
targetElement = firstElementAfterHeader;
}
// Focus on the target or body as fallback
setTimeout(() => {
if (targetElement) {
targetElement.focus();
} else {
document.body.setAttribute('tabindex', '-1');
document.body.focus();
document.body.removeAttribute('tabindex');
}
}, 10);
}
else if (e.shiftKey && document.activeElement === firstFocusableElement) {
// Backward tab from first element
e.preventDefault();
e.stopPropagation();
setTimeout(() => {
if (prevFocusableElement) {
prevFocusableElement.focus();
} else {
document.body.setAttribute('tabindex', '-1');
document.body.focus();
document.body.removeAttribute('tabindex');
}
}, 10);
}
};
// Use capture phase for the event
document.addEventListener('keydown', this.keyboardNavHandler, true);
}
setupMenuHover() {
const menuElement = this.cachedElements.menuElement;
if (!menuElement) return;
// Clean up existing hover handlers first
this.cleanupMenuHover();
// Create event handlers
const mouseenterHandler = () => {
menuElement.classList.add(this.config.openClass);
};
const mouseleaveHandler = () => {
menuElement.classList.remove(this.config.openClass);
};
// Add event listeners with passive flag for better performance
menuElement.addEventListener('mouseenter', mouseenterHandler, { passive: true });
menuElement.addEventListener('mouseleave', mouseleaveHandler, { passive: true });
// Store the handlers for cleanup
this.menuHoverHandlers = {
element: menuElement,
mouseenter: mouseenterHandler,
mouseleave: mouseleaveHandler
};
}
cleanupMenuHover() {
if (this.menuHoverHandlers) {
const { element, mouseenter, mouseleave } = this.menuHoverHandlers;
element.removeEventListener('mouseenter', mouseenter);
element.removeEventListener('mouseleave', mouseleave);
this.menuHoverHandlers = null;
}
}
setupMenuItemClicks() {
if (!this.cachedElements.menuItems || this.cachedElements.menuItems.length === 0) return;
// Clean up existing handlers first
this.cleanupMenuItemClicks();
const menuElement = this.cachedElements.menuElement;
const menuItemHandlers = new Map();
this.cachedElements.menuItems.forEach(item => {
const clickHandler = () => {
if (this.hasHeaderClass()) {
// Clear any existing timeout
if (this.menuItemClickTimeout) {
clearTimeout(this.menuItemClickTimeout);
}
// Set timeout before adding the class
this.menuItemClickTimeout = setTimeout(() => {
if (!menuElement.classList.contains(this.config.openClass)) {
menuElement.classList.add(this.config.openClass);
}
}, this.config.menuItemClickDelay);
}
};
menuItemHandlers.set(item, clickHandler);
item.addEventListener('click', clickHandler);
});
this.menuItemClickHandlers = menuItemHandlers;
}
cleanupMenuItemClicks() {
if (this.menuItemClickHandlers && this.menuItemClickHandlers instanceof Map) {
this.menuItemClickHandlers.forEach((handler, item) => {
item.removeEventListener('click', handler);
});
this.menuItemClickHandlers.clear();
}
if (this.menuItemClickTimeout) {
clearTimeout(this.menuItemClickTimeout);
this.menuItemClickTimeout = null;
}
}
setupDropdownHandlers() {
if (!this.hasHeaderClass() || !this.cachedElements.dropdownToggles) return;
// Clean up existing handlers first
this.cleanupDropdownHandlers();
this.cachedElements.dropdownToggles.forEach(toggle => {
const clickHandler = (event) => {
event.stopPropagation();
event.preventDefault();
const dropdown = toggle.closest(this.config.dropdownSelector);
if (dropdown) {
this.toggleActiveClasses(dropdown);
this.updateDropdownAccessibility();
}
};
this.dropdownClickHandlers.set(toggle, clickHandler);
toggle.addEventListener('click', clickHandler);
});
}
cleanupDropdownHandlers() {
if (this.dropdownClickHandlers.size > 0) {
this.dropdownClickHandlers.forEach((handler, toggle) => {
toggle.removeEventListener('click', handler);
});
this.dropdownClickHandlers.clear();
}
}
handleOutsideClick(event) {
if (event.target.tagName === 'A') return;
if (!event.target.closest(this.config.dropdownSelector)) return;
if (!event.target.closest(this.config.submenuToggleSelector)) {
event.preventDefault();
event.stopPropagation();
}
}
updateDropdownAccessibility() {
// Only run if hasHeaderClass() is true
if (!this.hasHeaderClass() || !this.cachedElements.dropdowns) return;
this.cachedElements.dropdowns.forEach(dropdown => {
const content = dropdown.querySelector(this.config.dropdownContentSelector);
const button = dropdown.querySelector('button');
// Check if dropdown has all active classes
const isOpen = this.hasAllActiveClasses(dropdown);
if (content) {
if (isOpen) {
content.removeAttribute('inert');
} else {
content.setAttribute('inert', '');
}
}
if (button) {
button.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
}
});
}
}
const sidebarNav = new SidebarNavigation().init();
In de trainingen van Equanimity kom je trainers met uiteenlopende professionele achtergronden tegen, zoals de creatieve sector, de medische wereld, het onderwijs en de jeugdzorg. Afhankelijk van de trainingsvraag wordt een passend team samengesteld met relevante sector ervaring, wat zorgt voor herkenning en erkenning bij deelnemers van onze trainingen.
Alle trainers zijn professionals uit het nationale en internationale netwerk van Nonviolent communication trainers, en hebben daarnaast aanvullende ervaring met ondermeer Systemisch (team) coachen, Transactionele Analyse, Deep democracy, DISC en Mindfulness. Zo kunnen we maatwerk leveren voor iedere opdrachtgever.
Dit zijn de Verbindende communicatie / Geweldloze communicatie trainers die je het meest zult zien in de cursussen en workshops van Equanimity (klik voor meer info):

Certified NVC trainer, Mindfulness coach, Equanimity

Boris Nauta is de oprichter van trainingsbureau Equanimity en geeft sinds 2019 trainingen Verbindende / Geweldloze communicatie en workshops op het gebied van Emotionele intelligentie aan particulieren, commerciële instellingen en organisaties in de creatieve sector, de podiumkunsten en de zorg.
Daarvoor werkt hij ruim 20 jaar als interim Creative producer en Product owner voor ondermeer Heineken International, Philips Design, CLUSE, Polestar en verschillende reclame en design bureau’s. Hij was tevens betrokken bij de start van THNK School of Creative Leadership.
Boris studeerde Communicatiewetenschappen in Nijmegen en volgde meerdere jaargangen (trainers)opleiding op het gebied van Verbindende / Geweldloze communicatie. Sinds 2024 is hij gecertificeerd als International Nonviolent communication trainer door het CNVC.
Verder volgde hij de opleiding tot Mindfulness en compassie coach bij de Academie voor Leven, en een post-academische opleiding MBSR aan het Centrum voor Mindfulness van het Radboud UMC Nijmegen. Tenslotte is hij gecertificeerd DISC trainer.
Wil je nog wat meer weten over Boris, lees dan verder op zijn profielpagina.

Certified NVC trainer, docent en coach, EmpaTilya

Suzanne Nederlof heeft een achtergrond in internationale samenwerking. Ze heeft een MsC in rurale ontwikkelingssociologie en een PhD in communicatie en innovatie van de Universiteit Wageningen. Ze deed onderzoek naar het verbeteren van de omstandigheden van kleine boeren in West-Afrika en woonde en werkte ruim tien jaar in Burkina Faso, Ghana en Togo. Daarna droeg ze vanuit Nederland in met name Afrika bij aan innovatie en “multi-stakeholder processen”.
Suzanne Nederlof is internationaal gecertificeerd trainer Nonviolent communication en kandidaat assessor van het CNVC en eigenaar van trainingsbureau EmpaTilya. Daarnaast werkt Suzanne als senior lecturer voor Van Hall Larenstein University of applied sciences.
Suzanne en Boris geven samen een Jaartraining en diverse Thematische verdiepingstrainingen op het gebied van Verbindende communicatie, die zowel in Amsterdam als in Wageningen gevolgd kunnen worden. Tevens trekken Suzanne en Boris graag samen op bij maatwerk trajecten voor teams en organisaties in een internationale, multiculturele setting.

Pedagoog, NVC trainer & ouderschapscoach, Equanimity

Mae Heffelaar werkte ruim 15 jaar als Pedagoog in de jeugdzorg en gaf veel opvoedkundige trainingen en persoonlijke begeleiding aan ouders met verschillende vraagstukken.
Tegenwoordig werkt Mae als zelfstandig coach en trainer en faciliteert zij workshops in effectieve communicatie vanuit het gedachtegoed van Geweldloze Communicatie. Tevens ondersteunt ze ouders in individuele vraagstukken over opvoeding.
Mae heeft een sterke behoefte zich in te zetten voor de manier waarop kinderen worden opgevoed. Wat werkt hierin zowel voor het kind als de ouder? Hoe kun je als ouder dicht bij jezelf blijven, je uiten op een manier die bij jou en je kind past?
Mae en Boris houden samen ‘kantoor’ op Fort Nigtevecht waar zij ook de maandelijkse Verbindende / Geweldloze communicatie oefengroep ‘Het Verbindingsfort’ faciliteren. Daarnaast geven zij de Verdiepingstraining Ouderschap in Verbinding en werken ze samen in maatwerk trajecten voor teams en organisaties.

Team & Organisational coach, Process Facilitator, Equanimity

Met een achtergrond in de creatieve sector en jarenlange ervaring in internationale projecten en producties, heeft Maren als project- en operations manager uiteenlopende teams en organisaties begeleid. Haar passie voor verbindend werken en fascinatie voor team dynamieken leidde tot een opleiding als ORSC teamcoach (Organisational Relationship Systems Coaching). Tegenwoordig werkt Maren als team- en organisational coach én operations consultant. Met een systemische blik en vanuit de principes van Geweldloze Communicatie helpt ze leiders en teams een veilige, beweeglijke en inclusieve werkcultuur te creëren: een basis voor innovatieve en vernieuwende samenwerkingen.
Samen met Boris biedt Maren workshops en trajecten aan voor teams en organisaties die op basis van verbindende communicatie en systemic team coaching hun samenwerking willen verbeteren.

Gecertificeerd coach en trainer, Vanuit bewustzijn in beweging

Elles Bindels werkte als arts in de Psychiatrie, de JGZ en de revalidatiesector. Daarnaast was ze docent binnen het medisch curriculum. Al vanaf haar coschappen interesseerde Elles zich ook voor niet-medische competenties. Ze volgde opleidingen in diverse communicatie-, gedrags-, en persoonlijkheidsmodellen. Ze gelooft in de kracht van reflectie en zelfzorg van zorgprofessionals. Centraal staat voor haar daarbij de vraag: “Hoe kan ik de dokter/zorgprofessional zijn die ik altijd voor ogen had, op een manier die zinvol en houdbaar blijft voelen?”
Vanuit haar praktijk Vanuit Bewustzijn in beweging coacht Elles medisch professionals en ontwikkelt zij trainingen op het gebied van Verbindende communicatie, het vergroten van mentale flexibiliteit en het omgaan met impactvolle gebeurtenissen in de medische sector.
Elles en Boris werken samen op het gebied van trainingen in de zorg, voor artsen en medisch specialisten zowel voor Open Inschrijving als In Company binnen organisaties.

Certified NVC trainer, docent en coach, Een Goed Gesprek

Annette Lubbers studeerde Franse taal- en letterkunde en Culturele Studies aan de Universiteit van Amsterdam, en werkte als persvoorlichter en woordvoerder op het ministerie van Onderwijs, Cultuur en Wetenschap. Daarna werkte ze als interim-communicatieadviseur voor diverse overheids-, zorg- en culturele organisaties en schreef ze twee boeken: over de geschiedenis van het Lloyd Hotel in Amsterdam en over 40 jaar Hoger Onderwijs in Nederland.
Sinds 2017 heeft Annette haar eigen trainingsbureau Een Goed Gesprek en is ze actief als docent aan de faculteit Communicatie van de Hogeschool Utrecht. Ze is gecertificeerd als International Nonviolent communication trainer door het CNVC.
Annette en Boris geven samen de maandelijkse Verdiepingsdag Verbindende / Geweldloze communicatie van Equanimity en werken graag samen bij maatwerk trainingen voor organisaties in de zorg, het onderwijs, de creatieve sector en de overheid.
Wil je op de hoogte wilt blijven van nieuwe trainingsdata en blogposts? Je ontvangt een paar keer per jaar een mail en je kunt je te allen tijde eenvoudig afmelden.






