Images & Graphics
Working with photos, SVG paths, and custom graphics in Sone
Images & Graphics
Sone provides powerful tools for working with images and creating custom graphics, including the Photo
component for images and Path
component for SVG-based graphics.
Photo Component
Basic Image Usage
import { Photo, Column } from "sone";
// From file path
Photo("./assets/image.jpg")
.size(300, 200)
// From URL (Node.js)
Photo("https://example.com/image.png")
.width(400)
// From buffer data
Photo(imageBuffer)
.height(250)
Image Scaling
Control how images fit within their containers:
// Cover: Scale to fill container, may crop image
Photo("image.jpg")
.size(300, 200)
.scaleType("cover")
// Contain: Scale to fit within container, preserves aspect ratio
Photo("image.jpg")
.size(300, 200)
.scaleType("contain")
// Fill: Stretch to fill container exactly
Photo("image.jpg")
.size(300, 200)
.scaleType("fill")
Scale Alignment
Control which part of the image is visible when cropping:
// Using named positions
Photo("image.jpg")
.size(300, 200)
.scaleType("cover", "center") // "start", "center", "end"
// Using numeric alignment (0.0 = start, 0.5 = center, 1.0 = end)
Photo("image.jpg")
.size(300, 200)
.scaleType("cover", 0.8) // Closer to end
Image Transformations
Photo("image.jpg")
.size(200, 200)
.flipHorizontal(true) // Mirror horizontally
.flipVertical(true) // Mirror vertically
.rotate(45) // Rotate in degrees
.scale(1.2) // Scale uniformly
.scale(1.5, 0.8) // Scale X and Y separately
Aspect Ratio Preservation
Photo("image.jpg")
.width(300)
.preserveAspectRatio(true) // Height adjusts automatically
Image Styling
Apply visual effects to images:
Photo("image.jpg")
.size(300, 200)
.rounded(15) // Rounded corners
.shadow("0 4px 8px rgba(0,0,0,0.3)") // Drop shadow
.opacity(0.8) // Transparency
.blur(2) // Blur effect
.brightness(1.2) // Brightness adjustment
.contrast(1.1) // Contrast adjustment
.grayscale(0.5) // Partial grayscale
Image as Background
Use images as container backgrounds:
import { Column, Text, Photo } from "sone";
Column(
Text("Overlay text")
.size(32)
.color("white")
.weight("bold")
.dropShadow("2px 2px 4px rgba(0,0,0,0.8)")
)
.size(400, 300)
.bg(Photo("background.jpg"))
.justifyContent("center")
.alignItems("center")
SVG Path Graphics
Basic Path Usage
import { Path } from "sone";
// Simple path
Path("M 10 10 L 100 100 Z")
.stroke("black")
.strokeWidth(2)
.fill("red")
Path Commands
SVG path commands for drawing shapes:
// Move and Line commands
Path("M 50 50 L 150 50 L 100 150 Z") // Triangle
.fill("blue")
// Curves
Path("M 10 10 Q 50 100 100 10") // Quadratic curve
.stroke("green")
.strokeWidth(3)
.fill("none")
// Arcs
Path("M 50 50 A 40 40 0 0 1 150 50") // Arc
.stroke("purple")
.strokeWidth(4)
.fill("none")
Path Styling
Path("M 0 0 L 100 0 L 50 100 Z")
.fill("red") // Fill color
.fillOpacity(0.7) // Fill transparency
.fillRule("evenodd") // Fill rule: "nonzero" | "evenodd"
.stroke("blue") // Stroke color
.strokeWidth(3) // Stroke thickness
.strokeLineCap("round") // Line endings: "butt" | "round" | "square"
.strokeLineJoin("round") // Line joins: "miter" | "round" | "bevel"
.strokeMiterLimit(10) // Miter limit for sharp joins
.strokeDashArray(5, 10, 15) // Dash pattern
.strokeDashOffset(5) // Dash offset
Path Scaling
Path("M 0 0 L 50 0 L 25 50 Z")
.scalePath(2) // Scale the path uniformly
.fill("green")
Complex Path Examples
Star Shape
function Star(size = 50) {
const points = [];
for (let i = 0; i < 10; i++) {
const angle = (i * Math.PI) / 5;
const radius = i % 2 === 0 ? size : size * 0.4;
const x = size + radius * Math.cos(angle - Math.PI / 2);
const y = size + radius * Math.sin(angle - Math.PI / 2);
points.push(i === 0 ? `M ${x} ${y}` : `L ${x} ${y}`);
}
points.push('Z');
return Path(points.join(' '))
.fill("gold")
.stroke("orange")
.strokeWidth(2);
}
Heart Shape
function Heart() {
return Path(`
M 50 85
C 20 65, 5 35, 25 25
C 40 15, 55 25, 50 45
C 45 25, 60 15, 75 25
C 95 35, 80 65, 50 85
Z
`)
.fill("red")
.stroke("darkred")
.strokeWidth(1);
}
QR Codes
Generate QR codes programmatically:
import { qrcode, Column } from "sone";
// Generate QR code
const qr = qrcode("https://example.com")
.size(200)
.margin(10)
.bg("white")
Column(
Text("Scan me!").size(18).weight("bold"),
qr
)
.alignItems("center")
.gap(15)
Practical Examples
Image Gallery
function ImageGallery(images) {
return Row(
...images.map(image =>
Photo(image.src)
.size(200, 150)
.scaleType("cover")
.rounded(8)
.shadow("0 2px 4px rgba(0,0,0,0.1)")
.margin(5)
)
)
.wrap("wrap")
.justifyContent("center");
}
Profile Card
function ProfileCard(profile) {
return Column(
// Profile image
Photo(profile.avatar)
.size(80)
.scaleType("cover")
.rounded(40) // Circular
.shadow("0 2px 8px rgba(0,0,0,0.2)"),
// Name and info
Text(profile.name)
.size(20)
.weight("bold")
.marginTop(15),
Text(profile.title)
.size(14)
.color("gray")
.marginTop(5)
)
.alignItems("center")
.padding(25)
.bg("white")
.rounded(15)
.shadow("0 4px 12px rgba(0,0,0,0.1)");
}
Logo with Text
function LogoHeader(logoSrc, companyName) {
return Row(
Photo(logoSrc)
.size(50)
.scaleType("contain"),
Text(companyName)
.size(24)
.weight("bold")
.color("black")
)
.alignItems("center")
.gap(15);
}
Custom Icon
function CheckIcon(size = 24, color = "green") {
return Path(`
M ${size * 0.2} ${size * 0.5}
L ${size * 0.4} ${size * 0.7}
L ${size * 0.8} ${size * 0.3}
`)
.stroke(color)
.strokeWidth(3)
.strokeLineCap("round")
.strokeLineJoin("round")
.size(size);
}
Image with Caption
function ImageWithCaption(imageSrc, caption) {
return Column(
Photo(imageSrc)
.width(300)
.preserveAspectRatio(true)
.rounded(8),
Text(caption)
.size(14)
.color("gray")
.align("center")
.marginTop(10)
.maxWidth(300)
)
.alignItems("center");
}
Background Pattern
function PatternBackground(content) {
const pattern = Path(`
M 0 0 L 20 0 L 20 20 L 0 20 Z
M 5 5 L 15 5 L 15 15 L 5 15 Z
`)
.fill("rgba(0,0,0,0.05)")
.stroke("none");
return Column(content)
.bg(pattern)
.padding(40);
}
Image Comparison
function BeforeAfter(beforeImage, afterImage) {
return Row(
Column(
Text("Before").size(16).weight("bold").align("center"),
Photo(beforeImage)
.size(200, 150)
.scaleType("cover")
.rounded(8)
),
Column(
Text("After").size(16).weight("bold").align("center"),
Photo(afterImage)
.size(200, 150)
.scaleType("cover")
.rounded(8)
)
)
.gap(30)
.alignItems("center");
}
Best Practices
Image Performance
- Optimize images before using them in Sone
- Use appropriate image formats (JPEG for photos, PNG for graphics)
- Consider image dimensions relative to output size
Path Optimization
- Use simple paths when possible for better performance
- Combine multiple simple shapes into single paths where appropriate
- Cache complex path calculations
Visual Consistency
- Maintain consistent image sizes and aspect ratios
- Use consistent corner radius values across images
- Apply uniform shadow and spacing patterns
Accessibility
- Provide alt text descriptions in surrounding text components
- Ensure sufficient contrast for path graphics
- Consider how graphics will appear when exported to different formats