Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

English · Türkçe

scrollcraft

A scroll-driven interaction runtime. Vanilla JS, zero dependencies, and it generates no DOM.

You author real, semantic HTML and mark it up with data-sc-* attributes. The engine reads them and drives everything from a single scroll value on a single requestAnimationFrame loop.

That constraint is the point. A runtime that builds its own DOM from a config object makes every site it touches look identical — which is the failure mode this replaces. You get a mechanism and a vocabulary; the composition stays yours.


Quick start

<link rel="stylesheet" href="scrollcraft.css">

<section data-sc-act="pin" data-sc-span="2">
  <div class="sc-stage">
    <h2 data-sc-kinetic="words" data-sc-cue="0.05 0.75">Words arrive as you scroll</h2>
  </div>
</section>

<script src="scrollcraft.js"></script>
<script>ScrollCraft.mount(document.body)</script>

mount(root, opts) takes an element or a selector, and returns nothing you have to keep. ScrollCraft.reduce reports whether reduced motion is on; ScrollCraft.instances holds what has been mounted.

A running example is in example/ — open example/index.html.


Acts — the unit of scroll time

An act owns a stretch of scroll and exposes a normalized progress p (0..1), published as --sc-p on the element so CSS can read it too.

<section data-sc-act="pin" data-sc-span="2.5"></section>
data-sc-act scrub · pin · pan · flow (default flow)
data-sc-span viewport-heights of scroll this act owns. Pinned acts only. Default 1.5

The engine sets the outer height and sticks the first .sc-stage child.


Devices — what p drives

Attribute On What it does
data-sc-scrub <video> p scrubs currentTime. Blob-loaded, so seeking needs no HTTP range support
data-sc-sequence="a/{i}.webp:120:1" <canvas> p scrubs an image sequence — path template : frame count : start index
data-sc-pan="0.6" a wide rail horizontal travel; the value is an extra-travel multiplier
data-sc-parallax="-0.2" any translateY by rate × progress × viewport. Negative recedes
data-sc-cue="0.1 0.5" any opacity and rise keyed to p. One value = enter+hold, two = enter..leave
data-sc-kinetic="lines" text lines · words · chars. Splits and staggers across the cue window
data-sc-reveal="up" any up · down · left · right · iris clip-path wipe
data-sc-count="0 4200" any number bloom across the cue window
data-sc-in a flow section reveal that fires once on entry. data-sc-stagger="60" staggers children
data-sc-drift="#07090c" an act page background interpolates toward this colour while the act is on screen
data-sc-progress any the engine fills it as the page scrolls; it draws no chrome of its own

Worldflight — one continuous camera move

Acts cut a page into pinned blocks, which is right for chapters and wrong for a single unbroken flight: the reader hits the end of an act, the stage unsticks, a static page slides past, and the next act starts the whole thing again.

Worldflight removes the seams by removing the blocks. There is one fixed stage for the whole page. The only element in flow is a spacer whose height the engine sets from the sum of segment weights. Every clip stays mounted for the life of the page and segments crossfade over a seam band — nothing ever swaps a src, because a src swap is a black frame, and a black frame is the cut this mode exists to avoid.

data-sc-w viewport-heights this segment owns. Default 1.3
data-sc-linger dwell remap for this leg only. Max 0.6
data-sc-seam crossfade band, in viewport-heights. Default 0.12
data-sc-waypoint label published on --sc-seg and the sc:waypoint event, so a page can draw its own route rail. The engine draws none
data-sc-window on a copy block: hero · finale · from to [in [out]] as fractions of the whole track

Pointer — interactivity that isn't scroll

data-sc-tilt="8" 3D tilt toward the pointer, spring-damped, in degrees
data-sc-magnet="0.35" element drifts toward the pointer inside its bounds
data-sc-spotlight publishes --sc-mx / --sc-my (0..1) for a light that follows the pointer

All three are gated to (hover: hover) and (pointer: fine) and switched off under reduced motion. Touch never fires them.


Reduced motion

Fewer and gentler, not zero. Cues still fade, so comprehension survives. Translation collapses, video clips are never fetched at all (the poster holds), and pointer devices are inert. A worldflight still tells its whole story: the posters cross-dissolve through the same seams and the same copy windows.

A rail is the exception that proves the rule. Zeroing its transform the way parallax is zeroed would park the act on its first screenful and make everything past the fold unreachable — a device that degrades into inaccessible content is not an accessibility win.


The playhead

Every scrub clip on the page — act or worldflight — is driven by one smoothed playhead. Scroll only ever writes a target; a standalone loop walks the current time toward it at a fixed fraction per frame (data-sc-lerp, default 0.18; 1.0 under reduced motion, which is no smoothing).

Without the smoothing a trackpad flick reads as a stutter, because wheel events do not arrive at a constant rate and a 1:1 write reproduces every gap in them.


Class vocabulary — the one trap

Two families, and mixing them up bites:

Class Where it belongs
.sc-body · .sc-lede · .sc-display · .sc-label flowing text. .sc-body caps line length at the reading measure — it is a paragraph class, not a page class. Putting it on <body> locks the whole document to about 700px
.sc-copy · .sc-copy--lead · .sc-copy--center · .sc-copy--trail overlay on a stage. These are position: absolute and anchor to the nearest positioned ancestor. Used in a flow section they escape to the top of the page

.sc-stage is position: sticky, so it is the containing block a .sc-copy inside it anchors to. That pairing is the intended one.


Styling

scrollcraft.css is two layers, and the split matters:

  • Tokens — what you override per brand. Colour roles, the type ramp, the spacing scale, elevation, motion. A brand is about twelve values; override six colour roles and the page is rebranded.
  • Devices — what the engine drives. Don't restyle these to taste; they are the mechanism. Style your own markup instead.

There are no card, badge or pill classes, because a stylesheet that ships those is how every page built on it ends up with the same shapes.

The stylesheet is required — the devices depend on it. It reads the document dir, so a rail in an RTL page pans the other way without a second attribute.


Licence

MIT — LICENSE. Use it, change it, sell it.

Built for and running in production on hanzala.com.tr.

About

A scroll-driven interaction runtime. Vanilla JS, zero dependencies, generates no DOM.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages