Skip to content

bulldev/bleed-slider

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Bleed Slider

A tiny, pure-CSS full-bleed product slider. Zero dependencies.

The first slide lines up with your page grid on the left, while the track runs all the way out to the right edge of the browser. Scroll right and the slides pass under the left gutter — bleeding off-grid on the left too.

License: MIT Dependencies: none CSS

▶ Live demo


Contents


Why

Most "carousel" libraries ship thousands of lines of JS to do something the browser already does natively. Bleed Slider is the opposite:

  • 🧊 Core is 100% CSS — built on native scroll-snap. Swipe, trackpad, wheel, and keyboard all work with no JavaScript.
  • 🪶 ~1.6 KB CSS. Optional ~1.5 KB JS only adds arrow buttons + mouse-drag.
  • 📐 Aligns to your grid with a single variable (--bs-content).
  • 🫥 Scrollbar hidden by default (still fully scrollable).
  • ♿ Keyboard-scrollable, respects prefers-reduced-motion.
  • 📦 No build step, no framework, no dependencies.

Install

There is no build step. Copy the two files into your project:

bleed-slider.css   ← required (the whole library)
bleed-slider.js    ← optional (arrows + mouse drag only)
<link rel="stylesheet" href="bleed-slider.css" />
<!-- optional -->
<script src="bleed-slider.js" defer></script>

Or with a package manager:

npm install bleed-slider
import "bleed-slider/bleed-slider.css";
import "bleed-slider"; // optional enhancement; auto-inits on DOMContentLoaded

Quick start

<link rel="stylesheet" href="bleed-slider.css" />

<!-- normal page content goes inside .bs-container so it sits on the grid -->
<div class="bs-container">
  <h2>Featured products</h2>
</div>

<!-- the slider is full-width and bleeds to the edges -->
<ul class="bleed-slider" tabindex="0" aria-label="Featured products">
  <li>…your card…</li>
  <li>…your card…</li>
  <li>…your card…</li>
</ul>

Point one variable at your grid width and you're done:

:root { --bs-content: 1180px; } /* ← your content/grid width */

Two rules for correct alignment

  1. .bleed-slider must be full-width — a direct child of <body> or any wrapper that has no horizontal padding / max-width of its own.
  2. Wrap your normal content (headings, text) in .bs-container so it lands on the same grid line as the first slide.

How it works

Three native CSS features do all the work — there is no measuring, no resize observers, no JS:

.bleed-slider {
  /* distance from the viewport edge in to where the grid content starts */
  --bs-edge: max(var(--bs-gutter), (100% - var(--bs-content)) / 2);

  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;                /* snap each slide into place    */
  scroll-padding-inline-start: var(--bs-edge);  /* snap to the GRID, not to 0    */
  padding-inline: var(--bs-edge);               /* first/last slide on the grid  */
}
.bleed-slider > * {
  flex: 0 0 var(--bs-slide);
  scroll-snap-align: start;
}

The element itself spans the full viewport width. Only its padding and scroll-padding pull the snap points in to your grid. So the ends of the track stay aligned to the grid while the middle bleeds edge-to-edge. That's the entire trick.

