/* 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);
Case studies
The Importance of Fitness Facilities for Hospitality Businesses
The hospitality industry has changed rapidly in the last decade with the rise of Airbnb and other rental marketplaces. One primary advantage hotels have maintained is the on-site fitness facility. However, if your hospitality business is missing a fitness center or your equipment is outdated, you could lose this edge. You might wonder, “Should I update my hotel gym? Are hospitality fitness centers important?” We have the answers you are looking for.
The hospitality industry has changed rapidly in the last decade with the rise of Airbnb and other rental marketplaces. One primary advantage hotels have maintained is the on-site fitness facility. However, if your hospitality business is missing a fitness center or your equipment is outdated, you could lose this edge. You might wonder, “Should I update my hotel gym? Are hospitality fitness centers important?”
We have the answers you are looking for. Learn more about the five main benefits fitness amenities provide to hospitality businesses below:
1. Attract more guests with fitness amenities
The wellness travel trend is on the rise — and it doesn’t show any signs of slowing down. A recent study found that 16% of parents and 9% of nonparents traveled to improve physical health. As more guests travel with health and wellness in mind, is your hotel ready?
Health-conscious travelers will likely choose a hotel with an advanced fitness suite. Without an on-site gym facility, you may lose out on hospitality guests traveling for health, wellness, sporting events, and training conferences. Conversely, by offering a fitness center at your hospitality business, your accommodations will be more attractive to 33% of travelers, according to research by Accor.
2. Improve guest loyalty
While it is critical to appeal to potential travelers, it is even more paramount that you keep guests coming back for return visits and staying longer. Such repeat revenue is vital for sustaining hospitality businesses. For example, say a guest travels to your area every few months for a quarterly business meeting. They likely won’t want to research new hotels for each visit — unless they find your hotel lacking a necessary amenity. In these cases, an on-site gym facility can help you earn valuable recurring revenue and lengthier stays.
3. Earn more positive hospitality reviews
Reviews on social media and various websites are increasingly influential to travelers. According to TripAdvisor, 82% of guests report online reviews as “extremely or very important” when booking accommodations. As travelers put more trust into the ratings they see online, it is essential for hospitality businesses to reflect positively in reviews. As mentioned above, fitness centers are becoming increasingly important to guests, which can influence your online feedback. Lacking this central accommodation could lead to criticism. Conversely, a high-quality fitness center is one way to improve guest experience and reviews.
4. Greater convenience for guests
Fitness is a vital part of many daily routines. However, guests may stay in your hotel for their business or personal adventures. Especially when traveling with a family or group, these guests may need to fit their workout into the early morning hours to stay on schedule for the day. However, without an on-site hotel gym, even small workouts can become a huge inconvenience. Guests will need to find a local gym, buy a day pass, and arrange travel to/from your hotel. Conversely, the increased convenience of an on-site can translate to higher guest satisfaction and rebooking rates.
Increase your hotel’s business value and rates
In the hospitality industry, your business value is your livelihood. A new or upgraded fitness center can increase the value of your hotel, helping you earn more money from investors or any future buyouts. Additionally, many travelers will pay more to stay in a hotel featuring a state-of-the-art fitness center.
Choose Advantage Sport & Fitness For Hospitality Fitness Amenities And Services
At Advantage Sport & Fitness (ASF), we know the value of fitness amenities and services for those in the hospitality industry. As a result, we offer commercial fitness services and products tailored to hospitality clients. When you are ready to build a new hotel fitness center or retrofit an existing facility, you can turn to us for leading design services. We also offer a wide selection of gym equipment, delivery and installation services, and repairs + preventative maintenance services.
We invite you to learn more about our hospitality fitness amenities today. When you’re ready to get started, contact our team of fitness amenity experts to schedule a complimentary site visit and consultation.