← Back to Index

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

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:

  1. !important CSS declarations (highest priority)
  2. Inline style="" attribute
  3. CSS rules (by specificity, then source order - as normal)
  4. Presentation attributes (lowest authored priority - treated like specificity 0,0,0)
  5. Inheritance from parent element
  6. Initial/default value (e.g. fill defaults to black)
This is where SVG catches people who know CSS

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?

I have fill="blue" as an attribute But CSS .demo-cascade-rect { fill: red; } wins Result: I'm red (CSS overrides presentation attribute)

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

Compositing & transforms

Colour & visibility

Text properties

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):

SVG 2 exception: geometry properties in CSS

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:

<g fill="coral" stroke="#b04950"> - all children inherit both properties

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:

opacity on <g> opacity on each child Composited as unit, then 50% opacity Each circle semi-transparent individually (overlaps visible)

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.

Ad

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>
These shapes use fill="currentColor" (inheriting from the SVG's color property)

All shapes use fill="currentColor" and inherit the SVG element's color value. Change the theme to see them adapt.

This is how icon systems work

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 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 HTMLYesYesYesYesYes
<object> / <iframe>YesNoYesYesYes
<img src="...">YesNoNoNoYes
CSS background-imageYesNoNoNoYes

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.

Inline SVG = full CSS power. External SVG = isolated.

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:

Don't rely on CSS geometry properties yet

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>
Bonjour Hallo Hello

Your browser language determines which greeting renders. Only one <text> is visible - the first whose systemLanguage matches (or the fallback).

The conditions available are:

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?

Blue - attributes are directly on the element, so they win
Red - CSS rules override presentation attributes
It depends on the order they appear in the source
Neither - they cancel out and the default (black) is used

Question 2

What's the effective specificity of a presentation attribute in SVG?

Same as an inline style (highest priority)
Equivalent to an element selector (0,0,1)
Lower than any CSS rule - effectively specificity 0,0,0 in the author stylesheet
Same as a class selector (0,1,0)

Question 3

You set opacity="0.5" on a <g> element containing 3 overlapping circles. What happens?

Each circle becomes 50% transparent - you see darker areas where they overlap
The group is composited as a unit first, then 50% opacity is applied - overlapping areas look solid within the group
Only the group's background becomes transparent, child elements are unaffected
The opacity is multiplied - each child gets 0.5 × 0.5 × 0.5 = 0.125 opacity

Hands-On Exercise

Put this into practice. Create an SVG that demonstrates each concept from this lesson:

  1. Style with presentation attributes: Create an SVG with several shapes (rect, circle, polygon) using presentation attributes for fill, stroke, and stroke-width.
  2. Override with CSS: Add an internal <style> block that overrides some of those presentation attributes - verify that CSS wins.
  3. Use currentColor: Make a shape that uses fill="currentColor" and set color on the parent <svg> element - verify the shape inherits the colour.
  4. Test group inheritance: Set fill on 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>