Creating a JavaScript-Free Tabs Gallery for WordPress
Categories

Sometimes, simplicity is key—and in the escort industry, where galleries often showcase multiple photoshoots throughout the year, having a versatile and elegant solution for multiple galleries is essential.
Today, I’m sharing another trick from my bag: a JavaScript-free tabs gallery. It’s a straightforward yet interactive approach that relies purely on HTML and CSS. While I’m all for the power of JavaScript, there’s a longstanding movement to build web components without it whenever possible, without compromising interactivity. Let’s break down how this tabs gallery works and how you can implement it in WordPress using Advanced Custom Fields (ACF).
The Foundation: HTML Structure
Here’s the basic HTML for our tabs gallery:
<div class="tabs">
<input type="radio" id="tab-1" name="tabs" class="tabs__input" checked>
<input type="radio" id="tab-2" name="tabs" class="tabs__input">
<input type="radio" id="tab-3" name="tabs" class="tabs__input">
<div class="tabs__labels">
<label for="tab-1" class="tabs__label">New York <span>Fall 2023</span></label>
<label for="tab-2" class="tabs__label">Los Angeles <span>Spring 2024</span></label>
<label for="tab-3" class="tabs__label">London <span>Fall 2024</span></label>
</div>
<div class="tabs__content">
<div class="tabs__panel" id="tab-panel-1">
<div class="tabs__panel-image"><img src="image1.jpg" alt="New York Gallery"></div>
<!-- Add more images here -->
</div>
<div class="tabs__panel" id="tab-panel-2">
<div class="tabs__panel-image"><img src="image2.jpg" alt="Los Angeles Gallery"></div>
<!-- Add more images here -->
</div>
<div class="tabs__panel" id="tab-panel-3">
<div class="tabs__panel-image"><img src="image3.jpg" alt="London Gallery"></div>
<!-- Add more images here -->
</div>
</div>
</div>
Key Elements:
- Radio Inputs: These control which tab is active. Only one can be selected at a time because they share the same
name
attribute. - Labels: These act as tab buttons. Each label is associated with a specific input via the
for
attribute. - Content Panels: Each panel holds the content for a specific tab. Panels are styled to display only when their corresponding radio input is checked.
The Styling: CSS
The CSS does the heavy lifting to make this structure functional and visually appealing. Let’s break it down:
Tab Labels
.tabs__label {
display: block;
flex-grow: 1;
padding: 1rem;
background-color: #f9f9f9;
border-bottom: 1px solid #ddd;
text-align: center;
cursor: pointer;
transition: background-color 0.3s ease;
}
.tabs__label:hover {
background-color: #f1f1f1;
}
These styles make the labels look like clickable buttons. The hover
effect improves the user experience.
Active Tab Highlight
#tab-1:checked ~ .tabs__labels label[for="tab-1"],
#tab-2:checked ~ .tabs__labels label[for="tab-2"],
#tab-3:checked ~ .tabs__labels label[for="tab-3"] {
background-color: #fff;
border-bottom: 2px solid #111;
}
Here, we use the :checked
pseudo-class to style the active tab dynamically. I’ll admit that even after many years of CSS experience – this code makes me dizzy.
Panels
.tabs__panel {
display: none;
animation: fadeIn 0.3s ease;
columns: 3;
column-width: 300px;
column-gap: 2rem;
}
.tabs__panel-image {
margin-bottom: 2rem;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
#tab-1:checked ~ .tabs__content #tab-panel-1,
#tab-2:checked ~ .tabs__content #tab-panel-2,
#tab-3:checked ~ .tabs__content #tab-panel-3 {
display: block;
}
Only the panel corresponding to the checked input is displayed. The fadeIn
animation adds a nice touch.
Example
Integrating into WordPress with ACF
Although I haven’t given it a thorough try, I suspect implementing this in WordPress via the Gutenberg editor would be quite challenging. Gutenberg tends to be strict about stray inputs, often treating them as potentially malicious and either sanitizing or removing them altogether. To make this work effectively within WordPress, leveraging Advanced Custom Fields (ACF) would be the most reliable approach. ACF allows greater control over custom inputs, ensuring they are handled safely and seamlessly integrated without Gutenberg interfering.
Step 1: Set Up ACF
Create a Repeater Field in ACF for your custom post type. Inside the repeater, add the following subfields:
- Gallery Title: A text field for the tab’s title.
- Gallery Images: A gallery field to upload multiple images for each tab.
Step 2: Add a Unique ID to Each Panel
Each tab and panel needs a unique ID to ensure proper functionality. We use PHP to dynamically generate these IDs based on the index of the repeater field.
Step 3: Create a Custom Template
Here’s how to render the tabs in your theme:
<div class="tabs">
<?php if (have_rows('galleries')) : ?>
<?php $index = 1; ?>
<?php while (have_rows('galleries')) : the_row(); ?>
<input type="radio" id="tab-<?php echo $index; ?>" name="tabs" class="tabs__input" <?php echo $index === 1 ? 'checked' : ''; ?>>
<?php $index++; endwhile; ?>
<div class="tabs__labels">
<?php $index = 1; ?>
<?php while (have_rows('galleries')) : the_row(); ?>
<label for="tab-<?php echo $index; ?>" class="tabs__label">
<?php the_sub_field('gallery_title'); ?>
</label>
<?php $index++; endwhile; ?>
</div>
<div class="tabs__content">
<?php $index = 1; ?>
<?php while (have_rows('galleries')) : the_row(); ?>
<div class="tabs__panel" id="tab-panel-<?php echo $index; ?>">
<?php $images = get_sub_field('gallery_images'); ?>
<?php if ($images) : foreach ($images as $image) : ?>
<div class="tabs__panel-image">
<img src="<?php echo esc_url($image['url']); ?>" alt="<?php echo esc_attr($image['alt']); ?>">
</div>
<?php endforeach; endif; ?>
</div>
<?php $index++; endwhile; ?>
</div>
<?php endif; ?>
</div>
Explanation:
- Dynamic IDs: The
$index
variable ensures each input, label, and panel has a unique ID (e.g.,tab-1
,tab-2
). This is critical for associating labels with inputs and displaying the correct content panel. - Gallery Field: The
get_sub_field('gallery_images')
retrieves the images uploaded via ACF. Each image is looped through and displayed inside the corresponding panel. - Repeater Logic: The
have_rows()
andthe_row()
functions loop through the repeater field, allowing us to output a tab and panel for each gallery.
Why Go JavaScript-Free?
- Accessibility: Works even when JavaScript is disabled.
- Performance: Pure HTML and CSS are lightweight.
- Simplicity: Easier to maintain and debug.
What About Mobile?
Great news—this tabbed gallery works seamlessly on mobile devices. For most setups, it should display beautifully with a small number of galleries. However, if you find yourself dealing with a large number of galleries, you might consider simplifying the mobile experience by removing the tab functionality altogether and stacking the galleries vertically instead.
To adapt for this approach, include an <h2>
element inside each tab panel with the gallery name. This ensures users can still identify each gallery easily. For the desktop version, you’d want to hide these headings and show them only for mobile users. Similarly, you should hide the radio inputs and labels for the mobile layout to eliminate unnecessary elements. This creates a cleaner, more user-friendly experience while maintaining functionality across devices.
Final Thoughts
By combining thoughtful HTML, CSS, and dynamic WordPress functionality, you can create a stunning, interactive tabs gallery without JavaScript. This approach is particularly useful for showcasing photo galleries in industries like escort services, where the need for multiple, sleek galleries is a must. Sharing these tricks is my way of giving back and promoting smarter web design.