José Moreira

TEST · Code + images + text — Rendering ACF galleries in a static blog

Test post: text, code blocks (PHP, SCSS, HTML) and loose images mixed together.

Test post mixing text, code and images. The topic is fitting: turning the acf/gallery block into plain HTML in the static generator.

Placeholder image
Loose image before the first code block

Parsing the block

WordPress stores the gallery as a block comment with the attachment IDs in its JSON. parse_blocks() does the hard part.

<?php
// Find every ACF gallery block in a post and return its attachment IDs.
function acf_gallery_ids(string $content): array {
    $ids = [];
    foreach (parse_blocks($content) as $block) {
        if ($block['blockName'] !== 'acf/gallery') {
            continue;
        }
        $ids = array_merge($ids, array_map('intval', $block['attrs']['data']['gallery'] ?? []));
    }
    return $ids;
}

With the IDs in hand, each image becomes a <figure> with a proper srcset.

<?php
// Render one gallery as static HTML, no JavaScript required.
function render_gallery(array $ids): string {
    $items = array_map(function (int $id): string {
        $src    = wp_get_attachment_image_url($id, 'large');
        $srcset = wp_get_attachment_image_srcset($id, 'large');
        $alt    = esc_attr(get_post_meta($id, '_wp_attachment_image_alt', true));
        return "<figure><img src=\"{$src}\" srcset=\"{$srcset}\" alt=\"{$alt}\" loading=\"lazy\"></figure>";
    }, $ids);
    return '<div class="gallery">' . implode('', $items) . '</div>';
}
Bonsai photo
A portrait image between code blocks

Styling it

// Responsive grid that keeps portrait photos from towering over the text.
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
  gap: 0.5rem;

  img {
    width: 100%;
    aspect-ratio: 3 / 4;
    object-fit: cover;
    border-radius: 0.25rem;
  }
}

The output

<div class="gallery">
  <figure><img src="…/bonsai-1.webp" alt="Olive" loading="lazy"></figure>
  <figure><img src="…/bonsai-2.webp" alt="Juniper" loading="lazy"></figure>
</div>
Placeholder image
Loose image at the end

End of the test post.