A theme value duplicated in a stylesheet and in a JavaScript constant is two sources of truth, and custom properties are readable and writable from both.
const root = document.documentElement;
getComputedStyle(root).getPropertyValue('--brand').trim();
root.style.setProperty('--brand', '#7b341e');
// and the pattern that makes a scroll-driven effect cheap
root.style.setProperty('--scroll', String(window.scrollY));
// .header { transform: translateY(calc(var(--scroll) * -0.2px)); }
The trim() is required because the returned value includes the leading whitespace from the declaration, which is a small thing that costs ten minutes the first time. Writing a property on the root rather than manipulating classes is what lets CSS do the work, which keeps the animation off the main thread. Reading is a forced style calculation, so doing it in a scroll handler is the mistake this pattern is meant to avoid.