Core Concepts
Understanding Sone's component system and architecture
Core Concepts
Sone uses a declarative, component-based approach to building layouts. If you're familiar with React, you'll feel right at home with Sone's API.
Components
Sone provides several core components that you can use to build your layouts:
Layout Components
- Column - A vertical container that stacks children vertically
- Row - A horizontal container that arranges children side by side
- TextDefault - A wrapper that applies text styling to all child components
Content Components
- Text - For displaying text content with rich formatting
- Span - For styling portions of text within a Text component
- Photo - For displaying images with various scaling options
- Path - For drawing custom SVG paths and shapes
- Table, TableRow, TableCell - For creating structured table layouts
Basic Structure
Every Sone document starts with a root component, typically a Column
or Row
:
import { Column, Text, sone } from "sone";
function MyDocument() {
return Column(
Text("Header").size(24).weight("bold"),
Text("Body content").size(16)
).padding(20);
}
Method Chaining
Sone components use a fluent API with method chaining for styling:
Text("Hello World")
.size(24)
.color("blue")
.weight("bold")
.align("center")
This is equivalent to setting multiple properties at once:
Text("Hello World").apply({
size: 24,
color: "blue",
weight: "bold",
align: "center"
})
Component Composition
Components can be nested to create complex layouts:
Column(
// Header section
Row(
Photo("logo.png").size(40),
Text("My App").size(20).weight("bold")
).gap(10),
// Content section
Text("Welcome to my application!")
.size(16)
.align("center")
.padding(20)
).bg("white").padding(30)
Flexbox Layout
Sone uses Yoga Layout (Facebook's Flexbox implementation) for consistent layout behavior:
Row(
Text("Left").flex(1), // Takes 1/3 of space
Text("Center").flex(2), // Takes 2/3 of space
Text("Right").flex(1) // Takes 1/3 of space
).justifyContent("space-between")
Styling Properties
All components inherit from a base set of layout properties:
Spacing
padding()
,paddingTop()
,paddingLeft()
, etc.margin()
,marginTop()
,marginLeft()
, etc.
Sizing
width()
,height()
,size(width, height)
minWidth()
,maxWidth()
,minHeight()
,maxHeight()
Positioning
position("absolute" | "relative" | "static")
top()
,left()
,right()
,bottom()
Background & Borders
bg()
for background colors, gradients, or imagesborderWidth()
,borderColor()
rounded()
for border radius
Visual Effects
opacity()
shadow()
for drop shadowsrotate()
,scale()
for transforms
Rendering & Export
Once you've built your component tree, render it with the sone()
function:
const document = Column(
Text("Hello World").size(24)
).padding(40);
// Export in different formats
const jpgBuffer = await sone(document).jpg();
const pngBuffer = await sone(document).png();
const svgBuffer = await sone(document).svg();
const pdfBuffer = await sone(document).pdf();
// Get the canvas directly
const canvas = await sone(document).canvas();
Next.js Integration
For Next.js applications, add this configuration:
// next.config.js
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
serverExternalPackages: ["skia-canvas"],
webpack: (config, options) => {
if (options.isServer) {
config.externals = [
...config.externals,
{ "skia-canvas": "commonjs skia-canvas" },
];
}
return config;
},
};
export default nextConfig;
Browser Usage
In browsers, you need to provide a renderer implementation. The basic structure is the same, but rendering is handled differently:
import { render, Column, Text } from "sone";
// You'll need to implement a browser renderer
const canvas = await render(
Column(Text("Hello")),
browserRenderer
);
Common Patterns
Card Layout
function Card(title, content) {
return Column(
Text(title).size(18).weight("bold"),
Text(content).size(14).color("gray")
)
.padding(20)
.bg("white")
.rounded(8)
.shadow("0 2px 4px rgba(0,0,0,0.1)");
}
Header with Logo
function Header() {
return Row(
Photo("logo.png").size(40),
Text("Company Name").size(24).weight("bold")
)
.alignItems("center")
.gap(15)
.padding(20);
}
Responsive Text
function ResponsiveText(content) {
return Text(content)
.autofit(true) // Automatically adjusts font size to fit
.maxWidth("100%");
}