jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

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.}

MethodWhenTrade-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-imagedecorative, 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 plain href; the legacy xlink:href is only needed for very old browsers. {<use href> hiện đại dùng href thường; xlink:href cũ 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 graphicrole="img" + a <title> (short name) and optional <desc> (detail), wired with aria-labelledby: {Đồ hoạ độc lập có nghĩarole="img" + một <title> (tên ngắn) và <desc> tuỳ chọn (chi tiết), nối bằng aria-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 stdDeviation every frame. {Filter và blur lớn tốn GPU — tính toán chúng, đừng animate stdDeviation mỗ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. {Animate transform/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 viewBox or referenced ids. Test the output, don’t trust it blindly. {Đừng để SVGO bỏ viewBox hay id được tham chiếu. Kiểm tra output, đừng tin mù quáng.}
  • currentColor for reusable icons. Hard-coded fills can’t be themed by context. {currentColor cho 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}

  1. Optimize a real export {Tối ưu một bản export thật}: run npx svgo on an icon from a design tool and diff the bytes. {chạy npx svgo lên một icon từ công cụ thiết kế và so byte.}
  2. Build a 4-icon sprite {Dựng sprite 4 icon}: <symbol>s with currentColor, then re-theme them via a parent color. {<symbol> với currentColor, rồi đổi theme qua color của cha.}
  3. Audit for a11y {Kiểm tra a11y}: take one decorative and one meaningful SVG and add the correct aria treatment 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.}