AtelierLayout
Compose precise SVG layouts from geometry.
The shared geometry layer before rendering. Solve grids, labels, tracks, and connections once, then draw the resulting rectangles and points in SVG, Canvas, or any other renderer.
Features
- Geometry, never markup
Rect, Point, Size, Insets, Bounds and Circle are immutable values with no behaviour beyond their own maths. Nothing here emits SVG, HTML or a DOM. - Composition primitives
Stacks along one axis, grids on two, groups that carry a bounding box, overlays anchored to a corner, spacers, alignment and distribution. - Solved in one pass
Constraints travel down the node tree, sizes travel back up, and the solved tree answers by name:frameOf('title'). - Text is geometry
Wrapping, per-line frames, baselines, and overflow reported rather than hidden. The default measurer is deterministic on purpose, so tests and server output do not vary with the fonts installed. - Links between boxes
Where a link leaves one rectangle, where it enters another, and the right-angle path between: points, a label position, and the final tangent for an arrowhead. - Lanes, strips and keys
Parallel lanes with a reserved header, an edge strip for an axis, and a key made of swatches and labels. Each takes a rectangle and returns placed frames.
Solve a real grid
use Atelier\Layout\Element\Frame;
use Atelier\Layout\Element\Grid;
use Atelier\Layout\Geometry\Rect;
use Atelier\Layout\Grid\TrackSize;
use Atelier\Layout\LayoutContext;
$grid = Grid::tracks('workspace', [TrackSize::fixed(128), TrackSize::fr()])
->rows([TrackSize::fixed(52), TrackSize::fr(), TrackSize::fixed(40)])
->gap(12)
->add(Frame::stretch('header'), columnSpan: 2)
->add(Frame::stretch('sidebar'), rowSpan: 2)
->add(Frame::stretch('content'))
->add(Frame::stretch('footer'));
$result = $grid->solve(new LayoutContext(), new Rect(16, 16, 608, 260));
$content = $result->frameOf('content'); // Rect(156, 80, 468, 144)
Wrap a label before drawing it
use Atelier\Layout\Element\TextBlock;
$label = TextBlock::of('summary', 'A label that wraps predictably', 12)
->breakWords()
->layout(new LayoutContext(), new Rect(0, 0, 120, 48));
$label->lines;
$label->hasOverflow();
Connect two boxes
use Atelier\Layout\Connection\OrthogonalConnector;
use Atelier\Layout\Geometry\Rect;
$from = new Rect(20, 20, 80, 40);
$to = new Rect(180, 60, 90, 40);
$connection = (new OrthogonalConnector())->connect($from, $to);
Configuration
LayoutContext accepts a text measurer. Its default is deliberately deterministic, so server-side output and snapshot tests do not vary by installed fonts. Swap in a precise measurer when your renderer needs it.
Documentation
Start
- Getting started: solve a first layout and choose the right primitive.
Composition
- Composition: combine one-dimensional and two-dimensional layout primitives.
- Stacks and Grid: arrange children along one axis or across tracks.
- Alignment and Distribution: position children and share remaining space.
- Groups and Overlays: compose solved bounds and anchor one element to another.
Geometry and connections
- Geometry: use immutable points, sizes, rectangles, bounds, and insets.
- Constraints and Fit: negotiate available space and preserve proportions.
- Text: measure, wrap, align, and inspect overflow.
- Connections: route labelled links between rectangles.
- Tracks, bands, and legends: place lanes, axis strips, and keys.