No description
  • TypeScript 98.4%
  • HTML 0.9%
  • JavaScript 0.7%
Find a file
2026-07-24 22:30:22 +02:00
src Cleanup 2026-07-24 22:30:22 +02:00
.gitignore Initial commit 2026-02-07 22:30:44 +01:00
eslint.config.js Added transforms to svg attributes 2026-04-04 23:10:49 +02:00
index.html Initial commit 2026-02-07 22:30:44 +01:00
package-lock.json Cleanup 2026-07-24 22:30:12 +02:00
package.json Cleanup 2026-07-24 22:30:12 +02:00
README.md Add README with usage examples (#2) 2026-04-05 13:56:23 +02:00
tsconfig.json Initial commit 2026-02-07 22:30:44 +01:00
tsdown.config.ts Updated libs, ci and changed to tsdown for build 2026-04-14 10:32:11 +02:00
vitest.config.ts Add vitest tests for primitives, utils, and attributes (#1) 2026-04-05 13:46:59 +02:00

@chocbite/ts-lib-svg

A TypeScript library for programmatic SVG generation with a fluent, chainable API.

Installation

npm install @chocbite/ts-lib-svg

Quick Start

import { svg } from "@chocbite/ts-lib-svg";

// Create an SVG canvas and append a red circle
const canvas = svg.svg(200, 200).elem;
canvas.appendChild(
  svg.circle(100, 100, 40).fill("red").stroke("black").stroke_width(2).elem
);
document.body.appendChild(canvas);

API

All drawing functions return an SVGAttributes wrapper that supports chaining. Call .elem to get the underlying SVGElement.

Primitives

svg.svg(width, height, viewbox?)

Creates an <svg> root element. The viewbox defaults to "0 0 <width> <height>".

const canvas = svg.svg(400, 300).elem;

svg.circle(center_x, center_y, radius)

Creates a <circle> element.

svg.circle(50, 50, 25).fill("blue").elem;

svg.ellipse(center_x, center_y, radius_x, radius_y)

Creates an <ellipse> element.

svg.ellipse(100, 100, 60, 30).fill("green").stroke("black").elem;

svg.rectangle_from_center(center_x, center_y, width, height, corner_radius)

Creates a <rect> positioned by its center point.

svg.rectangle_from_center(100, 100, 80, 40, 5).fill("orange").elem;

svg.rectangle_from_corner(start_x, start_y, width, height, corner_radius)

Creates a <rect> positioned by its top-left corner.

svg.rectangle_from_corner(10, 10, 80, 40, 0).fill("purple").elem;

svg.line(start_x, start_y, end_x, end_y)

Creates a <line> element.

svg.line(0, 0, 100, 100).stroke("black").stroke_width(2).elem;

svg.path(d)

Creates a <path> element with the given path data string.

svg.path("M 10 80 C 40 10, 65 10, 95 80").stroke("black").fill("none").elem;

svg.isosceles_triangle(center_x, center_y, width, height)

Creates an isosceles triangle as a <path>.

svg.isosceles_triangle(100, 100, 60, 80).fill("yellow").stroke("black").elem;

svg.circle_arc(center_x, center_y, radius, start_angle, end_angle)

Draws a circular arc. Angles are in degrees.

svg.circle_arc(100, 100, 50, 0, 90).stroke("red").fill("none").stroke_width(2).elem;

svg.ellipse_arc(center_x, center_y, radius_x, radius_y, start_angle, end_angle)

Draws an elliptical arc. Angles are in degrees.

svg.ellipse_arc(100, 100, 60, 30, 0, 180).stroke("blue").fill("none").elem;

svg.text(x, y, text, size, anchor)

Creates a single-line <text> element. The anchor parameter controls text alignment using SVGAnchorPoint.

import { svg, SVGAnchorPoint } from "@chocbite/ts-lib-svg";

svg.text(100, 50, "Hello", 16, SVGAnchorPoint.middleCenter).fill("black").elem;

svg.multi_line_text(x, y, width, height, text, size, anchor)

Creates a multi-line text block using a <foreignObject> with an inner <div>.

svg.multi_line_text(10, 10, 200, 100, "Long text that wraps", 14, SVGAnchorPoint.topLeft).elem;

svg.group()

Creates a <g> group element. Append child elements to it.

const g = svg.group().translate(50, 50).elem;
g.appendChild(svg.circle(0, 0, 20).fill("red").elem);
g.appendChild(svg.circle(40, 0, 20).fill("blue").elem);

svg.create(tagName)

Creates an arbitrary SVG element by tag name (e.g. "defs", "clipPath").

const defs = svg.create("defs").elem;

Chaining Attributes

Every primitive returns an SVGAttributes wrapper with chainable methods:

Method Shorthand Description
.fill(color) .f(color) Set fill color
.stroke(color) .s(color) Set stroke color
.stroke_width(n) .sw(n) Set stroke width
.class_list(...names) .cl(...names) Add CSS class names
.attribute(name, value) .a(name, value) Set any attribute
.translate(x, y) Apply translate transform
.rotate(angle, cx?, cy?) Apply rotate transform
.scale(sx, sy?) Apply scale transform
.skewX(angle) Apply skewX transform
.skewY(angle) Apply skewY transform
.transform(str) Apply a custom transform string
.elem Get the underlying SVGElement

Transforms are accumulated and written to the transform attribute when .elem is accessed.

svg.circle(0, 0, 10)
  .fill("red")
  .stroke("black")
  .stroke_width(1)
  .class_list("my-circle")
  .translate(50, 50)
  .rotate(45)
  .elem;

Utilities

SVGAnchorPoint

An enum-like object describing text anchor positions:

import { SVGAnchorPoint } from "@chocbite/ts-lib-svg";

SVGAnchorPoint.topLeft;       // 2
SVGAnchorPoint.topCenter;     // 3
SVGAnchorPoint.topRight;      // 4
SVGAnchorPoint.middleLeft;    // 1
SVGAnchorPoint.middleCenter;  // 8
SVGAnchorPoint.middleRight;   // 5
SVGAnchorPoint.bottomLeft;    // 0
SVGAnchorPoint.bottomCenter;  // 7
SVGAnchorPoint.bottomRight;   // 6

svg.angle_to_anchor_point(angle)

Converts an angle (in radians) to the nearest SVGAnchorPoint. Useful for placing labels around a circle.

const anchor = svg.angle_to_anchor_point(Math.PI / 4); // bottomRight

Full Example

import { svg, SVGAnchorPoint } from "@chocbite/ts-lib-svg";

const canvas = svg.svg(300, 300).elem;

// Background
canvas.appendChild(
  svg.rectangle_from_corner(0, 0, 300, 300, 0).fill("#f0f0f0").elem
);

// Grouped shapes
const g = svg.group().translate(150, 150).elem;
g.appendChild(svg.circle(0, 0, 50).fill("none").stroke("steelblue").stroke_width(2).elem);
g.appendChild(svg.line(-50, 0, 50, 0).stroke("gray").stroke_width(1).elem);
g.appendChild(svg.line(0, -50, 0, 50).stroke("gray").stroke_width(1).elem);
g.appendChild(svg.text(0, -60, "Center", 12, SVGAnchorPoint.bottomCenter).fill("black").elem);
canvas.appendChild(g);

document.body.appendChild(canvas);

License

MIT