Accordion Override for Articles Module
If you have followed my writings in the Magazine, you already know that I really like overrides. Overrides have helped me understand how Joomla works and they give me the possibility to change almost everything without breaking Joomla core.
Overrides need maintenance like extensions do, but to a small extent, normally are only a few files you need to check after a Joomla update. Of course it is nice to use extensions with ready-to-use options and styles instead of writing your own code, but if you don’t try you don’t learn.
I’m always looking for inspiration for new overrides, new ways to display content on a website. Some weeks ago I stumbled upon a fancy accordion design created by Bramus Van Damme, a web developer from Belgium who is part of the Chrome Developer Relations team at Google, focusing on CSS, Web UI, and DevTools. The accordion is based on the <details> and <summary> elements and that is great, because these elements don’t need any kind of JavaScript and are accessible by default.
I immediately had the idea to create an override for the Articles Module based on this accordion. And that is what we will build:
We need
- A category “Travels” with three childs: Mountains, City trips and Road trips (I’m reusing the example of a previous article: https://magazine.joomla.org/all-issues/july-2024/create-a-banner-from-joomla-s-category-description)
- A text field for the categories to add an icon class (we will use Font Awesome icons). See above mentioned article to read more about creating fields.
The category Mountains for example gets the icon “mountain-sun”
- Some articles in each category, each with an intro image and a short intro text.
- An Article Module with these settings:
Override
I converted the HTML code from Bramus’ demo into PHP. The file accordion.php has to be copied into html\mod_articles inside your template structure.
accordion.php
<?php
/**
* @package Joomla.Site
* @subpackage mod_articles
*
* @copyright (C) 2024 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\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\Component\Fields\Administrator\Helper\FieldsHelper;
use Joomla\CMS\Router\Route;
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $app->getDocument()->getWebAssetManager();
if (!$list) {
return;
}
$items = $list;
// Get the category object
$categories = Factory::getApplication()->bootComponent('com_content')->getCategory();
?>
<div class="accordion-wrapper">
<?php foreach ($items as $item) : ?>
<?php
// Get the category id and the fields
$category = $categories->get($item->catid);
$jcfields = FieldsHelper::getFields('com_content.categories', $category, true);
// Create an associative array for easier access by field name
foreach($jcfields as $jcfield) {
$jcfields[$jcfield->name] = $jcfield;
}
// Get the intro image of the article
$images = json_decode($item->images);
$layoutAttr = [
'src' => $images->image_intro,
'alt' => empty($images->image_intro_alt) ? '' : $images->image_intro_alt,
];
?>
<details name="accordion" open >
<summary aria-label="<?php echo $item->displayCategoryTitle; ?>">
<span class="<?php echo $jcfields['icon-cat']->value; ?>" aria-hidden="true"></span>
<?php echo LayoutHelper::render('joomla.html.image', $layoutAttr); ?>
</summary>
<div class="details-content-wrapper">
<?php $item_heading = $params->get('item_heading', 'h4'); ?>
<<?php echo $item_heading; ?>>
<?php echo $item->title; ?>
</<?php echo $item_heading; ?>>
<?php echo $item->displayIntrotext; ?>
<a class="btn btn-accordion" href="/<?php echo $item->link; ?>" aria-label="<?php echo Text::sprintf('JGLOBAL_READ_MORE_TITLE', $item->title); ?>">
<?php echo '<span class="icon-chevron-right" aria-hidden="true"></span>'; ?>
</a>
</div>
</details>
<?php endforeach; ?>
</div>
We use the category icon in the <summary> element, that is the visible part of the accordion, where one can click to open the details.
The intro image also needs to be inside the <summary>, because otherwise it would be hidden when the <details> element is not open. With CSS we will position the image to look like a background image. We will also use the title and the intro text from each article:
CSS
I added some lines into the CSS from the demo and copied it in the user.css (using Cassiopeia):
.accordion-wrapper {
display: flex;
flex-direction: row;
gap: 1rem;
width: min-content;
margin: 0 auto;
}
details {
display: flex;
flex-direction: row;
background: transparent;
color: white;
height: 30rem;
border-radius: 2rem;
overflow: hidden;
/* To make the image work …*/
position: relative;
z-index: 1;
/* Hide marker */
::marker {
content: '';
}
/* The image is tucked in the summary, because otherwise it would be hidden when not [open] as it ends up in the ::details-content pseudo */
summary img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: -1;
transition: filter 0.5s ease;
}
/* Animate the image */
&[open] summary img {
filter: brightness(.3);
}
summary {
padding: 1rem 1em;
width: 6rem;
flex-shrink: 0; /* Prevent shrinking */
text-align: center;
span {
display: grid;
place-content: center;
width: 100%;
aspect-ratio: 1;
border-radius: 50%;
background: rgb(0, 0, 0, .25);
}
&:focus {
outline: none;
}
}
.details-content-wrapper {
padding: 1.5rem 1em;
width: 330px;
}
&:hover, &:has(summary:focus-visible) {
outline: 3px solid var(--cassiopeia-color-primary);
outline-offset: 3px;
}
span::before {
font-size: 1.5rem;
}
}
.details-content-wrapper {
display: flex;
flex-direction: column;
/* We need margin-trim … */
:first-child {
margin-top: 0;
}
:last-child {
margin-bottom: 0;
}
/* Animate-in the text when open */
p, a {
transform: translateY(2rem);
opacity: 0;
transition: opacity 0.5s ease;
transition-delay: 0.5s;
}
[open] & p,
[open] & a {
transform: none;
opacity: 1;
transition-delay: 0.5s;
}
.btn-accordion {
background: rgb(0, 0, 0, .25);
aspect-ratio: 1;
border-radius: 50%;
color: currentColor;
margin-inline-start: auto;
&:hover, &:focus-visible {
outline: 3px solid currentColor;
outline-offset: 2px;
}
}
}
The special thing in this override is that it uses “display: flex;” for the <details> element. Sadly it will only work as expected on the last version of Chrome. In other browsers the intro text will display next to the category icon and not side-by-side. Hopefully in the near future other browsers will also implement “display: flex;” (and other display options) for <details>.
Chrome
Firefox
Conclusion
There are lots of examples out there waiting to be converted into Joomla overrides. If you find something inspiring, but you are not able to create an override by yourself, please don’t hesitate to contact me. Maybe you can help me with my next Magazine article 😉
Some articles published on the Joomla Community Magazine represent the personal opinion or experience of the Author on the specific topic and might not be aligned to the official position of the Joomla Project
By accepting you will be accessing a service provided by a third-party external to https://magazine.joomla.org/
Comments 5
I have successfully followed your presentation but I can't get the intro text and the icons on the screen. What do i wrong? Example site can be found on demo.joomlaheerenveen.nl/motoren
Hi Berend, I can see the intro text in your demo...
The icon class for the span-element is missing, are you sure you have created a custom field for the categories?
Hi Viviana,
Yes i made an field for the Categories
And are you sure you are using the correct name of the field in the code? In my example the field is called "icon-cat":
echo $jcfields['icon-cat']->value;
I found out that I had forgotten to enter something in the tab General -->Name to enter the code
Thank you for the explanation