Over the weekend I was asked how to create a joomla 4 module to display images in an attractive way without installing and third party extensions. I did this for the gallery on the home page of my demo site (https://brianstest.site) and I realised I had never shared the steps taken to achieve it.
As with everything there are many ways to achieve the same thing but my objective here was that the gallery of images would be dynamically created from the content so that once setup a website administrator could change the images without having to look at any code.
Step 1 - The content
- Create a single category
- Create as many articles as you need making sure that you add both an intro and full image. Don’t forget to size and optimise the images appropriately before uploading them to your site
Step 2 - The module
- Create a module of type Articles - Newsflash and publish it in the desired template module position
- Make sure that you select the correct category and number of articles to display in the module options
Step 3 - The template override
This is the closest you will get to any code and you only have to do it once.
-
Create a template override for mod_articles_news by creating a new folder in your template eg /templates/cassiopeia/html/mod_articles_news/
-
Create a new file in that folder called gallery.php
-
Copy and paste the following code into the gallery.php file
<?php /** * @package Joomla.Site * @subpackage mod_articles_news * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Joomla\CMS\Layout\LayoutHelper; if (!$list) { return; } $templatePath = 'media/templates/site/' . $app->getDocument()->template; /** @var Joomla\CMS\WebAsset\WebAssetManager $wa */ $wa = $app->getDocument()->getWebAssetManager(); $wa->useScript('bootstrap.modal'); $wa->useScript('bootstrap.carousel'); $wa->registerAndUseScript('lightbox', $templatePath . '/js/index.bundle.min.js', [], ['defer' => true], []); ?> <div class="container"> <div class="row gallery"> <?php foreach ($list as $item) : $images = json_decode($item->images); $item->imageSrcIntro = htmlspecialchars($images->image_intro, ENT_COMPAT, 'UTF-8'); $item->imageSrcFull = \Joomla\CMS\Helper\MediaHelper::getCleanMediaFieldValue(htmlspecialchars($images->image_fulltext, ENT_COMPAT, 'UTF-8')); $item->imageAltIntro = htmlspecialchars($images->image_intro_alt, ENT_COMPAT, 'UTF-8'); $item->imageAltFull = htmlspecialchars($images->image_fulltext_alt, ENT_COMPAT, 'UTF-8'); $item->imageCaptionIntro = htmlspecialchars($images->image_intro_caption, ENT_COMPAT, 'UTF-8'); $item->imageCaptionFull = htmlspecialchars($images->image_fulltext_caption, ENT_COMPAT, 'UTF-8'); ?> <div class="photo"> <a href="/<?php echo $item->imageSrcFull; ?>" data-toggle="lightbox" data-gallery="photo-gallery" data-caption="<?php echo $item->imageAltFull; ?>"> <?php echo LayoutHelper::render ( 'joomla.html.image', [ 'src' => $item->imageSrcIntro, 'alt' => $item->imageAltIntro, ] ); ?> </a> </div> <?php endforeach; ?> </div> </div>
Step 4 - The JavaScript
For the final part we need to add one javascript file - Lightbox for Bootstrap 5
All the setup was done for this in step 3 so just download the file (right-click, Save As...) and then upload it to your website and save it into the js folder of your template. eg "/media/templates/site/cassiopeia/js/index.bundle.min.js"
Step 5 - The module (again)
- Re-open the module you created in step 2 and on the advanced tab set the first field layout to be gallery
Step 6 - The css (optional)
The actual css to use will depend on your site and template but this is the css I used and placed in the "/media/templates/site/cassiopeia/css/user.css"
.gallery div {
flex: auto;
width: 220px;
margin: .5vw
}
.gallery div img {
width: 100%;
height: auto;
box-shadow: .3rem .4rem .4rem rgba(0,0,0,0.4);
transition: transform 400ms ease-out
}
.gallery div img:hover {
transform: scale(1.15)
}
.gallery .photo:hover {
background: no-repeat center/20% url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path fill="darkmagenta" d="m28.34 24.12-6.5-6.85A10.49 10.49 0 1 0 12.5 23h.55a1 1 0 1 0-.1-2 8.5 8.5 0 1 1 5.4-2.32A8.72 8.72 0 0 1 16.53 20a1 1 0 0 0-.41 1.35.89.89 0 0 0 .36.36v.08l6.77 7.13A3.48 3.48 0 0 0 25.73 30h.09a3.43 3.43 0 0 0 2.39-1 3.47 3.47 0 0 0 .13-4.88zm-1.5 3.47a1.45 1.45 0 0 1-1.06.41 1.51 1.51 0 0 1-1-.46l-6.15-6.49a10.07 10.07 0 0 0 1.14-.93 10.54 10.54 0 0 0 1-1.12l6.16 6.5a1.47 1.47 0 0 1-.09 2.09z"/><path fill="darkmagenta" d="M8.55 8.16a1 1 0 0 0-1.39.29 7.19 7.19 0 0 0 1.17 9.29A1 1 0 0 0 9 18a1 1 0 0 0 .67-1.74A5.32 5.32 0 0 1 8 12.91a5.24 5.24 0 0 1 .84-3.36 1 1 0 0 0-.29-1.39z"/></svg>');
opacity: 1
}
.gallery img:hover {
opacity: .5
}
.carousel-item,.carousel-item .bg-dark {
min-block-size: 300px
}
.carousel-control-next-icon,.carousel-control-prev-icon {
background-color: var(--brand);
border-radius: 0
}
.lightbox-caption {
background-color: var(--brand)
}
.modal-backdrop.show {
opacity: .8
}
@media screen and (max-width: 400px) {
.gallery div {
margin:0
}
.gallery {
padding: 0
}
}