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.