Shapes and Stacks

Circles and rectangles arranged with stackH and stackV - stacks nest to build up larger layouts

import { Graphic } from "@modular-svg/react";

export function ShapesAndStacks() {
	return (
		<Graphic
			style={{
				border: "1px solid #ddd",
				borderRadius: "8px",
				display: "inline-block",
			}}
			margin={15}
		>
			<stackV spacing={20}>
				<stackH spacing={15}>
					<circle r={15} fill="crimson" />
					<circle r={15} fill="gold" />
					<circle r={15} fill="limegreen" />
				</stackH>
				<stackH spacing={15}>
					<rect width={30} height={30} fill="dodgerblue" />
					<rect width={30} height={30} fill="mediumpurple" />
				</stackH>
			</stackV>
		</Graphic>
	);
}

React Components

Using React components to create reusable shapes

import { Graphic } from "@modular-svg/react";

function ColoredCircle({ color }: { color: string }) {
	return <circle r={18} fill={color} />;
}

export function ReactComponents() {
	return (
		<Graphic
			style={{
				border: "1px solid #ddd",
				borderRadius: "8px",
				display: "inline-block",
			}}
			margin={10}
		>
			<stackH spacing={12}>
				<ColoredCircle color="red" />
				<ColoredCircle color="green" />
				<ColoredCircle color="blue" />
			</stackH>
		</Graphic>
	);
}

Custom Styling

Graphic accepts standard div props like className and style

import { Graphic } from "@modular-svg/react";

export function CustomStyling() {
	return (
		<Graphic
			className="custom-canvas"
			style={{
				border: "3px solid #333",
				borderRadius: "12px",
				display: "inline-block",
				backgroundColor: "#fafafa",
				padding: "10px",
				boxShadow: "0 4px 6px rgba(0,0,0,0.1)",
			}}
			margin={15}
		>
			<stackV spacing={15}>
				<circle r={20} fill="indigo" />
				<rect width={60} height={40} fill="tomato" />
			</stackV>
		</Graphic>
	);
}

Distinct Colors

DistinctColors spreads the children's hues evenly around the wheel, so a set of shapes picks distinct colors automatically - none are chosen in the markup

import { Graphic } from "@modular-svg/react";
import { useState } from "react";

// A row of circles with no fill. DistinctColors spreads their hues evenly
// around the wheel, so each gets a different color automatically - no color
// is chosen in the markup.
export function DistinctColorsExample() {
	const [count, setCount] = useState(5);
	const [startHue, setStartHue] = useState(30);
	const ids = Array.from({ length: count }, (_, i) => `c${i}`);

	return (
		<div>
			<div style={{ marginBottom: "1rem" }}>
				<label style={{ display: "block", marginBottom: "0.5rem" }}>
					Circles: {count}
					<input
						type="range"
						min="2"
						max="8"
						value={count}
						onChange={(e) => setCount(Number(e.target.value))}
						style={{ marginLeft: "1rem", width: "180px" }}
					/>
				</label>
				<label style={{ display: "block", marginBottom: "0.5rem" }}>
					Start hue: {startHue}°
					<input
						type="range"
						min="0"
						max="360"
						value={startHue}
						onChange={(e) => setStartHue(Number(e.target.value))}
						style={{ marginLeft: "1rem", width: "180px" }}
					/>
				</label>
			</div>
			<Graphic
				style={{
					border: "1px solid #ddd",
					borderRadius: "8px",
					display: "inline-block",
				}}
				margin={12}
			>
				<group>
					<stackH spacing={12} alignment="centerY">
						{ids.map((id) => (
							<circle key={id} r={22} stroke="#333" stroke-width={1} />
						))}
					</stackH>
					<distinctColors startHue={startHue} saturation={0.6} lightness={0.55}>
						{ids.map((id) => (
							<ref key={id} target={id} />
						))}
					</distinctColors>
				</group>
			</Graphic>
		</div>
	);
}

Same Color

SameColor copies one source color onto every follower - the color analogue of a span. Change the source and they all follow

import { Graphic } from "@modular-svg/react";
import { useState } from "react";

// One source swatch defines the color; SameColor copies it onto every
// follower shape, whatever their kind. Change the source and they all follow.
export function SameColorExample() {
	const [color, setColor] = useState("#2A9D8F");

	return (
		<div>
			<label style={{ display: "block", marginBottom: "1rem" }}>
				Source color
				<input
					type="color"
					value={color}
					onChange={(e) => setColor(e.target.value)}
					style={{ marginLeft: "1rem", verticalAlign: "middle" }}
				/>
			</label>
			<Graphic
				style={{
					border: "1px solid #ddd",
					borderRadius: "8px",
					display: "inline-block",
				}}
				margin={12}
			>
				<group>
					<stackH spacing={16} alignment="centerY">
						<rect key="source" width={44} height={44} fill={color} />
						<circle key="f1" r={20} stroke="#333" stroke-width={1} />
						<rect
							key="f2"
							width={40}
							height={40}
							stroke="#333"
							stroke-width={1}
						/>
						<circle key="f3" r={14} stroke="#333" stroke-width={1} />
					</stackH>
					<sameColor>
						<ref target="source" />
						<ref target="f1" />
						<ref target="f2" />
						<ref target="f3" />
					</sameColor>
				</group>
			</Graphic>
		</div>
	);
}

Readable Contrast

Contrast adjusts a label's lightness to meet a WCAG ratio against its background, so text stays legible over any color

import { Graphic } from "@modular-svg/react";
import { useState } from "react";

// The label has no readable color of its own - Contrast adjusts its
// lightness to meet a WCAG ratio against the background. Drag the background
// from light to dark and the text flips to stay legible.
export function ContrastExample() {
	const [bg, setBg] = useState("#f4e04d");

	return (
		<div>
			<label style={{ display: "block", marginBottom: "1rem" }}>
				Background
				<input
					type="color"
					value={bg}
					onChange={(e) => setBg(e.target.value)}
					style={{ marginLeft: "1rem", verticalAlign: "middle" }}
				/>
			</label>
			<Graphic
				style={{
					border: "1px solid #ddd",
					borderRadius: "8px",
					display: "inline-block",
				}}
				margin={12}
			>
				<group>
					<background key="box" padding={18} fill={bg} stroke-width={0} rx={8}>
						<text key="label" font-size={18}>
							Always readable
						</text>
					</background>
					<contrast ratio={4.5}>
						<ref target="label" />
						<ref target="box" />
					</contrast>
				</group>
			</Graphic>
		</div>
	);
}