let anchor = document.querySelector( '.anchor' ).getBBox();
let menu = document.querySelector( '.menu' ).getBBox();
document.querySelector( 'path' ).setAttribute( 'd', `
M ${anchor.x} ${anchor.y}
Q ${menu.x} ${anchor.y},
${menu.x} ${menu.y}
v ${menu.height}
Q ${menu.x} ${anchor.y + anchor.height},
${anchor.x} ${anchor.y + anchor.height}
h ${anchor.width}
v ${-anchor.height}
z` );
<svg width="300" height="200">
<rect class="anchor" fill="#ddd" y="85" width="100" height="30" />
<rect class="menu" fill="#ddd" x="140" width="120" height="200" />
<path stroke="#00ff00" stroke-width="2" />
</svg>
svg { pointer-events: none; }
path { pointer-events: auto; }
<div class="wrapper">
<ul class="content">
<li>One</li>
<li>Two</li>
<li>Three</li>
...
</ul>
</div>
.wrapper {
position: relative;
width: min-content;
}
.content {
overflow: auto;
width: 200px;
height: 300px;
}
.wrapper:before,
.wrapper:after {
content: '';
position: absolute;
height: 10%;
width: 100%;
}
.wrapper:before {
top: 0;
background: linear-gradient(to bottom, rgba(0,0,0,0.4), transparent);
opacity: var(--scroll-progress, 0);
}
.wrapper:after {
bottom: 0;
background: linear-gradient(to top, rgba(0,0,0,0.4), transparent);
opacity: calc(1 - var(--scroll-progress, 0));
}
let wrapper = document.querySelectorAll( '.wrapper' ),
content = document.querySelector( '.content' );
content.addEventListener( 'scroll', event => {
let scrollRange = content.scrollHeight - content.clientHeight;
let scrollProgress = content.scrollTop / scrollRange;
wrapper.style.setProperty( '--scroll-progress', scrollProgress );
} );
scrollHeight
scrollTop
clientHeight