Refrakt v1.0.0

Troubleshooting

Troubleshooting

New to Refrakt? The five problems that account for nearly every first-hour issue — and their fixes — are at Start here, step 9. This page is the exhaustive version: every console message the engine can emit, what causes it, and what to do.

Stuck on any of it? Email jawad.ahbab.turna@gmail.com — that reaches me, the developer who wrote the slider, not a ticket queue. Usually answered within a working day. What support covers.

Refrakt is engineered to degrade, not to break: bad options clamp, missing media falls back, and a machine without WebGL2 gets a CSS slider with the identical API. When something still looks wrong, it is almost always one of the situations below — and almost always announced by a [Refrakt] line in the browser console, so open the console first.

Blank slider

SymptomCauseFix
A demo page shows “Build the library first”, or the console says 404 for refrakt/dist/refrakt.umd.js The one-time build has not been run yet. This download ships the source; refrakt/dist/ is produced by the build Run npm install then npm run build inside the refrakt/ folder — see Start here, step 2
Nothing renders; console says container has no .rf-slide children; instance is inert Slides are missing, use a different class, or are not direct children of the container (a wrapper div in between breaks detection) Make every slide a <figure class="rf-slide"> that is an immediate child of the data-refrakt element
Nothing renders; console says element not found for "…" The selector passed to new Refrakt() matched nothing — typo, or the script ran before the element existed Fix the selector, or construct after DOMContentLoaded (the UMD auto-init already waits for it)
Raw stacked images, unstyled arrows, no warnings dist/refrakt.css is not linked — the JS bundle deliberately injects no CSS Add <link rel="stylesheet" href="dist/refrakt.css"> in <head>
Container collapses to zero height Slides are absolutely positioned, so the container has no intrinsic height Give the container explicit size in your CSS: height: 100vh, a fixed height, or aspect-ratio
Slider area shows the page background instead of slides Theme CSS paints an opaque background over the media area — the shared canvas is a fixed transparent overlay at z-index: 0, so opaque layers above it hide the rendering Remove opaque background from the container, its slides, and any wrapper that covers the slider’s box
Blank only inside a scrolling/overflow panel An ancestor’s clip rect fully excludes the container (e.g. it sits inside a collapsed overflow: hidden element) Ensure the slider’s box is actually visible inside every clipping ancestor

CSS fallback engaged

When WebGL2 is unavailable, Refrakt mounts its CSS mode: same markup, same API, same events — transitions become crossfades with a backdrop-blur glass veil instead of refraction. This is by design, and it announces itself through the fallback event, which fires before ready:

const slider = new Refrakt('#hero');
slider.on('fallback', ({ reason }) => {
  // reason: 'no-webgl2' | 'forced' | 'context-lost'
  console.log('Refrakt is in CSS mode:', reason);
});
ReasonMeaningWhat to do
no-webgl2 The browser has no WebGL2 context — an old browser, a blocklisted GPU driver, hardware acceleration disabled in browser settings, or an enterprise policy Nothing; this is the designed path for that hardware. Verify your own machine at a WebGL2 test page or check chrome://gpu if you did not expect it
forced forceFallback: true is set in the configuration Remove the option — it exists for testing the fallback and for pages that opt out of GL deliberately (see page-builder conflicts)
context-lost The GPU context was lost at runtime (driver reset, GPU pressure from other tabs) and could not be restored — Refrakt migrated the live instance to CSS mode, keeping its position Nothing at the page level; a reload gets GL back. Frequent losses on one machine indicate driver or GPU-memory trouble beyond the page

Users with prefers-reduced-motion do not get the fallback — they get the GL engine with motion effects reduced per the a11y.reducedMotion option (crossfades, no tear).

Images stay low-res / frosted

