Improving “likes” on my site

I recently published an article that got quite a bit of attention and it resulted in a lot of “likes” at the bottom of the page. I pull likes from Mastodon, Twitter and anyone who posts a webmention “like” to my site.

First of all the block was ridiculously large…

A massive block avatar images with a heading, 'likes (882)".
A ridiculous wall of faces

Secondly, this particular post was loading around 40mb of images to render those likes 😱

The fix

The first part was leaning in to HTML’s built in lazy loading capability. This way, the browser only loads the images if they are in, or near the viewport boundary (depending on browser):

<img loading="lazy" src="<?= $avatar; ?>" alt="<?= $like->comment_author ?>" />

Next, I got my sketch on. The idea for display logic was:

  1. If there are more than 15 comments, render a <details> element, with the <summary> being an overview of the comments
  2. If not, render the comments as they are
A rough sketch that shows a closed state with the text "Liked by lorem ipsum and 60 others".

The second state is the open state with the same text, but the wall of avatars now visible.
My rough sketch in procreate

With the sketch done, I had some HTML and PHP logic to write:

<?php if(count($likes) > 15): ?>
  <details>
    <summary>
      <span>Liked by <?= $likes[0]->comment_author; ?>, <?= $likes[1]->comment_author; ?>, <?= $likes[2]->comment_author; ?> and <strong><?= count($likes) - 3; ?> others</strong></span>
    </summary>
    <!-- Avatars go here -->
  </details>
<?php else: ?>
  <div class="sidebar">
    <h3 class="text-base">Liked by</h3>
    <div>
      <!-- Avatars go here -->
    </div>
  </div>
<?php endif; ?>

This renders the <details> and <summary> if there are more than 15 comments, or renders the original <h3> and <ul> of likes, but this time, I use The Sidebar from Every Layout to render the heading inline with list.

Styling up the the <details> and <summary> smells like global CSS, using the CUBE CSS principles, so I added this CSS as high up as I could:

summary {
  display: flex;
  align-items: baseline;
  line-height: 1.2;
  gap: 0 1ch;
  cursor: pointer;
  list-style: none;
}

summary:hover,
summary:focus-visible {
  color: var(--color-primary-shade);
}

summary::before {
  content: '+';
  display: block;
  color: var(--color-primary);
  line-height: 0.5;
  margin-inline-start: -2ch;
  transform: scale(1.4);
  transform-origin: right bottom;
}

[open] summary::before {
  content: '—';
}

/* Hide default marker */
summary::-webkit-details-marker,
summary::marker {
  content: '';
  display: none;
}

This renders either a + or depending on the closed and open state. A fun trick is because I’m using a monospaced font, I can use ch units for gap and margin-inline-start, to get a nice negative indent, just like I do with headings on this site too.

Also, this is why I’m using transform to scale the symbol, because then, the ch unit is still predictable. If I sized it with font-size, there’d be weird in-between sizes, so I’d have to do margin-inline-start: -1.6ch; or something.

The last neat CSS trick was this:

.comments .sidebar {
  --sidebar-target-width: max-content;
  --gutter: var(--space-xs);
}

I modified The Sidebar slightly by making it completely configurable with CSS Custom Properties:

.sidebar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gutter, var(--space-s-l));
}

.sidebar > :first-child {
  flex-basis: var(--sidebar-target-width, 20rem);
  flex-grow: 1;
}

.sidebar > :last-child {
  flex-basis: 0;
  flex-grow: 999;
  min-width: var(--sidebar-content-min-width, 50%);
}

Because our heading is the first child, I can use max-content to set the --sidebar-target-width, which means it’ll only ever be as big as the text itself. Lovely stuff.

And with all that, we get the finished products:

A heading of "responses", followed by a toggle that reads "like by Jon Baldwin, riz, Lucas Mallmann and 970 others"
The details and summary view
A heading of responses, then on the left, it reads "Liked by". On the right is 12 avatar images rendered side-by-side
The standard view with the sidebar

👋 Hello, I’m Andy and this is my little home on the web.

I’m the founder of Set Studio, a creative agency that specialises in building stunning websites that work for everyone and Piccalilli, a publication that will level you up as a front-end developer.


Back to blog