By Shehab Waleed, Founder, Cairo Studio 2 min read
The client logos on our homepage are drawn with CSS masks. Each logo is a mask over a solid block of colour, so the logos take the text colour and switch with the theme without a second set of files.
<span
class="bg-title mask-contain mask-center mask-no-repeat"
style:mask-image={`url(${logo})`}
></span>
During a performance pass, those logos showed up among the first requests on the homepage, at full size, even though the strip sits far below the first screen.
CSS images do not wait
An img element with loading="lazy" waits until it is near the viewport. A CSS image has no such option. The browser requests it when it styles and renders the element, and position on the page plays no part.
We checked this in headless Chromium with a mask placed 5000 pixels below the top. Without any scrolling, the mask file was requested. A lazy img element in the same spot was not.
Set the mask late
The fix is to leave the mask out of the markup until the strip is close. An IntersectionObserver with a generous margin flips one flag, and the style only exists after that.
const observer = new IntersectionObserver(
(entries) => {
if (!entries.some((e) => e.isIntersecting)) return;
observer.disconnect();
near = true;
},
{ rootMargin: '100% 0px' }
);
style:mask-image={near
? `url('${sized(logo, 640)}')`
: undefined}A margin of 100% means the logos start loading one full screen before the strip arrives, so a normal scroll never meets an empty mask.
Ask for the size you draw
The second half of the fix was size. The masks pointed at the original uploads, some of them large. Prismic serves images through imgix, so a width parameter returns a smaller file. The grid now asks for 640 pixels and the marquee for 320.
const sized = (url, w) =>
`${url}${url.includes('?') ? '&' : '?'}w=${w}`;
A third step had the same problem: a script sampled every logo at 96 pixels on load to decide how to frame it. That now waits for the same flag.
We shipped this with several other loading fixes on the same day, so we cannot give you a separate number for it. It is the same lesson as our five copies of one font: read the request list, and question everything on it that the first screen does not use.
