Bạn đang phụ thuộc vào các thư viện icon nặng nề như Font Awesome hay Feather Icons? Muốn kiểm soát hoàn toàn bộ icon trong dự án mà vẫn giữ được hiệu năng tối ưu? Bài viết này sẽ hướng dẫn bạn tạo một hệ thống icon SVG tự chủ chỉ với một file JavaScript duy nhất – siêu nhẹ, dễ sử dụng và hoàn toàn tùy biến.
Kết quả: Một bộ icon SVG chỉ vài KB, tải nhanh như chớp, dễ theme theo ý muốn và thân thiện với công cụ đọc màn hình.
Tại sao nên tự tạo hệ thống icon thay vì dùng thư viện có sẵn?
Các thư viện icon phổ biến thường có những hạn chế:
- Dung lượng lớn: Font Awesome 6 có thể nặng đến 1MB+ chỉ để bạn dùng 10-15 icon
- Thiếu tính linh hoạt: Khó tùy biến màu sắc, kích thước theo design system riêng
- Phụ thuộc ngoại vi: CDN có thể chậm hoặc bị chặn trong một số môi trường
- Không tối ưu: Tải cả bộ icon trong khi chỉ sử dụng một phần nhỏ
Hệ thống icon tự tạo giải quyết toàn bộ vấn đề trên:
- Chỉ 2-5KB cho 20-30 icon thường dùng
- Tùy biến hoàn toàn màu sắc bằng CSS
- Không phụ thuộc bên thứ ba
- Tải tức thì cùng với trang web
Kiến trúc hệ thống Init Icons
Hệ thống bao gồm 3 thành phần chính:
- InitIcons Object: Lưu trữ tất cả SVG dưới dạng chuỗi
- InitIconsInit Function: Quét DOM và chèn icon vào vị trí phù hợp
- Data Attribute: Sử dụng
data-init-iconđể đánh dấu vị trí cần hiển thị icon
Code hoàn chỉnh: init-icons.js
Dán đoạn code sau vào file init-icons.js và import vào dự án:
/* init-icons.js - Hệ thống icon SVG tự chủ */
const InitIcons = {
eye: '<svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true"><circle fill="none" stroke="currentColor" cx="10" cy="10" r="3.45"></circle><path fill="none" stroke="currentColor" d="m19.5,10c-2.4,3.66-5.26,7-9.5,7h0,0,0c-4.24,0-7.1-3.34-9.49-7C2.89,6.34,5.75,3,9.99,3h0,0,0c4.25,0,7.11,3.34,9.5,7Z"></path></svg>',
eyeoff: '<svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true"><path fill="none" stroke="currentColor" d="m7.56,7.56c.62-.62,1.49-1.01,2.44-1.01,1.91,0,3.45,1.54,3.45,3.45,0,.95-.39,1.82-1.01,2.44"></path><path fill="none" stroke="currentColor" d="m19.5,10c-2.4,3.66-5.26,7-9.5,7h0,0,0c-4.24,0-7.1-3.34-9.49-7C2.89,6.34,5.75,3,9.99,3h0,0,0c4.25,0,7.11,3.34,9.5,7Z"></path><line fill="none" stroke="currentColor" x1="2.5" y1="2.5" x2="17.5" y2="17.5"></line></svg>',
star: '<svg width="20" height="20" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true"><polygon fill="none" stroke="currentColor" stroke-width="1.01" points="10 2 12.63 7.27 18.5 8.12 14.25 12.22 15.25 18 10 15.27 4.75 18 5.75 12.22 1.5 8.12 7.37 7.27"></polygon></svg>',
user: '<svg width="20" height="20" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true"><circle fill="none" stroke="currentColor" stroke-width="1.1" cx="9.9" cy="6.4" r="4.4"></circle><path fill="none" stroke="currentColor" stroke-width="1.1" d="M1.5,19 C2.3,14.5 5.8,11.2 10,11.2 C14.2,11.2 17.7,14.6 18.5,19.2"></path></svg>',
check: '<svg width="20" height="20" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true"><polyline fill="none" stroke="currentColor" stroke-width="1.1" points="4,10 8,15 17,4"></polyline></svg>',
clock: '<svg width="20" height="20" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true"><circle fill="none" stroke="currentColor" stroke-width="1.1" cx="10" cy="10" r="9"></circle><rect width="1" height="7" x="9" y="4"></rect><path fill="none" stroke="currentColor" stroke-width="1.1" d="M13.018,14.197 L9.445,10.625"></path></svg>',
calendar: '<svg width="20" height="20" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true"><path d="M 2,3 2,17 18,17 18,3 2,3 Z M 17,16 3,16 3,8 17,8 17,16 Z M 17,7 3,7 3,4 17,4 17,7 Z"></path><rect width="1" height="3" x="6" y="2"></rect><rect width="1" height="3" x="13" y="2"></rect></svg>',
camera: '<svg width="20" height="20" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true"><circle fill="none" stroke="currentColor" stroke-width="1.1" cx="10" cy="10.8" r="3.8"></circle><path fill="none" stroke="currentColor" d="M1,4.5 C0.7,4.5 0.5,4.7 0.5,5 L0.5,17 C0.5,17.3 0.7,17.5 1,17.5 L19,17.5 C19.3,17.5 19.5,17.3 19.5,17 L19.5,5 C19.5,4.7 19.3,4.5 19,4.5 L13.5,4.5 L13.5,2.9 C13.5,2.6 13.3,2.5 13,2.5 L7,2.5 C6.7,2.5 6.5,2.6 6.5,2.9 L6.5,4.5 L1,4.5 L1,4.5 Z"></path></svg>'
};
/* SVG cho badge demo */
const InitBadgeSVG = '<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor" class="init-badge-icon"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87L18.18 22 12 18.56 5.82 22 7 14.14l-5-4.87 6.91-1.01L12 2z"/></svg>';
/* Hàm chính: quét và chèn icon */
function InitIconsInit(root = document) {
root.querySelectorAll("[data-init-icon]:not(.init-icon-rendered)").forEach(element => {
const iconName = element.getAttribute("data-init-icon");
if (InitIcons[iconName]) {
element.innerHTML = InitIcons[iconName];
element.setAttribute("aria-hidden", "true");
element.classList.add("init-icon-rendered");
}
});
}
/* Hàm mở rộng: tạo badge với icon */
function InitBadgesInit(root = document) {
root.querySelectorAll(".init-badge-level:not(.init-badge-enhanced)").forEach(element => {
element.classList.add("init-badge-enhanced");
const number = element.textContent.trim();
element.textContent = "";
// Thêm icon
const iconWrapper = document.createElement("span");
iconWrapper.innerHTML = InitBadgeSVG;
element.appendChild(iconWrapper.firstElementChild);
// Thêm số
const numberSpan = document.createElement("span");
numberSpan.className = "init-badge-number";
numberSpan.textContent = number;
element.appendChild(numberSpan);
});
}
/* Tự động khởi tạo khi DOM sẵn sàng */
document.addEventListener("DOMContentLoaded", () => {
InitIconsInit();
InitBadgesInit();
});
Cách sử dụng trong HTML
Sau khi import file JavaScript, việc hiển thị icon trở nên cực kỳ đơn giản:
<!-- Icon đơn giản -->
<span class="icon" data-init-icon="star"></span> Yêu thích
<!-- Icon trong button -->
<button class="btn-demo" type="button">
<span class="icon" data-init-icon="eye"></span>
Xem chi tiết
</button>
<!-- Badge với icon (tự động) -->
<span class="init-badge-level">12</span>
Styling và theming với CSS
Icon tự động kế thừa màu sắc từ thuộc tính color của phần tử cha nhờ currentColor:
/* CSS cơ bản cho icon */
.icon {
display: inline-flex;
width: 1em;
height: 1em;
line-height: 1;
vertical-align: -2px;
}
.icon svg {
width: 100%;
height: 100%;
}
/* Theming - thay đổi màu dễ dàng */
.btn-primary {
color: #3b82f6;
}
.btn-primary:hover {
color: #1d4ed8;
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
.icon {
color: #e5e7eb;
}
}
/* Kích thước khác nhau */
.icon-sm { font-size: 0.875rem; }
.icon-lg { font-size: 1.25rem; }
.icon-xl { font-size: 1.5rem; }
Tối ưu SVG để giảm dung lượng
Để có hiệu năng tốt nhất, hãy tối ưu SVG trước khi thêm vào InitIcons:
- Tham khảo: Nén SVG để tối ưu hóa dung lượng mã SVG
- Sử dụng currentColor:
fill="currentColor"hoặcstroke="currentColor" - Loại bỏ thuộc tính thừa: id, class, style không cần thiết
- Giữ viewBox chuẩn: Thường là “0 0 20 20” hoặc “0 0 24 24”
- Minify SVG: Xóa khoảng trắng, xuống dòng không cần thiết
Sử dụng với SPA và DOM động
Khi làm việc với React, Vue hoặc các framework SPA, bạn có thể khởi tạo icon cho phần DOM mới:
// Chỉ khởi tạo icon trong container cụ thể
const newPanel = document.querySelector('#dynamic-content');
InitIconsInit(newPanel);
// Hoặc sau khi component mount
useEffect(() => {
InitIconsInit(containerRef.current);
}, []);
// Trong Vue
mounted() {
InitIconsInit(this.$refs.container);
}
Mở rộng hệ thống
Hệ thống có thể dễ dàng mở rộng:
- Thêm icon mới: Chỉ cần bổ sung vào object
InitIcons - Tạo theme riêng: Nhóm icon theo chức năng (social, ui, business)
- Lazy loading: Tách icon ít dùng ra file riêng
- Build tool: Tự động tạo
InitIconstừ thư mục SVG
So sánh hiệu năng
| Phương pháp | Dung lượng | Tốc độ tải | Tùy biến |
|---|---|---|---|
| Font Awesome 6 | 900KB+ | Chậm | Hạn chế |
| Feather Icons | 45KB+ | Trung bình | Khá tốt |
| Init Icons | 3-8KB | Tức thì | Hoàn toàn |
Checklist trước khi deploy
- Tất cả icon sử dụng
currentColorđể dễ theming - Đã test hiển thị trên nhiều kích thước màn hình
- Icon trang trí có
aria-hidden="true" - CSS đã tối ưu cho cả light và dark mode
- Test với screen reader để đảm bảo accessibility
- Đo đạc bundle size trước và sau khi áp dụng
Kết luận
Hệ thống Init Icons mang lại sự tự chủ hoàn toàn về icon trong dự án web. Với chỉ vài KB dung lượng, bạn có được một bộ icon tải nhanh, dễ tùy biến và không phụ thuộc vào bên thứ ba. Đây là giải pháp lý tưởng cho những ai muốn tối ưu hiệu năng mà vẫn giữ được tính linh hoạt trong thiết kế.
Bước tiếp theo: thử nghiệm với dự án nhỏ, đo lường hiệu quả, sau đó mở rộng dần cho toàn bộ ứng dụng. Bạn sẽ ngạc nhiên về sự khác biệt mà một hệ thống icon tự tạo có thể mang lại!
Bình luận