Refrakt v1.0.0

Customizing · Level 2

Change one thing at a time.

You have a slider running with your own pictures and words. This page covers the twenty or so things people change next — size, colours, autoplay, arrows, video, clickable slides. Each one is a short recipe: what you want on the left, the line to paste on the right. You do not have to read it in order, and you do not have to understand the whole settings system to use any single recipe.

Three guides, in order of depth. Each links on to the next — take them as far as you need and stop.

  1. Level 1 Start here A slider on your page with your own pictures and words. About fifteen minutes, and no settings at all.
  2. Level 2 Customizing Colours, height, autoplay, arrows and dots, clickable slides, video. Ready-made lines you paste and edit. You are here
  3. Level 3 Reference manual Every option, the JavaScript API, events. The full contract, for developers.

Not there yet? Start here first — this page assumes a slider already running with your own images in it. Want the exact range of a single setting rather than a recipe? That's the options reference.

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.

01 How settings work

Everything on this page is changed in one of three places. Knowing which is which saves a lot of hunting.

WhereWhat it controlsLooks like
Your CSS Size, colours, caption typography and position #hero { height: 70vh; }
The preset name The optical character as a whole data-refrakt="darkroom"
data-rf-options Individual behaviours: autoplay, which controls show, how it moves data-rf-options='{"nav": {"arrows": false}}'

About that third one

The data-rf-options attribute holds settings written in JSON — a strict but simple format. Every recipe below hands you the complete, working attribute, so in practice you copy a line and change a number or swap true for false. You never have to compose one from scratch.

Three rules keep JSON out of trouble:

  • The attribute is wrapped in single quotes; everything inside uses double quotes. data-rf-options='{"hash": true}'. Swapping those around is the single most common mistake.
  • No comma after the last item. {"a": 1, "b": 2} — not {"a": 1, "b": 2,}.
  • true and false and numbers are written bare, without quotes. "arrows": false, not "arrows": "false".

You cannot break the slider from here. If the JSON is malformed, Refrakt logs one console message telling you exactly what is wrong and carries on with the preset. If a number is out of range it is pulled to the nearest allowed value; if a name is misspelled it is ignored — again, each with one plain-English console line. A mistake in this attribute changes how the slider looks. It never stops it working.

Long attributes are easier to read broken over several lines. HTML is fine with that:

<div id="hero"
     data-refrakt="boardroom"
     data-rf-options='{"motion": {"autoplay": {"enabled": true, "interval": 5000}},
                       "nav": {"counter": true}}'>

02 Size and shape

The slider fills whatever box you put it in — no settings involved, just CSS on your container. Nothing else on this page matters if the container has no height, so start here.

Height

You wantWrite
A fixed height#hero { height: 600px; }
A proportion of the screen height#hero { height: 70vh; }
A fixed shape, whatever the width#hero { aspect-ratio: 16 / 9; }
Taller on phones, wider on desktopSee below
#hero { height: 70vh; }

/* phones: taller, so portrait subjects aren't cropped to a letterbox */
@media (max-width: 700px) {
  #hero { height: 78vh; }
}

Edge to edge, full screen

#hero {
  height: 100svh;   /* small-viewport height — correct under mobile browser bars */
  margin: 0;
}
body { margin: 0; }

100svh rather than 100vh matters on phones: it measures the viewport with the browser's address bar showing, so the bottom of your slider is not hidden behind it.

Several sliders on one page? Give each container its own id and its own height. They run independently, can use different presets, and share a single canvas behind the scenes — see the vitrine demo.

03 Colours

Arrows, dots, the counter, the progress bar, focus outlines, the enlarge view's backdrop and your caption text all take their colour from exactly two CSS variables. Set those two and the whole slider is re-themed. There is no third one to learn.

#hero {
  --rf-ink:   #f5f2ea;   /* text, arrows, dots, active states */
  --rf-paper: #0c0c0e;   /* control backgrounds, scrims, backdrops */
}

Think of it as writing with ink on paper. Dark ink on light paper for a bright page; light ink on dark paper for a dark one.

Page stylePaste this
Light page — white or near-white background --rf-ink: #111111; --rf-paper: #fafafa;
Dark page — black or near-black background --rf-ink: #f5f2ea; --rf-paper: #0c0c0e;
Warm / editorial — cream paper, near-black ink --rf-ink: #241a10; --rf-paper: #f4ede3;
An accent colour — a brand tint on a dark page --rf-ink: #ff9d2e; --rf-paper: #000000;

