Markets Served
MultifamilyRegional Coverage
Eastern PA & Mid-Atlantic
Based In
Pennsylvania
Our services
We are ready to support your facility in every phase.
Partner Brands
Explore our collection of partner brands.
All Partner BrandsCase Studies
/* update variables when header is on dark background*/
.on-dark-bg {
--header-bg: var(--current-header-bg);
--chevron-clr: white;
--menu-item-clr: white;
--menu-item-hover-bg: #172235;
--menu-item-hover-clr: #bc92e6;
--menu-toggle-clr: var(--menu-item-clr);
--dropdown-content-border: solid 1px rgb(220 220 220 / 20%);
--mobile-menu-bg: var(--header-bg);
}
.on-light-bg {
--menu-item-clr: black;
/* update variables when header is on light background*/
}
/* update variable when on dark or light background */
.on-dark-bg,
.on-light-bg {
--overlay-header-bg-active: var(--current-header-bg);
--dropdown-content-bg: var(--current-header-bg);
}
.on-video-section {
/* update variables when header is on video section*/
--menu-item-clr: white;
--chevron-clr: white;
--menu-item-hover-bg: rgb(0 0 0 / 30%);
--menu-item-hover-clr: #bc92e6;
--dropdown-content-bg: rgb(0 0 0 / 70%);
--overlay-header-bg: rgb(0 0 0 / 25%);
--overlay-header-bg-active: var(--dropdown-content-bg);
--dropdown-content-border: solid 1px rgb(220 220 220 / 20%);
--current-header-bg: rgb(0 0 0 / 14%) !important;
--header-bg: rgb(0 0 0 / 70%);
--menu-toggle-clr: white;
--mobile-menu-bg: rgb(0 0 0 / 95%);
--menu-cta-border: var(--dropdown-content-border)
}
.on-hero-section {
/* update variables when header is on hero section*/
}
/* header bg blure when on video section */
html:not(.dwc-mobile) .dwc-nest-header.on-video-section {
backdrop-filter: blur(var(--overlay-header-blur));
}
/* overlay header border when on video section or dark background*/
.dwc-nest-header:is(.on-dark-bg, .on-video-section)[data-overlay-header="true"] {
border: 1px solid rgba(255, 255, 255, 0.2);
}
/* header bottom border when on dark background or video section*/
.dwc-nest-header:is(.on-dark-bg, .on-video-section) {
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
/* logo color when on dark background*/
.on-dark-bg .dwc-nest-menu__logo path {
fill: white;
}
/* logo color when on light background*/
.on-dark-bg .dwc-nest-menu__logo path {
/*define svg fill when logo is on light background */
}
/* logo color when on video background*/
.on-video-section .dwc-nest-menu__logo path {
fill: white;
}
/*custom */
.on-dark-bg .mega-menu-magic__menu-icon-large{
background-color: var(--current-header-bg);
fill: #bc92e6;
color: #bc92e6;
}
.on-video-section .mega-menu-magic__menu-icon-large{
background-color: var(--current-header-bg);
fill: #bc92e6;
color: #bc92e6;
}
.on-video-section .mega-menu-magic__dropdown-title,
.on-dark-bg .mega-menu-magic__dropdown-title{
color: rgb(210 210 210);
}// Constants
const BREAKPOINT_DESKTOP = 0;
const DEBOUNCE_DELAY = 50;
// Configuration object for all header-related selectors
const HEADER_CONFIG = {
// Main header element
headerElement: '.dwc-nest-header',
// Wrapper element selectors (can be classes, IDs, or data attributes)
wrapperSelectors: [
// '.dwc-nest-header__container'
],
// Elements to monitor for header styling (can include any CSS selector)
// Default monitors sections, but you can add footer, divs, or any element
monitoredElements: 'section, [data-header-zone]',
// Store original header background color
originalHeaderBgColor: null
};
// Unified section type configuration
// Add new data attributes here - they will be automatically mapped to header classes
const SECTION_TYPES = {
'data-hero': 'on-hero-section',
'data-video': 'on-video-section',
'data-custom': 'on-custom-section',
'data-sticky': 'on-media-section'
// Add new: 'data-feature': 'on-feature-section'
};
// Configuration for excluded sections
const EXCLUSION_CONFIG = {
// Data attribute that identifies sections to exclude from header styling
sectionAttribute: 'data-exclude-header'
};
// Text color classes
const TEXT_CLASSES = {
light: 'on-dark-bg',
dark: 'on-light-bg'
};
// Generate classes to remove dynamically from SECTION_TYPES and text classes
const ALL_CLASSES_TO_REMOVE = [
// Add all header classes from section types
...Object.values(SECTION_TYPES),
// Add text color classes
TEXT_CLASSES.light,
TEXT_CLASSES.dark
];
// Performance optimization: Cache DOM elements and computed values
const CACHE = {
headerElement: null,
wrapperElement: null,
sections: [],
sectionsData: new Map(),
colorCache: new Map(),
tempColorElement: null,
lastKnownSection: null,
isUpdating: false,
pendingColorUpdate: false
};
// Initialize cache
function initializeCache() {
// Cache header elements
CACHE.headerElement = document.querySelector(HEADER_CONFIG.headerElement);
// Try each wrapper selector until one is found
for (const selector of HEADER_CONFIG.wrapperSelectors) {
CACHE.wrapperElement = document.querySelector(selector);
if (CACHE.wrapperElement) break;
}
// Cache sections and their positions
CACHE.sections = Array.from(document.querySelectorAll(HEADER_CONFIG.monitoredElements));
// Create reusable element for color calculations
CACHE.tempColorElement = document.createElement('div');
CACHE.tempColorElement.style.position = 'absolute';
CACHE.tempColorElement.style.visibility = 'hidden';
CACHE.tempColorElement.style.pointerEvents = 'none';
document.body.appendChild(CACHE.tempColorElement);
// Cache section metadata
updateSectionsCache();
}
// Update sections cache (call when DOM changes)
function updateSectionsCache() {
CACHE.sections = Array.from(document.querySelectorAll(HEADER_CONFIG.monitoredElements));
CACHE.sectionsData.clear();
CACHE.sections.forEach((section, index) => {
const sectionData = {
index,
isExcluded: section.hasAttribute(EXCLUSION_CONFIG.sectionAttribute)
};
// Dynamically check for all section types defined in SECTION_TYPES
Object.keys(SECTION_TYPES).forEach(attribute => {
sectionData[attribute] = section.hasAttribute(attribute);
});
CACHE.sectionsData.set(section, sectionData);
});
}
// Function to remove all section classes from :root only
function removeAllSectionClasses() {
const rootElement = document.documentElement;
// Remove classes from :root only
rootElement.classList.remove(...ALL_CLASSES_TO_REMOVE);
}
// Function to update the CSS variable on :root
function updateRootBackgroundVariable(color) {
document.documentElement.style.setProperty('--current-header-bg', color);
}
// Function to apply classes to :root only
function applyClassesToRoot(headerClass, textClass) {
const rootElement = document.documentElement;
// Apply header class to :root only
if (headerClass) {
rootElement.classList.add(headerClass);
}
// Apply text class to :root only
if (textClass) {
rootElement.classList.add(textClass);
}
}
// Optimized color conversion using cached temp element
function getRGBValues(color) {
const cacheKey = color;
if (CACHE.colorCache.has(cacheKey)) {
return CACHE.colorCache.get(cacheKey);
}
CACHE.tempColorElement.style.color = color;
const computedColor = window.getComputedStyle(CACHE.tempColorElement).color;
// Extract RGB values
const match = computedColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
const result = match ? {
r: parseInt(match[1]),
g: parseInt(match[2]),
b: parseInt(match[3])
} : { r: 255, g: 255, b: 255 };
// Cache the result
CACHE.colorCache.set(cacheKey, result);
return result;
}
// Function to check if a background color is transparent or empty
function isBackgroundTransparent(color) {
if (!color) return true;
const transparentValues = [
'transparent',
'rgba(0, 0, 0, 0)',
'rgba(0,0,0,0)',
'initial',
'inherit',
''
];
return transparentValues.includes(color.toLowerCase().replace(/\s/g, ''));
}
// Function to determine if a color is dark or light
function isColorDark(color) {
if (color === 'transparent' || color === 'rgba(0, 0, 0, 0)') {
return false;
}
const rgb = getRGBValues(color);
const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
return luminance < 0.5;
}
// Optimized section detection using cached data
function getCurrentSection() {
if (!CACHE.headerElement || CACHE.sections.length === 0) return null;
const headerHeight = CACHE.headerElement.offsetHeight;
// Quick check if we're still in the same section
if (CACHE.lastKnownSection) {
const rect = CACHE.lastKnownSection.getBoundingClientRect();
if (rect.top <= headerHeight && rect.bottom > headerHeight) {
return CACHE.lastKnownSection;
}
}
// Binary search would be ideal here, but simple loop is sufficient for most cases
for (const section of CACHE.sections) {
const rect = section.getBoundingClientRect();
if (rect.top <= headerHeight && rect.bottom > headerHeight) {
CACHE.lastKnownSection = section;
return section;
}
}
const firstSection = CACHE.sections[0] || null;
CACHE.lastKnownSection = firstSection;
return firstSection;
}
// Function to get computed background color with caching
function getComputedBackgroundColor(element) {
const cacheKey = element;
if (CACHE.colorCache.has(cacheKey)) {
return CACHE.colorCache.get(cacheKey);
}
const bgColor = window.getComputedStyle(element).backgroundColor;
CACHE.colorCache.set(cacheKey, bgColor);
return bgColor;
}
// Function to initialize original header background color
function initializeOriginalHeaderBgColor() {
if (CACHE.headerElement) {
HEADER_CONFIG.originalHeaderBgColor = window.getComputedStyle(CACHE.headerElement).backgroundColor;
}
}
// Function to determine header style configuration based on section
function determineHeaderConfig(section) {
if (!section) return null;
const sectionData = CACHE.sectionsData.get(section);
if (!sectionData) return null;
// If section is excluded, return null to skip styling
if (sectionData.isExcluded) {
return null;
}
// Dynamically check for any section type defined in SECTION_TYPES
for (const [attribute, headerClass] of Object.entries(SECTION_TYPES)) {
if (sectionData[attribute]) {
return {
backgroundColor: null,
headerClass: headerClass,
needsWhiteText: null,
isSpecialSection: true
};
}
}
// Get the actual background color of the section
const sectionBgColor = getComputedBackgroundColor(section);
if (isBackgroundTransparent(sectionBgColor)) {
return {
backgroundColor: HEADER_CONFIG.originalHeaderBgColor || 'rgb(255, 255, 255)',
headerClass: null,
needsWhiteText: isColorDark(HEADER_CONFIG.originalHeaderBgColor || 'rgb(255, 255, 255)'),
isSpecialSection: false
};
}
const needsWhiteText = isColorDark(sectionBgColor);
return {
backgroundColor: sectionBgColor,
headerClass: null,
needsWhiteText: needsWhiteText,
isSpecialSection: false
};
}
// Function to apply styles (background color to header, classes to :root only)
function applyHeaderStyles(config) {
if (!CACHE.headerElement || !config) return;
const elementsToStyle = [CACHE.headerElement, CACHE.wrapperElement].filter(Boolean);
// Use requestAnimationFrame for smooth visual updates
requestAnimationFrame(() => {
// First, remove all section classes and text classes from :root only
removeAllSectionClasses();
// Update the root CSS variable with background color
if (config.backgroundColor !== null) {
const appliedBgColor = config.backgroundColor || '';
updateRootBackgroundVariable(appliedBgColor);
} else {
updateRootBackgroundVariable('');
}
// Determine text class to apply
const textClass = config.needsWhiteText !== null
? (config.needsWhiteText ? TEXT_CLASSES.light : TEXT_CLASSES.dark)
: null;
// Apply classes to :root only
applyClassesToRoot(config.headerClass, textClass);
// Handle background color on header elements only
elementsToStyle.forEach(element => {
if (config.backgroundColor !== null) {
const appliedBgColor = config.backgroundColor || '';
element.style.backgroundColor = appliedBgColor;
} else {
element.style.backgroundColor = '';
}
});
// For special sections, allow CSS to apply first, then get computed background color
if (config.isSpecialSection && config.headerClass) {
// Use setTimeout(0) to ensure CSS has been applied, but batch multiple calls
if (!CACHE.pendingColorUpdate) {
CACHE.pendingColorUpdate = true;
setTimeout(() => {
// Process all elements that need color updates in this batch
elementsToStyle.forEach(el => {
// Check if element has any of the section classes (though they're not applied anymore)
const hasSectionClass = Object.values(SECTION_TYPES).some(className =>
el.classList.contains(className)
);
if (hasSectionClass) {
const computedBgColor = getComputedBackgroundColor(el);
updateRootBackgroundVariable(computedBgColor);
}
});
CACHE.pendingColorUpdate = false;
}, 50);
}
}
CACHE.isUpdating = false;
});
}
// Throttled update function using requestAnimationFrame
function updateHeaderStyles() {
// Prevent multiple updates in the same frame
if (CACHE.isUpdating) return;
// Apply only on screens larger than the breakpoint
if (window.innerWidth <= BREAKPOINT_DESKTOP) {
return;
}
CACHE.isUpdating = true;
const currentSection = getCurrentSection();
if (!currentSection) {
CACHE.isUpdating = false;
return;
}
const headerConfig = determineHeaderConfig(currentSection);
if (!headerConfig) {
CACHE.isUpdating = false;
return;
}
applyHeaderStyles(headerConfig);
}
// Debounce function for resize events (less frequent)
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// RAF-based throttle for scroll events
let rafId = null;
function throttleWithRAF(func) {
return function(...args) {
if (rafId) return;
rafId = requestAnimationFrame(() => {
func.apply(this, args);
rafId = null;
});
};
}
// Initialize everything
function initialize() {
initializeCache();
initializeOriginalHeaderBgColor();
updateHeaderStyles();
}
// Cleanup function
function cleanup() {
if (CACHE.tempColorElement && CACHE.tempColorElement.parentNode) {
CACHE.tempColorElement.parentNode.removeChild(CACHE.tempColorElement);
}
if (rafId) {
cancelAnimationFrame(rafId);
}
}
// Initialize on page load
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialize);
} else {
initialize();
}
// Create optimized event handlers
const throttledUpdateHeaderStyles = throttleWithRAF(updateHeaderStyles);
const debouncedUpdateHeaderStyles = debounce(() => {
updateSectionsCache(); // Refresh cache on resize
updateHeaderStyles();
}, DEBOUNCE_DELAY);
// Add event listeners with passive option for better performance
window.addEventListener('scroll', throttledUpdateHeaderStyles, { passive: true });
window.addEventListener('resize', debouncedUpdateHeaderStyles, { passive: true });
// Cleanup on page unload
window.addEventListener('beforeunload', cleanup);Eastern PA & Mid-Atlantic
Pennsylvania
Our services