Modal, Tabs và Accordion

Component tương tác

Các thành phần tương tác — modal, accordion, tab panel — là những khối xây dựng cốt lõi cho mọi ứng dụng web. Tất cả đều dùng HTML + CSS + JavaScript thuần, không cần thư viện.

Kết quả
<button class="open-btn" onclick="openModal('m1')">Mở Modal</button>

<div id="m1" class="modal-overlay" onclick="closeModal('m1')">
  <div class="modal" onclick="event.stopPropagation()">
    <button class="modal-close" onclick="closeModal('m1')"></button>
    <h2>Xác nhận</h2>
    <p>Bạn có chắc muốn thực hiện thao tác này không?</p>
    <div class="modal-actions">
      <button class="btn btn-cancel" onclick="closeModal('m1')">Hủy</button>
      <button class="btn btn-ok">Đồng ý</button>
    </div>
  </div>
</div>
body { font-family: sans-serif; padding: 2rem; }
.open-btn { padding:0.5rem 1.2rem;background:#4472c4;color:white;border:none;border-radius:6px;cursor:pointer;font-size:1rem; }
.modal-overlay {
  display: none;
  position: fixed; inset: 0;
  background: rgba(0,0,0,.45);
  align-items: center; justify-content: center;
}
.modal-overlay.active { display: flex; }
.modal {
  background: white;
  border-radius: 10px;
  padding: 2rem;
  min-width: 300px;
  position: relative;
  box-shadow: 0 20px 60px rgba(0,0,0,.3);
  animation: pop .25s ease;
}
@keyframes pop { from { transform:scale(.85);opacity:0; } to { transform:scale(1);opacity:1; } }
.modal h2 { margin: 0 0 .5rem; }
.modal p  { color:#555; margin-bottom: 1.25rem; }
.modal-close { position:absolute;top:.75rem;right:.75rem;background:none;border:none;font-size:1.2rem;cursor:pointer;color:#888; }
.modal-actions { display:flex;gap:.75rem;justify-content:flex-end; }
.btn { padding:.45rem 1rem;border:none;border-radius:6px;cursor:pointer;font-size:.9rem; }
.btn-cancel { background:#eee;color:#333; }
.btn-ok     { background:#4472c4;color:white; }
function openModal(id)  { document.getElementById(id).classList.add('active');    }
function closeModal(id) { document.getElementById(id).classList.remove('active'); }
// Đóng bằng phím Escape
document.addEventListener('keydown', e => {
  if (e.key === 'Escape')
    document.querySelectorAll('.modal-overlay.active')
            .forEach(m => m.classList.remove('active'));
});

Accordion

Kết quả
<div class="accordion">
  <div class="item">
    <button class="acc-header">HTML là gì? <span class="icon"></span></button>
    <div class="acc-body">
      <p>HTML (HyperText Markup Language) là ngôn ngữ đánh dấu dùng để xây dựng cấu trúc của trang web. HTML sử dụng các thẻ để định nghĩa nội dung như tiêu đề, đoạn văn, hình ảnh, liên kết, v.v.</p>
    </div>
  </div>
  <div class="item">
    <button class="acc-header">CSS là gì? <span class="icon"></span></button>
    <div class="acc-body">
      <p>CSS (Cascading Style Sheets) là ngôn ngữ dùng để định nghĩa phong cách trình bày cho trang web. CSS kiểm soát màu sắc, font chữ, bố cục và các hiệu ứng hình ảnh.</p>
    </div>
  </div>
  <div class="item">
    <button class="acc-header">JavaScript là gì? <span class="icon"></span></button>
    <div class="acc-body">
      <p>JavaScript là ngôn ngữ lập trình chạy trên trình duyệt, giúp trang web trở nên động và có khả năng tương tác với người dùng như xử lý sự kiện, gọi API, cập nhật DOM.</p>
    </div>
  </div>
</div>
* { box-sizing:border-box; font-family:sans-serif; }
body { padding: 1.5rem; max-width: 520px; margin: auto; }
.accordion { border-radius: 8px; overflow: hidden; border: 1px solid #ddd; }
.item { border-bottom: 1px solid #ddd; }
.item:last-child { border-bottom: none; }
.acc-header {
  width: 100%; background: white; border: none; padding: 1rem 1.25rem;
  font-size: .95rem; font-weight: 600; text-align: left;
  cursor: pointer; display: flex; justify-content: space-between; align-items: center;
  transition: background .2s;
}
.acc-header:hover { background: #f7f7f7; }
.acc-header.open  { background: #f0f5ff; color: #4472c4; }
.icon { transition: transform .3s; display: inline-block; font-size: .8rem; }
.acc-header.open .icon { transform: rotate(180deg); }
.acc-body {
  max-height: 0; overflow: hidden;
  transition: max-height .35s ease, padding .35s ease;
  padding: 0 1.25rem;
  background: #fafafe;
}
.acc-body.open { max-height: 200px; padding: 1rem 1.25rem; }
p { margin: 0; color: #444; line-height: 1.55; }
document.querySelectorAll('.acc-header').forEach(btn => {
  btn.addEventListener('click', () => {
    const body = btn.nextElementSibling;
    const isOpen = btn.classList.contains('open');

    // Đóng tất cả
    document.querySelectorAll('.acc-header').forEach(b => b.classList.remove('open'));
    document.querySelectorAll('.acc-body').forEach(b => b.classList.remove('open'));

    // Mở cái vừa click nếu nó đang đóng
    if (!isOpen) {
      btn.classList.add('open');
      body.classList.add('open');
    }
  });
});

Tab Panel

Kết quả
<div class="tab-widget">
  <div class="tab-bar">
    <button class="tab active" data-tab="html">HTML</button>
    <button class="tab" data-tab="css">CSS</button>
    <button class="tab" data-tab="js">JavaScript</button>
  </div>
  <div class="tab-content">
    <div id="tab-html" class="pane active">
      <h3>HTML</h3>
      <p>HTML là ngôn ngữ đánh dấu tạo cấu trúc trang web. Trình duyệt đọc HTML và hiển thị nội dung cho người dùng.</p>
    </div>
    <div id="tab-css" class="pane">
      <h3>CSS</h3>
      <p>CSS kiểm soát giao diện: màu sắc, font, bố cục, animation. CSS giúp trang web trông đẹp trên mọi thiết bị.</p>
    </div>
    <div id="tab-js" class="pane">
      <h3>JavaScript</h3>
      <p>JavaScript là ngôn ngữ lập trình của web. JS xử lý tương tác người dùng, gọi API và cập nhật giao diện.</p>
    </div>
  </div>
</div>
* { box-sizing:border-box; font-family:sans-serif; margin:0; padding:0; }
body { padding: 1.5rem; }
.tab-widget { border: 1px solid #ddd; border-radius: 8px; overflow: hidden; max-width: 480px; }
.tab-bar { display: flex; background: #f5f5f5; border-bottom: 1px solid #ddd; }
.tab {
  padding: .65rem 1.25rem; background: none; border: none;
  font-size: .9rem; cursor: pointer; color: #555;
  border-bottom: 3px solid transparent; transition: all .2s;
}
.tab:hover  { color: #4472c4; }
.tab.active { color: #4472c4; font-weight: 600; border-bottom-color: #4472c4; background: white; }
.tab-content { background: white; }
.pane { display: none; padding: 1.25rem; }
.pane.active { display: block; animation: fadeIn .25s; }
@keyframes fadeIn { from { opacity:0;transform:translateY(4px); } to { opacity:1;transform:none; } }
h3 { margin-bottom: .5rem; color: #333; }
p  { color: #555; line-height: 1.6; }
const tabs  = document.querySelectorAll('.tab');
const panes = document.querySelectorAll('.pane');

tabs.forEach(tab => {
  tab.addEventListener('click', () => {
    tabs.forEach(t  => t.classList.remove('active'));
    panes.forEach(p => p.classList.remove('active'));

    tab.classList.add('active');
    document.getElementById('tab-' + tab.dataset.tab).classList.add('active');
  });
});

Bình luận