---
title: "Epic Slider | Advanced Slider Element for YOOtheme Pro Websites"
description: "Create cinematic sliders in YOOtheme Pro with 34 transitions, video slides, animated text, Ken Burns effects and responsive navigation on every screen."
url: "https://www.yoozecase.com/?item_id=25"
date: "2026-10-08T06:45:24+00:00"
language: "en-GB"
---

#  Epic Slider

Always in transition

**Latest Version**
1.0.4
**Requires**
Yootheme Pro 5+, Joomla 5+, WordPress 6.5+, PHP 8.1 or newer
**Release date**
02 October 2026
**Made for**
Yootheme Pro, Joomla, Wordpress
**License**
GPL-2.0-or-later
**Price**
€25.00 (incl. 6 months of updates)

 [ 🛒 Get it for Joomla 👍 ](https://yoozecase.lemonsqueezy.com/checkout/buy/a0af2f20-fc91-43f6-b972-34dc8fec90d9)

 [ 🛒 Get it for WordPress👍 ](https://yoozecase.lemonsqueezy.com/checkout/buy/0b94eea1-11d6-43c8-8d97-28a8efdde8e0)

 [ Demo page ](https://www.yoozecase.com/epic-slider-demo)

Epic Slider is a premium slideshow element for YOOtheme Pro with 34 cinematic transitions, moving text, video slides and a real page-curl. Works in Joomla and WordPress, with no extra frameworks.

##  What's new in version 1.0.4

- **Changed:** The licence client now identifies the product to the YOOzeCase gateway as yc\_epic\_slider and states the platform it runs on, so a key bought for the WordPress edition is refused instead of accepted. Keys activated with 1.0.3 or earlier keep working; nothing needs to be entered again.
- **Changed:** On activation and on the daily check the plugin reports its own version and the versions of Joomla, PHP and YOOtheme Pro to the gateway, and to nothing else. The YOOzeCase account page lists them under Installations. These values never change the outcome of the check.
- **Changed:** Deactivating the key while the gateway cannot be reached no longer clears the key locally; the panel says the deactivation did not get through.
- **Changed:** Saving the licence state writes only the five licence fields back to the plugin settings, so a setting saved by an administrator in the meantime is kept.
- **Changed:** LICENSE.txt carries the full text of the GNU General Public License version 2 and nothing else. The trademark notice moved to NOTICE.txt.
- **Changed:** BUILDINFO.txt is written in the fingerprint format the YOOzeCase build tooling checks.

 ![Epic Slider 1](https://www.yoozecase.com/files/epic-slider_1.png)

 [](#)

 [](#)

---

##  Description

Epic Slider takes your hero section to the next level. Where most sliders stop at fading and sliding, Epic Slider gives you 34 transitions: tiles, spirals, blinds, sweeps, a 3D flip and a real page-curl where the page turns over with perspective, sheen and shadow. Every slide can have its own transition, so you decide the exact rhythm of your story.

You build everything right inside the YOOtheme Pro builder. Per slide you set a title, subtitle and button, with nine text animations (from a calm fade to letters that fly in and spin into place) and nine positions. Want moving imagery? Add an mp4, YouTube or Vimeo URL: it plays muted and looped, and by default fills the whole slider with no black bars. The slider loops endlessly.

For a modern hero, Epic Slider can slide underneath your logo and menu, with an adjustable header opacity and a readability gradient at the top. On top of that you get Ken Burns zoom, navigation with arrows, dots or thumbnails, swipe and mouse drag, a progress bar with play/pause, and a configurable alert box overlay (with a button or a newsletter field).

Key features:

- 34 transitions, with a per-slide effect override
- Real page-curl (Flipbook) with perspective and sheen
- Video slides (mp4, YouTube, Vimeo) that fill the whole frame, muted and looped
- 9 text animations, 9 positions, text panel or outline text
- Endless loop, Ken Burns, full-width and behind-header hero
- Navigation: arrows, dots, thumbnails, swipe and mouse drag
- Alert box overlay with a button or newsletter signup
- Lightweight, framework-free engine that respects reduced motion (accessible)
- For Joomla 5/6 and WordPress 6/7, with automatic updates

In short: a spectacular, fast and accessible slider that you set up in a few clicks and that looks sharp on every screen.

##  Feature request

 ### What is the next feature for this plugin?

Submit your idea or vote on existing ones.

  #### Top voted

   Website (leave empty)

'; } html += ''; if (oc.title) { html += '##### ' + escapeHtml(oc.title) + '

'; } if (oc.text) { html += '' + escapeHtml(oc.text).replace(/\\n/g, '
') + '

'; } html += '

'; } // Auto winner(s) - top N from the already vote-sorted features if (mode === 'auto_winner' || mode === 'both') { var n = oc.winnerCount | 0; if (n '; for (var i = 0; i ' + (w.image_url ? '![](https://www.yoozecase.com/'%20+%20escapeHtml(w.image_url)%20+%20')

' : '') + '' + (i + 1) + '' + '' + escapeHtml(w.title) + '' + '' + getIcon(this.cfg.voteIcon) + ' ' + (w.votes | 0) + '' + ''; } html += ''; } } if (html === '') { this.elOutcome.hidden = true; return; } this.elOutcome.innerHTML = html; this.elOutcome.hidden = false; }; /** * Roadmap view (v3.2.0): renders the items as status columns, in the * canonical status order from cfg.statuses. Each column is an 12. inside \* the existing .yc-fb-list , holding its own inner  of items - \* this keeps the delegated click/change handlers on elList working \* unchanged. Unknown statuses fall back into the "open" column so no item \* can silently disappear. Empty columns are skipped when \* cfg.roadmapHideEmpty is set. \*/ Board.prototype.renderRoadmap = function (items) { var stats = (this.cfg.statuses && this.cfg.statuses.length) ? this.cfg.statuses : \['open', 'planned', 'in\_progress', 'shipped', 'declined'\]; var labels = this.cfg.statusLabels || {}; var groups = {}; var i, st; for (i = 0; i ' + '##### ' + '' + escapeHtml(labels\[sv\] || sv) + '' + arr.length + '' + '

    ' + '' + itemsHtml + '
    ' + '
'; } return html; }; Board.prototype.renderItem = function (f, index) { var disabled = this.cfg.isClosed ? 'disabled' : ''; var voted = f.voted ? ' yc-voted' : ''; var descHtml = f.description ? '' + escapeHtml(f.description).replace(/\\n/g, '
') + '

' : ''; var thumbHtml = f.image_url ? '![](https://www.yoozecase.com/'%20+%20escapeHtml(f.image_url)%20+%20')

' : ''; // Status workflow (v3.0.0): badge for any non-default status. The // roadmap view (v3.2.0) skips the badge - the column already // communicates the status, a badge would be redundant noise. var st = (f.status || 'open'); var labels = this.cfg.statusLabels || {}; var statusBadge = (!this.isRoadmap && st && st !== 'open' && labels[st]) ? '' + escapeHtml(labels\[st\]) + '' : ''; // Design themes (v3.1.0): arena / neon / pixel render extra per-item // furniture - rank number, avatar (image or initials), a "leader" pill // on rank 1, and a "votes" label. Classic/colour themes are untouched. var t = this.cfg.theme || 'light'; var themed = (t === 'arena' || t === 'neon' || t === 'pixel'); var rank = (typeof index === 'number') ? (index + 1) : 0; var rankHtml = '', leaderHtml = '', avatarHtml = '', votesLabel = ''; if (themed) { rankHtml = '' + pad2(rank) + ''; if (rank === 1) { leaderHtml = '' + escapeHtml(this.cfg.leaderLabel || 'Leader') + ''; } if (f.image_url) { avatarHtml = '![](https://www.yoozecase.com/'%20+%20escapeHtml(f.image_url)%20+%20')

'; } else { avatarHtml = '' + escapeHtml(initialsOf(f.title)) + '

'; } votesLabel = '' + escapeHtml(this.cfg.votesLabel || 'votes') + ''; } // Moderation overlay: subtle corner buttons, only when canModerate. // Buttons appear on hover/focus (styled in CSS). Even in customizer // preview we render them when canModerate is true so the moderator // can see what visitors with moderation rights will see. var modHtml = ''; if (this.cfg.canModerate) { var parts = []; var hideTitle = escapeHtml(this.t('hideTitle', 'Hide this feature')); var deleteTitle = escapeHtml(this.t('deleteTitle', 'Delete this feature')); if (this.cfg.allowSoftHide) { parts.push( '' ); } if (this.cfg.allowHardDelete) { parts.push( '' ); } if (parts.length || (this.cfg.statuses && this.cfg.statuses.length)) { var sParts = parts.slice(); if (this.cfg.statuses && this.cfg.statuses.length) { var opts = ''; for (var si = 0; si ' + escapeHtml(sl) + ''; } var setStatus = escapeHtml(this.t('setStatus', 'Set status')); sParts.push( '' ); } modHtml = '' + sParts.join('') + '

'; } } var mediaHtml = themed ? avatarHtml : thumbHtml; return ( '32. ' + leaderHtml + rankHtml + '' + mediaHtml + '' + '##### ' + escapeHtml(f.title) + '

    ' + statusBadge + descHtml + '

    ' + modHtml + '
' ); }; /** * Detects whether a JSON response looks like a CSRF/token rejection. * Two cases: * 1. com_ajax-level rejection (Joomla's auto-token-check on POST): top * level message contains the JINVALID_TOKEN_NOTICE text and HTTP * status is typically 403. * 2. Plugin-level rejection from requireToken(): payload.error === "Invalid token". */ Board.prototype.isTokenError = function (status, raw, payload) { if (status === 403) return true; var hay = ''; try { hay = JSON.stringify(raw || {}) + '|' + JSON.stringify(payload || {}); } catch (e) {} return /invalid (security )?token/i.test(hay); }; /** * Fetch a fresh CSRF token from the server. Updates this.cfg.csrfToken so * the next call (and any future calls in this Board's lifetime) use the * correct one. Cache-bust with a timestamp so no upstream CDN caches the * response. */ Board.prototype.fetchFreshToken = function () { var self = this; var url = this.cfg.ajaxUrl + '&task=token&_=' + Date.now(); return fetch(url, { method: 'GET', credentials: 'same-origin', headers: { 'Accept': 'application/json', 'Cache-Control': 'no-cache' } }) .then(function (r) { return r.json(); }) .then(function (data) { var p = self.unwrapAjax(data); if (p && p.ok === true && typeof p.token === 'string' && p.token.length >= 16) { self.cfg.csrfToken = p.token; return p.token; } throw new Error('No fresh token in response'); }); }; /** * Generic POST-with-token helper. Builds FormData from a plain object, * adds the current token, posts. On 403 / token-error response, fetches a * fresh token and retries exactly once. * * @param {string} task The task name (appended to URL and body) * @param {object} fields Key-value form fields (no token) * @return {Promise} Resolves to the unwrapped payload on success. \* Rejects with Error containing .reason on failure. \*/ Board.prototype.postWithToken = function (task, fields) { var self = this; function attempt(token) { var fd = new FormData(); fd.append('task', task); for (var k in fields) { if (Object.prototype.hasOwnProperty.call(fields, k)) { fd.append(k, fields\[k\]); } } fd.append(token, '1'); return fetch(self.cfg.ajaxUrl + '&task=' + encodeURIComponent(task), { method: 'POST', credentials: 'same-origin', body: fd, headers: { 'Accept': 'application/json' } }).then(function (r) { return r.json().then(function (data) { return { status: r.status, raw: data, payload: self.unwrapAjax(data) }; }); }); } return attempt(this.cfg.csrfToken).then(function (res) { // Success path if (res.payload && res.payload.ok === true) { return res.payload; } // Token rejection? Fetch fresh and retry once. if (self.isTokenError(res.status, res.raw, res.payload)) { return self.fetchFreshToken().then(function (fresh) { return attempt(fresh).then(function (res2) { if (res2.payload && res2.payload.ok === true) { return res2.payload; } var err = new Error( (res2.payload && res2.payload.error) || (res2.raw && res2.raw.message) || self.t('requestFailed', 'Request failed.') ); err.payload = res2.payload || null; throw err; }); }); } // Some other error - surface it var err = new Error( (res.payload && res.payload.error) || (res.raw && res.raw.message) || self.t('requestFailed', 'Request failed.') ); err.payload = res.payload || null; throw err; }); }; Board.prototype.handleSubmit = function () { var self = this; if (isInBuilder()) { this.showStatus(this.t('previewSubmit', 'Preview mode - submissions are disabled in the YOOtheme builder. Try this on the published page.'), 'info'); return; } if (this.cfg.isClosed) { this.showStatus(this.cfg.closedMessage || this.t('votingClosed', 'Voting is closed.'), 'info'); return; } var title = (this.elTitle && this.elTitle.value || '').trim(); var desc = (this.elDesc && this.elDesc.value || '').trim(); // Client-side validation: minimum title length. Server re-validates. var minLen = (this.cfg.minTitleLength | 0) || 2; if (title.length for hide/delete, remember parent + sibling for rollback. var li = btn.closest('li'); var parentEl = li ? li.parentNode : null; var nextEl = li ? li.nextSibling : null; if (li && (action === 'hide' || action === 'delete')) { li.remove(); } btn.disabled = true; var task = action === 'hide' ? 'moderate\_hide' : action === 'delete' ? 'moderate\_delete' : action === 'restore' ? 'moderate\_restore' : ''; if (!task) { btn.disabled = false; return; } this.postWithToken(task, { feature\_id: featureId }) .then(function (payload) { // Success - keep the UI as-is (already removed) and refresh // so vote counts, top box, and hidden-features panel reflect // the new state. self.refresh(); if (self.hiddenOpen) { self.hiddenLoaded = false; // force a refetch self.fetchHiddenFeatures(); } var msg = action === 'delete' ? self.t('deleted', 'Feature deleted.') : action === 'hide' ? self.t('hidden', 'Feature hidden.') : action === 'restore' ? self.t('restored', 'Feature restored.') : self.t('done', 'Done.'); self.showStatus(msg, 'success'); setTimeout(function () { self.hideStatus(); }, 3000); }) .catch(function (err) { // Rollback: re-insert the row if we removed it. if (li && parentEl) { if (nextEl) { parentEl.insertBefore(li, nextEl); } else { parentEl.appendChild(li); } } btn.disabled = false; self.showStatus(err && err.message ? err.message : self.t('modFailed', 'Moderation action failed.'), 'error'); }); }; Board.prototype.handleStatusChange = function (featureId, status, sel) { var self = this; if (!featureId || !status) return; sel.disabled = true; this.postWithToken('moderate\_status', { feature\_id: featureId, status: status }) .then(function () { self.refresh(); var label = (self.cfg.statusLabels && self.cfg.statusLabels\[status\]) ? self.cfg.statusLabels\[status\] : status; self.showStatus(self.tf('statusSet', 'Status: %s', label), 'success'); setTimeout(function () { self.hideStatus(); }, 3000); }) .catch(function (err) { sel.disabled = false; self.showStatus(err && err.message ? err.message : self.t('statusFailed', 'Could not set status.'), 'error'); }); }; /\*\* \* Toggle the hidden-features panel open/closed. First open triggers \* an AJAX fetch; subsequent toggles just hide/show the cached list. \*/ Board.prototype.toggleHiddenPanel = function () { if (!this.elHiddenList || !this.elHiddenLabel) return; var arrow = this.elHiddenToggle && this.elHiddenToggle.querySelector('.yc-fb-hidden-arrow'); if (this.hiddenOpen) { this.hiddenOpen = false; this.elHiddenList.hidden = true; this.elHiddenLabel.textContent = this.t('showHidden', 'Show hidden features'); if (arrow) arrow.classList.remove('yc-open'); return; } this.hiddenOpen = true; this.elHiddenList.hidden = false; this.elHiddenLabel.textContent = this.t('hideHidden', 'Hide hidden features'); if (arrow) arrow.classList.add('yc-open'); if (!this.hiddenLoaded) { this.fetchHiddenFeatures(); } }; /\*\* \* Fetch the hidden-features list via the moderator-only endpoint. \* Re-callable to refresh the list after a restore. \*/ Board.prototype.fetchHiddenFeatures = function () { var self = this; if (!this.elHiddenList) return; this.elHiddenList.innerHTML = '2. ' + escapeHtml(this.t('loading', 'Loading...')) + '
'; var url = this.cfg.ajaxUrl + '&task=moderate\_list\_hidden&board\_id=' + encodeURIComponent(this.cfg.boardId); fetch(url, { credentials: 'same-origin', headers: { 'Accept': 'application/json' } }) .then(function (r) { return r.json(); }) .then(function (data) { var payload = self.unwrapAjax(data); if (!payload || payload.ok !== true) { self.elHiddenList.innerHTML = '4. ' + escapeHtml((payload && payload.error) ? payload.error : self.t('hiddenLoadFailed', 'Failed to load hidden features.')) + '
'; return; } self.hiddenLoaded = true; var features = payload.features || \[\]; if (features.length === 0) { self.elHiddenList.innerHTML = '6. ' + escapeHtml(self.t('noHidden', 'No hidden features.')) + '
'; return; } var html = ''; for (var i = 0; i ' + escapeHtml(f.description).replace(/\\n/g, '
') + '' : ''; var restoreTitle = escapeHtml(self.t('restoreTitle', 'Restore this feature')); html += '9. ' + '' + '##### ' + escapeHtml(f.title) + '

    ' + descHtml + '' + escapeHtml(self.tf('votesMeta', '%s votes', f.votes | 0)) + '' + '

    ' + '' + '
'; } self.elHiddenList.innerHTML = html; }) .catch(function () { self.elHiddenList.innerHTML = '11. ' + escapeHtml(self.t('hiddenNetwork', 'Network error while loading hidden features.')) + '
'; }); }; function initAll(scope) { var roots = (scope || document).querySelectorAll('.yc-fb-root'); for (var i = 0; i ---

##  Changelog

---

 [ v1.0.4 Breaking 02/10/2026 ](#) Changed- The licence client now identifies the product to the YOOzeCase gateway as `yc\_epic\_slider` and

- On activation and on the daily check the plugin reports its own version and the versions of
- Deactivating the key while the gateway cannot be reached no longer clears the key locally. The
- Joomla: saving the licence state writes only the five licence fields back to the plugin settings
- WordPress: the updater reads `package` from the update feed and falls back to `download\_url`, the
- `LICENSE.txt` carries the full text of the GNU General Public License version 2 and nothing else.
- `BUILDINFO.txt` is written in the fingerprint format the YOOzeCase build tooling checks, and no

 [ v1.0.3 Breaking 15/09/2026 ](#) Added- The thumbnail strip follows the slider. On every slide change the active thumbnail is brought into

 Fixed- With more thumbnails than fit the slider width, the first thumbnails could not be reached or
- The horizontal scrollbar over the bottom of the slider is gone. The strip still scrolls; only the

 [ v1.0.2 Breaking 06/09/2026 ](#) Changed- The pre-release check now flags a form field that redeclares a Joomla `FormField` method with a

- WordPress: version alignment with the Joomla package only. The WordPress plugin is functionally

 Fixed- On Joomla 5.4 the plugin settings page returned a server error (500). The licence key field

 [ v1.0.1 Breaking 11/08/2026 ](#) Fixed- The 1.0.0 package on the update server contained an older engine build in which sliders were

 [ v1.0.0 Breaking 11/08/2026 ](#) Added- \*\*Custom CSS per slider\*\*, on the Advanced tab. Rules are scoped to that one slider automatically,

- \*\*Navigation: Arrows + Thumbnails.\*\* Side arrows and the thumbnail strip can now be shown together.
- \*\*Consent for the newsletter form.\*\* The alert box signup now shows a required consent checkbox and
- \*\*Licence key.\*\* A Licence tab in the plugin settings with Activate and Deactivate, a status badge
- \*\*Build fingerprint.\*\* `BUILDINFO.txt` now carries a build id and a checksum over the package
- \*\*Preload hint for the first slide image.\*\* The first slider on a page now emits a
- \*\*Initialisation is spread out.\*\* When several sliders enter the viewport in the same

 Changed- Dead files are no longer shipped: `services/`, `src/Extension/` and the two `element.json` files

 Fixed- \*\*Custom CSS was missing entirely.\*\* The Advanced tab referenced YOOtheme's `${builder.css}`, which
- \*\*The consent checkbox and the email field shared a CSS rule.\*\* `.yc-slider\_\_alert-form input`
- \*\*The email field grew to 175 pixels tall below 480 pixels.\*\* In the stacked layout the main axis
- \*\*The alert bar could fall outside the slider.\*\* It is now bounded by the height of the slider and

 [ v0.12.0 06/08/2026 ](#) Added- The size readout now also reports the weight of the first slide and of all slides together. That

 Changed- Only the first image now holds up the start. The rest load in the background, one at a time, in
- Navigating to a slide whose image is not in yet waits for that one image and then continues, so a

 Fixed- The first image was drawn at the wrong scale. `\_build()` sizes the background before any image has
- Frame size changes other than a browser resize are now picked up as well, through a ResizeObserver

 [ v0.11.0 06/08/2026 ](#) Added- \*\*Custom height (px)\*\* as a Height option. The frame keeps the height you enter and the width runs

- \*\*Image width (px)\*\*: draw the image at a fixed pixel width instead of scaling it to the frame,
- \*\*Show image size\*\*: a design aid that puts a small readout on the slider with the dimensions and

 Changed- The Custom size field now serves both Custom ratio and Custom height. With Custom ratio it takes
- Worth knowing before you pick a fixed height: it does not crop less than a ratio does, it crops
- If you want no cropping at all, Custom ratio remains the answer: enter the dimensions of the image

 [ v0.10.0 06/08/2026 ](#) Added- Height can now follow the image instead of forcing the image into a fixed frame. Two new options

- Height and ratio can be set per YOOtheme breakpoint: S from 640 pixels, M from 960, L from 1200
- Three more presets in the Height field: Ratio 4:3, Ratio 3:1 and Ratio 1:1.
- New field \*\*Image fit\*\*: "Crop to fill" is the existing behaviour and stays the default, while
- New field \*\*Image position\*\* with nine positions, default centre. It decides which part of the

 Changed- Height and aspect ratio are now driven by CSS custom properties on the element (`--yc-ar`,
- The engine now computes the image rectangle through `\_fit()`, which handles both cropping and
- Existing sliders are unaffected. A saved Height keeps rendering the same frame, cropping stays the
- A typed ratio is validated strictly, since it ends up in a style attribute: two whole numbers with

 [ v0.9.9 05/08/2026 ](#) Changed- Joomla: no change to the package. This release exists to keep the two platforms on the same

 Fixed- WordPress: the stylesheet did not load at all on 0.9.8, which left the slider unstyled and

 [ v0.9.8 05/08/2026 ](#) Added- Admin notice when YOOtheme Pro is missing (YC.7). Without the YOOtheme Pro template the plugin

- Status indicator on the plugin settings page (YC.7). A new custom form field reports whether

 Changed- The Interval field now states its lower bound: "Time each slide stays visible. Minimum 2 seconds."
- WordPress: asset loading moved from a direct `&lt;link&gt;` and `&lt;script&gt;` to the WordPress asset
- WordPress: the plugin description said 34 transition effects. The engine ships 37 (the flipbook
- License changed to GPL-2.0-or-later for all code in this package. Joomla and WordPress treat
- The engine, the slide templates and the styling are identical to 0.9.7. Brand, product names,
- Correction added on 2026-08-05: this entry originally claimed there was no functional change.

 Fixed- The product description counted 34 transition effects. The engine ships 37 (the flipbook
- The element icons in the builder picker now carry the required 24 x 24 size. They were 30 x 30

 Removed- `TERMS.txt` is no longer part of the package. The commercial terms (subscription, key usage,
- The redistribution ban, the modification ban and the AI ingestion ban are gone from the source

## Schema

```json
{
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    "itemListElement": [
        {
            "@type": "ListItem",
            "position": 1,
            "name": "Home",
            "item": "https://www.yoozecase.com"
        },
        {
            "@type": "ListItem",
            "position": 2,
            "name": "Extensions",
            "item": "https://www.yoozecase.com/extensions"
        },
        {
            "@type": "ListItem",
            "position": 3,
            "name": "Epic Slider",
            "item": "https://www.yoozecase.com/"
        }
    ]
}
```
