SVG Basics

SVG (Scalable Vector Graphics) is an XML-based image format that describes graphics using mathematical shapes rather than pixels. SVG images are resolution-independent — they look perfectly sharp at any size, from a tiny favicon to a massive billboard screen.

SVG is natively supported in all modern browsers and can be placed inside HTML in multiple ways: as an <img> tag, as a CSS background, or inline directly in the HTML document.


1. Why SVG?

Comparison of raster vs vector graphics scaling

Source: Wikimedia Commons – Bitmap VS SVG.svg · CC BY-SA 2.5

Text description: The left side shows a pixelated, blocky star icon when zoomed in (a raster/bitmap image). The right side shows the same star zoomed in, still perfectly sharp and smooth (an SVG vector image). This demonstrates that SVG maintains quality at any resolution.

FeatureRaster (PNG/JPEG)SVG
Quality at any sizeDegrades (pixelates)Always sharp
File size for iconsCan be largeUsually tiny
Editable with CSSNoYes
AnimatableNo (without JS)Yes (CSS or JS)
AccessibilityVia alt onlyBuilt-in text & ARIA
Search engine readableNoYes (it's XML text)
Best forPhotosIcons, logos, charts, illustrations

2. Adding SVG to HTML

Method 1 — As an <img> Tag

<img src="logo.svg" alt="Nandhoo logo" width="200" height="60">
  • Simple, cacheable, consistent with other images
  • Cannot be styled or animated with CSS
  • Cannot be manipulated with JavaScript

Method 2 — As a CSS Background

.hero {
  background-image: url('pattern.svg');
  background-size: cover;
}
  • Good for decorative patterns and textures
  • No accessibility — cannot have alt text

Method 3 — Inline SVG in HTML

<svg
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 100 100"
  width="100"
  height="100"
  role="img"
  aria-label="Blue circle icon"
>
  <circle cx="50" cy="50" r="45" fill="#3b82f6" />
</svg>
  • Can be styled with CSS (fill, stroke, transitions)
  • Can be animated with CSS or manipulated with JavaScript
  • Extra markup in your HTML source

Which to Use?

Icon that needs hover colour change or animation? → Inline SVG
Logo that just needs to display? → <img src="logo.svg">
Decorative background pattern? → CSS background-image

3. SVG Coordinate System and viewBox

SVG has its own internal coordinate system defined by viewBox.

<svg viewBox="0 0 200 100" width="400" height="200">
  <!--
    viewBox="0 0 200 100"
      ↑   ↑ ↑   ↑   ↑
      │   │ │   │   └── internal height of canvas
      │   │ │   └────── internal width of canvas
      │   │ └────────── starting y coordinate
      │   └──────────── starting x coordinate
      └──────────────── attribute name
  -->
</svg>

The viewBox coordinates are internal units. The width and height attributes control the actual display size in the browser. This is what makes SVG scale perfectly:

<!-- Both of these display the same drawing — just at different sizes -->
<svg viewBox="0 0 100 100" width="50"  height="50">  <!-- small  -->
<svg viewBox="0 0 100 100" width="500" height="500">  <!-- large  -->

SVG's coordinate origin (0,0) is the top-left corner. x increases rightward, y increases downward.


4. Basic SVG Shapes

Rectangle — <rect>

<svg viewBox="0 0 200 100" width="400" height="200">
  <!-- Basic rectangle -->
  <rect x="10" y="10" width="80" height="50" fill="#3b82f6" />

  <!-- Rectangle with rounded corners -->
  <rect x="110" y="10" width="80" height="50" rx="12" ry="12" fill="#10b981" />

  <!-- Rectangle with stroke (border) and no fill -->
  <rect x="10" y="70" width="80" height="20"
        fill="none" stroke="#ef4444" stroke-width="2" />
</svg>
AttributeMeaning
x, yTop-left position
width, heightSize
rx, ryCorner radius
fillBackground colour (or none)
strokeBorder colour
stroke-widthBorder thickness

Circle — <circle>

<svg viewBox="0 0 200 100" width="400" height="200">
  <circle cx="50" cy="50" r="40" fill="#f59e0b" />
  <circle cx="150" cy="50" r="40" fill="none" stroke="#8b5cf6" stroke-width="3" />
</svg>
AttributeMeaning
cx, cyCentre x and y
rRadius

Ellipse — <ellipse>

<svg viewBox="0 0 200 100" width="400" height="200">
  <ellipse cx="100" cy="50" rx="80" ry="40" fill="#ec4899" />
</svg>
AttributeMeaning
rxHorizontal radius
ryVertical radius

Line — <line>

<svg viewBox="0 0 200 100" width="400" height="200">
  <line x1="10" y1="10" x2="190" y2="90"
        stroke="#374151" stroke-width="2" stroke-linecap="round" />
</svg>

Polyline — <polyline>

A series of connected line segments (not closed).

<svg viewBox="0 0 200 100" width="400" height="200">
  <!-- A simple zigzag line chart -->
  <polyline
    points="0,80 40,30 80,60 120,20 160,50 200,10"
    fill="none"
    stroke="#3b82f6"
    stroke-width="2"
  />
</svg>

Polygon — <polygon>

A closed shape defined by a series of connected points.

<svg viewBox="0 0 200 200" width="200" height="200">
  <!-- Triangle -->
  <polygon points="100,10 190,190 10,190" fill="#f59e0b" />

  <!-- Six-pointed star -->
  <polygon
    points="100,10 120,80 190,80 135,125 155,195 100,150 45,195 65,125 10,80 80,80"
    fill="#ef4444"
  />
</svg>

5. The <path> Element

<path> is the most powerful SVG element. It can draw any shape using a series of commands in the d attribute.

<svg viewBox="0 0 200 200" width="200" height="200">
  <!-- A simple curved shape -->
  <path
    d="M 20 80 C 20 20, 180 20, 180 80 S 180 160, 100 160 S 20 160, 20 80"
    fill="#6366f1"
  />
</svg>

Path Commands

CommandAbsoluteRelativeMeaning
M / mM x ym dx dyMove to (no drawing)
L / lL x yl dx dyLine to
H / hH xh dxHorizontal line
V / vV yv dyVertical line
C / cC x1 y1, x2 y2, x yrelativeCubic Bézier curve
Q / qQ x1 y1, x yrelativeQuadratic Bézier
A / aA rx ry rotation large-arc sweep x yrelativeArc
Z / zZClose path (line back to start)

Uppercase = absolute coordinates (relative to SVG origin). Lowercase = relative to current position.

Example — A Heart Shape

<svg viewBox="0 0 100 90" width="200" height="180">
  <path
    d="M 50 85 C 25 70, 0 50, 0 30 C 0 10, 15 0, 30 0 C 40 0, 48 5, 50 12 C 52 5, 60 0, 70 0 C 85 0, 100 10, 100 30 C 100 50, 75 70, 50 85 Z"
    fill="#ef4444"
  />
</svg>

6. Text in SVG

SVG includes native text rendering. Text in SVG is real, selectable, and accessible.

<svg viewBox="0 0 300 80" width="300" height="80">
  <!-- Basic text -->
  <text x="10" y="30" font-size="20" fill="#1e293b" font-family="system-ui, sans-serif">
    Hello, Nandhoo!
  </text>

  <!-- Centred text -->
  <text x="150" y="70" text-anchor="middle" font-size="16" fill="#64748b">
    Scalable Vector Graphics
  </text>
</svg>

Text Attributes

AttributeValuesMeaning
x, ynumbersPosition of baseline start
text-anchorstart middle endHorizontal alignment relative to x
dominant-baselineauto middle hangingVertical alignment
font-sizenumberSize in SVG units
font-familyfont namesFont
fillcolourText colour
font-weightnormal boldWeight

Text Along a Path

<svg viewBox="0 0 300 100" width="300" height="100">
  <defs>
    <path id="curve" d="M 20 80 Q 150 10 280 80" />
  </defs>
  <text font-size="16" fill="#6366f1">
    <textPath href="#curve">Text flowing along a curved path!</textPath>
  </text>
</svg>

7. Grouping with <g>

<g> (group) wraps multiple elements so you can transform, style, or manipulate them together.

<svg viewBox="0 0 200 200" width="200" height="200">
  <!-- House icon made from grouped shapes -->
  <g id="house" stroke="#1e293b" stroke-width="2">
    <!-- Roof -->
    <polygon points="100,20 180,90 20,90" fill="#f59e0b" />
    <!-- Walls -->
    <rect x="40" y="90" width="120" height="90" fill="#e2e8f0" />
    <!-- Door -->
    <rect x="80" y="130" width="40" height="50" fill="#a78bfa" />
    <!-- Window -->
    <rect x="50" y="105" width="30" height="25" fill="#93c5fd" />
    <rect x="120" y="105" width="30" height="25" fill="#93c5fd" />
  </g>
</svg>

8. Transforms

SVG elements support transforms: translate, rotate, scale, and skew.

<svg viewBox="0 0 300 200" width="300" height="200">
  <!-- Original square -->
  <rect x="10" y="75" width="50" height="50" fill="#3b82f6" />

  <!-- Translated (moved) -->
  <rect x="10" y="75" width="50" height="50" fill="#10b981"
        transform="translate(80, 0)" />

  <!-- Rotated 45° around its centre -->
  <rect x="10" y="75" width="50" height="50" fill="#f59e0b"
        transform="translate(190, 100) rotate(45) translate(-25, -25)" />

  <!-- Scaled to 1.5× -->
  <rect x="10" y="75" width="50" height="50" fill="#ef4444"
        transform="translate(240, 50) scale(1.5)" />
</svg>

Transform Functions

FunctionExampleEffect
translate(x, y)translate(50, 20)Move right 50, down 20
rotate(deg)rotate(45)Rotate 45° around origin
rotate(deg, cx, cy)rotate(45, 50, 50)Rotate around point (50,50)
scale(factor)scale(2)Double size
scale(x, y)scale(2, 0.5)Scale x and y independently
skewX(deg)skewX(20)Skew along x axis

9. <defs> — Reusable Definitions

<defs> stores shapes that can be reused multiple times without re-drawing them.

Reusing Shapes with <use>

<svg viewBox="0 0 300 100" width="300" height="100">
  <defs>
    <circle id="dot" r="12" fill="#3b82f6" />
  </defs>

  <!-- Reuse the same circle in three positions -->
  <use href="#dot" x="30"  y="50" />
  <use href="#dot" x="80"  y="50" fill="#ef4444" />   <!-- override fill -->
  <use href="#dot" x="130" y="50" fill="#10b981" />
</svg>

Gradients

<svg viewBox="0 0 200 100" width="400" height="200">
  <defs>
    <!-- Linear gradient from blue to purple -->
    <linearGradient id="gradient-bp" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="0%"   stop-color="#3b82f6" />
      <stop offset="100%" stop-color="#8b5cf6" />
    </linearGradient>

    <!-- Radial gradient golden glow -->
    <radialGradient id="gradient-gold" cx="50%" cy="50%" r="50%">
      <stop offset="0%"   stop-color="#fbbf24" />
      <stop offset="100%" stop-color="#92400e" />
    </radialGradient>
  </defs>

  <rect x="10"  y="10" width="80" height="80" fill="url(#gradient-bp)" />
  <circle cx="155" cy="50" r="40" fill="url(#gradient-gold)" />
</svg>

Clipping Paths

<svg viewBox="0 0 200 200" width="200" height="200">
  <defs>
    <!-- Circle clip mask -->
    <clipPath id="circle-clip">
      <circle cx="100" cy="100" r="90" />
    </clipPath>
  </defs>

  <!-- Image clipped to a circle -->
  <image
    href="landscape.jpg"
    x="0" y="0" width="200" height="200"
    clip-path="url(#circle-clip)"
  />
</svg>

Patterns

<svg viewBox="0 0 200 200" width="200" height="200">
  <defs>
    <pattern id="dots" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
      <circle cx="5" cy="5" r="3" fill="#6366f1" />
    </pattern>
  </defs>

  <rect x="0" y="0" width="200" height="200" fill="url(#dots)" />
</svg>

10. Filters and Effects

SVG filters apply visual effects like blur, glow, and drop shadows.

<svg viewBox="0 0 200 100" width="400" height="200">
  <defs>
    <!-- Blur filter -->
    <filter id="blur-filter">
      <feGaussianBlur stdDeviation="3" />
    </filter>

    <!-- Drop shadow filter -->
    <filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
      <feDropShadow dx="4" dy="4" stdDeviation="3" flood-color="#00000033" />
    </filter>
  </defs>

  <circle cx="50" cy="50" r="40" fill="#3b82f6" filter="url(#blur-filter)" />
  <circle cx="150" cy="50" r="40" fill="#ef4444" filter="url(#shadow)" />
</svg>

11. CSS Styling of SVG

SVG elements can be styled with CSS using both presentation attributes and class-based rules.

Using CSS Classes

<style>
  .icon-primary   { fill: #3b82f6; }
  .icon-secondary { fill: #10b981; }
  .icon-stroke    { fill: none; stroke: #1e293b; stroke-width: 2; }

  .icon-primary:hover { fill: #1d4ed8; transition: fill 0.2s; }
</style>

<svg viewBox="0 0 100 100" width="100" height="100">
  <circle cx="50" cy="50" r="40" class="icon-primary" />
  <rect x="30" y="30" width="40" height="40" class="icon-stroke" />
</svg>

CSS Properties That Work on SVG Elements

CSS propertySVG equivalent attributeWhat it styles
fillfillInside colour
strokestrokeBorder colour
stroke-widthstroke-widthBorder thickness
opacityopacityOverall transparency
fill-opacityfill-opacityFill transparency only
stroke-opacitystroke-opacityStroke transparency only
stroke-dasharraystroke-dasharrayDashed stroke pattern
stroke-linecapstroke-linecapLine end caps (butt/round/square)
transformtransformTranslate, rotate, scale

12. Animating SVG with CSS

Rotate an Icon

<style>
  .spin { animation: rotate 2s linear infinite; transform-origin: center; }
  @keyframes rotate {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
  }
</style>

<svg viewBox="0 0 100 100" width="80" height="80">
  <g class="spin">
    <circle cx="50" cy="50" r="40" fill="none" stroke="#3b82f6" stroke-width="4" stroke-dasharray="20 10" />
  </g>
</svg>

Animated Stroke Drawing Effect

<style>
  .draw-path {
    stroke-dasharray: 300;
    stroke-dashoffset: 300;
    animation: draw 2s ease forwards;
  }
  @keyframes draw {
    to { stroke-dashoffset: 0; }
  }
</style>

<svg viewBox="0 0 200 100" width="400" height="200">
  <path
    class="draw-path"
    d="M 10 50 Q 100 10 190 50"
    fill="none"
    stroke="#6366f1"
    stroke-width="3"
  />
</svg>

Pulse Effect

<style>
  .pulse {
    animation: pulse 1.5s ease-in-out infinite;
    transform-origin: center;
  }
  @keyframes pulse {
    0%, 100% { transform: scale(1);   opacity: 1; }
    50%       { transform: scale(1.15); opacity: 0.7; }
  }
</style>

<svg viewBox="0 0 100 100" width="100" height="100">
  <circle cx="50" cy="50" r="40" fill="#ef4444" class="pulse" />
</svg>

13. JavaScript and SVG Interaction

SVG is part of the DOM and fully accessible from JavaScript.

<svg id="my-svg" viewBox="0 0 200 200" width="200" height="200">
  <circle id="target-circle" cx="100" cy="100" r="60" fill="#3b82f6"
          style="cursor:pointer" />
  <text x="100" y="105" text-anchor="middle" fill="white" font-size="16"
        id="count-label" pointer-events="none">Click me!</text>
</svg>

<script>
  const circle = document.getElementById('target-circle');
  const label  = document.getElementById('count-label');
  const colours = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6'];
  let count = 0;

  circle.addEventListener('click', () => {
    count++;
    // Cycle through colours
    circle.setAttribute('fill', colours[count % colours.length]);
    label.textContent = `Clicked ${count}×`;
  });
</script>

Creating SVG Elements Dynamically

// Create a circle and add it to an existing SVG
const svg = document.getElementById('my-svg');
const NS  = 'http://www.w3.org/2000/svg';  // SVG namespace

const circle = document.createElementNS(NS, 'circle');
circle.setAttribute('cx', '50');
circle.setAttribute('cy', '50');
circle.setAttribute('r',  '30');
circle.setAttribute('fill', '#10b981');

svg.appendChild(circle);

// Animate position with requestAnimationFrame
let frame = 0;
function animate() {
  const x = 100 + Math.sin(frame * 0.05) * 60;
  const y = 100 + Math.cos(frame * 0.05) * 60;
  circle.setAttribute('cx', x.toFixed(1));
  circle.setAttribute('cy', y.toFixed(1));
  frame++;
  requestAnimationFrame(animate);
}
animate();

Important: Always use createElementNS with the SVG namespace URI 'http://www.w3.org/2000/svg' when creating SVG elements in JavaScript. createElement won't work correctly.


14. SVG Accessibility

SVGs should be accessible — especially icons and charts.

Decorative SVG — Hide from Screen Readers

<!-- Decorative icon — screen readers will ignore it -->
<svg aria-hidden="true" focusable="false" viewBox="0 0 24 24" width="24" height="24">
  <path d="M12 2L2 7l10 5 10-5-10-5z" fill="currentColor"/>
</svg>

Meaningful SVG — Describe It

<!-- Standalone meaningful icon — must be labelled -->
<svg role="img" aria-labelledby="star-title star-desc"
     viewBox="0 0 100 100" width="100" height="100">
  <title id="star-title">Achievement star</title>
  <desc id="star-desc">A gold star icon representing a completed achievement or award.</desc>
  <polygon points="50,5 61,35 95,35 68,57 79,91 50,70 21,91 32,57 5,35 39,35"
           fill="#fbbf24" />
</svg>

SVG Within an <img> Tag

When using <img src="icon.svg">, describe it only in the alt attribute:

<img src="achievement-star.svg" alt="Achievement star — course completed" width="80" height="80">

Accessible SVG Chart

<figure>
  <figcaption>Monthly active users — Q1 2025</figcaption>
  <svg role="img" aria-label="Bar chart: January 1200, February 1850, March 2400"
       viewBox="0 0 320 180" width="320" height="180">
    <!-- January -->
    <rect x="20"  y="80" width="60" height="80" fill="#3b82f6" />
    <!-- February -->
    <rect x="130" y="20" width="60" height="140" fill="#10b981" />
    <!-- March -->
    <rect x="240" y="0"  width="60" height="160" fill="#6366f1" />
    <!-- Labels -->
    <text x="50"  y="175" text-anchor="middle" font-size="12" fill="#374151">Jan</text>
    <text x="160" y="175" text-anchor="middle" font-size="12" fill="#374151">Feb</text>
    <text x="270" y="175" text-anchor="middle" font-size="12" fill="#374151">Mar</text>
  </svg>
</figure>

15. Building a Reusable SVG Icon System

Instead of repeating SVG markup, define all icons in a hidden <svg> at the top of the page and reference them with <use>.

<!-- Hidden sprite at the top of the page -->
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
  <symbol id="icon-home" viewBox="0 0 24 24">
    <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
  </symbol>

  <symbol id="icon-code" viewBox="0 0 24 24">
    <path d="M9.4 16.6 4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0 4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/>
  </symbol>

  <symbol id="icon-star" viewBox="0 0 24 24">
    <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
  </symbol>
</svg>

<!-- Use icons anywhere with a single line -->
<svg width="24" height="24" aria-hidden="true" focusable="false">
  <use href="#icon-home" />
</svg>

<svg width="24" height="24" aria-hidden="true" focusable="false">
  <use href="#icon-code" />
</svg>

Style icons with CSS fill: currentColor so they inherit their colour from the parent text:

svg { fill: currentColor; }

.nav-link { color: #3b82f6; }
/* The icon inside .nav-link automatically turns blue */

16. Optimising SVGs

Raw SVG exported from design tools (Figma, Illustrator) often contains unnecessary metadata. Use SVGO to clean it up.

# Install globally
npm install -g svgo

# Optimise a single file
svgo icon.svg -o icon.min.svg

# Optimise a folder
svgo --folder ./icons

Typical savings: 20–70% smaller file size with no visible quality loss.

Manual Optimisation Tips

<!-- ❌ Exported SVG with editor metadata and unnecessary attributes -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 100 100"
     style="enable-background:new 0 0 100 100;" xml:space="preserve">
  <style type="text/css">.st0{fill:#333333;}</style>
  <circle class="st0" cx="50" cy="50" r="40"/>
</svg>

<!-- ✅ Cleaned up version -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <circle cx="50" cy="50" r="40" fill="#333"/>
</svg>

17. Common Mistakes

MistakeWhat goes wrongFix
No viewBoxSVG has fixed size, won't scaleAlways set viewBox="minX minY width height"
Forgetting aria-hidden on decorative SVGsScreen readers read out <path> data stringsAdd aria-hidden="true" and focusable="false"
No role="img" on meaningful standalone SVGAssistive tech doesn't announce it as an imageAdd role="img" and aria-labelledby
Using createElement in JS for SVGCreates wrong element type — not renderedUse createElementNS(NS, tagName)
Not using <title> and <desc>Chart or logo has no descriptionAdd inside the <svg> for meaningful graphics
Inline CSS transform-origin buggy cross-browser for SVGRotation happens around wrong pointUse transform="rotate(45, cx, cy)" in the attribute, or set transform-box: fill-box in CSS
Huge unoptimised SVG files from editorsUnnecessary kilobytes of metadataRun SVGO before deploying
Hard-coded fill coloursIcons can't be recoloured with CSSUse fill="currentColor"

18. Mini Exercises

  1. Create an SVG with a coloured circle, a rectangle with rounded corners, and a line between them.
  2. Draw a simple house shape using <polygon> for the roof and <rect> for the walls and door.
  3. Define a linear gradient in <defs> and apply it as the fill of a large rectangle.
  4. Write an SVG <text> element that centres "Nandhoo" horizontally in a 300-wide SVG.
  5. Use <clipPath> to clip a large rectangle to a circular shape.
  6. Animate a circle with CSS: make it pulse (scale in and out) over 1.5 seconds.
  7. Create an SVG icon sprite with two symbols and reference them with <use> in two different places on the page.

19. Review Questions

  1. What does SVG stand for and what makes it different from PNG or JPEG?
  2. What is the viewBox attribute and why does it make SVGs resolution-independent?
  3. What is the difference between <rect>, <circle>, <polygon>, and <path>?
  4. When would you use <defs> and <use> together?
  5. How do you apply a CSS hover colour change to an inline SVG element?
  6. What is fill="currentColor" and when is it useful?
  7. How do you make a decorative SVG icon invisible to screen readers?
  8. How do you make a meaningful SVG chart accessible?
  9. Why should you use createElementNS (not createElement) when creating SVG elements with JavaScript?
  10. What tool is used to optimise SVG file sizes for production?