SVG Manipulation with Claude Code: Practical Guide
Use Claude Code for SVG icons, viewBox, accessibility, currentColor theming, animation, and SVGO optimization with safe examples.
What to Decide Before Asking Claude Code
SVG is useful for icons, logos, diagrams, and small data visuals because it stays sharp at any size and can be styled from CSS. Claude Code can read the existing codebase, edit files, and run checks, so it is a good fit for turning raw SVG into maintainable UI components.
The risk is that a vague prompt such as “make an SVG icon” often misses the boring parts that matter in production: viewBox, accessible names, theme colors, reduced motion, and safe handling of untrusted SVG. Masa ran into this on a small ClaudeCodeLab UI experiment. The first icon looked fine, but it could not be reused across a text button, an icon-only button, and dark mode without manual edits.
This guide gives Claude Code a safer implementation pattern. We will cover inline SVG basics, viewBox, accessible icons, currentColor and CSS variables, simple animation, SVGO optimization, use cases, layout mistakes, and security pitfalls.
Official references worth keeping open are MDN <svg>, MDN viewBox, MDN ARIA img role, MDN aria-hidden, SVGO usage, and the Claude Code overview.
The Workflow
Treat SVG work as a UI workflow, not as a drawing task. The coordinate system, color strategy, accessibility behavior, optimization step, and final review all affect the quality of the result.
flowchart LR
A["Define the purpose"] --> B["Lock the viewBox"]
B --> C["Use currentColor"]
C --> D["Choose aria-label or aria-hidden"]
D --> E["Embed in HTML or React"]
E --> F["Optimize with SVGO"]
F --> G["Review layout and safety"]
A better Claude Code prompt is specific: “Create a 24px SVG icon component with viewBox="0 0 24 24", stroke="currentColor", decorative icons hidden with aria-hidden, meaningful standalone icons labeled with role="img" and title, and an SVGO config that preserves viewBox.”
Inline SVG and viewBox Basics
Inline SVG means the <svg> markup lives directly inside HTML or JSX instead of being loaded through an <img> tag. It is useful when the icon must inherit color, react to hover state, or animate lightly.
The viewBox is the internal coordinate system. MDN defines it as four numbers, min-x min-y width height, that describe the SVG viewport in user space. For beginners, it is easiest to think of it as a drawing grid. viewBox="0 0 24 24" means the icon is drawn on a 24 by 24 grid, even if it is displayed at 16px, 24px, or 48px.
<button class="icon-button" type="button" aria-label="Search">
<svg
class="icon"
viewBox="0 0 24 24"
width="24"
height="24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
focusable="false"
>
<circle cx="11" cy="11" r="7" />
<path d="M20 20l-4.5-4.5" />
</svg>
</button>
The button has the accessible name, so the icon is decorative. If the SVG were the only visible content and the button did not have aria-label, assistive technology users would not know what the control does.
Theme with currentColor and CSS Variables
Hard-coded SVG colors become maintenance debt. If paths contain fill="#111827" or stroke="#0ea5e9", Claude Code has to edit the SVG every time the theme changes. A better pattern is to let SVG inherit CSS color.
:root {
--color-text: #172033;
--color-muted: #667085;
--color-accent: #0f766e;
--color-danger: #b42318;
}
[data-theme="dark"] {
--color-text: #eef2f7;
--color-muted: #a9b4c3;
--color-accent: #2dd4bf;
--color-danger: #f97066;
}
.icon {
color: var(--icon-color, var(--color-text));
display: inline-block;
inline-size: 1.25rem;
block-size: 1.25rem;
flex: 0 0 auto;
}
.icon-button {
color: var(--color-muted);
}
.icon-button:hover {
color: var(--color-accent);
}
.icon-button[data-variant="danger"] {
--icon-color: var(--color-danger);
}
<button class="icon-button" type="button" aria-label="Delete" data-variant="danger">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" aria-hidden="true">
<path d="M4 7h16" />
<path d="M10 11v6" />
<path d="M14 11v6" />
<path d="M6 7l1 14h10l1-14" />
<path d="M9 7V4h6v3" />
</svg>
</button>
Ask Claude Code to search for fixed colors after the refactor. A useful review command is rg "fill=\"#|stroke=\"#" inside the component and asset folders.
A React Icon Component
Meaningful SVG needs an accessible name. Decorative SVG should be hidden from the accessibility tree. MDN recommends role="img" and a label for embedded SVG that represents an image, while aria-hidden="true" is appropriate for non-interactive decorative content.
import { useId } from "react";
type IconName = "search" | "check" | "close";
const paths: Record<IconName, string> = {
search: "M10.5 18a7.5 7.5 0 1 1 5.3-12.8 7.5 7.5 0 0 1-5.3 12.8Zm5.3-2.2L21 21",
check: "M5 12.5l4.5 4.5L19 7",
close: "M6 6l12 12M18 6L6 18",
};
type SvgIconProps = {
name: IconName;
title?: string;
decorative?: boolean;
size?: number;
className?: string;
};
export function SvgIcon({
name,
title,
decorative = false,
size = 24,
className,
}: SvgIconProps) {
const titleId = useId();
const isMeaningful = !decorative && Boolean(title);
return (
<svg
className={className}
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
role={isMeaningful ? "img" : undefined}
aria-labelledby={isMeaningful ? titleId : undefined}
aria-hidden={decorative ? true : undefined}
focusable="false"
>
{isMeaningful ? <title id={titleId}>{title}</title> : null}
<path d={paths[name]} />
</svg>
);
}
<button type="button">
<SvgIcon name="check" decorative />
Save
</button>
<SvgIcon name="search" title="Search" />
Do not put aria-hidden="true" on a focusable element. If an icon-only button needs a name, put aria-label on the button or provide visible text.
Simple Animation Without Layout Damage
SVG animation is best for small UI signals: loading, success, drawing attention to one chart value. Keep the element size stable and respect prefers-reduced-motion.
<svg class="spinner" viewBox="0 0 48 48" width="48" height="48" role="img" aria-label="Loading">
<circle class="spinner-track" cx="24" cy="24" r="20" />
<circle class="spinner-head" cx="24" cy="24" r="20" />
</svg>
.spinner {
color: #0f766e;
animation: spin 900ms linear infinite;
}
.spinner-track,
.spinner-head {
fill: none;
stroke-width: 4;
}
.spinner-track {
stroke: #d0d5dd;
}
.spinner-head {
stroke: currentColor;
stroke-linecap: round;
stroke-dasharray: 80 45;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: reduce) {
.spinner {
animation: none;
}
}
For broader motion patterns, connect this with Claude Code CSS animation techniques. SVG should define shape. CSS should define state and motion.
Small Data SVG with Escaping
SVG can also render small charts. The important rule is to escape text before injecting labels into <text>.
type BarDatum = {
label: string;
value: number;
};
function escapeXml(value: string): string {
return value.replace(/[<>&"']/g, (char) => {
const entities: Record<string, string> = {
"<": "<",
">": ">",
"&": "&",
'"': """,
"'": "'",
};
return entities[char];
});
}
export function createMiniBarChart(data: BarDatum[]): string {
const width = 420;
const height = 180;
const padding = 32;
const gap = 12;
const maxValue = Math.max(...data.map((item) => item.value), 1);
const barWidth = (width - padding * 2 - gap * (data.length - 1)) / data.length;
const bars = data
.map((item, index) => {
const barHeight = (item.value / maxValue) * 100;
const x = padding + index * (barWidth + gap);
const y = height - padding - barHeight;
return `
<rect x="${x}" y="${y}" width="${barWidth}" height="${barHeight}" rx="6" fill="currentColor" />
<text x="${x + barWidth / 2}" y="${height - 10}" text-anchor="middle" font-size="12">
${escapeXml(item.label)}
</text>`;
})
.join("");
return `<svg viewBox="0 0 ${width} ${height}" role="img" aria-label="Monthly inquiries" xmlns="http://www.w3.org/2000/svg">
<g color="#0f766e">${bars}</g>
</svg>`;
}
Use this for a tiny dashboard, an article illustration, or a landing-page proof point. Use a real chart library when you need axes, legends, tooltips, zooming, or a large data set.
Optimize with SVGO
Designer exports often include editor metadata and unnecessary precision. SVGO removes that noise. Start with a conservative config and inspect the diff.
// svgo.config.mjs
export default {
multipass: true,
plugins: [
{
name: "preset-default",
params: {
overrides: {
cleanupIds: false
}
}
},
"removeDimensions",
{
name: "removeAttrs",
params: {
attrs: ["data-name"]
}
}
]
};
{
"scripts": {
"svg:optimize": "svgo --config svgo.config.mjs --folder src/assets/icons"
},
"devDependencies": {
"svgo": "^4.0.0"
}
}
SVGO documents preset-default, removeDimensions, and other plugins. Be careful with removeViewBox: the SVGO docs warn that it can prevent SVGs from scaling and may cause clipping. For responsive icons, preserving viewBox is usually the right default.
Use Cases and Pitfalls
Common use cases:
- Product UI icons for search, close, save, warning, and external links.
- Article diagrams that explain a workflow without requiring a screenshot.
- Landing-page visuals near pricing, checklists, and purchase CTAs.
- Small dashboard charts for article completion, CTA clicks, or inquiry volume.
| Mistake | Result | Fix |
|---|---|---|
Removing viewBox | Icons clip or stop scaling | Preserve it in SVGO and review diffs |
Fixed fill colors | Dark mode and hover states fail | Use currentColor |
| Hiding meaningful SVG | Screen readers miss the action | Label the button or SVG |
| Reading decorative SVG | Repeated names become noisy | Use aria-hidden="true" |
| Inlining uploaded SVG | Script or event-handler risk | Inline only trusted SVG |
| Ignoring reduced motion | Motion may burden users | Use prefers-reduced-motion |
MDN documents SVG <script>, so do not treat uploaded SVG text as harmless markup. Claude Code can help review diffs, but you should still restrict the target folder and inspect file changes. The Claude Code security page is also relevant when a tool is allowed to edit many files or run optimization commands.
Prompt to Reuse
Create an SVG icon system for this repository.
Requirements:
- Keep viewBox="0 0 24 24"
- Use currentColor for fill or stroke
- Hide decorative icons with aria-hidden=true
- Use role=img and title for meaningful standalone icons
- Add a reduced-motion friendly loading SVG
- Add an SVGO config that does not remove viewBox
- Report risks, changed files, and verification commands
Then run the usual checks. For this topic, Claude Code performance optimization is the natural next article because every icon, diagram, and animation eventually affects page weight and rendering.
CTA and Tested Result
If you want a reusable setup instead of one-off prompts, start with the ClaudeCodeLab products and templates or bring a real repository to Claude Code training and consultation. SVG work is a good first target because the changes are small but touch accessibility, design tokens, performance, and review discipline.
In Masa’s test UI, moving fixed SVG colors to currentColor made the same icons work across light mode, dark mode, and destructive buttons. The one real bug was accessibility: an icon-only search button lost its name when aria-hidden was applied mechanically. The fix was to label the button and reserve aria-hidden for decorative icons only.
Free PDF: Claude Code Cheatsheet
Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.
We handle your data with care and never send spam.
Level up your Claude Code workflow
Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.
About the Author
Masa
Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.
Related Posts
Claude Code Permission Safety Ladder: Expand Access Without Losing Control
A beginner-friendly ladder for moving Claude Code from read-only to limited edits, proof commands, and deploy checks.
Claude Code Small PR Proof Pack: Make Tiny Changes Reviewable
A practical proof pack for Claude Code PRs: diff, checks, public URL, CTA path, and rollback note.
Claude Code Review Gate Before Commit: Diff, Tests, Public URL, and CTA Checks
A commit-time review gate for Claude Code work: diff scope, build, public URL, revenue CTA links, missing tests, and unrelated files.
Related Products
50 Battle-Tested Claude Code Prompt Templates
Copy, paste, ship. 50 production-ready prompts.
Use proven prompts for code review, refactoring, testing, documentation, debugging, architecture, and incident response.
The Complete Claude Code Setup & Configuration Guide
From install to team-ready workflow.
A practical guide to installation, CLAUDE.md, hooks, MCP servers, permissions, IDE setup, and CI/CD workflows.