Menu bars fold into a cross. Despite living in the morph section this is not a morph at all — three rects translate and rotate, and no path data changes, which is true of most icon "morphs" people reach for MorphSVG to do.
Every animation on the page redraws with the shape you pick.
<!-- 1. Markup -->
<svg viewBox="0 0 120 120" class="k-hamburger-close" aria-hidden="true">
<rect class="bar bar-top" x="26" y="38" width="68" height="9" rx="4.5" fill="currentColor" />
<rect class="bar bar-mid" x="26" y="55.5" width="68" height="9" rx="4.5" fill="currentColor" />
<rect class="bar bar-bottom" x="26" y="73" width="68" height="9" rx="4.5" fill="currentColor" />
</svg>
// 2. JS
function hamburgerClose(svg) {
const _reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
const _duration = _reducedMotion ? 0.01 : 0.6
const _delay = _reducedMotion ? 0 : 0
const bars = [...svg.querySelectorAll('.bar')]
const [top, mid, bottom] = bars
// Without fill-box, an SVG transform origin resolves against the whole
// canvas, so each bar would rotate around the SVG's top-left corner.
bars.forEach((bar) => {
bar.style.transformBox = 'fill-box'
bar.style.transformOrigin = 'center'
})
const easing = 'linear(0.0000,0.0000,0.0001,0.0003,0.0009,0.0022,0.0045,0.0083,0.0142,0.0227,0.0346,0.0506,0.0717,0.0988,0.1328,0.1751,0.2266,0.2888,0.3630,0.4507,0.5493,0.6370,0.7112,0.7734,0.8249,0.8672,0.9012,0.9283,0.9494,0.9654,0.9773,0.9858,0.9917,0.9955,0.9978,0.9991,0.9997,0.9999,1.0000,1.0000)'
const timing = { duration: _duration * 1000, delay: _delay * 1000, fill: 'both' }
// One animation per bar, eased per phase: the outer bars converge on the
// centre, then cross while the middle one fades. Two animations stacked on
// the same transform would overwrite each other instead of taking turns.
const cross = (y, deg) => [
{ transform: 'translateY(0px) rotate(0deg)', easing },
{ transform: `translateY(${y}px) rotate(0deg)`, offset: 0.5, easing },
{ transform: `translateY(${y}px) rotate(${deg}deg)` },
]
const animations = [
top.animate(cross(17.5, 45), timing),
bottom.animate(cross(-17.5, -45), timing),
mid.animate([{ opacity: 1 }, { opacity: 1, offset: 0.5, easing }, { opacity: 0 }], timing),
]
return () => {
animations.forEach((a) => a.cancel())
bars.forEach((bar) => {
bar.style.transformBox = ''
bar.style.transformOrigin = ''
})
}
}
hamburgerClose(document.querySelector('.k-hamburger-close'))