SymptomCauseFix
A slide stays a frosted blur; console shows slide N media failed: <url> The data-rf-src path 404s. Paths resolve relative to the page URL, not the script or CSS location Fix the path; check the failing URL in the console warning, or the Network panel
Console shows slide N decode timeout The full-resolution file took longer than 8 s to fetch and decode — usually a multi-MB original on a slow connection Export at 1600 px longest edge per the media guide; the LQIP remains up meanwhile
CDN-hosted images never sharpen Missing CORS header — WebGL cannot sample a cross-origin image without Access-Control-Allow-Origin Configure the header on the CDN (how), or serve media from the page’s own origin
data-rf-srcset always picks the wrong size, or is ignored Density descriptors (2x) — only width descriptors (800w) are supported. Or the container was resized after load: selection is one-shot by design Use width descriptors; author candidates around real container widths
Image is sharp but low-res on hi-DPI displays, no warnings Only the inline src (the LQIP) was provided — it is then both placeholder and final image Add data-rf-src with the full-resolution file
A slide renders as a flat ink-colored panel Both LQIP and full-resolution failed (slide N has no media / media failed warnings) — the ink panel is the terminal fallback Fix the media paths; the slide is intentionally kept in the sequence so indices and deep links stay stable

Video won’t play

SymptomCauseFix
Poster shows but playback never starts Browser autoplay policy blocked the muted play() (rare — usually data-saver or an embedded webview) Nothing required: Refrakt retries on the visitor’s next pointer interaction. Never set media.video.muted to false expecting autoplay with sound — no browser allows it, and Refrakt forces mute while autoplaying
Video plays, then pauses “randomly” By design: a video plays only while its slide is centered and the tab is visible; leaving center pauses it (it resumes, not restarts) None — this is the §media playback model and saves battery/decoders
Works in Safari, dead in Chrome/Firefox; console shows slide N video failed HLS (.m3u8) source — natively supported only by Safari/iOS; no hls.js is bundled (zero-dependency rule) Provide H.264 MP4 — the universal format (see encoding requirements)
Console shows slide N video failed: <url> and the poster image is used 404, unsupported codec (e.g. HEVC in Firefox), or a CORS-blocked video host Check the URL, re-encode to H.264 + AAC, or fix CORS (notes)
Some video slides show stills on a video-heavy page The shared decoder pool holds at most 4 <video> elements; furthest-from-center slides release theirs and show posters None — always supply data-rf-poster so the still is intentional
YouTube/Vimeo slide never plays “inline” Not a bug: cross-origin iframes cannot be WebGL textures. Provider slides show a refractable poster; playback opens in the lightbox That is the designed behavior — see the poster pattern

Strict CSP pages

Refrakt is CSP-friendly by construction: no eval, no Web Workers, no runtime style injection, no inline scripts, no external fetches for its own code. What a Content-Security-Policy needs to allow depends only on where your media lives:

DirectiveNeeded?Value
script-src Yes Your own host (wherever refrakt.umd.js / refrakt.esm.js is served). No 'unsafe-inline', no 'unsafe-eval' required
style-src Yes Your own host for refrakt.css. Refrakt injects no <style> elements, so no inline-style allowance is required
img-src Yes Every host serving slide images. Add i.ytimg.com only if you rely on YouTube poster derivation instead of data-rf-poster
media-src If using video Every host serving data-rf-video MP4 files
frame-src If using YouTube/Vimeo slides www.youtube-nocookie.com and/or player.vimeo.com — used only by lightbox playback
connect-src Only without data-rf-poster vimeo.com for oEmbed poster derivation. Supply data-rf-poster on Vimeo slides and this entry is unnecessary
worker-src No Refrakt uses no workers

Fully self-hosted pages (own images, own MP4s, data-rf-poster on provider slides) run under default-src 'self' with no exceptions at all.

Page-builder conflicts

Refrakt draws all instances on one shared <canvas> appended to <body> with position: fixed; z-index: 0 — a transparent overlay that paints only inside each slider’s rectangle. Page builders and theme frameworks interact with that design in two known ways:

Z-index stacking

The canvas sits at z-index: 0 in the root stacking context; slide text (z: 10) and navigation (z: 20) stack above it. Two things can go wrong:

  • Content hidden behind the slider’s pixels: a theme element with z-index: -1 or auto-stacking under the canvas will be covered inside the slider’s rect. Give page content a normal (≥ 0) stacking position.
  • Slider pixels hidden behind content: builders that wrap sections in opaque, positioned containers can paint over the canvas. Keep the slider’s section background transparent where the media should show.

The transform containing-block limitation

