Lesson 4
Styling: Presentation Attributes vs CSS
You know how to draw shapes and paths. Now: how do you style them? SVG has a unique relationship with CSS that surprises even experienced web developers. The core surprise: SVG has a styling layer that HTML simply doesn't have - presentation attributes - and they sit in a different place in the cascade than expected.
1. Three Ways to Style SVG
SVG elements can receive visual styling through three distinct mechanisms:
1. Presentation attributes
Style properties written directly as XML attributes on the element:
<rect fill="#e06c75" stroke="#528bcc" stroke-width="2"/>
These look like regular HTML attributes, but they're actually CSS properties expressed as attributes. They're unique to SVG - HTML has no equivalent mechanism.
2. Inline style attribute
The familiar style="" attribute, exactly as in HTML:
<rect style="fill: #e06c75; stroke: #528bcc; stroke-width: 2px;"/>
Same syntax as CSS. Same highest-priority behaviour you know from HTML.
3. CSS rules (internal or external)
Selectors targeting SVG elements, in a <style> block or external stylesheet:
<style>
.my-rect { fill: #e06c75; stroke: #528bcc; stroke-width: 2px; }
</style>
<rect class="my-rect"/>
Works identically to CSS on HTML elements - selectors, specificity, media queries, all of it.
Presentation attributes are NOT the same as inline styles. They have LOWER priority than any CSS rule - even a plain class selector. This is the key difference from HTML, where the only attribute-based styling (style="") has highest priority. In SVG, fill="red" (presentation attribute) loses to .anything { fill: blue; } (CSS rule). The style="fill: red" (inline style) still wins as you'd expect.
2. The SVG Cascade (Priority Order)
Here's the full priority order for how a styling property gets resolved on an SVG element, from highest to lowest priority:
!importantCSS declarations (highest priority)- Inline
style=""attribute - CSS rules (by specificity, then source order - as normal)
- Presentation attributes (lowest authored priority - treated like specificity 0,0,0)
- Inheritance from parent element
- Initial/default value (e.g.
filldefaults to black)
In HTML, if you put color="red" as an attribute, it's either a deprecated HTML attribute (handled differently) or ignored entirely. HTML doesn't have presentation attributes - the only attribute-based styling is style="", which sits at the TOP of the cascade. In SVG, presentation attributes are a FOURTH layer in the cascade, sitting BELOW all CSS. A simple .box { fill: blue; } beats fill="red" on the element. This catches everyone the first time.
Proof: CSS overrides a presentation attribute
Here's a live demo. The rect has fill="blue" as a presentation attribute, but the internal <style> rule sets fill: red. Which wins?
The rect's fill="blue" presentation attribute is overridden by the CSS class rule. CSS always wins over presentation attributes.
<svg viewBox="0 0 200 100">
<style>
.my-rect { fill: red; }
</style>
<!-- fill="blue" is a presentation attribute -->
<!-- .my-rect { fill: red } is a CSS rule -->
<!-- CSS wins! The rect is RED -->
<rect class="my-rect" fill="blue"
x="10" y="10" width="180" height="80"/>
</svg>
3. Which Properties Are Presentation Attributes?
Not all SVG attributes are presentation attributes. Only specific CSS-like properties work as both an attribute AND a CSS property. Here are the key ones:
Fill & stroke properties
fill,fill-opacity,fill-rulestroke,stroke-width,stroke-linecap,stroke-linejoinstroke-dasharray,stroke-dashoffset,stroke-opacity
Compositing & transforms
opacity,transform,transform-origin
Colour & visibility
color(forcurrentColorinheritance),visibility,display
Text properties
font-family,font-size,font-weight,font-styletext-anchor,dominant-baseline
Effects
What is NOT a presentation attribute
Geometry attributes define the shape itself, not its appearance. These can ONLY be set as attributes (not CSS properties):
x,y,width,height- positioning and sizingcx,cy,r,rx,ry- circle/ellipse geometryd- path datapoints- polyline/polygon verticesviewBox- coordinate system definition
SVG 2 introduced the ability to set some geometry attributes (x, y, width, height, cx, cy, r, rx, ry, d) via CSS. This is powerful - you can transition d for path morphing! - but browser support is still incomplete. Chrome/Edge support most of these; Firefox and Safari have partial support. Don't rely on it yet for production, but it's the direction things are heading.
4. Inheritance in SVG
SVG inheritance works like CSS - children inherit computed values from their parents. But there are some important differences from what you're used to in HTML:
fill and stroke inherit
Unlike most CSS properties on HTML elements (where you typically set styles per-element), SVG's fill and stroke properties inherit by default. This means setting fill on a <g> element colours all its children - extremely useful for grouping related shapes:
A single fill and stroke on the <g> element is inherited by all three child shapes.
<g fill="coral" stroke="#b04950" stroke-width="2">
<circle cx="80" cy="70" r="35"/>
<rect x="150" y="35" width="70" height="70" rx="6"/>
<polygon points="300,35 340,105 260,105"/>
</g>
<!-- All three shapes are coral with a dark stroke -->
<!-- No fill/stroke needed on individual elements -->
opacity on a group composites first
Setting opacity on a <g> is NOT the same as setting it on each child individually. The group is rendered as a single composite image first, THEN opacity is applied to that composite:
Left: <g opacity="0.5"> - overlapping areas look solid because the group composites first. Right: individual opacity="0.5" - overlaps are visible as darker areas.
5. currentColor - The Bridge
currentColor in SVG works identically to CSS - it resolves to the inherited value of the color property. This is the technique that makes SVGs adapt to their surrounding text context:
<!-- The SVG inherits 'color' from its parent/context -->
<svg style="color: navy;" viewBox="0 0 100 100" width="60">
<circle fill="currentColor" cx="50" cy="50" r="40"/>
</svg>
<!-- Change the surrounding text colour and the icon follows -->
<p style="color: crimson;">
<svg viewBox="0 0 100 100" width="16">
<circle fill="currentColor" cx="50" cy="50" r="40"/>
</svg>
This icon is crimson, matching the text.
</p>
All shapes use fill="currentColor" and inherit the SVG element's color value. Change the theme to see them adapt.
Icon SVGs use fill="currentColor" (or stroke="currentColor") so the icon automatically matches surrounding text colour. No need to pass colour props or create variants - the icon just inherits. Angular's MatIcon, FontAwesome SVG icons, and most modern icon libraries rely on this pattern.
6. When to Use What
Practical guidance for choosing between the three styling mechanisms:
| Use case | Best approach | Why |
|---|---|---|
| Default appearance | Presentation attributes | Clean markup, easily overridden by CSS later |
| Theming / responsive | CSS rules | Cascade lets you restyle without touching SVG markup |
| Dynamic JS changes | Inline style or class toggle | Highest specificity (style) or easy toggle (class) |
| Icon systems | currentColor + CSS |
Icons adapt to text context automatically |
| SVG from design tools | Presentation attributes | What tools produce; CSS can override later |
The mental model: presentation attributes are defaults; CSS is overrides. This makes SVG remarkably flexible - a design tool exports an SVG with hardcoded colours as presentation attributes, and you can completely retheme it with a few CSS rules without touching the SVG file at all.
7. Advanced CSS Features & Embedding Context
When SVG is inline in HTML, SVG elements are regular DOM nodes. The browser's CSS engine doesn't distinguish them from HTML elements. This means every CSS feature works:
@layer- cascade layers for managing style priority@scope- scoping styles to SVG subtrees@container- container queries targeting SVG elements- CSS nesting,
:has(),:is(),:where() - Custom properties (
--var) - Media queries,
@supports
@layer base, theme;
@layer base {
circle { fill: gray; }
}
@layer theme {
circle { fill: coral; } /* wins - later layer */
}
But it depends on how the SVG is embedded
The key question: can external CSS reach the SVG elements? It depends entirely on the embedding method:
| Embedding method | CSS inside SVG? | Page CSS reaches SVG? | JavaScript? | User interactivity? | SMIL animation? |
|---|---|---|---|---|---|
Inline <svg> in HTML | Yes | Yes | Yes | Yes | Yes |
<object> / <iframe> | Yes | No | Yes | Yes | Yes |
<img src="..."> | Yes | No | No | No | Yes |
CSS background-image | Yes | No | No | No | Yes |
Note: SMIL animations (declared inside the SVG markup) play in all contexts - even <img> and background-image where JS is blocked. This is one of SMIL's key advantages: self-contained animation that works everywhere without scripting.
If you need external CSS to style your SVG (theming, dark mode, responsive changes), use inline SVG. If the SVG is loaded via <img> or background-image, it can only use styles defined inside its own <style> block - the page's stylesheets can't reach it. This is a security/isolation boundary, same reason <img> can't run JavaScript.
8. SVG 2 Geometry Properties in CSS
SVG 2 introduced the ability to set geometry attributes via CSS. This means properties like cx, cy, r, x, y, width, height, rx, ry, and even d (path data!) can be set and transitioned in CSS:
<style>
.morphing-circle {
cx: 50px;
cy: 50px;
r: 20px;
transition: r 0.3s ease;
}
.morphing-circle:hover {
r: 40px; /* Animates the radius on hover! */
}
</style>
<svg viewBox="0 0 100 100">
<circle class="morphing-circle"/>
</svg>
The most exciting part is d in CSS - this enables pure-CSS path morphing animations. But browser support is still incomplete:
- Chrome/Edge: Good support for geometry properties including
d - Firefox: Partial support -
r,cx,cywork;dsupport is limited - Safari: Partial and evolving - check caniuse.com for current status
While the future is exciting, cross-browser support is incomplete for geometry properties in CSS. For production work, continue using attributes for geometry and CSS for styling. Use feature detection or progressive enhancement if you want to experiment with CSS-based geometry animation.
9. The <switch> Element
SVG includes a <switch> element for conditional rendering. It evaluates its children in order and renders only the first child whose conditions are met:
<svg viewBox="0 0 200 60" xmlns="http://www.w3.org/2000/svg">
<switch>
<text systemLanguage="fr" x="10" y="35" font-size="18">Bonjour</text>
<text systemLanguage="de" x="10" y="35" font-size="18">Hallo</text>
<text x="10" y="35" font-size="18">Hello</text>
</switch>
</svg>
Your browser language determines which greeting renders. Only one <text> is visible - the first whose systemLanguage matches (or the fallback).
The conditions available are:
systemLanguage- matches the user's browser language (e.g.,"en","fr","ja")requiredFeatures- tests for SVG feature support (deprecated in SVG 2)requiredExtensions- tests for extension support (rarely used)
The last child without any condition attributes acts as the fallback (like default in a switch statement). In practice, <switch> is mainly useful for internationalisation in standalone SVG files where you can't use JavaScript or server-side language detection.
Quiz: Check Your Understanding
Question 1
A <rect> has fill="blue" as an attribute and .box { fill: red; } in CSS. What colour is it?
Question 2
What's the effective specificity of a presentation attribute in SVG?
Question 3
You set opacity="0.5" on a <g> element containing 3 overlapping circles. What happens?
Hands-On Exercise
Put this into practice. Create an SVG that demonstrates each concept from this lesson:
- Style with presentation attributes: Create an SVG with several shapes (rect, circle, polygon) using presentation attributes for fill, stroke, and stroke-width.
- Override with CSS: Add an internal
<style>block that overrides some of those presentation attributes - verify that CSS wins. - Use currentColor: Make a shape that uses
fill="currentColor"and setcoloron the parent<svg>element - verify the shape inherits the colour. - Test group inheritance: Set
fillon a<g>element and verify that child shapes inherit it without needing their own fill attribute.
<svg width="500" height="300" viewBox="0 0 500 300" xmlns="http://www.w3.org/2000/svg">
<style>
/* 2. CSS override - these will beat the presentation attributes below */
.override-me { fill: #c678dd; stroke: #9b59b6; }
</style>
<!-- 1. Presentation attributes only -->
<rect x="20" y="20" width="80" height="60" rx="6"
fill="#61afef" stroke="#528bcc" stroke-width="2"/>
<circle cx="180" cy="50" r="30"
fill="#e5c07b" stroke="#b8993f" stroke-width="2"/>
<!-- 2. CSS overrides the green fill/stroke presentation attributes -->
<rect class="override-me" x="240" y="20" width="80" height="60" rx="6"
fill="#98c379" stroke="#6a9a4d" stroke-width="2"/>
<!-- 3. currentColor inherits from SVG's color -->
<svg x="20" y="120" width="100" height="80" style="color: coral;">
<circle fill="currentColor" cx="50" cy="40" r="30"/>
</svg>
<!-- 4. Group inheritance -->
<g fill="#56b6c2" stroke="#3d8f99" stroke-width="2">
<rect x="160" y="130" width="60" height="50" rx="4"/>
<circle cx="280" cy="155" r="25"/>
<polygon points="350,130 380,180 320,180"/>
</g>
</svg>