Simple inline gallery solution for the gallery block

I am looking for a simple solution for the gallery block type in the frontend (template) part, that just builds on top of the normal image template but with arrows to navigate through the next/previous images dynamically (on touch screen devices swiping should be possible as an alternative to clicking), similar to this example:

I don’t want to use a big JavaScript file just for this purpose but I couldn’t find a simple solution for a flexible number of images.
Any suggestions or recommendations would be much appreciated.

You could use Siema which I think is just about the smallest Slider (just 3kb)

Thanks, that looks and was easy to implement.
I just need to figure out how to define different classes for all images except the first one in the same for each loop.

1 Like

Great, I ended with this code:

<?php /** @var \Kirby\Cms\Block $block */ 

$galleryimagecount = $block->images()->toFiles()->count(); ?>

<div class="w-full relative">
    <figure class="siema justify-center !my-0">
    <?php foreach ($block->images()->toFiles() as $image): ?>
    <div>
    <a href="<?= $image->url() ?>" data-lightbox="<?= $image->url() ?>" class="nomark">
    ​  <picture>
        <!-- <source srcset="<?= $image->thumb(['width'   => 512, 'quality' => 48, 'format' => 'avif'])->url() ?> 512w, <?= $image->thumb(['width'   => 1024, 'quality' => 48, 'format' => 'avif'])->url() ?> 1024w, <?= $image->thumb(['width'   => 1536, 'quality' => 48, 'format' => 'avif'])->url() ?> 1536w, <?= $image->thumb(['width'   => 2048, 'quality' => 48, 'format' => 'avif'])->url() ?> 2048w" sizes="(max-width: 767px) calc(0.8 * 100vw), calc(0.61 * 100vw)" type="image/avif"> -->
        <source srcset="<?= $image->thumb(['width'   => 512, 'quality' => 55, 'format' => 'webp'])->url() ?> 512w, <?= $image->thumb(['width'   => 1024, 'quality' => 55, 'format' => 'webp'])->url() ?> 1024w, <?= $image->thumb(['width'   => 1536, 'quality' => 55, 'format' => 'webp'])->url() ?> 1536w, <?= $image->thumb(['width'   => 2048, 'quality' => 55, 'format' => 'webp'])->url() ?> 2048w" sizes="(max-width: 767px) calc(0.8 * 100vw), calc(0.61 * 100vw)" type="image/webp">
        <img src="<?= $image->toUrl() ?>" alt="<?= $image->alt() ?>" class="!my-1 max-h-[50vh] mx-auto md:px-2" srcset="<?= $image->thumb(['width'   => 512, 'quality' => 50])->url() ?> 512w, <?= $image->thumb(['width'   => 1024, 'quality' => 50])->url() ?> 1024w, <?= $image->thumb(['width'   => 1536, 'quality' => 50])->url() ?> 1536w, <?= $image->thumb(['width'   => 2048, 'quality' => 50])->url() ?> 2048w" decoding="async" loading="lazy">
        </picture>
    </a>

    <?php if ($image->caption()->isNotEmpty()): ?>
        <?php if ($image->link()->isNotEmpty()): ?><a href="<?= $image->link() ?>" class="nomark"><?php endif ?>
    <figcaption class="img-caption text-center !my-0 !py-0">
        <?= $image->caption() ?>
    </figcaption>
    <?php if ($image->link()->isNotEmpty()): ?></a><?php endif ?>
    </div>
    <?php endif ?>
<?php endforeach ?>
</figure>

<button class="prev -rotate-90 absolute left-0 top-1/2 <?php if ($block->images()->toFiles()->count() == "2"): ?>md:hidden<?php elseif ($block->images()->toFiles()->count() == "2"): ?>lg:hidden<?php endif ?>">            
    <svg class="rounded-full opacity-90" viewBox="0 0 48 48" width="48" height="48px" xmlns="http://www.w3.org/2000/svg">
        <path class="fill-[#1f40af]" d="M0 0h48v48h-48z"></path>
        <path class="fill-[white]" d="M14.83 30.83l9.17-9.17 9.17 9.17 2.83-2.83-12-12-12 12z"></path>
    </svg>
</button>
<button class="next rotate-90 absolute right-0 top-1/2 <?php if ($block->images()->toFiles()->count() == "2"): ?>md:hidden<?php elseif ($block->images()->toFiles()->count() == "2"): ?>lg:hidden<?php endif ?>">            
    <svg class="rounded-full opacity-90" viewBox="0 0 48 48" width="48" height="48px" xmlns="http://www.w3.org/2000/svg">
        <path class="fill-[#1f40af]" d="M0 0h48v48h-48z"></path>
        <path class="fill-[white]" d="M14.83 30.83l9.17-9.17 9.17 9.17 2.83-2.83-12-12-12 12z"></path>
    </svg>
</button>
</div>
<script src="<?= Bnomei\Fingerprint::url('/node_modules/siema/dist/siema.min.js'); ?>"></script>
<script>
const mySiema = new Siema({
    perPage: {
        768: 2,
        1024: ('<?=$galleryimagecount?>' >= 3) ? "3" : "2"
    },
    loop: true,
});
const prev = document.querySelector('.prev');
const next = document.querySelector('.next');

prev.addEventListener('click', () => mySiema.prev());
next.addEventListener('click', () => mySiema.next());
</script>

I know that it doesn’t really belong here but maybe you faced this issue as well and can share your solution with me?

You have not defined your variable totalSlides as e.g.