Known limitation: if any ancestor of the canvas has a transform, filter, backdrop-filter, perspective, or will-change: transform, CSS makes that ancestor the containing block for position: fixed descendants. The shared canvas then stops being viewport-fixed — it scrolls and offsets with that ancestor, and the rendered slides drift out of alignment with their containers.

This is a CSS specification behavior, not a bug Refrakt can code around. It typically appears with smooth-scrolling libraries that translate a page wrapper, and with builder “page transition” or zoom effects that put a transform on body or a top-level wrapper that ends up containing the canvas.

Fixes, in order of preference:

  1. Remove the transform/filter from the ancestor. Scope animations to sections that do not contain the slider; prefer opacity-based transitions, which do not create containing blocks.
  2. Accept the CSS fallback on pages where the ancestor transform is non-negotiable: set "forceFallback": true in data-rf-options. CSS mode renders entirely inside the container — no fixed canvas, no containing-block sensitivity — with the same API and events.

Console warning glossary

Every warning Refrakt can emit, verbatim. Each fires at most once per cause, is prefixed [Refrakt], and never accompanies an exception — warnings are advice, not failures.

WarningMeaning & fix
element not found for "<selector>"; instance is inert The constructor selector matched nothing. The instance is a safe no-op stub. Fix the selector or construction timing
container has no .rf-slide children; instance is inert No direct .rf-slide children found. Check the markup contract (blank slider)
element already bound; returning the existing instance new Refrakt() was called twice on one element (often auto-init plus a manual call). Harmless — you received the original instance
data-rf-options ignored: <parse error> The attribute is not valid JSON — usually single-quoted keys or a trailing comma. The preset still applies; the overrides do not
unknown preset "<name>"; using defaults No preset registered under that name. Check spelling against Refrakt.presets, or register your custom preset before constructing
unknown option "<path>" ignored The option path is not in the schema — typo or wrong nesting. See the options reference in the reference manual
<path> <value> clamped to <limit> The value was outside its documented range and was clamped to the nearest bound. The slider runs with the clamped value
<path> invalid value <v>; using default <d> Wrong type for the option (e.g. a string where a number belongs). The default is used
"<path>" requires rebuild(); stored, not applied setOption() was called on a non-live option (like layout.mode). The value is stored and takes effect on the next rebuild()
preset "<name>" overwritten registerPreset() replaced an existing name. Intentional overrides can ignore this
registerPreset: invalid preset name <name> The name passed to registerPreset() is empty or not a string. The call was ignored
instance destroyed; further calls are ignored A method was called after destroy(). All post-destroy calls are safe no-ops; this reminds you once
non-numeric index <i> ignored goTo() / openLightbox() received something that is not a finite number
index out of range; wrapped to <i> / clamped to <i> The index exceeded the slide count — wrapped when layout.loop is on, clamped when off
unknown event "<name>" on() received an event name outside the documented set (check spelling: ready, change, drag, transitionstart, transitionend, mediaplay, lightboxopen, lightboxclose, fallback, destroy)
on("<name>") requires a function The second argument to on() was not a function; the subscription was ignored
slide <n> has no media The slide has no <img>, no data-rf-src, no data-rf-video, and no provider attribute. It renders as an ink-colored panel and stays in the sequence
slide <n> media failed: <url> The full-resolution image 404ed or failed to decode. The LQIP stays up if present, else the ink panel. Fix the URL (details)
slide <n> decode timeout: <url> Fetch + decode exceeded 8 s. Usually an oversized source file — resize per the media guide
slide <n> video failed: <url> The video errored or its codec is unsupported. Falls back to data-rf-poster, else the ink panel (details)
slide <n> has no full-size media; lightbox not opened openLightbox() targeted a slide whose media failed or never existed — there is nothing sharp to show
unrecognized video URL data-rf-video holds a URL that is neither a media file nor a parseable YouTube/Vimeo link. Use the bare-ID attributes (data-rf-youtube / data-rf-vimeo) instead
Vimeo oEmbed failed for <id>; supply data-rf-poster Poster derivation could not reach Vimeo (offline, private video, provider change). Add data-rf-poster — required for Vimeo slides (why)
shader program failed: <log> The GPU driver rejected the shader — extremely rare, usually a broken driver. The instance falls back to CSS mode; please report the console log if you see this on current hardware