Slideshow Banner

Topic summary

A user working with Shopify’s Horizon Theme v3.0.1 needs a custom slideshow banner with separate images for desktop (1500x577) and mobile (750x1100) viewports.

Desired features include:

  • Clickable slides with swipe functionality in both directions
  • Crossfade transitions with looping
  • No navigation controls, buttons, or text overlays
  • Unlimited slide capacity

Current challenges:

  • Existing theme settings result in cropped images, unwanted whitespace, or broken transparent header functionality
  • Found ‘Clickable Banner Slider’ app but prefers avoiding third-party apps and needs better feature alignment

The user references mythical.com as inspiration and seeks the simplest implementation method, acknowledging limited coding skills. They’ve provided website access credentials for troubleshooting and are open to adjusting image dimensions if necessary. The discussion remains open with no solutions proposed yet.

Summarized with AI on October 27. AI used: claude-sonnet-4-5-20250929.

Hello,

I am working with the Horizon Theme v3.0.1 and want a slideshow banner that offers separate images for the desktop and mobile viewports. For inspiration, the slideshow banner on this website: https://mythical.com/ is roughly what I’m seeking.

I seek no navigation controls, no buttons, no text, etc. I want each slide to be clickable and have the ability to be swiped in either direction, with a crossfade transition, and loop. I do not want to be limited with the number of slides I can include, if possible. I seek the simplest way to accomplish the above. I can do minimal coding, but it’s not my strong-suit - less is more.

The images are 1500x577 for the desktop viewport and 750x1100 for the mobile viewport - these can be adjusted if necessary, but I want to work with these sizes, if possible. I found ‘Clickable Banner Slider’ within the Shopify App Store, which works similar to what I’m seeking, but I want to avoid apps wherever possible, and it does not 100% fit my needs.

I have attempted various settings for the slideshow, slide, and image sections, but they all resort in the images being cut-off laterally, leaving white space underneath the image, or cause the transparent header to stop functioning or leave whitespace rather than showing the banner image. I can go in-depth for my tests and results, if necessary.

Website: https://fangandfiber.com/

Password: MerchMania

If any additional information is needed, please let me know and I will provide it promptly.

Thanks in advance, I appreciate any assistance given!

2 Likes

Hi :waving_hand:
You can achieve that setup by adding a custom section to your theme — this gives you separate desktop/mobile images, clickable slides, and crossfade transitions without using apps.

Steps:

  1. From your Shopify admin, go to Online Store → Themes → Edit code.

  2. Under Sections, click Add a new section → name it custom-slideshow.liquid.

  3. Paste this code inside:

    {% for block in section.blocks %} {{ block.settings.alt_text }} {% endfor %}
    .custom-slideshow { position: relative; overflow: hidden; } .custom-slideshow .slide { position: absolute; inset: 0; opacity: 0; transition: opacity 1s ease-in-out; display: block; } .custom-slideshow .slide img { width: 100%; height: auto; display: block; } .custom-slideshow .slide.active { opacity: 1; z-index: 1; }

    {% schema %}
    {
    “name”: “Custom Slideshow”,
    “settings”: ,
    “blocks”: [
    {
    “type”: “slide”,
    “name”: “Slide”,
    “settings”: [
    { “type”: “image_picker”, “id”: “desktop_image”, “label”: “Desktop Image” },
    { “type”: “image_picker”, “id”: “mobile_image”, “label”: “Mobile Image” },
    { “type”: “url”, “id”: “link”, “label”: “Link URL” },
    { “type”: “text”, “id”: “alt_text”, “label”: “Alt Text” }
    ]
    }
    ],
    “presets”: [{ “name”: “Custom Slideshow” }]
    }
    {% endschema %}

    How to use:

    • In the theme editor, add the Custom Slideshow section to your homepage.

    • Upload desktop and mobile versions for each slide.

    • Add as many slides as you want — there’s no hard limit.

    • Adjust the fade speed by changing the interval in the <script> (e.g., 4000 = 4 seconds).

    This creates a full-width, looping crossfade slideshow that automatically swaps images by viewport size and keeps your transparent header working properly.

1 Like

Thanks, Wesley!

