Robert Birming

Micro.blog gallery

This Bearming add-on for Bear fetches and displays your latest images from a Micro.blog JSON feed in a responsive grid. Each photo links back to the original post.1

Preview

How to use

Add the markup wherever you want the gallery to appear, then add the script and styles to your theme. Replace the feed URL with your own Micro.blog JSON feed URL. Adjust data-limit to control how many photos are shown.

Markup

<section class="mb-gallery"
         data-feed="https://yoursite.com/categories/photos/feed.json"
         data-limit="12">
  <div class="mb-gallery-grid" aria-live="polite">
    <p>Loading...</p>
  </div>
</section>

Script

<script>
/* Micro.blog gallery | robertbirming.com */
(async () => {
  const root = document.querySelector('.mb-gallery');
  if (!root) return;

  const grid = root.querySelector('.mb-gallery-grid');
  const feedUrl = root.getAttribute('data-feed');
  const limit = parseInt(root.getAttribute('data-limit') || '12', 10);

  if (!feedUrl) { grid.innerHTML = '<p>Feed not configured.</p>'; return; }

  async function fetchItems(url, max) {
    const items = [];
    let next = url;
    while (next && items.length < max) {
      try {
        const res = await fetch(next);
        if (!res.ok) break;
        const data = await res.json();
        items.push(...(data.items || []));
        next = data.next_url || null;
      } catch { break; }
    }
    return items;
  }

  const tmp = document.createElement('div');

  function parsePhoto(item) {
    if (!item.content_html) return null;
    tmp.innerHTML = item.content_html;
    const img = tmp.querySelector('img');
    if (!img?.src) return null;
    return {
      src: img.src,
      alt: (item.content_text || tmp.textContent || '').trim().slice(0, 120) || img.alt || 'Photo',
      url: item.url || '#',
      date: new Date(item.date_published || 0).getTime()
    };
  }

  try {
    const items = await fetchItems(feedUrl, limit * 3);
    const photos = items
      .map(parsePhoto)
      .filter(Boolean)
      .sort((a, b) => b.date - a.date)
      .slice(0, limit);

    if (!photos.length) { grid.innerHTML = '<p>No recent photos found.</p>'; return; }

    const fragment = document.createDocumentFragment();
    photos.forEach(photo => {
      const cell = document.createElement('div');
      cell.className = 'mb-gallery-item';
      const link = document.createElement('a');
      link.href = photo.url;
      link.rel = 'noopener';
      const img = document.createElement('img');
      img.src = photo.src;
      img.alt = photo.alt;
      img.loading = 'lazy';
      img.decoding = 'async';
      link.appendChild(img);
      cell.appendChild(link);
      fragment.appendChild(cell);
    });

    grid.replaceChildren(fragment);
  } catch {
    grid.innerHTML = "<p>Couldn't load photos right now.</p>";
  }
})();
</script>

Styles

/* Micro.blog gallery | robertbirming.com */
.mb-gallery {
  margin-block: var(--space-block);
}

.mb-gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
  gap: 0.5rem;
}

.mb-gallery-item {
  overflow: hidden;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
}

.mb-gallery-item a {
  display: block;
  width: 100%;
  height: 100%;
}

.mb-gallery-item img {
  display: block;
  width: 100%;
  height: 100%;
  margin: 0;
  object-fit: cover;
  transition: transform 0.2s ease;
}

@media (hover: hover) {
  .mb-gallery-item a:hover img {
    transform: scale(1.04);
  }
}

.mb-gallery-grid > p {
  margin-block: 0;
  color: var(--muted);
}

Want more? Check out all available Bearming add-ons.

Happy blogging, and keep shooting.

  1. Built for the Bearming theme. Using a different theme? Add the Bearming tokens to make them work with your setup.