Build a WordPress Hero Slider Block with Thumbnail Navigation

Create a full-screen hero slider in WPBits Block Studio with repeatable slides, background images, CTA buttons, thumbnail navigation, and autoplay.

WPBits Team
6 min read
#gutenberg-blocks #wordpress-development #hero-slider #repeater-fields #blockstudio
WPBits Block Studio template editor for a WordPress hero slider block

A hero slider is often the first thing visitors see on a landing page, product page, or campaign page. It needs strong imagery, clear messaging, a CTA, and navigation that feels polished without making the block difficult for editors to manage.

This tutorial shows how to build a Hero Slider block in WPBits Block Studio. Editors will be able to add multiple slides, upload a background image for each slide, write the heading text, configure a CTA button, and control whether the slider starts automatically.

The final block includes a full-screen background slider, animated content, vertical thumbnail navigation, circular progress animation, and a small frontend script for slide transitions.

Hero slider block - Frontend Hero slider block. Backend

Why This Block Is Usually More Work Than It Looks

A hero slider sounds like a single component, but the conventional WordPress development route can quickly expand: custom block setup, repeatable slide data, media controls, link controls, frontend markup, responsive layout, animation timing, thumbnail navigation, and autoplay behavior.

That is a lot of wiring for a block that editors mostly need to manage as structured content. WPBits Block Studio keeps the workflow focused: define the fields, add the template, add the styles and script, then use the block in the WordPress editor.

In this tutorial, you will create:

  • A reusable Hero Slider Gutenberg block
  • A slides repeater for multiple hero slides
  • Image, heading, punchline, subtitle, and CTA fields
  • An autoStart setting for autoplay control
  • Full-screen slide backgrounds
  • Animated slide content
  • Thumbnail navigation with circular progress

Requirements

Before you start, make sure you have:

  • WordPress 6.0 or newer
  • WPBits Block Studio installed and activated
  • Access to the WordPress admin

For broader product setup and feature references, see the WPBits Block Studio documentation.

Step 1: Create a New Block

Open the WordPress admin and go to Block Builder. Create a new block for the slider.

Use these settings:

SettingValue
Block Namehero-slider
Block TitleHero Slider
DescriptionDisplay a full-screen hero slider with thumbnails and CTA buttons.
Categorydesign or widgets

The block name should stay short and specific because it becomes part of the block identity and CSS wrapper class.

Step 2: Add the Autoplay Setting

Hero slider block attribute repeater fied

Add a top-level attribute for autoplay.

SettingValue
Attribute NameautoStart
Control Typetoggle
LabelAuto Start
Default Valuetrue

The template prints this value as data-auto-start, and the frontend script reads that attribute to decide whether the slider should start cycling automatically.

Step 3: Add the Slides Repeater

Hero slider block attribute repeater fied

Add a repeater attribute for the slides.

SettingValue
Attribute Nameslides
Control Typerepeater
LabelSlides

Inside the slides repeater, add these fields.

FieldAttribute NameControl TypeLabelNotes
ImageimageimageImageUsed for the background and thumbnail
HeadingheadingtextHeadingMain hero title
PunchlinepuchlinetextPuchlineSmall uppercase text above the heading
SubtitlesubtitletextSubtitleOptional second large heading line
CTA ButtonctalinkCTA ButtonButton URL, label, and new-tab setting

Use the attribute names exactly as shown if you use the template below. The template reads these names directly from each slide item.

Step 4: Add the Template

Open the Template tab and add the template below.

