Selkie.git | docs/api/ | Selkie--Align.md
NAME
====
Selkie::Align - Text alignment and box cross-axis alignment enums
SYNOPSIS
========
```raku
use Selkie::Align;
use Selkie::Widget::Text;
use Selkie::Layout::VBox;
use Selkie::Sizing;
# Centre a banner's text inside its own plane.
my $banner = Selkie::Widget::Text.new(
text => 'Selkie',
align => TextCenter,
sizing => Sizing.fixed(1),
);
$banner.set-align(TextRight);
# Centre a fixed-width card inside a full-width column.
my $col = Selkie::Layout::VBox.new(sizing => Sizing.flex, align-items => CrossCenter);
$col.add: Selkie::Widget::Text.new(
text => 'a narrow card',
cross-sizing => Sizing.fixed(20), # 20 columns, centred in the VBox
sizing => Sizing.fixed(3),
);
```
DESCRIPTION
===========
Two enums, kept in one module so every widget that positions something inside a wider slot speaks the same language.
`TextAlign` is about glyphs *within* a widget's own plane: [Selkie::Widget::Text](Selkie--Widget--Text.md) offsets each wrapped line by `Text.align-column`. `CrossAlign` is about widgets *within* a container: [Selkie::Layout::VBox](Selkie--Layout--VBox.md) and [Selkie::Layout::HBox](Selkie--Layout--HBox.md) place each child along the axis they do **not** stack on.
The two are orthogonal, and so is `gap`: `gap` reserves cells along the *main* axis (between children), alignment moves a child along the *cross* axis. A `VBox` with `gap =E<gt> 1` and `align-items =E<gt> CrossCenter` gets both, independently.
The cross axis
--------------
A `VBox` stacks children top to bottom, so its main axis is rows and its cross axis is **columns**. An `HBox` is the mirror image: main axis columns, cross axis **rows**. "Cross-axis alignment" therefore means horizontal placement in a VBox and vertical placement in an HBox.
Two attributes drive it, and they answer different questions:
* **How big is the child on the cross axis?** `Widget.cross-sizing` — a [Selkie::Sizing](Selkie--Sizing.md) resolved against the container's cross extent. Undefined (the default) means "as big as the container", which is what Selkie has always done.
* **Where does that size sit?** `CrossAlign` — on the container as `align-items`, overridable per child as `Widget.align-self`.
```raku
my $row = Selkie::Layout::HBox.new(sizing => Sizing.flex, align-items => CrossStart);
# Both are 1 row tall; the second opts out of the container's rule.
$row.add: $stamp; # cross-sizing => Sizing.fixed(1) — top
$row.add: $badge; # …and align-self => CrossEnd — bottom
$badge.set-align-self(CrossEnd);
```
Defaults are the old behaviour
------------------------------
`CrossFill` plus an undefined `cross-sizing` is exactly what VBox and HBox did before alignment existed: every child gets the container's full cross extent at offset 0. `TextLeft` likewise puts every line at column 0. Nothing in an existing app moves by a cell.
EXAMPLES
========
A centred, fixed-width login form
---------------------------------
```raku
my $screen = Selkie::Layout::VBox.new(
sizing => Sizing.flex,
gap => 1,
align-items => CrossCenter,
);
$screen.add: Selkie::Widget::Text.new(
text => 'Sign in',
align => TextCenter,
cross-sizing => Sizing.fixed(40),
sizing => Sizing.fixed(1),
);
$screen.add: $username-row; # cross-sizing => Sizing.fixed(40)
$screen.add: $password-row; # cross-sizing => Sizing.fixed(40)
```
A right-aligned status column
-----------------------------
```raku
my $col = Selkie::Layout::VBox.new(sizing => Sizing.flex, align-items => CrossEnd);
$col.add: Selkie::Widget::Text.new(
text => '3 unread',
cross-sizing => Sizing.percent(50), # half the column, flush right
sizing => Sizing.fixed(1),
);
```
Note the difference between the two ways of pushing text to the right: `TextRight` moves the *glyphs* inside a full-width widget, while `CrossEnd` moves the *widget* inside a wider container. Reach for `TextRight` when the widget's own background should span the row, and for `CrossEnd` when it shouldn't.
SEE ALSO
========
* [Selkie::Widget::Text](Selkie--Widget--Text.md) — `align` / `align-column`
* [Selkie::Layout::Allocate](Selkie--Layout--Allocate.md) — `resolve-cross-extent`, `cross-axis-offset`, `effective-cross-align`
* [Selkie::Layout::VBox](Selkie--Layout--VBox.md) / [Selkie::Layout::HBox](Selkie--Layout--HBox.md) — `align-items`
* [Selkie::Sizing](Selkie--Sizing.md) — the fixed/percent/flex model `cross-sizing` reuses
* [Selkie::BorderStyle](Selkie--BorderStyle.md) — `TitleAlign`, the same idea for a frame's title
Horizontal placement of a line of text inside its widget's plane. `TextLeft` is the default and the historical behaviour: every line starts at column 0. `TextCenter` and `TextRight` shift each line independently, so a wrapped paragraph comes out ragged-left rather than block-justified. Alignment is an *offset*, never padding — Selkie does not write spaces on either side of the line. That keeps the untouched cells showing the plane's base cell, which is what makes aligned text work over a scrim or a gradient.
Placement of a child along its container's cross axis — columns in a `VBox`, rows in an `HBox`. `CrossFill` is the default: the child spans the container's whole cross extent (unless it declares a `cross-sizing`, which always wins on size; `CrossFill` then places it at offset 0, like `CrossStart`). `CrossStart` is flush left / top, `CrossEnd` flush right / bottom, and `CrossCenter` splits the leftover space, rounding the leading side down.