Giới thiệu
Animation cho bullet list tăng tính tương tác, thu hút sự chú ý. Có 2 hướng tiếp cận: (1) Style bullet gốc với ::marker (hạn chế animation), (2) Tự tạo bullet bằng ::before + list-style: none (kiểm soát hoàn toàn animation).
Cách 1: Animation trên ::marker (Hạn chế)
::marker chỉ hỗ trợ animation color, font-size - không hỗ trợ transform, opacity, width.
li::marker { color: #27ae60; transition: color 0.2s, font-size 0.2s; }
li:hover::marker { color: #e74c3c; font-size: 1.5em; }
Cách 2: Custom Bullet với ::before (Khuyến nghị - Full Control)
Ví dụ 1: Bullet tròn scale + màu khi hover
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Animated Bullets</title>
<style>
* { box-sizing: border-box; }
body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; background: #fafafa; padding: 2rem; max-width: 600px; margin: 0 auto; }
ul.animatedBullets {
list-style: none; padding: 0;
}
.animatedBullets li {
position: relative;
padding-left: 2.5rem;
margin: 1rem 0;
font-size: 1.1rem;
color: #333;
cursor: pointer;
transition: color 0.2s;
}
.animatedBullets li:hover { color: #2c3e50; }
/* Custom Bullet */
.animatedBullets li::before {
content: "";
position: absolute;
left: 0; top: 50%; transform: translateY(-50%);
width: 12px; height: 12px;
border-radius: 50%;
background: linear-gradient(135deg, #6a1b9a, #8e24aa);
box-shadow: 0 2px 8px rgba(106, 27, 154, 0.3);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Hover Effects */
.animatedBullets li:hover::before {
transform: translateY(-50%) scale(1.5);
background: linear-gradient(135deg, #e74c3c, #c0392b);
box-shadow: 0 4px 16px rgba(231, 76, 60, 0.5);
}
/* Active/Selected state */
.animatedBullets li.active::before {
background: #27ae60;
box-shadow: 0 0 0 3px rgba(39, 174, 96, 0.3);
}
</style>
</head>
<body>
<h2 style="margin-bottom:1.5rem; color:#2c3e50;">Animated Bullets - Hover để xem hiệu ứng</h2>
<ul class="animatedBullets">
<li>Tính năng 1: Dashboard analytics</li>
<li>Tính năng 2: Real-time notifications</li>
<li>Tính năng 3: Dark mode support</li>
<li>Tính năng 4: Multi-language</li>
<li>Tính năng 5: API integration</li>
</ul>
<script>
// Optional: Click to toggle active
document.querySelectorAll('.animatedBullets li').forEach(li => {
li.addEventListener('click', () => li.classList.toggle('active'));
});
</script>
</body>
</html>
Ví dụ 2: Bullet slide-in từ trái (Staggered Animation on Load)
<style>
.staggeredList { list-style: none; padding: 0; }
.staggeredList li {
position: relative; padding-left: 2.5rem;
margin: 1rem 0; font-size: 1.1rem; color: #333;
opacity: 0; transform: translateX(-30px);
animation: slideIn 0.5s ease forwards;
}
.staggeredList li:nth-child(1) { animation-delay: 0.1s; }
.staggeredList li:nth-child(2) { animation-delay: 0.2s; }
.staggeredList li:nth-child(3) { animation-delay: 0.3s; }
.staggeredList li:nth-child(4) { animation-delay: 0.4s; }
.staggeredList li:nth-child(5) { animation-delay: 0.5s; }
@keyframes slideIn {
to { opacity: 1; transform: translateX(0); }
}
.staggeredList li::before {
content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
width: 10px; height: 10px; border-radius: 50%;
background: #8e24aa;
/* Bullet cũng animate cùng li */
}
</style>
<ul class="staggeredList">
<li>Mục đầu tiên xuất hiện</li>
<li>Mục thứ hai trượt vào</li>
<li>Mục thứ ba tiếp theo</li>
<li>Mục thứ tư hiện ra</li>
<li>Mục cuối cùng hoàn tất</li>
</ul>
Ví dụ 3: Numbered List với Counter Animation (CSS Counters + ::before)
<style>
.counterList {
list-style: none; padding: 0;
counter-reset: item;
}
.counterList li {
position: relative; padding-left: 3.5rem;
margin: 1.5rem 0; font-size: 1.1rem; color: #333;
background: white; padding: 1.5rem 1.5rem 1.5rem 3.5rem;
border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.06);
transition: transform 0.2s, box-shadow 0.2s;
}
.counterList li:hover { transform: translateX(8px); box-shadow: 0 8px 24px rgba(0,0,0,0.1); }
.counterList li::before {
counter-increment: item;
content: counter(item);
position: absolute; left: 1rem; top: 50%; transform: translateY(-50%);
width: 40px; height: 40px; border-radius: 50%;
background: linear-gradient(135deg, #6a1b9a, #8e24aa);
color: white; font-weight: 700; font-size: 1.1rem;
display: flex; align-items: center; justify-content: center;
box-shadow: 0 4px 12px rgba(106, 27, 154, 0.3);
transition: all 0.3s;
}
.counterList li:hover::before {
transform: translateY(-50%) scale(1.2) rotate(180deg);
background: linear-gradient(135deg, #27ae60, #2ecc71);
}
</style>
<ol class="counterList">
<li>Đăng ký tài khoản</li>
<li>Xác thực email</li>
<li>Thiết lập hồ sơ</li>
<li>Bắt đầu sử dụng</li>
</ol>
Ví dụ 4: Checkbox Animation (Từ bài 1003063638)
Checkbox custom với appearance: none + ::after slide animation:
input[type=checkbox] {
appearance: none; width: 24px; height: 24px;
border: 2px solid #ddd; border-radius: 6px;
position: relative; cursor: pointer; transition: border-color 0.2s;
}
input[type=checkbox]::after {
content: ""; position: absolute; top: 2px; left: 6px;
width: 6px; height: 12px; border: solid white;
border-width: 0 3px 3px 0; transform: rotate(45deg) scale(0);
opacity: 0; transition: transform 0.2s, opacity 0.2s;
}
input[type=checkbox]:checked {
background: #27ae60; border-color: #27ae60;
}
input[type=checkbox]:checked::after {
transform: rotate(45deg) scale(1); opacity: 1;
}
Best Practices
- Dùng
transformvàopacitycho animation (GPU accelerated). transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1)- easing tự nhiên (Material Design).- Respect
prefers-reduced-motion: tắt animation cho user nhạy cảm. - Stagger delay:
animation-delay: calc(var(--index) * 0.1s)(cần CSS custom property hoặc nth-child). - Bullet size: 8-12px cho body text, 16-24px cho heading/feature list.
Accessibility
- Luôn giữ
list-style: nonetrên<ul>/<ol>nhưng không xóa semantic - screen reader vẫn nhận diện list. - Custom bullet là decoration, không truyền thông tin quan trọng.
- Focus state cho
<li>nếu interactive:li:focus-visible { outline: 2px solid #8e24aa; outline-offset: 4px; border-radius: 4px; }.