A clean, efficient SVG rendering library that integrates with the layout engine to produce beautiful SVG graphics from layout trees. Also provides standalone SVG path construction and marker support for data visualization.
- Layout Integration: Seamlessly renders
layout.Nodetrees to SVG - PathBuilder: Fluent API for constructing complex SVG paths with bezier curves
- Markers: Define custom markers for path endpoints and vertices (arrows, circles, diamonds, etc.)
- Smooth Curves: Bezier curve interpolation with tension control
- Basic Shapes: Rect, Circle, Ellipse, Polygon, Polyline, Line, Text
- Transform Support: Full 2D transform support (translate, rotate, scale, skew)
- Styling System: Colors, borders, backgrounds, shadows
- Text Rendering: SVG text elements with proper positioning
- ClipPath Management: Thread-safe unique ID generation for clipping
- Gradient Support: Linear and radial gradients with multiple color spaces (OKLCH, OKLAB, sRGB, Display P3)
- Design Tokens: Themeable styling system
go get github.com/SCKelemen/svgpackage main
import (
"fmt"
"github.com/SCKelemen/layout"
"github.com/SCKelemen/svg"
)
func main() {
// Create a layout tree
root := &layout.Node{
Style: layout.Style{
Display: layout.DisplayFlex,
FlexDirection: layout.FlexDirectionRow,
Width: 400,
Height: 200,
},
Children: []*layout.Node{
{Style: layout.Style{Width: 100, Height: 100}},
{Style: layout.Style{Width: 100, Height: 100}},
},
}
// Perform layout
constraints := layout.Loose(800, 600)
layout.Layout(root, constraints, nil)
// Render to SVG
output := svg.RenderToSVG(root, svg.Options{
Width: 400,
Height: 200,
})
fmt.Println(output)
}// Create a styled renderer
renderer := svg.NewRenderer(svg.Options{
Width: 800,
Height: 600,
StyleSheet: svg.DefaultStyles(),
})
// Render with custom styles
output := renderer.Render(root)// Create a gradient with multiple color spaces
gradient := svg.LinearGradient{
ID: "myGradient",
X1: 0, Y1: 0,
X2: 100, Y2: 0,
Stops: []svg.GradientStop{
{Offset: "0%", Color: "#3B82F6"},
{Offset: "100%", Color: "#8B5CF6"},
},
ColorSpace: color.GradientOKLCH, // Perceptually uniform gradients
}
// Apply gradient to elements
svgElement := fmt.Sprintf(`<rect fill="url(#myGradient)" x="0" y="0" width="100" height="50"/>`)Create complex SVG paths using a chainable API:
// Build a smooth curved path
points := []svg.Point{
{X: 50, Y: 200},
{X: 100, Y: 100},
{X: 200, Y: 150},
{X: 300, Y: 50},
}
// Simple polyline
path := svg.PolylinePath(points)
// Smooth bezier curve with tension control
smoothPath := svg.SmoothLinePath(points, 0.3)
// Area chart path (filled region)
areaPath := svg.AreaPath(points, 200) // baseline at y=200
// Smooth area chart
smoothArea := svg.SmoothAreaPath(points, 200, 0.3)
// Manual path construction
pb := svg.NewPathBuilder().
MoveTo(10, 10).
LineTo(90, 10).
CurveTo(120, 10, 120, 40, 90, 40).
Close()Add markers (arrows, dots, shapes) to path endpoints:
// Create predefined markers
arrow := svg.ArrowMarker("arrow-blue", "#3B82F6")
circle := svg.CircleMarker("dot-green", "#10B981")
diamond := svg.DiamondMarker("diamond-purple", "#8B5CF6")
// Apply markers to a path
pathData := svg.SmoothLinePath(points, 0.3)
decoratedPath := svg.PathWithMarkers(
pathData,
svg.Style{Stroke: "#3B82F6", StrokeWidth: 2, Fill: "none"},
svg.MarkerURL("dot-green"), // start marker
"", // mid markers (optional)
svg.MarkerURL("arrow-blue"), // end marker
)
// Render with markers in defs
output := svg.RenderToSVG(svg.SVGConfig{
Width: 400, Height: 300,
}, arrow, circle, decoratedPath)Available marker types:
ArrowMarker- Directional arrowCircleMarker- Filled circleSquareMarker- Filled squareDiamondMarker- Diamond shapeTriangleMarker- Triangle shapeCrossMarker- Plus/cross symbolXMarker- X symbolDotMarker- Small dot (customizable radius)
This library focuses on:
- Simplicity: Clean API with sensible defaults
- Integration: First-class support for the layout engine
- Extensibility: Easy to add custom rendering logic
- Performance: Efficient string building and minimal allocations
The library includes comprehensive unit tests:
# Run all tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run benchmarks
go test -bench=. -benchmemCoverage: ~50% of statements with tests for PathBuilder, Markers, and core rendering.
Typical performance on modern hardware:
| Operation | Time/op | Allocations |
|---|---|---|
| PolylinePath (100 points) | ~2-3 μs | Minimal |
| SmoothLinePath (100 points) | ~15-20 μs | Moderate |
| Marker generation | ~200-500 ns | Low |
| Full SVG render | ~10-50 μs | Context-dependent |