You can also write them straight onto the element, which is handy while you're experimenting:

<div id="hero" data-refrakt="eclipse"
     style="--rf-ink:#ff9d2e; --rf-paper:#000000">

Keep the two far apart in lightness. If your ink and paper are similar — mid-grey on light grey, say — the arrows and dots become hard to see and the slider fails accessibility guidelines. Both pairs above measure better than 15:1; aim for at least 4.5:1 with your own.

Caption colour, separately

Captions inherit the ink colour, which is usually what you want. To colour them independently — white text over a dark photograph on an otherwise light page, for instance — style your own caption elements:

#hero .rf-text { color: #ffffff; }

If the photo behind the words is busy, add the shipped backing panel instead of guessing at a colour:

<figcaption class="rf-text rf-scrim">
  <h2>Harbour, 5 a.m.</h2>
</figcaption>

rf-scrim lays your paper colour at 72% opacity behind the caption — enough to read through, not enough to hide the picture.

Caption position and typography

The caption is your HTML in a layer that tracks the slide, so it takes ordinary CSS. Nothing about it is locked:

#hero .rf-text {
  position: absolute;
  left: 5vw;
  bottom: 8vh;
  max-width: 22em;
}
#hero .rf-text h2 {
  font-size: clamp(2rem, 5vw, 4rem);
  line-height: 1.05;
  letter-spacing: -0.03em;
  margin: 0 0 0.4em;
}

Every demo does exactly this — open any demo's index.html and read its <style> block for a worked example you can lift.

04 Autoplay

Off by default. To turn it on:

<div id="hero" data-refrakt="boardroom"
     data-rf-options='{"motion": {"autoplay": {"enabled": true}}}'>

To choose the pace, add an interval in milliseconds — 5000 is five seconds:

data-rf-options='{"motion": {"autoplay": {"enabled": true, "interval": 5000}}}'
SettingDefaultNotes
"enabled"falsetrue starts it advancing on its own.
"interval"6000Milliseconds per slide. Anything from 1500 to 60000. Below about 4000 is uncomfortable to read.
"pauseOnHover"trueStops while the pointer is over the slider. Leave it on.

Four things happen automatically once autoplay is on, and none of them are optional:

  • A pause button appears. Anything that moves on its own for more than five seconds must be stoppable — that is a hard accessibility requirement, so the button cannot be switched off. Style it with .rf-pause. The spacebar also pauses and resumes.
  • It stops when the tab is hidden, so a background tab is not quietly burning battery.
  • It stops when the slider scrolls off screen, and resumes when it comes back. A slider halfway down a long page does not advance to slide 9 while nobody is looking.
  • It stops while the enlarge view is open, so the picture someone is studying does not change under them.

Visitors who have asked their system for reduced motion still get the slides — autoplay keeps running — but the optical motion around them is stilled: no colour dispersion, no drifting light, no breathing. The slider stays useful without being distracting.

The boardroom preset already turns autoplay on at a slow 7-second pace. If you are using it and want a different rhythm, you only need the "interval" part.

05 Arrows, dots and counters

Five separate pieces of chrome, each independently on or off. All of them live under "nav".

PieceSettingDefaultWhat it is
Arrows"arrows"truePrevious / next buttons at the sides.
Dots"spectrum"trueOne dot per slide; the active one condenses to solid ink.
Counter"counter"falseA fraction — 03 / 08.
Progress bar"progressRule"falseA hairline rule that fills as you move through.
Editorial menu"editorialMenu"falseLarge numbered entries, labelled from each slide's first heading. Replaces dots on type-led designs.

Recipes

You wantPaste
Nothing but the picture — no controls at all data-rf-options='{"nav": {"arrows": false, "spectrum": false}}'
Dots only, no arrows data-rf-options='{"nav": {"arrows": false}}'
Arrows only, no dots data-rf-options='{"nav": {"spectrum": false}}'
A counter and a progress bar instead of dots data-rf-options='{"nav": {"spectrum": false, "counter": true, "progressRule": true}}'
The big numbered menu data-rf-options='{"nav": {"spectrum": false, "editorialMenu": true}}'

Hiding every control does not make the slider unusable: dragging, the mouse wheel, arrow keys and swipe all keep working, and screen-reader users still get slide announcements. But do give visitors some visible sign there is more than one slide.

Restyling rather than hiding? Each piece is an ordinary button with a stable class — .rf-arrow, .rf-dot, .rf-counter, .rf-rule, .rf-menu-item. Write normal CSS against them; the full list is in the reference.

06 How it moves

