Clip Pathing Color Changes
Clip Pathing Color Changes êŽë š
This is a nice post from Emil Kowalski on usage of the clip-path
property in CSS. Iâve always liked clip-path
. Maybe itâs because itâs such a sharp knife. When you clip an element, itâs clipped, yo. There isnât a lot of nuance to it, it does what it does. But moreso, I think I like the connection to SVG (oh, hey, by the way, I just made my old book Practical SVG entirely free). The value that you give clip-path
is stuff like circle()
, polygon()
, path()
, etc â the primitive shapes of SVG.
In Emilâs post, my favorite example is a navigation bar where a âpillâ shape animates from one navigation item to another when they are clicked. The pill is a different background color, and so the text color also changes. (If youâre over 100 years old like me, we used to call this kind of thing âlava lampâ navigation đŽ).
I would guess most people would assume what is happening here is an extra element set behind the links that moves position to underneath the newly active links. You could do it that way, but there is a minor aesthetic issue with it. Because the background-color
is changing here, the text also needs to change appropriately (from dark to light here). You could change that color instantly, but that will look weird like itâs changing too early. You could set a transition
on it, but youâll never get the fade to look quite right, especially as it has to go through an awkward gray color.
This ainât gonna happen with an underlay element alone.
See how the text is half-light and half-dark mid-animation when the highlight blue pill moves from one to another? Thatâs a lovely effect that makes this feel very polished and smooth. This idea first came from a tweet by Paco. Like Emil says:
You might say that not everyone is going to notice the difference, but I truly believe that small details like this add up and make the experience feel more polished. Even if they go unnoticed.
Agreed.
In Emilâs post, itâs done with React. Thatâs totally fine, but I figured Iâd make a vanilla one for yâall here:
Hereâs how this works:
- There is one set of semantic HTML navigation.
- If JavaScript executes, it duplicates the nav (weâll need two) but ensures the duplicate is hidden for screen readers.
- The duplicate is placed exactly on top of the original (itâs the âblueâ one) and canât directly be clicked (i.e.
pointer-events: none;
) - A
clip-path
is set that highlights one of the navigation items in particular by clipping the entire duplicate except one link. - As links are clicked, the
clip-path
is changed using positional math, highlighting the new one. Also high-five for theround
keyword that can be used withinset()
for rounded corners on inset rectangles. - The
clip-path
animates, thanks to a basic CSStransition
.
I think itâs cool as heck that it all comes together that cleanly.
Itâs also a nice touch that the new clip-path
positions are calculated based on their page position, meaning that there are really no magic numbers here. If we add navigation items or change them, this code will be resilient and it will all still work. And if none of this JavaScript runs at all, no big deal.