웹솔루션개발 25년 노하우! 해피CGI의 모든것

[해피CGI][cgimall] 모바일에서 쓰기 좋은 애니메이션 메뉴 Mobile Menu Animation 본문

웹프로그램밍 자료실/HTML 자료

[해피CGI][cgimall] 모바일에서 쓰기 좋은 애니메이션 메뉴 Mobile Menu Animation

해피CGI윤실장 2025. 9. 2. 09:11



모바일에서 사용하기 좋은 애니메이션 메뉴입니다.

소스를 변경하여 다양하게 응용 가능합니다.


HTML 구조

<div class="page">

  <span class="page__name">Mobile Menu Animation</span>

  <span class="page__hint">

    Push the button below to see the animation

  </span>

</div>

<nav class="cdpn-mobile-menu js-cdpn-mobile-menu">

  <div id="cdpn-mobile-menu" class="cdpn-mobile-menu__container">

    <ul class="cdpn-mobile-menu__list ra-list">

      <li>

        <a href="#0" class="js-cdpn-mobile-menu__link uia-control ra-link">

          <span class="uia-control__group">Home</span>

        </a>

      </li>

      <li>

        <a href="#0" class="js-cdpn-mobile-menu__link uia-control ra-link">

          <span class="uia-control__group">About me</span>

        </a>

      </li>

      <li>

        <a href="#0" class="cdpn-mobile-menu__link uia-control ra-link">

          <span class="uia-control__group">Services</span>

        </a>

      </li>

      <li>

        <a href="#0" class="js-cdpn-mobile-menu__link uia-control ra-link">

          <span class="uia-control__group">Portfolio</span>

        </a>

      </li>

      <li>

        <a href="#0" class="js-cdpn-mobile-menu__link uia-control ra-link">

          <span class="uia-control__group">Blog</span>    

        </a>

      </li>

      <li>

        <a href="#0" class="js-cdpn-mobile-menu__link uia-control ra-link">

          <span class="uia-control__group">Contacts</a>

      </li>

    </ul>

  </div>

  <button class="cdpn-mobile-menu__toggle js-cdpn-mobile-menu__toggle uia-control ra-button" type="button" aria-controls="cdpn-mobile-menu" data-uia-hamburger-skin="2">

    <span class="uia-control__group">

      <span class="cdpn-mobile-menu__hamburger uia-control__icon uia-hamburger">

        <span class="uia-hamburger__group">

          <span class="uia-hamburger__label">

            <span class="js-cdpn-mobile-menu__toggle-hint ha-screen-reader">Open menu</span>

          </span>

        </span>

      </span>

    </span>

  </button>

</nav>

 



CSS 소스

/*

=====

RESET

=====

*/

:where(.ra-link) {

  display: var(--ra-link-display, inline-flex);

}

 

:where(.ra-link[href]) {

  color: var(--ra-link-color);

  text-decoration: var(--ra-link-text-decoration, none);

}

 

:where(.ra-list) {

  padding-inline-start: var(--ra-list-padidng-inline-start, 0);

  margin-block: var(--ra-list-margin-block-start, 0) var(--ra-list-margin-block-end, 0);

  list-style: var(--ra-list-list-style, none);

}

 

:where(.ra-button) {

  background-color: var(--ra-button-background-color, transparent);

  padding: var(--ra-button-padding, var(--ra-button-padding-top, 0) var(--ra-button-padding-right, 0) var(--ra-button-padding-bottom, 0) var(--ra-button-padding-left, 0));

  border: var(--ra-button-border, var(--ra-button-border-width, 0) var(--ra-button-border-style, solid) var(--ra-button-border-color, currentcolor));

 

  font-family: var(--ra-button-font-family, inherit);

  font-size: var(--ra-button-font-size, 1em);

  font-weight: var(--ra-button-font-weight, inherit);

 

  color: var(--ra-button-color, currentcolor);

  text-align: var(--ra-button-text-align, center);

  text-transform: var(--ra-button-text-transform, inherit);

  letter-spacing: var(--ra-button-letter-spacing, inherit);

  word-spacing: var(--ra-button-word-spacing, inherit);

 

  text-shadow: var(--ra-button-text-shadow, none);

  display: var(--ra-button-display, inline-flex);

}

 

.ra-button::-moz-focus-inner,

.ra-button[type="button"]::-moz-focus-inner,

.ra-button[type="reset"]::-moz-focus-inner,

.ra-button[type="submit"]::-moz-focus-inner {

 

  /* Remove the inner border and padding in Firefox. */

 

  border-style: none;

  padding: 0;

}

 

/*

=====

UIA-HAMBURGER

=====

*/

 

.uia-hamburger {  

  --uia-control-icon-main-size: var(--uia-hamburger-width, 28px);

  --uia-control-icon-extra-size: var(--uia-hamburger-height, 20px);

  --_uia-hamburger-thickness: var(--uia-hamburger-thickness, 4px);

 

  display: var(--uia-hamburger-display, inline-flex);

  position: var(--uia-hamburger-position, relative);

}

 

.uia-hamburger__group::before, 

.uia-hamburger__group::after, 

