from xml.etree.ElementTree import Element, SubElement, tostring
from IPython.display import SVG, display # Optional: for Jupyter inline display
def svg_bar_chart(
=None, width=520, height=280, margin=40, colors=None, title=None
values, labels
):"""Return an SVG string for a responsive bar chart."""
= labels or [str(i+1) for i in range(len(values))]
labels = max(values) if values else 1
max_v = width - 2 * margin
plot_w = height - 2 * margin
plot_h = plot_w / max(len(values), 1)
bar_w
= Element("svg", {
svg "xmlns": "http://www.w3.org/2000/svg",
"viewBox": f"0 0 {width} {height}",
"width": "100%" # responsive
})
# Styles (scoped to this SVG)
= SubElement(svg, "style")
style = """
style.text .axis { stroke: #1f2937; stroke-width: 1; }
.grid { stroke: #e5e7eb; stroke-width: 1; }
.label { fill: #374151; font: 12px/1.2 DejaVu Sans, Arial; }
.title { fill: #111827; font: bold 14px/1.2 DejaVu Sans, Arial; }
"""
# Background
"rect", {"x": "0", "y": "0", "width": str(width), "height": str(height), "fill": "#ffffff"})
SubElement(svg,
# Grid (y)
for gy in range(5):
= margin + (gy * plot_h / 4.0)
y "line", {"x1": str(margin), "y1": f"{y:.2f}",
SubElement(svg, "x2": str(width - margin), "y2": f"{y:.2f}",
"class": "grid"})
# Axes
"line", {"x1": str(margin), "y1": str(height - margin),
SubElement(svg, "x2": str(width - margin), "y2": str(height - margin),
"class": "axis"})
"line", {"x1": str(margin), "y1": str(margin),
SubElement(svg, "x2": str(margin), "y2": str(height - margin),
"class": "axis"})
# Bars
for i, v in enumerate(values):
# Height proportional to value
= 0 if max_v == 0 else (v / max_v) * plot_h
bh = margin + i * bar_w + 6
x = (height - margin) - bh
y = (colors[i % len(colors)] if colors else "#60a5fa") # default blue-ish
fill "rect", {
SubElement(svg, "x": f"{x:.2f}", "y": f"{y:.2f}",
"width": f"{bar_w - 12:.2f}", "height": f"{bh:.2f}",
"fill": fill, "rx": "4", "ry": "4"
})# X labels
"text", {
SubElement(svg, "x": f"{x + (bar_w - 12)/2:.2f}",
"y": f"{height - margin + 16:.2f}",
"text-anchor": "middle",
"class": "label"
= labels[i]
}).text # Value labels
"text", {
SubElement(svg, "x": f"{x + (bar_w - 12)/2:.2f}",
"y": f"{y - 6:.2f}",
"text-anchor": "middle",
"class": "label"
= str(v)
}).text
# Title
if title:
"text", {
SubElement(svg, "x": f"{width/2:.2f}", "y": f"{margin - 12:.2f}",
"text-anchor": "middle", "class": "title"
= title
}).text
return tostring(svg, encoding="unicode")
# --- Example usage ---
= [3, 7, 5, 9, 4, 6]
vals = ["A", "B", "C", "D", "E", "F"]
labs = svg_bar_chart(vals, labs, title="Example SVG Bar Chart")
svg_str
# Save to file
with open("chart.svg", "w", encoding="utf-8") as f:
f.write(svg_str)
# In JupyterLab, render inline:
=svg_str)) display(SVG(data
SVG
svgs
What is SVG?
- SVG (Scalable Vector Graphics) is an XML format for 2D graphics (resolution-independent, searchable, stylable).
- Renders natively in all modern browsers; ideal for icons, logos, charts, diagrams, UI overlays, and annotation layers over Canvas/WebGL.
Core advantages & tradeoffs
Topic | Why it matters | Tips |
---|---|---|
Scalability | No pixelation on zoom | Prefer SVG for crisp UI, charts, text |
Styling | Works with CSS + inline styles | Use classes for reuse (.stroke-2 , .muted ) |
Interactivity | JS events, hover states, SMIL/CSS animation | Keep DOM small; batch updates |
Accessibility | Real text & titles/readers | Use <title> , <desc> , role , aria-* |
File size | Often smaller than PNG for simple art | Minify; gzip (.svgz ) |
Performance | DOM can get heavy for 10k+ nodes | Use <use> , simplify paths; swap to Canvas for very dense scenes |
Essential building blocks
Coordinate system & responsiveness
- Use
viewBox="minX minY width height"
to define drawing coords. - Make it responsive: set
width="100%"
and rely onviewBox
for scaling (omit fixed height or control via CSS).
svg viewBox="0 0 400 200" width="100%" xmlns="http://www.w3.org/2000/svg">
<<!-- content -->
svg> </
Styling
- Inline:
fill
,stroke
,stroke-width
,opacity
- CSS: target classes/IDs for reusable styles.
style>
<
.muted { stroke: #94a3b8; fill: none; }style>
</path class="muted" d="M10 10 L190 10"/> <
Definitions & reuse
- Put assets in
<defs>
: gradients, filters, symbols, clip/mask. - Reuse with
<use href="#symbolId" />
.
Text & labels
<text x y>
,<tspan>
for subspans,<textPath>
to flow along a curve.- For charts/diagrams: set
text-anchor="middle"
anddominant-baseline="middle"
for centering.
Effects & animation
Gradients/Patterns:
<linearGradient>
,<radialGradient>
,<pattern>
Filters:
<feGaussianBlur>
,<feDropShadow>
,<feColorMatrix>
β¦Animations:
- SMIL (built-in):
<animate>
,<animateTransform>
- CSS animations: for properties that are CSS-animatable
- JavaScript/D3: full control + data-binding
- SMIL (built-in):
Example SMIL:
circle cx="40" cy="40" r="10" fill="tomato">
<animate attributeName="cx" dur="2s" values="40;160;40" repeatCount="indefinite"/>
<circle> </
Accessibility & security
- Add
<title>
and<desc>
as first children for screen readers. - Avoid untrusted inline scripts in SVG; sanitize user-uploaded SVGs.
- For decorative SVGs:
role="img"
+aria-hidden="true"
(if appropriate).
Optimizing & production tips
Minify (SVGO) and gzip (
.svgz
).Break complex art into smaller symbols; instantiate via
<use>
.Convert extremely dense layers to Canvas/WebGL, overlay SVG for crisp labels.
When embedding in web apps:
- Inline (
<svg>β¦</svg>
) for full CSS/JS control. <img src="β¦">
when you want it static and sandboxed.- React: import as component or inline in JSX.
- Inline (
Python example: generate a responsive SVG bar chart (pure stdlib)
Works in JupyterLab (and anywhere Python runs). Produces an SVG string and writes to file.
Why this approach?
- Zero dependencies (stdlib
xml.etree.ElementTree
). - Responsive via
viewBox + width="100%"
. - Easy to extend (tooltips,
<title>
, animations, gradients).
Advanced features youβll likely use next
- Markers (arrows): use
<marker>
in<defs>
andmarker-end="url(#id)"
. - Text along curves: define a path (
<path id="wave" d="β¦">
) then<textPath href="#wave">
. - Masks & clips: spotlight effects, image brushes (
<mask>
,<clipPath>
). - Filters: crisp drop-shadows that print beautifully (
<feDropShadow>
beats CSS blur for vector fidelity). - Symbol sprites: define icons once with
<symbol>
and instantiate with<use>
all over.
Tooling & ecosystem
- Authoring: Inkscape, Illustrator, Figma (export SVG; simplify before shipping).
- Programmatic (Python):
svgwrite
(nice API),cairosvg
(convert SVGβPNG/PDF),lxml
(advanced XML),BeautifulSoup
(post-process). - Web: D3.js for data binding; React inline SVG components for UI; SVGO for minification.
Quick checklist for production
- β
Use
viewBox
+ responsive sizing - β
Add
<title>
/<desc>
for accessibility - β Minify & gzip
- β Sanitize user-supplied SVGs
- β
Keep DOM lightweight; prefer
<use>
for repetition - β Consider Canvas/WebGL for very dense layers; keep labels in SVG
Example
π¦ 1. Basic Rectangle + Circle
from IPython.display import SVG, display
= """
svg_str <svg xmlns="http://www.w3.org/2000/svg" width="200" height="120">
<rect x="10" y="10" width="80" height="100" fill="lightblue" stroke="navy" stroke-width="2"/>
<circle cx="150" cy="60" r="40" fill="salmon" stroke="red" stroke-width="2"/>
</svg>
"""
display(SVG(svg_str))
π 2. Line + Polygon
= """
svg_str <svg xmlns="http://www.w3.org/2000/svg" width="200" height="120">
<line x1="10" y1="10" x2="190" y2="110" stroke="green" stroke-width="3"/>
<polygon points="60,20 100,100 20,100" fill="orange" stroke="black" stroke-width="2"/>
</svg>
"""
display(SVG(svg_str))
π 3. Text Example
= """
svg_str <svg xmlns="http://www.w3.org/2000/svg" width="250" height="100">
<text x="20" y="40" font-family="Arial" font-size="20" fill="purple">Hello SVG!</text>
<text x="20" y="70" font-family="Courier New" font-size="16" fill="darkblue">In JupyterLab</text>
</svg>
"""
display(SVG(svg_str))
π 4. Gradient Fill
= """
svg_str <svg xmlns="http://www.w3.org/2000/svg" width="220" height="120">
<defs>
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:skyblue; stop-opacity:1" />
<stop offset="100%" style="stop-color:steelblue; stop-opacity:1" />
</linearGradient>
</defs>
<rect x="10" y="10" width="200" height="100" fill="url(#grad)" stroke="black"/>
</svg>
"""
display(SVG(svg_str))
π 5. Simple Animation
= """
svg_str <svg xmlns="http://www.w3.org/2000/svg" width="250" height="120">
<circle cx="40" cy="60" r="20" fill="tomato">
<animate attributeName="cx" from="40" to="200" dur="2s" repeatCount="indefinite" />
</circle>
</svg>
"""
display(SVG(svg_str))
π 6. Bouncing Ball
from IPython.display import SVG, display
= """
svg_bounce <svg xmlns="http://www.w3.org/2000/svg" width="300" height="150">
<circle cx="50" cy="50" r="20" fill="tomato">
<animate attributeName="cy" values="50;120;50" dur="1s" repeatCount="indefinite"/>
<animate attributeName="cx" values="50;250;50" dur="2s" repeatCount="indefinite"/>
</circle>
</svg>
"""
display(SVG(svg_bounce))
π 7. Color-Changing Circle
= """
svg_color <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
<circle cx="100" cy="100" r="50" fill="red">
<animate attributeName="fill" values="red;blue;green;purple;red" dur="4s" repeatCount="indefinite"/>
</circle>
</svg>
"""
display(SVG(svg_color))
π 8. Growing Bar Graph
= """
svg_bar <svg xmlns="http://www.w3.org/2000/svg" width="300" height="150">
<rect x="30" y="100" width="40" height="0" fill="steelblue">
<animate attributeName="height" from="0" to="80" dur="1s" fill="freeze"/>
<animate attributeName="y" from="100" to="20" dur="1s" fill="freeze"/>
</rect>
<rect x="100" y="100" width="40" height="0" fill="orange">
<animate attributeName="height" from="0" to="60" dur="1.5s" fill="freeze"/>
<animate attributeName="y" from="100" to="40" dur="1.5s" fill="freeze"/>
</rect>
<rect x="170" y="100" width="40" height="0" fill="tomato">
<animate attributeName="height" from="0" to="100" dur="2s" fill="freeze"/>
<animate attributeName="y" from="100" to="0" dur="2s" fill="freeze"/>
</rect>
</svg>
"""
display(SVG(svg_bar))
π 9. Rotating Loader (Spinner)
= """
svg_spinner <svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" stroke="blue" stroke-width="6" fill="none" stroke-dasharray="60 40">
<animateTransform attributeName="transform" type="rotate" from="0 50 50" to="360 50 50" dur="1s" repeatCount="indefinite"/>
</circle>
</svg>
"""
display(SVG(svg_spinner))
π 10. Star Path Glow Animation
= """
svg_star <svg xmlns="http://www.w3.org/2000/svg" width="250" height="250">
<polygon points="125,20 150,100 230,100 160,150 185,230 125,180 65,230 90,150 20,100 100,100"
fill="gold" stroke="orange" stroke-width="3">
<animate attributeName="fill" values="gold;yellow;orange;gold" dur="3s" repeatCount="indefinite"/>
<animate attributeName="stroke-width" values="3;6;3" dur="2s" repeatCount="indefinite"/>
</polygon>
</svg>
"""
display(SVG(svg_star))