The case for crescent-shaped avatars 🌙
Part of our work on Radiant — Thoughtspot’s design system — was to recreate our Avatar component, and more specifically its crescent shape when we need to display more than one at a time.
Our first intent was to set a white border to the round shape, which might look like this:
.avatar {
width: 32px;
height: 32px;
border-radius: 50%;
border: solid 1px white
}
As a side note, I often ask candidates to create a round-shaped element to see if they’ll use border-radius:50%
, it gives me an indication on how much they know about CSS.
OK, so this works but what we’re really doing is faking it on white background. It doesn’t look so great on dark or color.
So, instead of setting a border, we need to create a mask or clip on all except the last one in order to get the desired crescent shapes.
First try: using clip-path
The first idea that came to mind was to use clip-path
with a circle shape and translate it to the right:
Unfortunately, this is not how clip-path
works and there’s apparently no way to tell clip-path
to clip what’s inside its path, not outside.
OK so that doesn’t work, what else can we try?
Second attempt: using clip-path with SVG
So we can’t use a simple CSS shape but clip-path
also accepts SVGs as a path. Let’s create a more elaborated shape in S̵k̵e̵t̵c̵h̵ our preferred design tool then!
Before we go on, we need to keep in mind that clip-path
is not universally supported — Hello IE and Edge! — so we need to have some progressive enhancement strategy, which you can see in the following pen:
This time, it works as expected but:
- you have to create an SVG (i.e. hard to impossible to do it programmatically)
- you have to have it in the page because external SVGs are not really supported yet — good job Firefox!
- you have to hide this SVG one way or another (i.e. probably hacky)
Can we do better?
Third attempt: Ask the Expert™ Edition
Let’s go back to what we stated earlier: you cannot specify a clip-path
to cut the inner part of an element. That’s true but you can hack it:
Ana Tudor wrote this great article that uses polygons.
Great… but a polygon is not a circle you might say.
Except if you add a few more vertices.
And a few more.
So in theory you can create something like a clip-path
with a very complicated polygon, but that seems over-engineered, no?!
Fourth attempt: let’s try with mask instead
The main difference between mask
and clip-path
is that mask
is using an image to hide parts of the parent element: the image needs to have black (for transparent) and white (for opaque) parts to create the mask.
That’s great but how would that be better than creating an SVG if we have to create an external image to achieve our crescent shape?
But — as Ana Tudor hinted in her tweet — you can also use CSS gradients in a mask
! When you do, the transparent parts will be what’s hidden of the parent element.
And for our use case, we can use radial-gradient
!
Let’s dive in the meatier part of the code:
mask: radial-gradient(circle at 41px, transparent 17px, white 0);
17px
is the radius of the mask. Its calculation is half of our avatar component (32px / 2), plus 1px otherwise you won’t be able to see it with the following avatar on top of it (you can add more than 1px for a bolder effect).41px
it’s the 3/4th of our avatar element (32px * 3/4 = 24px) + the radius of the mask (17px)
Don’t forget to move the avatars — all but the first one — to the left by 1/4th of the avatar width (8px) minus the border that you chose (here 1px) to achieve the final result.
Now, that’s what I call a nice looking CSS-based crescent-shaped avatar! ✌️
I hope you found this article useful. Clap and I’ll keep them coming! :)