anchors.lua is a small library for building relative UI layouts in Lua using anchor-based positioning. It provides a declarative way to define UI elements that scale and position themselves relative to their parent containers.
local Anchors = require 'anchors'
-- Create a centered panel at 50% of screen size
local layout = {
anchors = { 0.25, 0.25, 0.75, 0.75 }
}
local frame = Anchors.build(layout, 800, 600)
local x, y, width, height = frame:toXYWH()
-- Result: x=200, y=150, width=400, height=300
-- Create a sidebar with two vertically stacked panels
local layout = {
anchors = { 0, 0, 0.3, 1 }, -- Left 30% of screen
children = {
{ anchors = { 0, 0, 1, 0.5 } }, -- Top half of sidebar
{ anchors = { 0, 0.5, 1, 1 } } -- Bottom half of sidebar
}
}
local frame = Anchors.build(layout, 1920, 1080)
-- Create a fixed-size button with 10px padding
local layout = {
anchors = { 0.5, 0.5, 0.5, 0.5 }, -- Centered anchor point
offsets = { -60, -20, 0, 0 }, -- Offset from center
size = { 120, 40 } -- Fixed 120x40px button
}
local frame = Anchors.build(layout, 800, 600)COMING SOON
local frame = Anchors.build(layout, screenWidth, screenHeight)Creates a frame hierarchy from a layout definition.
layoutis a table defining the UI structure (see Layout Structure below)screenWidthis the width of the screen/container in pixels (default: 800)screenHeightis the height of the screen/container in pixels (default: 600)frameis the root Frame object containing the calculated rectangles
A layout table can contain:
anchors = { left, top, right, bottom }Defines the anchor points as normalized coordinates (0-1) relative to the parent:
left- X position of left edge (0 = parent's left, 1 = parent's right)top- Y position of top edge (0 = parent's top, 1 = parent's bottom)right- X position of right edgebottom- Y position of bottom edge
Shortcuts:
anchors = 0.5- Centers the element (expands to{ 0.5, 0.5, 0.5, 0.5 })anchors = { 0.5 }- Same as above- Default (on nil or invalid input):
{ 0, 0, 1, 1 }(fills parent)
offsets = { left, top, right, bottom }Pixel offsets applied after anchor positioning:
- Positive values move inward/rightward/downward
- Negative values create padding/margins
- Supports scalar expansion like anchors
- Default:
{ 0, 0, 0, 0 }
size = { width, height }Fixed pixel dimensions. When specified:
rightandbottomanchors are ignored- Element is positioned from its top-left anchor point
- Useful for fixed-size buttons, icons, etc.
children = { layout1, layout2, ... }Array of child layouts. Each child:
- Is positioned relative to its parent's calculated rectangle
- Inherits the parent's position as its coordinate space
- Can have its own children for deep nesting
local x, y, width, height = frame:toXYWH()Returns position and dimensions in screen coordinates.
local left, top, right, bottom = frame:toAABB()Returns axis-aligned bounding box coordinates.
frame = {
rect = { left, top, right, bottom }, -- Pixel coordinates
children = { frame1, frame2, ... } -- Child frames (if any)
}Anchors work like attachment points:
Parent: 800x600px
anchors = { 0, 0, 0.5, 0.5 }
┌─────────┬─────────┐
│ Element │ │ Element fills top-left quadrant
├─────────┤ │ Left anchor: 0 = left edge (0px)
│ │ │ Top anchor: 0 = top edge (0px)
│ │ │ Right anchor: 0.5 = middle (400px)
└─────────┴─────────┘ Bottom anchor: 0.5 = middle (300px)
anchors = { 0.25, 0.25, 0.75, 0.75 }
┌───────────────────┐
│ ┌─────────┐ │ Element is centered with 25% margins
│ │ Element │ │ Spans from 25% to 75% on both axes
│ └─────────┘ │
└───────────────────┘
-- Center with specific size
{
anchors = 0.5, -- expands to { 0.5, 0.5, 0.5, 0.5 }
offsets = { -100, -50, 0, 0 },
size = { 200, 100 }
}
-- Center with relative size
{
anchors = { 0.3, 0.3, 0.7, 0.7 } -- 40% of parent, centered
}-- 10px padding on all sides
{
anchors = { 0, 0, 1, 1 }, -- could be nil
offsets = { 10, 10, -10, -10 }
}-- Left sidebar (30% width)
{ anchors = { 0, 0, 0.3, 1 } }
-- Right sidebar (25% width)
{ anchors = { 0.75, 0, 1, 1 } }-- 2x2 grid
{
anchors = { 0, 0, 1, 1 },
children = {
{ anchors = { 0, 0, 0.5, 0.5 } }, -- Top-left
{ anchors = { 0.5, 0, 1, 0.5 } }, -- Top-right
{ anchors = { 0, 0.5, 0.5, 1 } }, -- Bottom-left
{ anchors = { 0.5, 0.5, 1, 1 } } -- Bottom-right
}
}- Anchors are normalized coordinates (0-1), offsets are pixels
- When
sizeis specified,rightandbottomanchors are ignored - Invalid anchors (NaN, infinity, wrong types) default to full-area layout
{ 0, 0, 1, 1 } - Invalid size/offset proprieties default to 0 (like
{ NaN, 10, 0, 0 }->{ 0, 10, 0, 0 } - Coordinates are floored to integer pixels to prevent subpixel rendering issues
- The library validates all input, so you can't crash it with bad data
- Layouts are retained-mode, they are not intended to be recalculated every frame, instead prefer to rebuild on resizes, on initial load, or when the layout changes
This library is licensed under the 0BSD license.