For the complete documentation index, see llms.txt. This page is also available as Markdown.

UI Reference

Reference notes and patterns for CSL UI drawing.

This page collects UI rules, patterns, and examples that are helpful once you know the basics.

Read UI Fundamentals first.

Core principles

  1. Y grows upward. Screen coordinates use (0, 0) at the bottom-left.

  2. Start from screen rects. Use UI.get_safe_screen_rect() or UI.get_screen_rect(), then derive everything from those rects.

  3. Push/pop pattern. Use defer for every push to avoid leaking UI state.

  4. Mobile-first. Avoid hover-only interactions.

  5. Text uses UTF-8, but character coverage is incomplete. A character still needs a glyph in the selected font or its fallback fonts. Some characters, including emoji, are not supported yet.

  6. Use the player late-update stack. Draw all player UI from that player's ao_late_update under is_local_or_server().

Quick start: a simple HUD button

Player :: class : Player_Base {
    ao_late_update :: method(dt: float) {
        if this.is_local_or_server() {
            draw_my_hud(this);
        }
    }
}

draw_my_hud :: proc(player: Player) {
    rect := UI.get_safe_screen_rect()
        .bottom_right_rect()
        .grow(40, 150, 40, 150)
        .offset(-50, 50);

    bs := UI.default_button_settings();
    ts := UI.default_text_settings();

    if UI.button(rect, bs, ts, "Action").clicked {
        log_info("action from %", {player.get_username()});
    }
}

Basic drawing

Quads and images

Text

Layout: start from rects

Use cut for layout

The cut functions must be used for layouting when placing multiple UI elements:

Auto-scaling and unscaled rects

Regular rect functions take points and scale them by UI.get_current_scale_factor() (based on a 1080-point-tall reference canvas).

Use the _unscaled variants only when a value is already expressed in actual screen pixels, such as a dimension measured from an existing rect:

Buttons

Button sprite rules

UI.default_button_settings() already provides a complete button style. For a custom button, set sprite and optionally sprite_hovered and sprite_pressed. When the optional sprites are null, the base sprite is reused.

Modals

Use UI.begin_modal / UI.end_modal for a dimmed backdrop that closes when the player taps outside the modal or presses Escape.

UI state management

Use defer for every push/pop pair:

IDs for repeated elements

When drawing lists or repeated items, push unique IDs:

Common sizes

Some sizes that are known to look good across devices:

  • Screen text points: Title 52, Body 36

  • Screen rect points: Simple dialog 600x400, Standard button 210x74, Exit button 65x65

  • World-space text size: 0.30 meters

Best practices

  • Use defer for every push/pop pair.

  • Push IDs for repeated elements.

  • Use unscaled functions with computed dimensions.

  • Fit icon aspect ratios with rect.fit_aspect(texture.get_aspect()).

  • Always check is_local_or_server() before drawing interactive UI.

Last updated