Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> CSS

Cách tạo hiệu ứng Animation cho Bullet (Dấu đầu dòng) List với CSS

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 transform và opacity cho 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: none trê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; }.