Under "motion". These change how the slider responds to a drag, a flick or a wheel.

You wantPaste
Stop at the last slide instead of wrapping around data-rf-options='{"layout": {"loop": false}}'
Let a hard flick travel several slides data-rf-options='{"motion": {"maxStep": 4}}'
A longer, looser glide after release data-rf-options='{"motion": {"inertia": 0.8}}'
A crisper, more immediate stop data-rf-options='{"motion": {"inertia": 0.3, "snapStrength": 1}}'
Turn off the mouse wheel (it's inside a scrolling page) data-rf-options='{"motion": {"wheel": false}}'
Start on a slide other than the first data-rf-options='{"layout": {"startIndex": 2}}' — counting from 0, so this is the third slide
Slide vertically instead of horizontally data-rf-options='{"layout": {"axis": "y"}}'

Drag, wheel and keyboard can each be switched off with false — but leave "keyboard" alone. Arrow-key navigation is how keyboard and screen-reader users operate the slider at all.

09 Video and YouTube

A video slide is the same <figure> with one extra attribute. No settings, no separate mode.

Your own video file

<figure class="rf-slide"
        data-rf-video="video/atelier-loop.mp4"
        data-rf-poster="img/atelier-poster.jpg">
</figure>

The video plays inside the glass — it refracts and disperses exactly as a photograph does. The poster is the still frame shown until it is ready; always supply one.

YouTube or Vimeo

<figure class="rf-slide"
        data-rf-youtube="dQw4w9WgXcQ"
        data-rf-poster="img/talk-poster.jpg">
</figure>

<figure class="rf-slide"
        data-rf-vimeo="76979871"
        data-rf-poster="img/film-poster.jpg">
</figure>

Use the bare video ID, not the full web address. The poster refracts in the slider like any other image, and clicking plays the video full-size in the enlarge view.

Always supply data-rf-poster for YouTube and Vimeo slides. With a poster, your page makes no request to Google or Vimeo until a visitor actually chooses to play — which keeps the page fast and keeps third-party tracking off it. It also means the slide looks right on the first frame instead of popping in.

Formats, file sizes, autoplay rules on phones and everything else about media: the Media Guide.

11 Dialling the glass up or down

A preset is close but too strong, or not strong enough? You do not have to abandon it — override the one number you care about and everything else in the preset stays.

You wantPaste
Calmer glass — less bending data-rf-options='{"glass": {"ior": 0.05}}' (0 is flat, 0.35 is maximum)
Less colour fringing on drag data-rf-options='{"dispersion": {"amount": 0.1}}' (0–1)
No tearing at all, however hard you flick data-rf-options='{"dispersion": {"maxTear": 0}}'
Finer, more numerous ribs data-rf-options='{"glass": {"reeded": {"frequency": 30}}}' (4–96)
Move the light source data-rf-options='{"light": {"x": 0.2, "y": 0.1}}' (0,0 is top-left; 1,1 bottom-right)
More film grain data-rf-options='{"exposure": {"grain": 0.2}}' (0–0.5)
No glass at all — a clean, fast slider data-rf-options='{"glass": {"kind": "none"}}'

Don't guess at numbers — drag them. Open the optics-lab demo, move the sliders until the glass looks the way you want, and the panel gives you the exact line to copy. It is much faster than editing and reloading.

Every one of these is one row of a table in the Options reference, where you will find its exact range and what it means physically. Twelve finished combinations — condensation wiped clear by the pointer, a museum vitrine, cinematic bloom — are in the Optics Cookbook.

12 Putting several together

Recipes combine by nesting them under the same top-level names. Two autoplay settings and two nav settings share one "motion" and one "nav":

<div id="hero"
     data-refrakt="boardroom"
     data-rf-options='{"motion": {"autoplay": {"enabled": true, "interval": 5000}},
                       "nav": {"arrows": true, "spectrum": false, "counter": true},
                       "lightbox": {"enabled": true},
                       "hash": true}'>

Written out, that is: the boardroom look, advancing every five seconds, with arrows and a counter but no dots, clicking to enlarge, and the address bar tracking the slide.

Anything you don't mention keeps whatever the preset chose, and anything the preset doesn't mention keeps the factory default. You are always adjusting, never starting from nothing — which is why the recipes on this page are safe to paste one at a time.

Repeating a top-level name in the same attribute silently discards the first one — {"nav": {"arrows": false}, "nav": {"counter": true}} gives you the counter and keeps the arrows. Merge them into a single "nav" block instead.

13 Where to go next