<div {{ wrapper_attributes|raw }} data-hero-slider data-auto-start={{autoStart}}>
{% if slides is iterable and slides|length > 0 %}
{# Background Images Container #}
<div class="wpbits-hero-slider__backgrounds">
{% for item in slides %}
<div class="wpbits-hero-slider__slide-background {% if loop.first %}wpbits-hero-slider__slide-background--active{% endif %}" data-slide-index="{{ loop.index0 }}">
{% if item.image %}
<img src="{{ item.image|esc_url }}" alt="" class="wpbits-hero-slider__background-image" />
{% endif %}
<div class="wpbits-hero-slider__background-overlay"></div>
</div>
{% endfor %}
</div>
{# Content Wrapper #}
<div class="wpbits-hero-slider__content-wrapper">
<div class="wpbits-hero-slider__content">
{% for item in slides %}
<div class="wpbits-hero-slider__slide-content {% if loop.first %}wpbits-hero-slider__slide-content--active{% endif %}" data-slide-index="{{ loop.index0 }}">
{% if item.puchline %}
<div class="wpbits-hero-slider__description">{{ item.puchline|esc_html }}</div>
{% endif %}
{% if item.heading %}
<h2 class="wpbits-hero-slider__title">{{ item.heading|esc_html }}</h2>
{% endif %}
{% if item.subtitle %}
<h2 class="wpbits-hero-slider__subtitle">{{ item.subtitle|esc_html }}</h2>
{% endif %}
{% if item.cta and item.cta.url %}
<a href="{{ item.cta.url|esc_url }}"
class="wpbits-hero-slider__cta-button"
{% if item.cta.opensInNewTab %}target="_blank" rel="noopener noreferrer"{% endif %}>
{{ (item.cta.title or 'EXPLORE') }}
</a>
{% endif %}
</div>
{% endfor %}
</div>
</div>
{# Thumbnail Navigation - CSS-only circular progress #}
<nav class="wpbits-hero-slider__thumbnail-nav">
{% for item in slides %}
<div class="wpbits-hero-slider__thumbnail-wrapper {% if loop.first %}wpbits-hero-slider__thumbnail-wrapper--active{% endif %}"
data-slide-index="{{ loop.index0 }}"
data-thumbnail>
{# CSS-only circular progress (no SVG needed) #}
<div class="wpbits-hero-slider__circular-progress"></div>
{% if item.image %}
<img src="{{ item.image|esc_url }}" alt="Slide {{ loop.index }}" class="wpbits-hero-slider__thumbnail" />
{% endif %}
</div>
{% endfor %}
</nav>
{% endif %}
</div>

The template turns the block attributes into three frontend layers.

  • slides is the repeater value. Each item is one slide.
  • The first loop renders the background images and overlay.
  • The second loop renders the visible text and CTA content for each slide.
  • The third loop renders thumbnail navigation.
  • loop.first marks the first background, content panel, and thumbnail as active on initial page load.
  • loop.index0 gives the JavaScript a zero-based slide index to match backgrounds, content, and thumbnails.
  • item.image, item.heading, item.puchline, item.subtitle, and item.cta read the fields added inside the repeater.
  • wrapper_attributes keeps the block connected to the WordPress block wrapper and its generated classes.

Step 5: Add the Frontend JavaScript

Open the JavaScript tab and add the frontend script.

/**
* Hero Slider - Vanilla JavaScript
* Handles slide transitions, thumbnail navigation, and CSS-based progress animations
*/
(function () {
'use strict';
// Configuration
const SLIDE_DURATION = 5000; // 5 seconds per slide
class HeroSlider {
constructor(element) {
this.slider = element;
this.slides = Array.from(
this.slider.querySelectorAll('.wpbits-hero-slider__slide-background'),
);
this.slideContents = Array.from(
this.slider.querySelectorAll('.wpbits-hero-slider__slide-content'),
);
this.thumbnails = Array.from(
this.slider.querySelectorAll('[data-thumbnail]'),
);
// Check if auto-start is enabled (defaults to true for backward compatibility)
this.autoStart = this.slider.getAttribute('data-auto-start') !== 'false';
this.currentIndex = 0;
this.isPaused = false;
this.timeoutId = null;
this.init();
}
init() {
// Set up thumbnail click handlers
this.thumbnails.forEach((thumbnail, index) => {
thumbnail.addEventListener('click', () => this.goToSlide(index));
thumbnail.addEventListener('mouseenter', () => this.pause());
thumbnail.addEventListener('mouseleave', () => this.resume());
});
// Initialize first slide
this.updateSlide(0);
// Start auto-advance only if enabled
if (this.autoStart) {
this.startTimer();
}
}
startTimer() {
// Schedule next slide
this.timeoutId = setTimeout(() => {
this.nextSlide();
}, SLIDE_DURATION);
}
stopTimer() {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
}
pause() {
if (this.isPaused || !this.autoStart) return;
this.isPaused = true;
this.stopTimer();
// Pause CSS animation
const activeThumbnail = this.thumbnails[this.currentIndex];
const progressCircle = activeThumbnail.querySelector(
'.wpbits-hero-slider__circular-progress',
);
if (progressCircle) {
progressCircle.style.animationPlayState = 'paused';
}
}
resume() {
if (!this.isPaused || !this.autoStart) return;
this.isPaused = false;
this.startTimer();
// Resume CSS animation
const activeThumbnail = this.thumbnails[this.currentIndex];
const progressCircle = activeThumbnail.querySelector(
'.wpbits-hero-slider__circular-progress',
);
if (progressCircle) {
progressCircle.style.animationPlayState = 'running';
}
}
nextSlide() {
const nextIndex = (this.currentIndex + 1) % this.slides.length;
this.goToSlide(nextIndex);
}
goToSlide(index) {
if (index === this.currentIndex) return;
// Stop current timer
this.stopTimer();
// Update active states
this.updateSlide(index);
// Reset timer for new slide (only if auto-start is enabled)
if (!this.isPaused && this.autoStart) {
this.startTimer();
}
}
updateSlide(index) {
// Update backgrounds
this.slides.forEach((slide, i) => {
slide.classList.toggle(
'wpbits-hero-slider__slide-background--active',
i === index,
);
});
// Update content with animation reset
this.slideContents.forEach((content, i) => {
if (i === index) {
// Force reflow to restart animations
content.classList.remove('wpbits-hero-slider__slide-content--active');
void content.offsetWidth; // Trigger reflow
content.classList.add('wpbits-hero-slider__slide-content--active');
} else {
content.classList.remove('wpbits-hero-slider__slide-content--active');
}
});
// Update thumbnails - CSS handles the circular progress animation
this.thumbnails.forEach((thumbnail, i) => {
const isActive = i === index;
thumbnail.classList.toggle(
'wpbits-hero-slider__thumbnail-wrapper--active',
isActive,
);
// Reset animation on all thumbnails
const progressCircle = thumbnail.querySelector(
'.wpbits-hero-slider__circular-progress',
);
if (progressCircle) {
if (isActive) {
// Remove and re-add the element to restart CSS animation
const parent = progressCircle.parentNode;
const clone = progressCircle.cloneNode(true);
parent.replaceChild(clone, progressCircle);
}
}
});
this.currentIndex = index;
}
destroy() {
this.stopTimer();
// Remove event listeners
this.thumbnails.forEach((thumbnail) => {
thumbnail.replaceWith(thumbnail.cloneNode(true));
});
}
}
// Initialize all hero sliders on the page
function initHeroSliders() {
const sliders = document.querySelectorAll('[data-hero-slider]');
const instances = [];
sliders.forEach((slider) => {
instances.push(new HeroSlider(slider));
});
return instances;
}
// Auto-initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initHeroSliders);
} else {
initHeroSliders();
}
// Expose for manual initialization if needed
window.HeroSlider = HeroSlider;
window.initHeroSliders = initHeroSliders;
})();

The script controls the slider state and leaves the visual animation work to CSS.

  • It finds every block instance with data-hero-slider.
  • It collects slide backgrounds, slide content panels, and thumbnails.
  • It reads data-auto-start to decide whether autoplay should run.
  • Clicking a thumbnail calls goToSlide(index) and activates the matching background, content panel, and thumbnail.
  • nextSlide() advances through the slides and wraps back to the first slide.
  • Hovering a thumbnail pauses autoplay and the circular progress animation.
  • updateSlide(index) is the central state change: it toggles active classes and restarts the content/progress animations.

Step 6: Add the Frontend CSS

Open the CSS tab and add the slider styles.

/* Import Fonts */
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@300;400;600;700&family=Montserrat:wght@300;400;500;600&display=swap');
/* CSS @property for smooth conic-gradient animation */
@property --progress {
syntax: '<number>';
inherits: false;
initial-value: 0;
}
/* Reset and Base */
* {
box-sizing: border-box;
}
/* Hero Slider Base Styles */
.wp-block-sample-blocks-hero-slider {
position: relative;
width: 100%;
height: 100vh;
overflow: hidden;
background: #1a1a1a;
}
/* Background Images */
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__backgrounds {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__slide-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 1s cubic-bezier(0.4, 0, 0.2, 1);
}
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__slide-background--active {
opacity: 1;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__background-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transform: scale(1.1);
transition: transform 7s cubic-bezier(0.4, 0, 0.2, 1);
}
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__slide-background--active
.wpbits-hero-slider__background-image {
transform: scale(1);
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__background-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
135deg,
rgba(0, 0, 0, 0.4) 0%,
rgba(0, 0, 0, 0.2) 50%,
rgba(0, 0, 0, 0.5) 100%
);
mix-blend-mode: multiply;
}
/* Content Wrapper - CRITICAL FOR VERTICAL CENTERING */
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__content-wrapper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
padding: 0;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__content {
position: relative;
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 100px 0 140px;
text-align: center;
}
/* Slide Content Container */
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__slide-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
max-width: 900px;
padding: 0 2rem;
opacity: 0;
pointer-events: none;
transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1);
color: white;
text-align: center;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__slide-content--active {
opacity: 1;
pointer-events: auto;
}
/* Hero Text Styles */
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__description {
font-family: 'Montserrat', sans-serif;
font-size: 1rem;
font-weight: 300;
letter-spacing: 3px;
margin: 0 0 1.5rem 0;
text-transform: uppercase;
animation: wpbitsHeroSliderFadeInUp 0.8s cubic-bezier(0.4, 0, 0.2, 1) 0.3s
both;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__title {
font-family: 'Cormorant Garamond', serif;
font-size: clamp(4rem, 15vw, 10rem);
font-weight: 900;
color: #fff;
line-height: 0.9;
margin: 0;
letter-spacing: -0.02em;
animation: wpbitsHeroSliderFadeInUp 1s cubic-bezier(0.4, 0, 0.2, 1) 0.5s both;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__subtitle {
font-family: 'Cormorant Garamond', serif;
font-size: clamp(4rem, 15vw, 10rem);
font-weight: 700;
line-height: 0.9;
margin: 0 0 2.5rem 0;
letter-spacing: -0.02em;
background: linear-gradient(135deg, #ffffff 0%, #e0e0e0 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: wpbitsHeroSliderFadeInUp 1s cubic-bezier(0.4, 0, 0.2, 1) 0.7s both;
}
/* CTA Button */
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__cta-button {
display: inline-block;
padding: 1.2rem 3.5rem;
font-family: 'Montserrat', sans-serif;
font-size: 0.9rem;
font-weight: 500;
letter-spacing: 3px;
text-transform: uppercase;
color: #1a1a1a;
background: white;
border: none;
text-decoration: none;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
animation: wpbitsHeroSliderFadeInUp 0.8s cubic-bezier(0.4, 0, 0.2, 1) 0.9s
both;
margin-top: 1rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__cta-button::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.3),
transparent
);
transition: left 0.6s;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__cta-button:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__cta-button:hover::before {
left: 100%;
}
/* Thumbnail Navigation */
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__thumbnail-nav {
position: absolute;
left: 3rem;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
gap: 2rem;
z-index: 20;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__thumbnail-wrapper {
position: relative;
cursor: pointer;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__thumbnail-wrapper:hover {
transform: scale(1.05);
}
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__thumbnail-wrapper--active {
transform: scale(1.1);
}
/* Circular Progress - SMOOTH CONIC GRADIENT with @property */
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__circular-progress {
--progress: 0;
position: absolute;
top: -4px;
left: -4px;
width: 88px;
height: 88px;
border-radius: 50%;
background: conic-gradient(
from -90deg,
white 0deg,
white calc(var(--progress) * 1deg),
rgba(255, 255, 255, 0.2) calc(var(--progress) * 1deg),
rgba(255, 255, 255, 0.2) 360deg
);
-webkit-mask: radial-gradient(
farthest-side,
transparent calc(100% - 3px),
white calc(100% - 2px)
);
mask: radial-gradient(
farthest-side,
transparent calc(100% - 3px),
white calc(100% - 2px)
);
opacity: 0;
pointer-events: none;
}
/* Animate the progress on active thumbnail */
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__thumbnail-wrapper--active
.wpbits-hero-slider__circular-progress {
opacity: 1;
animation: wpbitsCircularProgress 5s linear forwards;
}
/* Smooth conic-gradient animation using @property */
@keyframes wpbitsCircularProgress {
from {
--progress: 0;
}
to {
--progress: 360;
}
}
/* Hide old SVG if it exists */
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__circular-progress svg,
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__progress-bg,
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__progress-fill {
display: none;
}
/* Thumbnail Image */
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__thumbnail {
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
border: 3px solid white;
display: block;
opacity: 0.6;
transition: all 0.3s ease;
position: relative;
z-index: 1;
}
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__thumbnail-wrapper--active
.wpbits-hero-slider__thumbnail {
opacity: 1;
}
/* Animations */
@keyframes wpbitsHeroSliderFadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__slide-content:not(
.wpbits-hero-slider__slide-content--active
)
.wpbits-hero-slider__description,
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__slide-content:not(
.wpbits-hero-slider__slide-content--active
)
.wpbits-hero-slider__title,
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__slide-content:not(
.wpbits-hero-slider__slide-content--active
)
.wpbits-hero-slider__subtitle,
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__slide-content:not(
.wpbits-hero-slider__slide-content--active
)
.wpbits-hero-slider__cta-button {
animation: none;
opacity: 0;
transform: translateY(30px);
}
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__slide-content--active
.wpbits-hero-slider__description,
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__slide-content--active
.wpbits-hero-slider__title,
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__slide-content--active
.wpbits-hero-slider__subtitle,
.wp-block-sample-blocks-hero-slider
.wpbits-hero-slider__slide-content--active
.wpbits-hero-slider__cta-button {
animation-play-state: running;
}
/* Responsive Design */
/* Large Desktop */
@media (min-width: 1440px) {
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__content {
max-width: 1400px;
padding: 0 120px 0 160px;
}
}
/* Desktop */
@media (max-width: 1366px) {
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__content {
padding: 0 80px 0 120px;
}
}
/* Tablet Landscape */
@media (max-width: 1024px) {
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__thumbnail-nav {
left: 2rem;
gap: 1.5rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__content {
padding: 0 60px 0 100px;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__title,
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__subtitle {
font-size: clamp(3rem, 12vw, 8rem);
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__description {
font-size: 0.9rem;
letter-spacing: 2.5px;
}
}
/* Tablet Portrait */
@media (max-width: 768px) {
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__thumbnail-nav {
left: 1.5rem;
gap: 1.2rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__thumbnail {
width: 60px;
height: 60px;
border-width: 2px;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__circular-progress {
width: 68px;
height: 68px;
top: -4px;
left: -4px;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__content {
padding: 0 2rem 0 90px;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__slide-content {
padding: 0 1rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__title,
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__subtitle {
font-size: clamp(2.5rem, 11vw, 6rem);
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__description {
font-size: 0.85rem;
letter-spacing: 2px;
margin-bottom: 1.2rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__subtitle {
margin-bottom: 2rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__cta-button {
padding: 1rem 2.5rem;
font-size: 0.8rem;
letter-spacing: 2px;
}
}
/* Mobile Large */
@media (max-width: 480px) {
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__thumbnail-nav {
left: 1rem;
gap: 1rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__thumbnail {
width: 50px;
height: 50px;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__circular-progress {
width: 58px;
height: 58px;
top: -4px;
left: -4px;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__content {
padding: 0 1.5rem 0 70px;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__slide-content {
padding: 0 0.5rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__title,
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__subtitle {
font-size: clamp(2rem, 10vw, 4.5rem);
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__description {
font-size: 0.75rem;
letter-spacing: 1.5px;
margin-bottom: 1rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__subtitle {
margin-bottom: 1.5rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__cta-button {
padding: 0.9rem 2rem;
font-size: 0.75rem;
letter-spacing: 1.5px;
}
}
/* Mobile Small */
@media (max-width: 375px) {
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__thumbnail-nav {
left: 0.75rem;
gap: 0.9rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__thumbnail {
width: 45px;
height: 45px;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__circular-progress {
width: 53px;
height: 53px;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__content {
padding: 0 1rem 0 60px;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__title,
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__subtitle {
font-size: clamp(1.8rem, 10vw, 4rem);
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__description {
font-size: 0.7rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__cta-button {
padding: 0.8rem 1.8rem;
}
}
/* Landscape Orientation */
@media (max-height: 600px) and (orientation: landscape) {
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__title,
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__subtitle {
font-size: clamp(2rem, 10vh, 5rem);
line-height: 0.85;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__description {
font-size: 0.75rem;
margin-bottom: 0.8rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__subtitle {
margin-bottom: 1.2rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__cta-button {
padding: 0.8rem 2rem;
font-size: 0.75rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__thumbnail-nav {
gap: 0.8rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__thumbnail {
width: 45px;
height: 45px;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__circular-progress {
width: 53px;
height: 53px;
}
}
/* Very Short Landscape */
@media (max-height: 450px) and (orientation: landscape) {
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__title,
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__subtitle {
font-size: clamp(1.5rem, 9vh, 3.5rem);
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__description {
font-size: 0.65rem;
margin-bottom: 0.6rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__subtitle {
margin-bottom: 1rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__cta-button {
padding: 0.7rem 1.8rem;
font-size: 0.7rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__thumbnail-nav {
gap: 0.6rem;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__thumbnail {
width: 40px;
height: 40px;
}
.wp-block-sample-blocks-hero-slider .wpbits-hero-slider__circular-progress {
width: 48px;
height: 48px;
}
}

The CSS controls the full visual system for the block.

  • The block wrapper creates the full-screen hero area.
  • Background classes stack slide images and fade the active slide in.
  • Content classes center the punchline, heading, subtitle, and CTA above the background.
  • Thumbnail classes position the vertical navigation and show the active slide.
  • The circular progress styles use a conic gradient and @property --progress so the active thumbnail can animate without SVG markup.
  • The media queries adjust thumbnail size, content padding, text scale, and CTA sizing across desktop, tablet, mobile, and landscape screens.

Step 7: Save and Test the Block

Save the block, then open a page in the WordPress block editor.

Insert Hero Slider and add at least three slides. For each slide, upload an image, add a punchline, heading, optional subtitle, and CTA button. Turn autoplay on and off to confirm autoStart is working.

On the frontend, check:

  • The first slide is visible on page load
  • Background images fade between slides
  • The heading and CTA animate when the active slide changes
  • Thumbnail clicks move to the correct slide
  • The circular progress restarts on the active thumbnail
  • Hovering thumbnails pauses autoplay
  • CTA links open correctly, including new-tab behavior
  • Text remains readable on mobile and landscape screens

Practical Extensions

Once the base slider works, you can extend the same pattern without changing the overall structure.

Useful additions include:

  • A color control for overlay strength
  • A select control for content alignment
  • A range control for slide duration
  • A toggle for showing or hiding thumbnails
  • Separate mobile images for art-directed hero crops

The main pattern stays the same: the repeater stores each slide, the template renders the layers, the CSS handles presentation, and the script controls active slide state.

Final Result

You now have a custom WordPress hero slider block with repeatable slides, large background imagery, animated content, thumbnail navigation, circular progress, and optional autoplay.

WPBits Block Studio lets you build this as a reusable editor-friendly block instead of maintaining a full custom block setup for a common hero component.