Fork of Chiri for Astro for my blog
1<div class="gradient-mask">
2 <slot />
3</div>
4
5<script>
6 let mask: HTMLElement | null = null
7 let ticking = false
8 let isMobileSafariBrowser = false
9
10 // Check if the browser is mobile Safari
11 function isMobileSafari(): boolean {
12 if (typeof window === 'undefined') return false
13
14 const ua = navigator.userAgent
15 const platform = navigator.platform
16
17 const isIOSDevice = /iP(ad|hone|od)/.test(ua) || (platform === 'MacIntel' && navigator.maxTouchPoints > 1)
18
19 if (!isIOSDevice) return false
20
21 const isSafari = /Safari\//.test(ua) && !/(CriOS|FxiOS|EdgiOS|OPiOS|DuckDuckGo)/.test(ua)
22 if (!isSafari) return false
23
24 const isMobileLike =
25 window.matchMedia?.('(pointer: coarse)').matches || window.matchMedia?.('(max-width: 768px)').matches
26
27 return Boolean(isMobileLike)
28 }
29
30 function updateMask() {
31 if (isMobileSafariBrowser) return
32 if (!mask) {
33 mask = document.querySelector('.gradient-mask') as HTMLElement
34 }
35 if (!mask) return
36
37 const threshold = 64
38 const scrollY = window.scrollY
39
40 if (scrollY >= threshold) {
41 mask.style.opacity = '1'
42 } else {
43 mask.style.opacity = '0'
44 }
45 }
46
47 function onScroll() {
48 if (ticking) return
49 ticking = true
50 requestAnimationFrame(() => {
51 updateMask()
52 ticking = false
53 })
54 }
55
56 function initGradientMask() {
57 isMobileSafariBrowser = isMobileSafari()
58 mask = document.querySelector('.gradient-mask') as HTMLElement
59
60 if (isMobileSafariBrowser && mask) {
61 mask.style.display = 'none'
62 return
63 }
64
65 updateMask()
66 }
67
68 // Use only astro:page-load as it fires on initial load and navigation
69 document.addEventListener('astro:page-load', initGradientMask)
70 window.addEventListener('scroll', onScroll, { passive: true })
71</script>
72
73<style>
74 .gradient-mask {
75 position: fixed;
76 top: 0;
77 left: 0;
78 width: 100%;
79 height: 2rem;
80 z-index: 99;
81 pointer-events: none;
82 background-color: var(--bg);
83 backdrop-filter: blur(1px);
84 mask-image: linear-gradient(
85 to bottom,
86 black 0%,
87 rgba(0, 0, 0, 0.738) 19%,
88 rgba(0, 0, 0, 0.541) 34%,
89 rgba(0, 0, 0, 0.382) 47%,
90 rgba(0, 0, 0, 0.278) 56.5%,
91 rgba(0, 0, 0, 0.194) 65%,
92 rgba(0, 0, 0, 0.126) 73%,
93 rgba(0, 0, 0, 0.075) 80.2%,
94 rgba(0, 0, 0, 0.042) 86.1%,
95 rgba(0, 0, 0, 0.021) 91%,
96 rgba(0, 0, 0, 0.008) 95.2%,
97 rgba(0, 0, 0, 0.002) 98.2%,
98 transparent 100%
99 );
100 -webkit-mask-image: linear-gradient(
101 to bottom,
102 black 0%,
103 rgba(0, 0, 0, 0.738) 19%,
104 rgba(0, 0, 0, 0.541) 34%,
105 rgba(0, 0, 0, 0.382) 47%,
106 rgba(0, 0, 0, 0.278) 56.5%,
107 rgba(0, 0, 0, 0.194) 65%,
108 rgba(0, 0, 0, 0.126) 73%,
109 rgba(0, 0, 0, 0.075) 80.2%,
110 rgba(0, 0, 0, 0.042) 86.1%,
111 rgba(0, 0, 0, 0.021) 91%,
112 rgba(0, 0, 0, 0.008) 95.2%,
113 rgba(0, 0, 0, 0.002) 98.2%,
114 transparent 100%
115 );
116 opacity: 0;
117 transition: background-color 0.2s ease-out;
118 }
119
120 @media (min-width: 768px) {
121 .gradient-mask {
122 height: 4rem;
123 }
124 }
125</style>