SVG from Zero to Senior · Part 10 — Production: Optimization, Sprites & Accessibility
Ship SVG like a senior: optimize with SVGO, choose the right delivery (inline vs img vs sprite), build a symbol + use icon system themed with currentColor, make graphics accessible with title/desc/role/aria, and respect perf budgets.
You can now draw, paint, animate, filter, mask, and script SVG. {Giờ con vẽ, sơn, animate, filter, mask, và script SVG được.} The final lesson is the one that separates a hobbyist from a senior: shipping it well. {Bài cuối là cái tách người chơi tài tử khỏi senior: ship nó cho tốt.} Optimized, themeable, accessible, and fast. {Được tối ưu, đổi theme được, truy cập được, và nhanh.} Master this and you’ve completed the journey from gà mờ to senior. {Làm chủ cái này là con hoàn thành hành trình từ gà mờ lên senior.}
See the byte savings, re-theme the icon sprite with one click, and read the accessibility checklist. {Xem mức tiết kiệm byte, đổi theme cho sprite icon bằng một click, và đọc checklist truy cập.}
Open the full demo {Mở demo đầy đủ}: /tools/svg-production-demo/.
Optimize: SVGO {Tối ưu: SVGO}
Design tools export bloated SVG — XML prologs, editor metadata, comments, id="Layer_1", redundant attributes, and coordinates with eight decimal places. {Công cụ thiết kế export SVG phình to — prolog XML, metadata trình biên tập, comment, id="Layer_1", thuộc tính dư, và toạ độ tám chữ số thập phân.} SVGO strips all of that losslessly, routinely cutting 40–70% of bytes. {SVGO loại bỏ tất cả không mất chất, thường cắt 40–70% byte.}
npx svgo icon.svg -o icon.min.svg
npx svgo --folder ./icons --recursive # whole directory
But optimize with intent, not blindly: {Nhưng tối ưu có chủ đích, đừng mù quáng:}
- Keep
viewBox— never let SVGO drop it, or scaling breaks. {GiữviewBox— đừng để SVGO bỏ, không thì scale hỏng.} - Keep ids you reference from CSS/JS/
<use>/gradients. {Giữ các id con tham chiếu từ CSS/JS/<use>/gradient.} - Keep
<title>/<desc>for accessibility. {Giữ<title>/<desc>cho khả năng truy cập.}
SVGO is plugin-based, so you configure exactly which transforms run via svgo.config.js. {SVGO dựa trên plugin, nên con cấu hình chính xác transform nào chạy qua svgo.config.js.}
Choose the right delivery {Chọn cách giao đúng}
There is no single “best” way to put SVG on a page — it depends on the job. {Không có cách “tốt nhất” duy nhất để đặt SVG lên trang — tuỳ việc.}
| Method | When | Trade-off |
|---|---|---|
Inline <svg>…</svg> | icons you style/animate/script {icon con style/animate/script} | full CSS+JS control, but bloats HTML & no caching {kiểm soát đủ, nhưng phình HTML & không cache} |
<img src="x.svg"> | static images, logos {ảnh tĩnh, logo} | cached, simple; no CSS/JS into it {cache, đơn giản; không CSS/JS vào trong} |
CSS background-image | decorative, repeatable {trang trí, lặp lại} | cached; not in DOM, not accessible {cache; không trong DOM, không truy cập} |
Sprite (<use>) | icon systems {hệ icon} | define once, reuse everywhere, themeable {định nghĩa một lần, dùng khắp nơi, đổi theme được} |
Senior heuristic: inline for interactive/themed icons, <img> for cacheable static art, a sprite for a shared icon set. {Quy tắc senior: inline cho icon tương tác/đổi theme, <img> cho art tĩnh cache được, sprite cho bộ icon dùng chung.}
The icon system: <symbol> + <use> + currentColor {Hệ icon: <symbol> + <use> + currentColor}
This is how professional icon sets work. {Đây là cách các bộ icon chuyên nghiệp hoạt động.} Define each icon once as a <symbol> (with its own viewBox), then stamp instances with <use>: {Định nghĩa mỗi icon một lần thành <symbol> (với viewBox riêng), rồi đóng dấu các instance bằng <use>:}
<!-- once per page (often injected at the top of <body>) -->
<svg style="display:none" aria-hidden="true">
<symbol id="i-star" viewBox="0 0 24 24">
<path fill="currentColor" d="M12 2l2.9 6.3 6.9.7…z" />
</symbol>
</svg>
<!-- anywhere, any number of times -->
<svg class="icon" aria-hidden="true"><use href="#i-star" /></svg>
The masterstroke is fill="currentColor". {Cú đánh quyết định là fill="currentColor".} currentColor resolves to the element’s CSS color, so icons inherit text color automatically and re-theme with one rule: {currentColor phân giải thành color CSS của element, nên icon tự kế thừa màu chữ và đổi theme bằng một luật:}
.icon { width: 1.25em; height: 1.25em; }
.btn-danger .icon { color: #ef4444; } /* every icon inside goes red */
Click the swatches in the demo — one color change re-themes the whole row. {Click các ô màu trong demo — một thay đổi color đổi theme cả hàng.} This is why you almost never bake hard-coded colors into reusable icons. {Vì thế con gần như không bao giờ nhúng màu cứng vào icon tái dùng.}
Modern
<use href>uses plainhref; the legacyxlink:hrefis only needed for very old browsers. {<use href>hiện đại dùnghrefthường;xlink:hrefcũ chỉ cần cho trình duyệt rất cũ.}
Accessibility — the part everyone skips {Khả năng truy cập — phần ai cũng bỏ qua}
SVG is in the DOM, so it has accessibility obligations. {SVG nằm trong DOM, nên nó có nghĩa vụ về khả năng truy cập.} The decision tree: {Cây quyết định:}
- Decorative (next to a text label) →
aria-hidden="true", no announcement. {Trang trí (cạnh nhãn chữ) →aria-hidden="true", không thông báo.} - Meaningful standalone graphic →
role="img"+ a<title>(short name) and optional<desc>(detail), wired witharia-labelledby: {Đồ hoạ độc lập có nghĩa →role="img"+ một<title>(tên ngắn) và<desc>tuỳ chọn (chi tiết), nối bằngaria-labelledby:}
<svg role="img" aria-labelledby="t d" viewBox="0 0 24 24">
<title id="t">Favorite</title>
<desc id="d">A filled star indicating this item is saved</desc>
<path d="…" />
</svg>
- Interactive → wrap in a real
<button>with an accessible name and visible focus styles. {Tương tác → bọc trong<button>thật với tên truy cập được và focus style nhìn thấy.} - Data viz → provide a text alternative (a summary or a hidden data table); a chart’s shapes mean nothing to a screen reader on their own. {Trực quan dữ liệu → cho một bản thay thế dạng chữ (tóm tắt hoặc bảng dữ liệu ẩn); các hình của biểu đồ tự nó vô nghĩa với screen reader.}
Performance budget {Ngân sách hiệu năng}
- Inline SVG bloats HTML and isn’t cached separately — for a big shared icon set, prefer a sprite or
<img>. {Inline SVG phình HTML và không cache riêng — cho bộ icon lớn dùng chung, ưu tiên sprite hoặc<img>.} - Filters and big blurs are GPU-expensive — budget them, don’t animate
stdDeviationevery frame. {Filter và blur lớn tốn GPU — tính toán chúng, đừng animatestdDeviationmỗi frame.} - Thousands of DOM nodes get heavy — past a few thousand shapes, move to
<canvas>/WebGL. {Hàng nghìn node DOM nặng — quá vài nghìn hình, chuyển sang<canvas>/WebGL.} - Animate
transform/opacity, the compositor-friendly properties. {Animatetransform/opacity, các property thân thiện compositor.}
The master’s warnings {Lời cảnh báo của sư phụ}
- Don’t let SVGO strip
viewBoxor referenced ids. Test the output, don’t trust it blindly. {Đừng để SVGO bỏviewBoxhay id được tham chiếu. Kiểm tra output, đừng tin mù quáng.} currentColorfor reusable icons. Hard-coded fills can’t be themed by context. {currentColorcho icon tái dùng. Fill cứng không đổi theme theo ngữ cảnh được.}- A graphic with no label is invisible to AT — or worse, announced as meaningless. Decide decorative vs meaningful for every SVG. {Đồ hoạ không nhãn vô hình với công nghệ hỗ trợ — hoặc tệ hơn, bị đọc vô nghĩa. Quyết định trang trí vs có nghĩa cho mọi SVG.}
Practice, or it didn’t happen {Luyện tập, không thì coi như chưa học}
- Optimize a real export {Tối ưu một bản export thật}: run
npx svgoon an icon from a design tool and diff the bytes. {chạynpx svgolên một icon từ công cụ thiết kế và so byte.} - Build a 4-icon sprite {Dựng sprite 4 icon}:
<symbol>s withcurrentColor, then re-theme them via a parentcolor. {<symbol>vớicurrentColor, rồi đổi theme quacolorcủa cha.} - Audit for a11y {Kiểm tra a11y}: take one decorative and one meaningful SVG and add the correct
ariatreatment to each. {lấy một SVG trang trí và một SVG có nghĩa và thêm xử lýariađúng cho mỗi cái.}
The journey’s end {Kết thúc hành trình}
Look back at where you started: a flat sheet of graph paper and a confusing viewBox. {Nhìn lại nơi con bắt đầu: một tờ giấy kẻ ô phẳng và một viewBox khó hiểu.} You now command the whole language — shapes, paths, paint, text, transforms, animation, filters, masks, scripting, and production craft. {Giờ con điều khiển cả ngôn ngữ — hình, path, sơn, chữ, transform, animation, filter, mask, scripting, và nghề ship production.} That is genuinely senior-level SVG. {Đó thực sự là SVG tầm senior.} Go build something sharp at every resolution. {Đi dựng thứ gì đó sắc nét ở mọi độ phân giải.} The master bows. {Sư phụ cúi chào.}