The symptom
On a client site there was a product-showcase carousel: one large slide in the center, and beneath it a thumbnail strip that was supposed to slide sideways as you advanced, keeping the active thumbnail centered. The strip never moved. Whatever slide was active, the thumbnails kept showing the exact same neighbors, as if the position were locked at index 0. The main slide changed smoothly; only the strip underneath stayed frozen.
The maddening part: zero console errors. No Uncaught, no warning, no CSS line struck out in red in DevTools. And it turned out this had been quietly broken for several versions, ever since a rename in v1.7.14, with nobody noticing until the client asked why the thumbnails never scrolled.
The dead ends
My first suspect was obvious: the index math was wrong. The strip slides via a translate3d whose offset depends on the active index, so if the index were stuck at 0, nothing moving would make sense. I logged the index on every slide change:
console.log("active index:", indexMV);It climbed cleanly: 0, 1, 2, 3. So the JS knew the right index. Not it.
Second suspect: maybe the handler was not firing, or I was targeting the wrong element. I dropped a log inside the handler and checked the element reference. The handler ran on every change, the element was correct. The JS really was executing the code that set the transform. Yet the strip stayed put.
What finally cracked it: I opened the strip element in DevTools and looked at its inline style. The JS was clearly computing and assigning a transform, but the element's style attribute was empty. No transform at all. I tried setting it manually from the console, pasting the exact string the code produced:
el.style.transform = "calc(-1 * (var(--wg-thumb-w) + var(--wg-thumb-gap)))";
console.log(el.style.transform); // "" (empty)It came back an empty string. The browser rejected the value without a sound. That was the lightbulb: the assignment was not failing on a JS error, it was failing because the value was invalid and the CSSOM silently discarded it.
The root cause
I looked closely at the string being produced: it contained var(--wg-thumb-w) and var(--wg-thumb-gap). I grepped the theme's CSS for those two names:
grep -rn -- "--wg-thumb" src/Nothing. Those names no longer existed anywhere. Back in v1.7.14 there had been a big namespace rename: every --wg-* custom property (a leftover from an older component called WearableGallery) was renamed to --folio-*. The rename was clean on the CSS side; every variable definition had been updated. But one reference was missed: a template string in animations.js:1432 was still assembling a calc() with the old variable names.
And this is the crux of why it failed silently. A calc() whose terms include an undefined custom property is invalid as a whole. When you set element.style.transform to an invalid value, the CSSOM does not throw; it simply ignores the assignment and leaves the property as it was, empty in this case. So there is no console error, no red line, nothing. The transform just never exists, and the strip sits at its starting position forever.
This is a different animal from the other CSS-variable bug class, like a variable dropped because a build tool tree-shook it out. Here the build was fine; the new --folio-* variables existed and were valid. The only thing wrong was a single JS string still pointing at a dead name.
The fix
The core fix is one line: point the JS reference at the new variable names.
// before (broken: vars undefined, calc invalid, transform discarded):
`calc(${-indexMV} * (var(--wg-thumb-w) + var(--wg-thumb-gap)))`
// after:
`calc(${-indexMV} * (var(--folio-thumb-w) + var(--folio-thumb-gap)))`Once swapped, the calc() became valid, the transform was accepted again, and the thumbnail strip immediately tracked the active slide the way it should.
But fixing one line was not enough to relax. This bug happened precisely because a rename I thought was finished had left one stray reference behind. So my last step was to prove there were no others. I grepped the whole theme for the old names, not just in CSS but in JS too:
grep -rn -e "--wg-" -e "wgallery" src/Zero hits. Only then was I confident the rename was actually clean.
Takeaways
- Renaming a CSS custom property does not stop at the CSS file. String references in JS, template literals,
setPropertycalls, andcalc()assembled at runtime are all in scope. Grep the old name across the entire codebase, not just the stylesheets. - A
calc()with an undefined custom property is invalid in its entirety, and it fails silently. No error, the transform simply never appears. - Setting
element.style.somePropto an invalid value throws nothing. The CSSOM ignores it quietly. If an element will not move even though JS is "setting" its style, check whether the value actually stuck by readingel.style.transformback. - When JS knows the right index but the element still will not move, suspect a rejected style value, not the event logic.
- A bug that produces no error at all can live for months. Close out a big rename with a verification grep, not with the assumption that you "got them all."