const siemaSelector = document.querySelector('.siema');
const totalSlides = siemaSelector.children.length;
const mySiema = new Siema({
  perPage: {
    768: Math.min(2, totalSlides),
    1024: totalSlides >= 3 ? Math.min(3, totalSlides) : Math.min(2, totalSlides),
  },
});

You don’t need your $galleryimagecount variable as you can count the items on the frontend.

Keep in mind that the library is no longer maintained.

1 Like

Thanks a lot for the extended support, much appreciated!
I tried the same before writing here desperately:

<?php /** @var \Kirby\Cms\Block $block */ 

$galleryimagecount = $block->images()->toFiles()->count(); ?>

<div class="w-full relative">
    <figure class="siema justify-center !my-0">
    <?php foreach ($block->images()->toFiles() as $image): ?>
        <div>
        <a href="<?= $image->url() ?>" data-lightbox="<?= $image->url() ?>" class="nomark">
        ​    <picture>
            <!-- <source srcset="<?= $image->thumb(['width'   => 512, 'quality' => 48, 'format' => 'avif'])->url() ?> 512w, <?= $image->thumb(['width'   => 1024, 'quality' => 48, 'format' => 'avif'])->url() ?> 1024w, <?= $image->thumb(['width'   => 1536, 'quality' => 48, 'format' => 'avif'])->url() ?> 1536w, <?= $image->thumb(['width'   => 2048, 'quality' => 48, 'format' => 'avif'])->url() ?> 2048w" sizes="(max-width: 767px) calc(0.8 * 100vw), calc(0.61 * 100vw)" type="image/avif"> -->
            <source srcset="<?= $image->thumb(['width'   => 512, 'quality' => 55, 'format' => 'webp'])->url() ?> 512w, <?= $image->thumb(['width'   => 1024, 'quality' => 55, 'format' => 'webp'])->url() ?> 1024w, <?= $image->thumb(['width'   => 1536, 'quality' => 55, 'format' => 'webp'])->url() ?> 1536w, <?= $image->thumb(['width'   => 2048, 'quality' => 55, 'format' => 'webp'])->url() ?> 2048w" sizes="(max-width: 767px) calc(0.8 * 100vw), calc(0.61 * 100vw)" type="image/webp">
            <img src="<?= $image->toUrl() ?>" alt="<?= $image->alt() ?>" class="!my-1 max-h-[50vh] mx-auto md:px-2" srcset="<?= $image->thumb(['width'   => 512, 'quality' => 50])->url() ?> 512w, <?= $image->thumb(['width'   => 1024, 'quality' => 50])->url() ?> 1024w, <?= $image->thumb(['width'   => 1536, 'quality' => 50])->url() ?> 1536w, <?= $image->thumb(['width'   => 2048, 'quality' => 50])->url() ?> 2048w" decoding="async" loading="lazy">
            </picture>
        </a>
        <?php if ($image->caption()->isNotEmpty()): ?>
            <?php if ($image->link()->isNotEmpty()): ?><a href="<?= $image->link() ?>" class="nomark"><?php endif ?>
            <figcaption class="img-caption text-center !my-0 !py-0">
                <?= $image->caption() ?>
            </figcaption>
        <?php if ($image->link()->isNotEmpty()): ?></a><?php endif ?>
        </div>
    <?php endif ?>
    <?php endforeach ?>
    </figure>

<button class="prev -rotate-90 absolute left-0 top-1/2 <?php if ($block->images()->toFiles()->count() == "2"): ?>md:hidden<?php elseif ($block->images()->toFiles()->count() == "2"): ?>lg:hidden<?php endif ?>">            
    <svg class="rounded-full opacity-90" viewBox="0 0 48 48" width="48" height="48px" xmlns="http://www.w3.org/2000/svg">
        <path class="fill-[#1f40af]" d="M0 0h48v48h-48z"></path>
        <path class="fill-[white]" d="M14.83 30.83l9.17-9.17 9.17 9.17 2.83-2.83-12-12-12 12z"></path>
    </svg>
</button>
<button class="next rotate-90 absolute right-0 top-1/2 <?php if ($block->images()->toFiles()->count() == "2"): ?>md:hidden<?php elseif ($block->images()->toFiles()->count() == "2"): ?>lg:hidden<?php endif ?>">            
    <svg class="rounded-full opacity-90" viewBox="0 0 48 48" width="48" height="48px" xmlns="http://www.w3.org/2000/svg">
        <path class="fill-[#1f40af]" d="M0 0h48v48h-48z"></path>
        <path class="fill-[white]" d="M14.83 30.83l9.17-9.17 9.17 9.17 2.83-2.83-12-12-12 12z"></path>
    </svg>
</button>
</div>
<script src="<?= Bnomei\Fingerprint::url('/node_modules/siema/dist/siema.min.js'); ?>"></script>
<script>
const siemaSelector = document.querySelector('.siema');
const siemaSelectorItems = siemaSelector.children.length;
const mySiema = new Siema({
    selector: siemaSelector,
    perPage: {
        768: Math.min(2, siemaSelector),
        1024: ('<?=$galleryimagecount?>' >= 3) ? "Math.min(3, siemaSelector)" : "Math.min(2, siemaSelector)"
    },
    loop: true,
});
const prev = document.querySelector('.prev');
const next = document.querySelector('.next');

prev.addEventListener('click', () => mySiema.prev());
next.addEventListener('click', () => mySiema.next());
</script>

But no matter if I use this code or yours I always end up with three images stacked on top of each other:

EDIT: I had the closing div element before the php endif but it must come afterwards.

I am considering to use Tiny-Slider instead of Siema because it is actively maintained but the minified JS file is nearly three times bigger…