Overlapping CSS avatars that respond to their container

A client came to me with a “Meet the Team” page that fell apart on mobile. They wanted the familiar look: a row of circular avatars, each one overlapping the next slightly. With 12 team members on a desktop screen it was fine. On a phone, the images either shrank to tiny dots or pushed the page into a horizontal scroll that went on for miles.

My first instinct, and I am owning it because most of us have done this, was to throw a handful of media queries at it. I would adjust the negative margin by hand at each breakpoint. “If screen width is less than 480px, set margin to -30px.” That held up for about ten minutes, until the client added a 13th team member and the row broke again. Hardcoding margins for content that keeps changing is a fool’s errand. There is a better way to build modern CSS avatars that actually respond to their container.

The math behind dynamic overlap

For this to respond properly, the CSS has to know two things: how many items are in the list, and how much room it has to place them. That is usually the point where a developer reaches for a JavaScript resize listener. A bit of arithmetic in CSS covers it instead, building on an approach I read about on css-tricks.com. Take the container width, subtract the combined width of all the images, then divide what is left over by the number of gaps between them.

.bbioon-avatar-container {
  --s: 100px; /* Image size */
  --g: 10px;  /* The minimum gap */
  display: flex;
  container-type: inline-size;
}

.bbioon-avatar-container img {
  width: var(--s);
  border-radius: 50%;
  /* The magic formula */
  --_m: min((100cqw - sibling-count() * var(--s)) / (sibling-count() - 1), var(--g));
  margin-right: var(--_m);
}

The piece doing the work is sibling-count(), a fairly new CSS function that returns the number of children in the container. Pair it with 100cqw, the container query width, and the avatars pull tighter together as the container shrinks. Not one media query involved. Once you start using container units rather than percentages for internal layout logic, percentages begin to feel like guesswork.

Fixing the mask with container queries

The overlap works, but the clean cut-out effect, where the background shows through between the images, needs a mask. My first attempt used percentage values inside the radial gradient and nothing lined up, because a percentage inside a mask is measured against the image rather than the container. Those same container query units sort it out.

.bbioon-avatar-container img {
  mask: radial-gradient(50% 50% at calc(150% + var(--_m)), #0000 calc(100% + var(--g)), #000);
}

@property --_m {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

Registering --_m with @property is what makes the transition animatable. When someone hovers an avatar, you reset the margin to zero and the mask slides out of the way instead of snapping. It is a small detail, but it is the difference between a page that works and one that feels finished.

Let the browser do the math

CSS has reached the point where you no longer have to micromanage every pixel. Between :has() selectors and sibling-count(), a component can respond to what is around it. If you are still writing 500 lines of media queries for a simple list of images, you are doing more work than the job needs.

Browser support and edge cases are where this gets complicated. If you are tired of debugging someone else’s code and you just want your site to behave on every device, drop my team a line. We have probably seen it before.

So, are you still measuring layout widths in JavaScript, or have you moved over to container queries?

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.