1// Liquid Glass Effect - Clean CodePen Implementation 2class LiquidGlass { 3 constructor() { 4 this.init(); 5 } 6 7 init() { 8 this.applyEffect(); 9 } 10 11 applyEffect() { 12 const header = document.querySelector('header, .navbar'); 13 const mobileNav = document.querySelector('.mobile-nav'); 14 15 if (header) { 16 // Add glass container class to header 17 header.classList.add('glass-container'); 18 19 // Only add glass layers if not already present 20 if (!header.querySelector('.glass-filter')) { 21 const glassHTML = ` 22 <div class="glass-filter"></div> 23 <div class="glass-overlay"></div> 24 <div class="glass-specular"></div> 25 `; 26 header.insertAdjacentHTML('afterbegin', glassHTML); 27 } 28 29 // Ensure container has glass-content class 30 const container = header.querySelector('.container'); 31 if (container) { 32 container.classList.add('glass-content'); 33 } 34 } 35 36 // Add the SVG filter to body (needs to be available for all glass effects) 37 if (!document.querySelector('#lensFilter')) { 38 const svgFilter = ` 39 <svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> 40 <defs> 41 <filter id="lensFilter" x="-50%" y="-50%" width="200%" height="200%"> 42 <!-- Create turbulence for displacement --> 43 <feTurbulence type="fractalNoise" baseFrequency="0.02 0.03" numOctaves="3" result="turbulence" seed="2" /> 44 45 <!-- Convert turbulence to alpha --> 46 <feColorMatrix in="turbulence" mode="saturate" values="5" result="colormatrix" /> 47 48 <!-- Blur for smooth displacement --> 49 <feGaussianBlur in="colormatrix" stdDeviation="4" result="blur" /> 50 51 <!-- Apply displacement --> 52 <feDisplacementMap in="SourceGraphic" in2="blur" scale="100" xChannelSelector="R" yChannelSelector="G" result="displaced" /> 53 54 <!-- Merge with original for subtle effect --> 55 <feComposite in="displaced" in2="SourceGraphic" operator="over" /> 56 </filter> 57 </defs> 58 </svg> 59 `; 60 document.body.insertAdjacentHTML('beforeend', svgFilter); 61 } 62 63 // Add glass layers to mobile nav too 64 if (mobileNav) { 65 mobileNav.classList.add('glass-mobile'); 66 67 // Only add glass layers if not already added 68 if (!mobileNav.querySelector('.glass-filter')) { 69 const mobileGlassHTML = ` 70 <div class="glass-filter"></div> 71 <div class="glass-overlay"></div> 72 <div class="glass-specular"></div> 73 `; 74 mobileNav.insertAdjacentHTML('afterbegin', mobileGlassHTML); 75 } 76 } 77 78 // Add the CSS 79 this.injectStyles(); 80 } 81 82 injectStyles() { 83 if (document.querySelector('#liquid-glass-styles')) return; 84 85 const style = document.createElement('style'); 86 style.id = 'liquid-glass-styles'; 87 style.textContent = ` 88 :root { 89 --lg-bg-color: #F7FAFC88; /* Much more transparent */ 90 --lg-highlight: rgba(255, 255, 255, 0.9); /* Subtle highlight */ 91 --lg-text: #333333; 92 } 93 94 .glass-container { 95 position: fixed !important; 96 top: 0; 97 left: 0; 98 right: 0; 99 display: flex; 100 align-items: center; 101 /* overflow: hidden; removed to allow mobile menu dropdown */ 102 color: var(--lg-text); 103 z-index: 1000; 104 } 105 106 .glass-filter, 107 .glass-overlay, 108 .glass-specular { 109 position: absolute; 110 inset: 0; 111 } 112 113 .glass-filter { 114 z-index: 0; 115 backdrop-filter: blur(10px); 116 filter: url(#lensFilter) saturate(120%); /* Removed saturation and brightness */ 117 } 118 119 .glass-overlay { 120 z-index: 1; 121 background: var(--lg-bg-color); 122 } 123 124 .glass-specular { 125 z-index: 2; 126 box-shadow: inset 1px 1px 0 var(--lg-highlight), 127 inset 0 0 10px var(--lg-highlight); 128 } 129 130 .glass-content { 131 position: relative; 132 z-index: 3; 133 display: flex; 134 flex: 1 1 auto; 135 align-items: center; 136 justify-content: space-between; 137 } 138 139 /* Ensure all navbar content stays on top */ 140 .navbar .logo,
141 .navbar .mobile-menu-toggle { 142 /* position: relative; removed as it was breaking mobile menu */ 143 z-index: 4; 144 } 145 146 /* nav-links z-index only when inside navbar (desktop) */ 147 @media (min-width: 769px) { 148 .navbar .nav-links { 149 z-index: 4; 150 } 151 } 152 153 /* Glass layers for mobile nav */ 154 .glass-mobile .glass-filter, 155 .glass-mobile .glass-overlay, 156 .glass-mobile .glass-specular { 157 position: absolute; 158 inset: 0; 159 transition: none !important; /* No transition on glass layers */ 160 } 161 162 .glass-mobile .glass-filter { 163 z-index: 0; 164 backdrop-filter: blur(10px); 165 -webkit-backdrop-filter: blur(10px); 166 filter: url(#lensFilter) saturate(120%); 167 transition: none !important; 168 } 169 170 .glass-mobile .glass-overlay { 171 z-index: 1; 172 background: var(--lg-bg-color); 173 } 174 175 .glass-mobile .glass-specular { 176 z-index: 2; 177 box-shadow: inset 1px 1px 0 var(--lg-highlight), 178 inset 0 0 10px var(--lg-highlight); 179 } 180 181 /* Mobile nav links need higher z-index than glass layers */ 182 .glass-mobile a { 183 position: relative; 184 z-index: 3; 185 } 186 `; 187 document.head.appendChild(style); 188 } 189} 190 191// Initialize on every page load (initial + client-side navigation) 192if (!window._liquidGlassListenerAdded) { 193 window._liquidGlassListenerAdded = true; 194 document.addEventListener('astro:page-load', () => new LiquidGlass()); 195}
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.