hryvinskyi / magento2-banner-slider-frontend-ui
Banner Slider frontend module: slider widget and block, responsive pictures, video embeds and slider scripts
Package info
github.com/hryvinskyi/magento2-banner-slider-frontend-ui
Type:magento2-module
pkg:composer/hryvinskyi/magento2-banner-slider-frontend-ui
Requires
- php: ~8.3.0||~8.4.0
- hryvinskyi/magento2-banner-slider: ^2.1
- hryvinskyi/magento2-banner-slider-api: ^2.1
- hryvinskyi/magento2-head-tag-manager: ^2.3
- hryvinskyi/magento2-splide-js: ^1.0
- magento/framework: ^103.0.7
- magento/module-cms: ^104.0
- magento/module-csp: ^100.4
- magento/module-customer: ^103.0
- magento/module-store: ^101.1
- magento/module-widget: ^101.2
- psr/clock: ^1.0
- psr/log: ^2.0 || ^3.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Storefront module of the banner slider: the slider block and CMS widget, responsive pictures, video embeds, and the slider scripts and styles.
Part of hryvinskyi/magento2-banner-slider-pack, the complete banner slider for Magento 2.
Requirements
- PHP 8.3 or 8.4, Magento 2.4.7 or later
hryvinskyi/magento2-banner-slider-apiandhryvinskyi/magento2-banner-slider2.xhryvinskyi/magento2-splide-js(Splide 4) andhryvinskyi/magento2-head-tag-manager
Placing a slider
A slider is chosen by id or by location code. When both are given, the id wins. With a location code, the enabled slider with the lowest priority value placed there is shown. A slider shows only when it is enabled, visible to the store view and customer group, inside its active dates, and has at least one slide that renders; otherwise the block renders nothing.
Layout XML
<referenceContainer name="content"> <block class="Hryvinskyi\BannerSliderFrontendUi\Block\Widget\Slider" name="home.banner.slider"> <arguments> <argument name="location" xsi:type="string">homepage-hero</argument> </arguments> </block> </referenceContainer>
or with <argument name="slider_id" xsi:type="number">1</argument>. The template argument is optional; it defaults
to Hryvinskyi_BannerSliderFrontendUi::slider.phtml, and 1.x layouts that name that template keep working.
CMS widget
{{widget type="Hryvinskyi\BannerSliderFrontendUi\Block\Widget\Slider" location="homepage-hero"}}
{{widget type="Hryvinskyi\BannerSliderFrontendUi\Block\Widget\Slider" slider_id="1"}}
| Parameter | Type | Description |
|---|---|---|
slider_id |
Select | The slider to show. Optional; "-- Use the location --" leaves it empty. |
location |
Text | Location code, used when no slider is chosen. |
template |
Select | The slider template. Optional in a widget directive. |
Themes with and without RequireJS
The block renders the same markup for every theme, and after each slider a small inline script (through
SecureHtmlRenderer, so CSP nonces apply).
- A theme with RequireJS (Luma and its children): the container's
data-mage-initloadsHryvinskyi_BannerSliderFrontendUi/js/banner-slider-requirejs, which starts the slider with Splide. The inline script does nothing. RequireJS is recognised by a globalrequirefunction withdefined; arequireconfiguration object or another AMD loader does not count. - A theme without RequireJS (for example Hyvä): the inline script injects Splide and
Hryvinskyi_BannerSliderFrontendUi::js/banner-slider.jsonce per page, in that order, and starts every slider when they have loaded. Sliders added to the page later, such as sections rendered on demand, start too. No Hyvä compatibility module is needed. The script URLs come fromFrontendAssetsindi.xml, so a theme can point them at its own copies. A script the page already has is not injected again: a theme's ownwindow.Splideis used as it is. Each slider starts on its own, so one that fails logs a warning and the others still start.
Styles are plain CSS (css/banner-slider.css) and need no preprocessor. The block adds them, and the Splide styles, to
the page head.
If Splide cannot load, the slider keeps showing its first slide, click-to-load videos still work, and the console gets one warning.
The slider script is also usable directly: window.HryvinskyiBannerSlider.mount(element, Splide) or
mountAll(document, Splide) without RequireJS, or the AMD module Hryvinskyi_BannerSliderFrontendUi/js/banner-slider
with it. It registers as an AMD module only when RequireJS is on the page; next to any other AMD loader it publishes
the global.
A slide's content may hold another slider (a widget in custom HTML): each slider only wires its own buttons, videos and slides, never those of the slider inside it.
Accessibility
- The container is the carousel region:
role="region", a translatedaria-roledescriptionand the slider name as its label. Splide's own element is not announced a second time. - A slider that plays automatically has a pause/play button (WCAG 2.2.2). Its label names the action a press performs. The slider's "Show Pause/Play Button" setting (on by default) can remove it; without it nothing on the page lets a visitor stop the slides, so switch it off only when the slider has another way to pause. Autoplay still pauses while the pointer is over the slider or focus is inside it.
- A visitor who prefers reduced motion gets autoplay paused (the button, when shown, starts it) and background videos paused behind a play button. Every background video has a pause/play button.
- A slide that leaves the view pauses its videos: native videos directly, YouTube and Vimeo players through their message protocols.
- Every Splide label is translated. Images use the banner title as alternative text, or an empty
altwhen the banner has none; video frames have a title. A linked image without a title is decorative, so its link is named byaria-labelwith the banner name instead. - Pagination dots have a 24 × 24 px hit area (WCAG 2.5.8). Buttons, arrows, dots and links show a focus outline.
Video and privacy
- Regular videos are click-to-load. The slide shows the banner image (or a neutral background) with a play button. The provider's player is created only when the visitor presses it, so no request reaches YouTube or Vimeo before that. Starting the video stops the slider's autoplay (the pause button, when shown, restarts it), and focus moves to the player.
- Background videos render their player directly. Only the first slide's background video starts with the page.
A background video on a later slide is rendered without
src(the URL waits indata-hbs-src): nothing is downloaded or played until its slide is shown, and not then either when the visitor paused it or prefers reduced motion; it loads when they press play. Right after the slider starts, videos in slides out of view are paused. - The module allows the video players' hosts as frame sources in the storefront's content security policy
(
etc/csp_whitelist.xml:www.youtube-nocookie.com,www.youtube.com,player.vimeo.com). Posters are the banner's own image, so no image host of a video provider is needed. - With privacy-enhanced mode on (the video setting in the banner slider configuration, on by default), embeds use
youtube-nocookie.comand Vimeo'sdnt=1. - Local MP4/WebM files play in a native
<video>element.
Images and performance
- Responsive crops render as
<picture>with one<source>per breakpoint and format (AVIF, WebP, then the original format). Every source carries its crop's width and height, so the browser reserves the right height before the image loads. The<img>inside the picture is the banner image with its stored size: a banner cropped for some breakpoints only (say, phones) shows its full image at the other widths. A banner without an image falls back to its widest crop. - The first slide loads eagerly with
fetchpriority="high". The slider's lazy-load setting applies to the slides after it. - Preload links for the first
preload banners countslides and any banner flagged for preloading, following what the<picture>shows at each width. The widths are split by the slider's enabled breakpoints: each covers its min width up to the next wider breakpoint, so breakpoints whose media queries overlap never preload two crops for one width. A range with a crop of the banner preloads the crop's first format with its type (a browser that cannot decode it skips the link, as it skips that<source>); a range without one preloads the banner image. Neighbouring ranges that preload the same image share one link, so a banner without crops gets a single link. Only the first slide's links carryfetchpriority="high". - Full page cache: the page is tagged with the slider, its banners and its location (or the requested slider id), so a saved slider or banner, or a slider whose dates start or end, refreshes the pages that show it.
- A slider rendered inside a separately cached fragment (an ESI block) cannot add head elements: place such a slider in the page itself if it needs preloads or its custom CSS.
Styling
css/banner-slider.css uses these classes; everything else is Splide's (splide__*).
| Class | Element |
|---|---|
.hbs-slider |
The container. Holds the custom properties below. |
.hbs-slider__toggle |
The autoplay pause/play button. |
.hbs-slide |
A slide (li). |
.hbs-slide__link |
The banner link around the image. |
.hbs-slide__media |
The slider's own <picture>, <img> and video wrapper (full width). |
.hbs-slide__overlay |
Banner content over an image or video; only its links and controls take clicks. |
.hbs-slide__content |
A custom HTML slide. |
.hbs-slide__video |
The video wrapper; its ratio comes from --hbs-aspect-ratio. |
.hbs-slide__player |
The iframe or <video>. |
.hbs-slide__facade |
The click-to-load button, with .hbs-slide__poster and a hidden label. |
.hbs-slide__video-toggle |
The pause/play button of a background video. |
Custom properties on .hbs-slider: --hbs-control-size, --hbs-control-background, --hbs-control-color,
--hbs-focus-color, --hbs-dot-size, --hbs-dot-color, --hbs-dot-active-color, --hbs-video-background,
--hbs-offset.
A slider's custom CSS (admin field) uses .banner-slider-{slider id} as its root selector; the container also keeps
the 1.x id banner-slider-{slider id} on its first render in a page.
Extension points
- Slide renderers (
Api/Render/SlideRendererInterface): add an item to therendererspool ofModel\Render\CompositeSlideRendererindi.xmlto render a banner type your own way. The first renderer (by sort order) that supports a type renders it. - Element attributes (
Api/Attribute/ElementAttributeProviderInterface): add a provider to theproviderspool ofModel\Attribute\ElementAttributePoolto add attributes to the container, slides and links (for example analytics data attributes). Values are strings, integers, booleans or null; event handler attributes (on…) are rejected. - Script and style files: the
stylesheetsandscriptsarguments ofModel\View\FrontendAssets. - Video players in the script:
HryvinskyiBannerSlider.registerVideoProvider(code, {pause: {…}, play: {…}})adds the pause/play messages of another embedded player, keyed by its provider code. - Templates:
slider.phtml,slide/*.phtmlandbootstrap.phtmlcan be overridden by a theme as usual. The templates read the view objects inApi/View:SliderView($block->getSliderView()inslider.phtml),SlideView,ImageSlideView(slide/image.phtml),PictureView(slide/picture.phtml) andVideoSlideView(slide/video-*.phtml). They are immutable read models; an override type-hints them and reads their getters. - Services (replace one with a
di.xmlpreference):Api/View/SliderViewBuilderInterfacebuilds the view of one slider render from the slider and its banners;Api/View/PictureViewBuilderInterfacebuilds a slide's<picture>or<img>, for slide renderers that show a banner image;Api/Head/HeadAssetRegistrarInterfaceadds a rendered slider's stylesheets, image preloads and custom CSS to the page head;Api/StorefrontContextProviderInterfacegives the store view, customer group and moment that decide which slider and banners a visitor sees. Its values must be ones the full page cache varies on.
Tests
vendor/bin/phpunit Test/Unit node Test/Js/run.mjs
Installation
composer require hryvinskyi/magento2-banner-slider-pack bin/magento setup:upgrade bin/magento cache:flush
Author
Volodymyr Hryvinskyi — volodymyr@hryvinskyi.com
License
MIT