Sone

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

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