Styling

Class names, data attributes, and the edge fade.

Loopem ships structural CSS only: what it needs to work, and nothing that is a look. No colors, fonts, radii or sizes. Those are yours.

What it puts on the DOM

HookWhereMeaning
.loopemyour containerpositioning context, clips the track
.loopem-trackinside ita zero-sized origin at the center
.loopem-slotone per visible itemabsolutely positioned, transformed per frame
.loopem-movingon the containerpresent only while in motion
.loopem-interactiveon the containerpresent only while it takes input
data-loopem-keyon each slotthe virtual index
data-loopem-centeredon one slotthe item at the center
aria-selectedon each slotthe chosen one, when interactive

Style your own element inside the slot, not the slot itself. The slot is the library’s; its transform is rewritten every frame.

css
.thumb {
  width: 62px;
  height: 82px;
  border-radius: 12px;
}

.loopem-slot[data-loopem-centered] .thumb {
  box-shadow: 0 0 0 2px #fff;
}

.loopem-slot[aria-selected="true"] .thumb {
  box-shadow: 0 0 0 3px var(--accent);
}

Positioning the container

The library’s own position: relative and overflow: hidden are written at zero specificity, so any plain class of yours wins:

css
.rail {
  position: absolute;
  inset: 0 auto 0 0;
  width: 200px;
}

Fading the edges

Two things dissolve a track, and mixing them up is the usual reason an edge still looks cut.

minOpacity and falloff ramp each item by its distance from the center, so an item fades out whole and is gone before it reaches the container. This is what makes a track look like it dissolves.

fade masks the container’s edges. It cannot stop overflow: hidden cutting through an item that reaches the boundary, only make that slice fainter.

Use the ramp for the look, and a small fade for whatever still touches the edge:

ts
createPicker(el, {
  items,
  renderItem,
  minOpacity: 0,
  falloff: 5,
  fade: 90,
})

fade is a mask rather than an overlay, so it works over any background, and it runs along the axis: top and bottom on a vertical track. It shows only where the track actually reaches the container, so a container much wider than the track fades empty background and appears to do nothing.

Overriding a slot’s opacity

The layout writes its own falloff to --loopem-fade, and the structural CSS reads opacity: var(--loopem-opacity, var(--loopem-fade, 1)). Set the first one to hold a slot out of the fade:

css
.loopem-slot[aria-selected="true"] {
  --loopem-opacity: 1;
}

Two names rather than one, because inline custom properties beat stylesheet rules. If the library wrote --loopem-opacity inline, your override would need !important.

While it is moving

.loopem-moving is on the container only during motion, which is also when will-change: transform is set and then cleared. Use it for anything that should switch off while the track moves.

css
.loopem-moving .thumb {
  pointer-events: none;
}