I appreciate your assistance with this. I created the custom-slideshow.liquid file within the Sections, however, pasting the code you shared here results in a ‘FileSaveError: Invalid JSON in tag ‘schema’ pop-up, along with 39 ‘problems’.

The description you gave sounds like exactly what I’m expecting. I appreciate your help!

@Damilola124 While I greatly appreciate the offer for assistance, I would much rather leave these conversations within the public view, in the hopes of it helping others in the future - I tend to lurk a lot and search for past topics like this for assistance prior to creating new ones.

Thanks!

1 Like

if i may ask have you try it out and does it work out for you?

I do not know what you’re asking here. Are you asking if I tested the code that Wes provided? If so, I answered that and it resulted in multiple errors and did not function.

I had others reaching out via DMs and such, even after requesting that all communication remain here so the public can see the results. After receiving AI-type responses from many, rather than useful information, I decided to take another whack at it. I wanted to edit what was already available, but decided best to start from scratch. The code below was added and works well-enough, and includes a few backend features most won’t notice. It isn’t the prettiest and I’m certain there’s much better ways to accomplish this task, but it functions and I can now move onto more pressing matters - like getting paid.

{% schema %}

{

“name”: “Custom Slideshow”,

“settings”: [

{ "type": "number", "id": "interval", "label": "Autoplay interval (ms)", "default": 5000 },

{ "type": "number", "id": "fade_duration", "label": "Fade duration (ms)", "default": 800 },

{ "type": "checkbox", "id": "pause_on_hover", "label": "Pause on hover", "default": true },

{

  "type": "select",

  "id": "lazy_behavior",

  "label": "Lazy-load behavior",

  "options": \[

    { "value": "preload_neighbors", "label": "Preload neighbors (recommended)" },

    { "value": "nearby_only", "label": "Load when near viewport" },

    { "value": "none", "label": "No lazy loading (load all immediately)" }

  \],

  "default": "preload_neighbors"

},

{

  "type": "select",

  "id": "image_format",

  "label": "Prefer image format",

  "options": \[

    { "value": "auto", "label": "Auto (AVIF -> WebP -> original)" },

    { "value": "webp", "label": "WebP preferred" },

    { "value": "avif", "label": "AVIF preferred" },

    { "value": "none", "label": "No format preference (original files only)" }

  \],

  "default": "auto"

}

],

“blocks”: [

{

  "type": "slide",

  "name": "Slide",

  "settings": \[

    { "type": "image_picker", "id": "desktop_image", "label": "Desktop image" },

    { "type": "image_picker", "id": "mobile_image", "label": "Mobile image" },

    { "type": "url", "id": "link", "label": "Link URL" },

    { "type": "text", "id": "alt_text", "label": "Alt text", "default": "Slide image" }

  \]

}

],

“presets”: [{ “name”: “Custom Slideshow”, “category”: “Hero” }]

}

{% endschema %}

{% comment %}

Server-side: generate explicit variant URLs per width for each slide using Shopify img_url.

WIDTH list: 280,320,360,420,480,640,750,900,1000,1280,1600,2048

{% endcomment %}

{% for block in section.blocks %}

{% assign d = block.settings.desktop_image %}

{% assign m = block.settings.mobile_image %}

{% assign slide_href = block.settings.link | default: "" %}

<div

  class="cs-slide"

  data-href="{{ slide_href | escape }}"

  role="link"

  aria-label="{{ block.settings.alt_text }}"

  data-index="{{ forloop.index0 }}"

  tabindex="0"

  {% comment %} store master references for client-side logic if needed {% endcomment %}

>

  <picture

    data-srcsets='{

      "desktop": {

        "280":"{{ d | img_url: '280x' }}",

        "320":"{{ d | img_url: '320x' }}",

        "360":"{{ d | img_url: '360x' }}",

        "420":"{{ d | img_url: '420x' }}",

        "480":"{{ d | img_url: '480x' }}",

        "640":"{{ d | img_url: '640x' }}",

        "750":"{{ d | img_url: '750x' }}",

        "900":"{{ d | img_url: '900x' }}",

        "1000":"{{ d | img_url: '1000x' }}",

        "1280":"{{ d | img_url: '1280x' }}",

        "1600":"{{ d | img_url: '1600x' }}",

        "2048":"{{ d | img_url: '2048x' }}"

      },

      "mobile": {

        "280":"{{ m | img_url: '280x' }}",

        "320":"{{ m | img_url: '320x' }}",

        "360":"{{ m | img_url: '360x' }}",

        "420":"{{ m | img_url: '420x' }}",

        "480":"{{ m | img_url: '480x' }}",

        "640":"{{ m | img_url: '640x' }}",

        "750":"{{ m | img_url: '750x' }}",

        "900":"{{ m | img_url: '900x' }}",

        "1000":"{{ m | img_url: '1000x' }}",

        "1280":"{{ m | img_url: '1280x' }}",

        "1600":"{{ m | img_url: '1600x' }}",

        "2048":"{{ m | img_url: '2048x' }}"

      }

    }'

  >

    <!-- mobile source placeholder (filled by JS when loading) -->

    <source media="(max-width: 749px)" data-srcset-mobile>

    <!-- img placeholder -->

    <img data-src-desktop alt="{{ block.settings.alt_text }}" loading="lazy" decoding="async">

  </picture>

</div>

{% endfor %}

.custom-slideshow { position: relative; width: 100%; overflow: hidden; touch-action: pan-y; -ms-touch-action: pan-y; user-select: none; } .custom-slideshow .cs-slide { position: absolute; inset: 0; display: block; opacity: 0; transition: opacity calc(var(--fade,800ms)) ease-in-out; -webkit-tap-highlight-color: transparent; cursor: pointer; } .custom-slideshow .cs-slide img { width: 100%; height: auto; display: block; object-fit: cover; pointer-events: none; user-select: none; } .custom-slideshow .cs-slide.is-active { opacity: 1; position: relative; z-index: 2; } .custom-slideshow .cs-slide:not(.is-active) { pointer-events: none; z-index: 1; } .custom-slideshow { --fade: {{ section.settings.fade_duration }}ms; } @media (max-width: 749px) { .custom-slideshow { height: auto; } }

Hmph. Not sure why my code was split up that way, this isn’t my forte, but it’s supposed to be a single file entry, in it’s entirety.

Hi @FangandFiber ,

Your issue is fixed??? Its working now ??

that is where the problem rely you need to write the code by yourself from scratch,

Hi @FangandFiber,

That’s a great design goal — having separate desktop and mobile slideshow images with full control over transitions and click actions isn’t something most Shopify themes (including Horizon) handle natively without a fair bit of custom Liquid + JS work.

If you’d like a no-code, flexible way to achieve this, you can try the free Slideshow Sections from Tapita AI Sections & Blocks.

  • Supports separate desktop & mobile images
  • Clickable slides (link anywhere)
  • Swipe gestures + loop + fade transition
  • No coding required — just add it via Theme Editor

View the demo slideshow sections here!

You can follow this quick setup guide here:
:backhand_index_pointing_right: How to customize the Tapita Slideshow Section

It’s free to use and works with any theme, including Horizon.
Might save you the coding headache while giving you full design flexibility.

Tell me if it works on your store.

Sophia - The Tapita team.

Please do not recommend an app when someone explicitly states the do not want an app in their initial post.

For something simple like this, a simple edit to the code that already exists should be sufficient. That said, I did rewrite the code - from scratch - as I stated, and pasted, here, to which you replied stating I did not.

@FangandFiber ,

Issue is fixed ?? If its not resolved then i’ll help.

Thanks

Yes it is resolved, thank you @Dev_Inflame for checking. I forgot to mark the solution, as it was my own.

1 Like

Hi @FangandFiber,

You’re absolutely right, my apologies for recommending an app even though you mentioned you’d prefer to avoid one. I completely understand that apps rarely fit 100% of what each merchant needs, as they’re usually built to cover the most common use cases.

That said, our team is always happy to help with minimal customizations or small code tweaks within reason, completely free of charge. You can simply reach out to our support team via in-app chat, and we’ll do our best to adjust things to better fit your needs.

We also truly appreciate feedback and suggestions, many of our features actually come directly from user requests, so your input could help shape future updates.

Once again, sorry about that, and thank you for pointing it out :folded_hands:

Best,
Sophia - The Tapita team.