More Articles
RSS

Jan 2024

Animated Nav Menu

Anyone else remember when Apple switched their site to be responsive, which was controversial since iPhones were capabale of viewing full-size page content? Here I recreate the other controversy of that launch: the two-line navicon that they still use.

<html>
<head>
  <style>
    @keyframes flip-top-forward {
      0% {  transform: none } 50% { transform: var(--translate-top) } 100% { transform: var(--translate-top) var(--rotate-top) } }
    @keyframes flip-bottom-forward {
      0% {  transform: none } 50% { transform: var(--translate-bottom) } 100% { transform: var(--translate-bottom) var(--rotate-bottom) } }
    @keyframes flip-top-reverse {
      0% { transform: var(--translate-top) var(--rotate-top) } 50% { transform: var(--translate-top) } 100% {  transform: none } }
    @keyframes flip-bottom-reverse {
      0% { transform: var(--translate-bottom) var(--rotate-bottom) } 50% { transform: var(--translate-bottom) } 100% {  transform: none } }
    :root {
      --translate-top: translate(0px,7px); --translate-bottom: translate(0px,-8px); --rotate-top: rotate(-45deg); --rotate-bottom: rotate(45deg) }
    body {
      background: #f1f1f1; display: flex; align-items: center; justify-content: center }
    svg {
      cursor: pointer; color: #9c9c9c }
    #line-top {
      transform-origin: 16px 7px }
    #line-bottom {
      transform-origin: 16px 22px }
    svg.active > #line-top {
      animation: 0.2s flip-top-forward ease-in-out;
      transform: var(--translate-top) var(--rotate-top) }
    svg.active > #line-bottom {
      animation: 0.2s flip-bottom-forward ease-in-out;
      transform: var(--translate-bottom) var(--rotate-bottom) }
    svg.reverse > #line-top {
      animation: 0.2s flip-top-reverse ease-in-out;
      transform: none }
    svg.reverse > #line-bottom {
      animation: 0.2s flip-bottom-reverse ease-in-out;
      transform: none }
  </style>
  <script>
    class App {
      constructor(){
        document.addEventListener('DOMContentLoaded',this.handleContentLoaded.bind(this));
      }
      handleContentLoaded(){
        this.svg = document.getElementById('svg');
        this.svg.addEventListener('click',this.handleClickSvg.bind(this));
      }
      handleClickSvg(event){
        const is_active = this.svg.classList.contains('active')
        this.svg.classList = is_active ? 'reverse' : 'active';
      }
    }
    const app = new App();
  </script>
</head>
<body>
  <svg id="svg" width="64" viewbox="0 0 32 28">
    <rect id="line-top" x="0" y="5" width="32" height="4" rx="1" fill="currentColor" />
    <rect id="line-bottom" x="0" y="20" width="32" height="4" rx="1" fill="currentColor" />
  </svg>
</body>
</html>

I created the SVG using rectangles, and then setup CSS animations for the transition between states. Binding to the click event, I can toggle the direction of the animation with Javascript by specififying a specific class name.