Hakim El Hattab
Swedish JavaScript coder and CSS tweaker • Co-founder of Slides
1
2
const button = document.querySelector( '.button' ).getBBox();
const menu = document.querySelector( '.menu' ).getBBox();
document.querySelector( 'path' ).setAttribute( 'd', `
M ${button.x} ${button.y}
Q ${menu.x} ${button.y},
${menu.x} ${menu.y}
v ${menu.height}
Q ${menu.x} ${button.y + button.height},
${button.x} ${button.y + button.height}
h ${button.width}
v ${-button.height}
z` );
<svg width="300" height="200">
<rect class="button" fill="#ddd" y="85" width="100" height="30" />
<rect class="menu" fill="#ddd" x="140" width="120" height="200" />
<path fill="#00ff00" />
</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));
}
const wrapper = document.querySelector( '.wrapper' );
const 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
By Hakim El Hattab
There's a lot more that goes into common interface elements like a scrollbar or dropdown menu than what meets the eye. Learn about the nuances of common UI patterns and how much room there is to improve them. Video: https://www.dotconferences.com/2019/12/hakim-el-hattab-what-breaking-interfaces-taught-me-about-building-better-interfaces