Text breathes between light and bold weight, endlessly.
export function weightMorph(el) {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return () => {}
const text = el.textContent ?? ''
el.setAttribute('aria-label', text)
el.style.position = 'relative'
el.style.whiteSpace = 'nowrap'
const bold = document.createElement('span')
bold.textContent = text
bold.className = 'k-weight-bold'
bold.setAttribute('aria-hidden', 'true')
el.append(bold)
const easing = 'linear(0.0000,0.0016,0.0065,0.0145,0.0257,0.0400,0.0573,0.0774,0.1003,0.1257,0.1536,0.1838,0.2160,0.2500,0.2857,0.3227,0.3609,0.4000,0.4397,0.4799,0.5201,0.5603,0.6000,0.6391,0.6773,0.7143,0.7500,0.7840,0.8162,0.8464,0.8743,0.8997,0.9226,0.9427,0.9600,0.9743,0.9855,0.9935,0.9984,1.0000)'
const anim = bold.animate([{ opacity: 0 }, { opacity: 1 }], {
duration: 0.9 * 1000,
delay: 0 * 1000,
easing,
iterations: Infinity,
direction: 'alternate',
fill: 'backwards',
})
return () => {
anim.cancel()
bold.remove()
el.style.whiteSpace = ''
}
}
/* CSS */
.k-weight-bold {
position: absolute;
inset: 0;
font-weight: 800;
opacity: 0;
}