Multi-Line Exclusion Tabs
Exclusion tabs inspired by Paco using WAAPI and Clip-path. I extend it to support wrapped content with flex-wrap
on the parent container.
You can use a span
element and move that based on the active item, but CSS
is pretty powerful right now and there's so much we can do with it. The trick is to use clip-path
to create a mask that reveals only the active tab.
How it works
To animate the clip-path
across the horizontal axis, you have to measure how much you have to clip on the left
and how much you have to clip on the right
.
We add a ref
to the activeTabElement and use that to get it's offsets.
const { offsetLeft, offsetWidth } = activeTabElement;
const clipLeft = offsetLeft;
const clipRight = offsetLeft + offsetWidth;
Once you've gotten that, you slot the values into the clip-path
. We are basically saying clip everything from the left of the element and everything after the right of the element.
container.style.clipPath = `inset(0 ${Number(100 - (clipRight / container.offsetWidth) * 100).toFixed()}% 0 ${Number((clipLeft / container.offsetWidth) * 100).toFixed()}% round 17px)`;
Emil has a great introduction to the feature.
To make this work for flex-wrap
containers, we need to figure out the top
and bottom
positions as well. With that we can by what percentage we have to clip.
In this case:
const { offsetLeft, offsetWidth, offsetTop, offsetHeight } = activeTabElement
const clipLeft = offsetLeft
const clipRight = offsetLeft + offsetWidth
const clipBottom = offsetTop + offsetHeight
const clipTop = offsetTop
With that, our clip-path
looks like this:
const clipBottomValue = Number(
100 - (clipBottom / container.offsetHeight) * 100).toFixed()
const clipTopValue = Number(
(clipTop / container.offsetHeight) * 100).toFixed()
const clipRightValue = Number(
100 - (clipRight / container.offsetWidth) * 100).toFixed()
container.style.clipPath = `inset(${clipTopValue}% ${clipRightValue}% ${clipBottomValue}% ${Number(
(clipLeft / container.offsetWidth) * 100).toFixed()}% round 17px)`
It wasn't intuitive at first but getting a pen and paper and drawing out a 4/4 rectangle helped simplify it.
Thanks for Emil for the initial prototype.
You can view the full code here.