PageSourceSearch

https://wearemichaelreese.org/wp-content/themes/mr/js/header-spacing.js?ver=1.0.0

js wearemichaelreese.org collected 2026-10-04 22:20:50 UTC 7,151 bytes, 225 lines download raw bytes

1/**
2 * Dynamic Header Height Calculation and Spacing Adjustment
3 * Ensures proper spacing for fixed header across all viewport sizes
4 */
5
6(function() {
7    'use strict';
8
9    // Configuration
10    const HEADER_SELECTOR = 'header';
11    const MAIN_SELECTOR = 'main#primary';
12    const STORY_HERO_SELECTOR = '.story-hero-no-image, .story-hero-with-image';
13    const DEBOUNCE_DELAY = 100;
14
15    // State
16    let headerHeight = 0;
17    let resizeTimeout = null;
18
19    /**
20     * Debounce function to limit resize event calls
21     */
22    function debounce(func, wait) {
23        return function executedFunction(...args) {
24            const later = () => {
25                clearTimeout(resizeTimeout);
26                func(...args);
27            };
28            clearTimeout(resizeTimeout);
29            resizeTimeout = setTimeout(later, wait);
30        };
31    }
32
33    /**
34     * Calculate the actual header height
35     */
36    function calculateHeaderHeight() {
37        const header = document.querySelector(HEADER_SELECTOR);
38        if (!header) {
39            console.warn('Header element not found');
40            return 0;
41        }
42
43        // Get computed style to include all spacing
44        const headerRect = header.getBoundingClientRect();
45        const computedStyle = window.getComputedStyle(header);
46
47        // Include margins in height calculation
48        const marginTop = parseFloat(computedStyle.marginTop) || 0;
49        const marginBottom = parseFloat(computedStyle.marginBottom) || 0;
50
51        return headerRect.height + marginTop + marginBottom;
52    }
53
54    /**
55     * Apply calculated spacing to main content
56     */
57    function adjustMainContentSpacing() {
58        const main = document.querySelector(MAIN_SELECTOR);
59        if (!main) {
60            console.warn('Main content element not found');
61            return;
62        }
63
64        // Calculate appropriate padding
65        const basePadding = 20; // Base padding in pixels
66        const calculatedPadding = Math.max(headerHeight + basePadding, 100);
67
68        // Apply the padding
69        main.style.paddingTop = `${calculatedPadding}px`;
70
71        // Add a CSS custom property for other elements to use
72        document.documentElement.style.setProperty('--header-height', `${headerHeight}px`);
73        document.documentElement.style.setProperty('--header-offset', `${calculatedPadding}px`);
74
75        // Debug logging
76        if (window.console && typeof window.console.log === 'function') {
77            console.log(`Header height: ${headerHeight}px, Applied padding: ${calculatedPadding}px`);
78        }
79    }
80
81    /**
82     * Adjust story hero sections specifically
83     */
84    function adjustStoryHeroSpacing() {
85        const storyHeros = document.querySelectorAll(STORY_HERO_SELECTOR);
86
87        storyHeros.forEach(hero => {
88            if (hero.classList.contains('story-hero-no-image')) {
89                // For heroes without images, ensure proper top spacing
90                const currentMarginTop = parseInt(window.getComputedStyle(hero).marginTop) || 0;
91                if (currentMarginTop > headerHeight * 2) {
92                    // If margin is excessive, reduce it
93                    hero.style.marginTop = '0';
94                    hero.style.paddingTop = `${Math.max(headerHeight + 40, 120)}px`;
95                }
96            }
97        });
98    }
99
100    /**
101     * Handle viewport changes and recalculate spacing
102     */
103    function handleResize() {
104        const newHeaderHeight = calculateHeaderHeight();
105
106        // Only update if height changed significantly (avoid micro-adjustments)
107        if (Math.abs(newHeaderHeight - headerHeight) > 5) {
108            headerHeight = newHeaderHeight;
109            adjustMainContentSpacing();
110            adjustStoryHeroSpacing();
111        }
112    }
113
114    /**
115     * Initialize spacing adjustments
116     */
117    function init() {
118        // Wait for DOM to be ready
119        if (document.readyState === 'loading') {
120            document.addEventListener('DOMContentLoaded', init);
121            return;
122        }
123
124        // Initial calculation
125        headerHeight = calculateHeaderHeight();
126
127        if (headerHeight > 0) {
128            adjustMainContentSpacing();
129            adjustStoryHeroSpacing();
130        }
131
132        // Set up resize listener with debouncing
133        const debouncedResize = debounce(handleResize, DEBOUNCE_DELAY);
134        window.addEventListener('resize', debouncedResize);
135
136        // Also listen for orientation changes on mobile
137        if ('onorientationchange' in window) {
138            window.addEventListener('orientationchange', () => {
139                // Delay to allow for orientation change to complete
140                setTimeout(debouncedResize, 200);
141            });
142        }
143
144        // Listen for font loading which might affect header height
145        if (document.fonts && document.fonts.ready) {
146            document.fonts.ready.then(() => {
147                setTimeout(handleResize, 100);
148            });
149        }
150
151        // For Alpine.js compatibility - recalculate after Alpine initializes
152        if (typeof Alpine !== 'undefined') {
153            document.addEventListener('alpine:init', () => {
154                setTimeout(handleResize, 100);
155            });
156        }
157
158        // Mark as initialized
159        document.documentElement.classList.add('header-spacing-initialized');
160
161        console.log('Header spacing system initialized');
162    }
163
164    /**
165     * Provide public API for manual recalculation
166     */
167    window.recalculateHeaderSpacing = function() {
168        handleResize();
169    };
170
171    /**
172     * CSS-in-JS fallback styles
173     */
174    function addFallbackStyles() {
175        const style = document.createElement('style');
176        style.textContent = `
177            /* Fallback styles for header spacing */
178            .header-spacing-fallback main#primary {
179                padding-top: 120px;
180            }
181
182            @media (min-width: 1024px) {
183                .header-spacing-fallback main#primary {
184                    padding-top: 140px;
185                }
186            }
187
188            @media (max-width: 767px) {
189                .header-spacing-fallback main#primary {
190                    padding-top: 100px;
191                }
192            }
193
194            /* Smooth transitions for spacing changes */
195            main#primary {
196                transition: padding-top 0.3s ease;
197            }
198
199            .story-hero-no-image,
200            .story-hero-with-image {
201                transition: margin-top 0.3s ease, padding-top 0.3s ease;
202            }
203        `;
204        document.head.appendChild(style);
205    }
206
207    // Add fallback styles immediately
208    addFallbackStyles();
209
210    // Add fallback class in case JS fails
211    document.documentElement.classList.add('header-spacing-fallback');
212
213    // Initialize when ready
214    init();
215
216    // Remove fallback class once initialized
217    document.addEventListener('DOMContentLoaded', () => {
218        setTimeout(() => {
219            if (document.documentElement.classList.contains('header-spacing-initialized')) {
220                document.documentElement.classList.remove('header-spacing-fallback');
221            }
222        }, 500);
223    });
224
225})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.