.uia-hamburger__label {

  inline-size: 100%;

  border-block-start: var(--_uia-hamburger-thickness) solid var(--uia-hamburger-background-color, currentColor);

  border-radius: var(--uia-hamburger-border-radius, 5px);

 

  position: absolute;

}

 

.uia-hamburger__group::before, 

.uia-hamburger__group::after {

  content:"";

}

 

.uia-hamburger__group::before {

  inset-block-start: var(--uia-hamburger-top-line-position, 0);

}

 

.uia-hamburger__group::after {

  inset-block-start: var(--uia-hamburger-last-line-position, calc(100% - var(--_uia-hamburger-thickness)));

}

 

.uia-hamburger__label {

  /* The calculation of middle hamburger button line position */

 

  inset-block-start: calc(50% - calc(var(--_uia-hamburger-thickness) / 2));

}

 

[data-uia-hamburger-skin="2"] .uia-hamburger__group::before {

  transform: var(--uia-hamburger-top-line-transform);

}

 

[data-uia-hamburger-skin="2"] .uia-hamburger__label {

  transform: var(--uia-hamburger-middle-line-transform);

  opacity: var(--uia-hamburger-middle-line-opacity);

}

 

[data-uia-hamburger-skin="2"] .uia-hamburger__group::after {

  transform: var(--uia-hamburger-last-line-transform);

}

 

@media (prefers-reduced-motion: no-preference) {

 

  [data-uia-hamburger-skin="2"] .uia-hamburger__group::before, 

  [data-uia-hamburger-skin="2"] .uia-hamburger__group::after, 

  [data-uia-hamburger-skin="2"] .uia-hamburger__label {

    transition-timing-function: ease;

    transition-duration: .15s;  

  }

 

  [data-uia-hamburger-skin="2"] .uia-hamburger__group::before, 

  [data-uia-hamburger-skin="2"] .uia-hamburger__group::after {

    transition-property: transform;

  }

 

  [data-uia-hamburger-skin="2"] .uia-hamburger__label {

    transition-property: transform, opacity;

  }

}
.
.
.

 

JS 소스

/*

!!!!!

This code is created to display element's behavior. I'm not a JS expert. So use it at your own risk

!!!!!

*/

 

 

(function(){

  'use strict';

 

  class Menu {

    constructor(settings) {

      this.menuRootNode = settings.menuRootNode;

      this.isInitialized = false;

      this.isOpened = false;

    }

 

    changeMenuState(menuState) {

      return this.isOpened = !menuState;

    }

  }

 

  class MenuBurger extends Menu {

 

    constructor(settings) {

      super(settings);

      this.openText = settings.openText;

      this.closeText = settings.closeText;

      this.menuClassesNames = settings.menuClassesNames;

      this.menuLinks = this.menuRootNode.querySelectorAll(`.${this.menuClassesNames.menuItemClass}`);

      this.hiddenElementsQuery = settings.hiddenElementsQuery;

      this.pageNodes = document.querySelectorAll(this.hiddenElementsQuery);  

      this.toggleNode = this.menuRootNode.querySelector(`.${this.menuClassesNames.toggleClass}`);

      this.a11yAttributes = ['aria-hidden', 'inert'];

      this.a11yAttributeValues = {

        'aria-hidden': true,

        'inert': ''

      }

    }

 

    init() {

      let currentMenuState = this.changeMenuState(this.isOpened);

 

      this.changeToggleHint(

        this.getCurrentToggleHint(currentMenuState, this.openText, this.closeText), 

        this.menuRootNode.querySelector(`.${this.menuClassesNames.toggleHintClass}`)

      );

      this.menuRootNode.classList.toggle(`${this.menuClassesNames.activeClass}`);

      this.setCurrentA11yAttribute(currentMenuState, this.toggleNode, "aria-expanded");  

      this.getFocusCurrentNode(currentMenuState, this.toggleNode, this.menuLinks[0]);

      this.pageNodes

        .forEach(

          (node) => this.setCurrentPageA11yAttributes(

            currentMenuState, 

            node, 

            this.a11yAttributes, 

            this.a11yAttributeValues)

      ); 

    }

 

    changeToggleHint(toggleHint, toggleNode) {

      toggleNode.textContent = toggleHint;

      return toggleHint; 

    }

 

    getCurrentToggleHint(currentMenuState, openText, closeText) {

      return (currentMenuState !== true) ? openText : closeText;

    }

 

    setCurrentA11yAttribute(currentMenuState, toggleNode, attribute) {

      return (currentMenuState !== true) ? 

        toggleNode.removeAttribute(attribute) : 

        toggleNode.setAttribute(attribute, currentMenuState);

    }

 

    getFocusCurrentNode(currentMenuState, importantFocusNode, toggleNode) {

      return (currentMenuState !== true) ? 

        importantFocusNode.focus() : 

        toggleNode.focus();

    }

 

    setCurrentPageA11yAttributes(currentMenuState, node, a11yAttributes, a11yAttributeValues) {

      return (currentMenuState !== true) ? 

        a11yAttributes.forEach((attribute) => node.removeAttribute(attribute)) :

 

Comments