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

[해피CGI][cgimall] Alpine.js ( Restaurant Menu Accordion ) 본문

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

[해피CGI][cgimall] Alpine.js ( Restaurant Menu Accordion )

해피CGI윤실장 2025. 6. 18. 09:07

Alpine.js를 사용한 메뉴판 입니다.
각 항목을 누르면 숨겨진 메뉴가 출력되는 형식입니다.
자세한 내용은 데모를 확인해 주시기 바랍니다.

 

 

 

HTML

<div class='wrapper'>
  <div class='logo'></div>
  <ul x-data='accordion' class='accordion'>
    <template x-for='(items,category) in menu'>
      <li>
        <button @click='toggle(category)'>
          <span x-text='category'></span>
        </button>
        <div x-show='activeItem === category && open' x-collapse.duration.800ms >
          <ul class='accordion-list' >
            <template x-for='item in items'>
              <li>
                <span x-text='item.name'></span>
                <span x-text='item.price'></span>
              </li>
            </template>
          </ul>
        </div>
      </li>
    </template>
  </ul>
</div>
 

CSS

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
 
 body {
  height: 100vh;
  display: grid;
  place-items: center;
  font: 1rem helvetica,sans-serif;
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
 
  & h1 {
    font: 600 2.5rem helvetica,san-serif;
    text-align: center;
    margin-bottom: 1rem;
  }
 
  & ul { list-style-type: none; }
 
  & .logo {
    width: 40vmin;
    height: 30vmin;
    background-size: cover;
    margin: 0 auto;
    mix-blend-mode: multiply;
  }
}
 
.accordion {
  width: min(300px,90vw);
 
  & > li:first-of-type  { margin-top: 0; }
  & > li                { margin-top: -2px; }
 
  & button {
    width: 100%;
    background-color: transparent;
    font-size: 0.9rem;
    line-height: 3.5;
    text-transform: uppercase;
    letter-spacing: 3px;
    border: none;
    border-top: 2px solid #000;
    border-bottom: 2px solid #000;
    outline: none;
    cursor: pointer;
  }
 
  & .accordion-list {
    display: grid;
    grid-template-columns: repeat(2,1fr);
    font-size: 0.7rem;
    gap: 0.5rem 1.5rem;
    padding: 1rem 0;
 
    & li {
      display: flex;
      justify-content: space-between;
    }
  }
}


JS

function registerComponent() {
  Alpine.data('accordion',function() {
    return {
      activeItem: 'appetizers',
      open: true,
      menu: {
        appetizers: [
          { name: 'Caesar Salad', price: 8 },
          { name: 'Spring Rolls', price: 6 },
          { name: 'Stuffed Mushrooms', price: 9 },
          { name: 'Zuchini Fritters', price: 12 },
          { name: 'Coconut Shrimps', price: 15 },
          { name: 'Clam Bruschetta', price: 7 },
          { name: 'Crab Cake', price: 10 },
          { name: 'Clam Chowder', price: 6 },
          { name: 'Beef Stew', price: 6 },
        ],
        main: [
          { name: 'Cedar-Plank Salmon', price: 18 },
          { name: 'Lobster Gnocchi', price: 25 },
          { name: 'Chicken Lasagna', price: 15 },
          { name: 'Grilled Spareribs', price: 30 },
          { name: 'Scallops Risotto', price: 20 },
          { name: 'New York Steak', price: 18 },
          { name: 'Jambalaya', price: 15 },
          { name: 'Seafood Pasta', price: 18 },
          { name: 'Beef Stroganoff', price: 18 },
          { name: 'Thai Green Curry', price: 20 },
        ],
        desserts: [
          { name: 'Caramel Flan', price: 5 },
          { name: 'Old English Trifle', price: 8 },
          { name: 'Budapest Roll', price: 6 },
          { name: 'Nanimo Bar', price: 5 },
          { name: 'Lemon Drizzle Cake', price: 4 },
        ],
        drinks: [
          { name: 'Martini', price: 7 },
          { name: 'Manhattan', price: 8 },
          { name: 'Cosmopolitan', price: 8 },
          { name: 'Whiskey Sour', price: 6 },
          { name: 'Mojito', price: 6 },
          { name: 'Margaria', price: 7 },
          { name: 'Old Fashion', price: 6 },
          { name: 'Bloody Mary', price: 9 },
          { name: 'White Russian', price: 7 },
          { name: 'Coffee', price: 3 },
          { name: 'Soda', price: 2 },
        ],
      },
      toggle(item) {
        this.activeItem = (this.activeItem === item ? null : item);
        this.open = true;
      },
    }
  });
}
 
document.addEventListener('alpine:init',registerComponent,false);


 

Comments