Easy tricks to improve site-speed with CSS
Did you know you can author some straight-up CSS to help with website perf? These tricks can come in super handy when you don't easily have access to the backend (ie a CMS like WordPress), you're not a backend developer, you don't wanna write some JS, and the cherry on top is that it's only a few lines of code in total.
CSS properties content-visibility
and contain-intrinsic-size
are your friends here
Gonna quote the MDN docs on content-visibility
verbatim:
"It enables the user agent to skip an element's rendering work (including layout and painting) until it is needed — which makes the initial page load much faster."
This property shines on a group of elements/markup that has a lot of children (eg a container of cards) AND it's below the viewport; there's no point in delaying the rendering if it's critical to the initial paint - if it's above the fold and you use another value besides the default content-visibility: visible;
you'll hurt UX.
.section {
content-visibility: auto;
contain-intrinsic-size: auto 250px;
}
The contain-intrinsic-size
property makes sure you reserve some space for the elements being 'hidden' till rendering/scrolling into view to prevent any jank and scrollbar-sizing issues. What it's doing is telling the browser 'hey, go ahead and reserve 250px
of space in each direction.' auto
then has the browser "remember" it's size for future paints.
You can kinda think of the combo above like setting loading="lazy"
for content that's not images.
The CSS @view-transition
API gives you the SPA treatment 💅 your users so deserve, without a full-blown framework necessary
Did you know that you don't need a full-blown framework to have that single page app (SPA), client-side navigation experience? You can get it in as little as three lines of CSS*!
*To respect accessibility you should wrap it in a media query to respect prefers-reduced-motion
though! Snippet borrowed from vtbag.dev.
@media (prefers-reduced-motion: no-preference) {
@view-transition {
navigation: auto;
}
}
It's to your benefit (to make sure it works!) that the pages you're navigating to/from both have this CSS declaration, and you're on the same site!
The sites I've used this on share the same header/nav and footer, so it's an easy and quick-win IMO.
Beware of bugs
Be mindful of testing your output when using content-visibility
on Safari.
I recently worked on a site where I had to write some JS to see if it was a Safari browser and change content-visibility: auto
to content-visibility: visible
.
Snippet below for the curious:
function isSafari() {
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
}
if (isSafari()) {
const cardsContainer = document.getElementById('example-cards');
if (cardsContainer) {
const observer = new IntersectionObserver(
(entries, obs) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const cards = cardsContainer.querySelectorAll('.example-card');
// made a class that had `content-visibility: visible`
cards.forEach((card) => card.classList.add('is-visible'));
obs.unobserve(cardsContainer); // Stop observing once visible
}
});
},
{
threshold: 0.01,
} // Trigger when 1% is visible
);
observer.observe(cardsContainer);
}
}
Yea, that was a few lines of JS but the perf gains I gained in my own example/testing was more than worth it.
Conclusion
I think it's definitely worth a shot to give these APIs a try: it's only a few LoC, it isn't pure magic, and can come in real handy when you can't/won't touch any server-side codes.
Also helps when you're trying to wrangle every bit of perf you can!