Satisfaction overview
(68 verified reviews)
Trusted by homeowners, strata communities, and businesses.
Impeccable safety record
/*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();Richmond construction services
Serving Richmond with full-scope construction expertise, VALROC delivers residential, strata, and commercial projects designed to perform in high-water conditions, aging infrastructure, and modern urban environments.
Customer confidence
Richmond construction planning benefits from close attention to low-lying terrain, groundwater, drainage, grading, occupied properties, and active access routes.
(68 verified reviews)
Trusted by homeowners, strata communities, and businesses.
Impeccable safety record
Richmond sites can place unusual importance on drainage, grading, groundwater, subgrade preparation, and how work is phased around active properties. Those conditions are reviewed before the construction sequence is set.
Customers gain a coordinated approach that connects site preparation, water management, equipment, materials, access, and finished construction.
Local construction planning
Construction planning in Richmond starts with the property, the municipal jurisdiction, and the practical conditions that affect approvals, access, sequencing, equipment, and material movement.
CONSTRUCTION SERVICE DIRECTORY
Open the complete construction service hub, choose a service category, or go directly to a core service.
Decisions before construction
A site conversation can identify alternatives before costs are committed, particularly where drainage, traffic flow, occupied strata or commercial areas, deliveries, material handling, and safe public access affect the plan.
Review drainage, traffic flow, occupied strata or commercial areas, deliveries, material handling, and safe public access in relation to the result you want and the equipment or material movement the work may require.
Identify likely City building, development, and zoning requirements, utility-locate responses, disposal requirements, and property-specific questions before finalizing the sequence.
Coordinate crews, deliveries, occupied areas, hauling, and site protection so the construction sequence reflects how this Richmond property is actually used.
A practical route forward
The exact sequence depends on the work and property. Early review of City of Richmond requirements, access, disposal, equipment, and customer priorities helps turn an idea into a coordinated plan.
Identify likely City of Richmond permits, utility locates, disposal needs, technical details, and site-specific approvals before finalizing the scope.
Plan crews, equipment, materials, hauling, water management, traffic, and occupied areas in a sequence suited to the property.
Complete the agreed work, review drainage, grades, structural or surface details, and provide a clear customer handoff.
Discuss the result, drainage, grades, groundwater considerations, access, occupants, existing services, and the space available for work.
What customers gain from the way we work
These advantages help customers evaluate more than a price, including project clarity, relevant experience, site coordination, in-house capability, and the way disruption will be managed.
We respect your time and space, ensuring efficient construction with minimal disruptions to your home or business.
Our contracts eliminate guesswork with clearly defined terms, pricing, timelines, and warranties, ensuring total transparency and peace of mind.
With a strong portfolio of completed work, our experience ensures high-quality craftsmanship and efficient project execution.
Our extensive fleet of heavy-duty machinery, mobile service trucks, and specialized tools ensures unmatched efficiency and precision on every job site.
In-house capability
Our Langley yard provides a base for selecting and staging equipment for Richmond projects around access, excavation depth, hauling, material movement, compaction, and the actual scope of work.

Excavation fleet
Our in-house range includes compact machines for tight access, mid-size excavators for precise residential work, larger Deere excavators for deeper excavation and material handling, plus hydraulic breaker capability for concrete and rock removal.

Heavy Machinery & Earthmoving Equipment
Delivers smooth, even asphalt and soil compaction, improving surface durability and road longevity.

Mobile Construction Equipped Service Vehicles
Facilitates equipment and material transport, allowing us to move multiple machines and long materials efficiently.

Heavy Machinery & Earthmoving Equipment
A compact, maneuverable track dumper that allows us to transport materials through tight spaces, improving site logistics and reducing manual labour.

Heavy Machinery & Earthmoving Equipment
A high-capacity hauler for transporting materials, debris, and equipment, streamlining job site cleanup and logistics.

Mobile Construction Equipped Service Vehicles
Our fully equipped mobile service truck, essential for transporting contracting tools, materials, and heavy-duty equipment, ensuring on-site readiness and maximum efficiency.

Heavy Machinery & Earthmoving Equipment
A compact excavator with strong digging power, perfect for tight job sites requiring precision excavation.
High-value planning information
Verified municipal, utility, recycling, and disposal contacts for construction decisions in Richmond. Use each section for a focused task, then confirm current requirements with the authority before acting.

Check Richmond building requirements, Review building approval, permit, and inspection information.
Contact: 604-276-4118
Before you proceed: Confirm permit and document requirements for the scope. Early scope confirmation can reduce revisions.
Check Richmond zoning requirements, Review zoning and development information.
Contact: zoning@richmond.ca · 604-276-4017
Before you proceed: Site-specific conditions may apply. Confirm zoning implications before finalizing the work plan.
Request a free utility locate, Request utility-owner information before digging or excavation.
Contact: info@bc1c.ca · 1-800-474-6886
Before you proceed: A locate request does not identify every privately owned line; review the response package before work starts. Allow time for members to respond and keep the ticket information available on site.
Find a facility for a specific material, Search for facilities that accept a specific construction, renovation, or landscaping material.
Contact: 604-732-9253
Before you proceed: Confirm the facility accepts the material type and load source before travelling. Use this directory when the material is not accepted at the nearest municipal or regional facility.
Check Richmond Recycling Depot materials, Check whether the depot accepts a separated project-related recyclable.
Contact: 604-276-4010
Before you proceed: Construction and commercial load restrictions may apply. Use Metro Vancouver Recycles for materials the depot does not accept.
Review asbestos requirements before disturbing materials, Understand asbestos identification, qualified-person, and safe-work requirements before renovation or demolition.
Before you proceed: Suspect materials must be assessed before they are disturbed; disposal facilities may require documentation. Older buildings can contain asbestos in products that are not obvious from appearance alone.
Check provincial hazardous-waste requirements, Find provincial guidance for hazardous waste that cannot enter ordinary construction-waste streams.
Before you proceed: Use an authorized pathway; facility rules and documentation vary by material. Confirm classification and receiving requirements before material is transported.
Common local questions
Open only the answer you need. These questions remain connected through the existing Location relationship.
Most construction projects in Richmond require a permit to ensure compliance with local zoning laws and building regulations. Projects such as new builds, home additions, retaining walls, and structural modifications typically require approval from the Building Approvals Department.
However, minor projects, such as interior renovations that don’t involve structural changes, may not require a permit. The Board of Variance can grant certain bylaw relaxations if strict compliance causes undue hardship.
Navigating the permit process can be complex, and errors can lead to costly delays. VALROC Development Ltd. can guide you through the requirements, determine whether your project needs a permit, and handle the application process on your behalf.
For more details, visit the City of Richmond Permits & Licences Page.
Most construction projects in Richmond require a permit to ensure compliance with local zoning laws and building regulations. Projects such as new builds, home additions, retaining walls, and structural modifications typically require approval from the Building Approvals Department.
However, minor projects, such as interior renovations that don’t involve structural changes, may not require a permit. The Board of Variance can grant certain bylaw relaxations if strict compliance causes undue hardship.
Navigating the permit process can be complex, and errors can lead to costly delays. VALROC Development Ltd. can guide you through the requirements, determine whether your project needs a permit, and handle the application process on your behalf.
For more details, visit the City of Richmond Permits & Licences Page.
Building permits in Richmond are managed by the Building Approvals Department, located at 6911 No. 3 Road, Richmond, BC V6Y 2C1. To apply, you will need:
• A completed application form
• Detailed site plans, architectural drawings, and engineering documents
• Zoning compliance and bylaw approvals
• Permit fees based on project size and complexity
Permit applications are reviewed to ensure they meet the British Columbia Building Code, National Building Standards, and Richmond’s Zoning & Tree Protection Bylaws.
If your project is complex, VALROC Development Ltd. can assist with permit applications, ensuring all required documents are submitted correctly to avoid unnecessary delays. For official information, contact Building Approvals at 604-276-4000 or building@richmond.ca.
Building permits in Richmond are managed by the Building Approvals Department, located at 6911 No. 3 Road, Richmond, BC V6Y 2C1. To apply, you will need:
• A completed application form
• Detailed site plans, architectural drawings, and engineering documents
• Zoning compliance and bylaw approvals
• Permit fees based on project size and complexity
Permit applications are reviewed to ensure they meet the British Columbia Building Code, National Building Standards, and Richmond’s Zoning & Tree Protection Bylaws.
If your project is complex, VALROC Development Ltd. can assist with permit applications, ensuring all required documents are submitted correctly to avoid unnecessary delays. For official information, contact Building Approvals at 604-276-4000 or building@richmond.ca.
The Board of Variance (BOV) is an independent panel that reviews applications for minor zoning and bylaw relaxations in cases where full compliance would cause undue hardship.
You may need to apply to the Board of Variance if your project involves:
• Building setbacks that conflict with property lines
• Height restrictions impacting your build
• Other zoning constraints that prevent reasonable property use
The Board does not grant variances for increased density or land use changesthose require a rezoning application. If you need an exemption, VALROC Development Ltd. can help prepare your application and present it to the Board.
For inquiries, contact the Board of Variance at 604-276-4007 or cityclerk@richmond.ca.
The Board of Variance (BOV) is an independent panel that reviews applications for minor zoning and bylaw relaxations in cases where full compliance would cause undue hardship.
You may need to apply to the Board of Variance if your project involves:
• Building setbacks that conflict with property lines
• Height restrictions impacting your build
• Other zoning constraints that prevent reasonable property use
The Board does not grant variances for increased density or land use changesthose require a rezoning application. If you need an exemption, VALROC Development Ltd. can help prepare your application and present it to the Board.
For inquiries, contact the Board of Variance at 604-276-4007 or cityclerk@richmond.ca.
Richmond’s growing residential and commercial developments have provided opportunities for a wide range of construction, renovation, and site development projects. Some of our completed work includes:
Custom Home Builds & Large-Scale Renovations
We specialize in custom home construction, heritage renovations, and high-end remodeling for Richmond homeowners.
Concrete Driveways, Sidewalks & Structural Pads
Our concrete team has installed durable driveways, walkways, and structural pads for residential and commercial properties, ensuring compliance with Richmond’s engineering requirements.
Commercial & Industrial Developments
We have worked with businesses and developers to build commercial storefronts, warehouse expansions, and industrial foundations.
Asphalt Paving for Parking Lots & Roadways
From private driveways to commercial parking lots, our asphalt paving services provide long-lasting, high-quality surfaces designed to withstand Richmond’s climate.
Excavation & Site Preparation
We specialize in grading, excavation, and land clearing for new developments, ensuring that sites are properly prepared for foundation work.
Drainage & Retaining Wall Construction
Given Richmond’s coastal location and high water table, proper drainage solutions are critical. Our team has designed and installed retaining walls, perimeter drainage, and erosion control systems for homeowners and businesses.
If you’re planning a construction project in Richmond, contact VALROC Development Ltd. for expert guidance and on-site consultation.
Richmond’s growing residential and commercial developments have provided opportunities for a wide range of construction, renovation, and site development projects. Some of our completed work includes:
Custom Home Builds & Large-Scale Renovations
We specialize in custom home construction, heritage renovations, and high-end remodeling for Richmond homeowners.
Concrete Driveways, Sidewalks & Structural Pads
Our concrete team has installed durable driveways, walkways, and structural pads for residential and commercial properties, ensuring compliance with Richmond’s engineering requirements.
Commercial & Industrial Developments
We have worked with businesses and developers to build commercial storefronts, warehouse expansions, and industrial foundations.
Asphalt Paving for Parking Lots & Roadways
From private driveways to commercial parking lots, our asphalt paving services provide long-lasting, high-quality surfaces designed to withstand Richmond’s climate.
Excavation & Site Preparation
We specialize in grading, excavation, and land clearing for new developments, ensuring that sites are properly prepared for foundation work.
Drainage & Retaining Wall Construction
Given Richmond’s coastal location and high water table, proper drainage solutions are critical. Our team has designed and installed retaining walls, perimeter drainage, and erosion control systems for homeowners and businesses.
If you’re planning a construction project in Richmond, contact VALROC Development Ltd. for expert guidance and on-site consultation.
Richmond’s zoning bylaws regulate land use, building height, property setbacks, and density. Some of the most common zoning considerations include:
• Maximum lot coverage for new homes and additions
• Height restrictions on buildings, including secondary dwellings and garages
• Setback requirements from property lines, roads, and waterways
• Environmental conservation regulations for properties near wetlands or protected areas
For projects that don’t conform to zoning bylaws, a variance application or rezoning request may be required. VALROC Development Ltd. can assess your project, determine if special approvals are necessary, and handle the zoning application process.
For more information, visit the City of Richmond’s Planning & Zoning Page.
Richmond’s zoning bylaws regulate land use, building height, property setbacks, and density. Some of the most common zoning considerations include:
• Maximum lot coverage for new homes and additions
• Height restrictions on buildings, including secondary dwellings and garages
• Setback requirements from property lines, roads, and waterways
• Environmental conservation regulations for properties near wetlands or protected areas
For projects that don’t conform to zoning bylaws, a variance application or rezoning request may be required. VALROC Development Ltd. can assess your project, determine if special approvals are necessary, and handle the zoning application process.
For more information, visit the City of Richmond’s Planning & Zoning Page.
Hiring the right contractor ensures your project is completed professionally, on time, and within budget. Here’s what to look for:
• Local experience – Choose a contractor familiar with Richmond’s building codes and permit processes.
• Licensing & insurance – Ensure the company is fully licensed and carries liability insurance.
• Customer reviews & references – Look for strong testimonials from past clients.
• Clear contracts & timelines – Work with a contractor who provides a detailed agreement outlining costs, scope, and completion deadlines.
VALROC Development Ltd. is a trusted construction company in Richmond, offering expertise in excavation, concrete, asphalt, and full-scale renovations. Before choosing a contractor, schedule a consultation with our team to ensure your project is handled professionally.
Hiring the right contractor ensures your project is completed professionally, on time, and within budget. Here’s what to look for:
• Local experience – Choose a contractor familiar with Richmond’s building codes and permit processes.
• Licensing & insurance – Ensure the company is fully licensed and carries liability insurance.
• Customer reviews & references – Look for strong testimonials from past clients.
• Clear contracts & timelines – Work with a contractor who provides a detailed agreement outlining costs, scope, and completion deadlines.
VALROC Development Ltd. is a trusted construction company in Richmond, offering expertise in excavation, concrete, asphalt, and full-scale renovations. Before choosing a contractor, schedule a consultation with our team to ensure your project is handled professionally.
Construction delays can be caused by permits, weather, supply chain disruptions, and unforeseen site conditions. To prevent delays and cost overruns:
• Work with an experienced contractor who understands Richmond’s local regulations
• Set clear expectations for materials and deadlines
• Maintain open communication with your contractor
VALROC Development Ltd. prioritizes detailed project planning, transparent budgeting, and proactive scheduling to keep your project on track. Contact us today to discuss your construction needs.
Conclusion
If you’re planning a home renovation, commercial development, or site preparation project in Richmond, VALROC Development Ltd. is here to assist with expert guidance and high-quality construction services.
Construction delays can be caused by permits, weather, supply chain disruptions, and unforeseen site conditions. To prevent delays and cost overruns:
• Work with an experienced contractor who understands Richmond’s local regulations
• Set clear expectations for materials and deadlines
• Maintain open communication with your contractor
VALROC Development Ltd. prioritizes detailed project planning, transparent budgeting, and proactive scheduling to keep your project on track. Contact us today to discuss your construction needs.
Conclusion
If you’re planning a home renovation, commercial development, or site preparation project in Richmond, VALROC Development Ltd. is here to assist with expert guidance and high-quality construction services.