Because --bs-edge is expressed with % (resolved against the element's full width) rather than vw, the slider and .bs-container land on exactly the same line — no scrollbar-width drift.


Markup reference

Minimal (CSS only):

<ul class="bleed-slider" tabindex="0" aria-label="Products">
  <li></li>
</ul>

Full (with the optional arrows + drag JS):

<div class="bs-slider">
  <div class="bs-container bs-slider__head">
    <h2>Featured products</h2>
    <div class="bs-nav">
      <button data-bs-prev aria-label="Previous"></button>
      <button data-bs-next aria-label="Next"></button>
    </div>
  </div>

  <ul class="bleed-slider" tabindex="0" aria-label="Featured products">
    <li></li>
  </ul>
</div>
Class / attribute Role
.bleed-slider The scroll container. Any direct children become slides.
.bs-container Wrapper that keeps normal content on the grid.
.bs-slider Optional outer wrapper that scopes the arrow buttons to one slider.
[data-bs-prev] / [data-bs-next] Buttons the JS wires up to scroll one slide.
tabindex="0" Makes the track keyboard-scrollable (recommended).

Slides can be any element<li>, <a>, <article>, a card component, etc. Bleed Slider only sets their width and snap alignment; all visual styling is yours.


Configuration

Set these custom properties globally (:root) or per-instance (inline style):

Variable Default Description
--bs-content 1180px Your grid width — the alignment line.
--bs-gutter 1.25rem Minimum space from the viewport edge.
--bs-gap 1.25rem Space between slides.
--bs-slide clamp(15rem, 72vw, 22rem) Slide width (mobile → desktop).
--bs-radius 18px Corner-radius hook for your cards.

Per-instance override — just one slider gets narrower slides:

<ul class="bleed-slider" style="--bs-slide: clamp(13rem, 60vw, 17rem)"></ul>

The default --bs-slide uses clamp() so each slide is roughly 72% of the viewport on phones (the next card peeks in, hinting it's swipeable) and caps at 22rem on desktop.


Modifiers

Class Effect
(none) Scrollbar hidden by default — still fully scrollable.
.bleed-slider--bar Opt back in to a slim, on-brand scrollbar.
<ul class="bleed-slider bleed-slider--bar"></ul>

JavaScript API

The library works without any JavaScript. The optional bleed-slider.js only adds prev/next buttons and click-and-drag (mouse / trackpad — touch already scrolls natively).

It auto-initialises every .bleed-slider on DOMContentLoaded. To control it manually:

// enhance everything (or pass a root element/subtree)
BleedSlider.init();
BleedSlider.init(document.querySelector("#shop"));

// enhance a single track element
BleedSlider.enhance(document.querySelector(".bleed-slider"));
Method Description
BleedSlider.init(root?) Enhance every .bleed-slider within root (default document). Safe to call repeatedly — already-enhanced sliders are skipped.
BleedSlider.enhance(el) Enhance one specific .bleed-slider element.

Arrow buttons ([data-bs-prev] / [data-bs-next]) are looked up inside the nearest .bs-slider wrapper and auto-disable at the start/end of the track. It's also available as a CommonJS module (require("bleed-slider")).

Opt a slider out of JS. Add data-bs-no-enhance to keep a specific slider pure CSS even when the script is loaded — init() will skip it:

<ul class="bleed-slider" data-bs-no-enhance></ul>

Accessibility

  • The track is keyboard-scrollable when given tabindex="0" — arrow keys, Page Up/Down, Home/End all work natively.
  • Add a meaningful aria-label to each .bleed-slider.
  • Arrow buttons should carry aria-labels (e.g. "Previous" / "Next").
  • scroll-behavior: smooth is disabled automatically under @media (prefers-reduced-motion: reduce).

Browser support

Works in all modern evergreen browsers (Chrome, Edge, Firefox, Safari).

Uses CSS scroll-snap, max() and clamp() for layout. color-mix() is used only for the optional --bar scrollbar styling and degrades gracefully where unsupported.


FAQ

Does it need JavaScript? No. Swiping, trackpad, wheel, and keyboard all work with CSS alone. JS only adds arrow buttons and mouse-drag. See example-no-js.html for a page that loads zero scripts, or add data-bs-no-enhance to opt a single slider out of the script.

My first slide isn't aligned to the grid. The .bleed-slider must be full-width — make sure no ancestor adds horizontal padding or a max-width. Wrap text content in .bs-container instead, and set --bs-content to match your grid.

Can slides be links or custom components? Yes — any direct child of .bleed-slider becomes a slide.

How do I show the scrollbar? Add the .bleed-slider--bar modifier.


License

MIT © Bullmade

About

A tiny pure-CSS, full-bleed product slider that aligns to your grid and bleeds to the browser edge. Zero dependencies.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors