Presentation attributes vs CSS
SVG defines two ways to set visual properties on an element: presentation attributes (like fill="red" directly on the element) and CSS (like .my-circle { fill: red; } in a stylesheet or style="fill:red" inline). Both set the same underlying visual property, but they sit at different positions in the CSS cascade.
The precedence order from lowest to highest priority is:
- Presentation attributes (
fill="..."on the element) — treated like a very-low-specificity author rule - CSS class or type rules (
.shape { fill: ... }) — override the attribute even at zero specificity - Inline
styleattribute (style="fill:...") — overrides both !importantdeclarations — override everything
This matters in practice: you can ship SVG files with presentation attributes set by an illustration tool, then override specific colors entirely in CSS without touching the SVG markup. The demo below proves it — the shape has fill="#d6452c" as an attribute, but the CSS class rule wins.
Attribute vs CSS rule
The rectangle below carries fill="#d6452c" as a presentation attribute, but the page's <style> block contains .override-demo .overridden { fill: #1f8a4c; }. The CSS class wins because any CSS rule — even a simple class rule — outranks a presentation attribute.
The left rectangle has the overridden class and renders green despite fill="#d6452c" being right there on the element. The right rectangle has no class, so the attribute controls its color. This is exactly how design systems override icon colors in third-party SVG assets without forking the files.
Inline style takes priority over CSS rules
Adding style="fill:#e07b00" directly to an element bumps it above any stylesheet rule. You can override that in turn only with !important — the same cascade rules as ordinary HTML styling.
currentColor
currentColor is a special CSS keyword that resolves to whatever color is in effect on the element or its nearest ancestor. In SVG, you can use it as the value for fill, stroke, or any other color-accepting property. This makes SVG icons automatically inherit the text color of their surrounding HTML — a powerful technique for building icon systems.
The mechanics: when the browser sees fill="currentColor" (or fill: currentColor in CSS), it walks up the DOM tree looking for the computed value of the color property, then applies that as the fill. Set color on a parent element and every currentColor SVG inside it recolors automatically.
Icon that follows its parent's color
The three icon copies below are identical SVG markup. Only the color CSS property on the wrapping container differs. Because the paths use fill="currentColor", each icon picks up a completely different shade without any SVG-specific CSS.
This technique is the foundation of most CSS icon systems. You ship one SVG (or one <symbol>), toggle a single CSS property, and all icon instances in that context recolor together. It also integrates with dark mode: when your theme switches the base color property on body, icons using currentColor adapt automatically.
CSS custom properties in SVG
CSS custom properties (also called CSS variables) cascade into SVG just like any other inherited CSS value. You can declare --my-color: red on any ancestor element and reference it with var(--my-color, fallback) inside SVG properties. This unlocks real theming: swap a handful of custom properties on a container, and every icon or graphic inside it re-renders accordingly.
The syntax is identical to HTML theming: define the property on a parent and consume it in the SVG child. The fallback value in var(--icon-fill, #555) ensures the SVG renders even if the property is not set — useful for standalone SVG files dropped into different contexts.
Themed icon set via custom properties
The SVG markup is identical across all three — only the parent class differs. To add a fourth theme you add one CSS rule with two custom property declarations; no SVG markup changes at all. This separation of concerns scales well: a design token system (e.g. a dark mode toggle) can redefine --icon-fill globally and every icon on the page responds.
Targeting parts & interaction
When SVG is inlined directly in an HTML document, its elements participate fully in the CSS cascade just like any other DOM node. You can add class and id attributes to individual SVG child elements — <rect>, <circle>, <path>, etc. — and target them from your page stylesheet.
This also unlocks CSS pseudo-classes like :hover, :focus, and :active on SVG shapes. The browser fires pointer events on the visible fill area of each shape, so a rect:hover rule triggers when the mouse enters that rectangle's painted region. This does not work for SVG loaded via <img src="..."> — those are treated as opaque images with no accessible DOM.
Hoverable shape (inline SVG only)
The hexagon below changes from green to red and scales up slightly on hover. Open DevTools and inspect it — it is a real <polygon> in the DOM, styled exactly like any other element.
The transform-box: fill-box declaration is important: by default, CSS transforms on SVG elements use the SVG viewport as the reference box, which causes unexpected pivot points. Setting transform-box: fill-box makes the element transform relative to its own bounding box — so transform-origin: 50px 50px (or simply 50% 50%) centers the scale correctly on the shape itself.
For more complex interactive diagrams — tooltips, selection states, click handlers — inline SVG with CSS classes and :hover/:focus is the right starting point. For programmatic animation and event-driven behavior, the next step is the JavaScript DOM API, which treats SVG elements as first-class nodes.