Zornux docs
Get started Spec

Mobile

Android UI Patterns

Beyond the basics of column, row, and button, Zornux gives you a full widget toolkit, a theming system, and layout controls that compile to native Jetpack Compose — no XML, no Kotlin, no Gradle files to edit.

Theming

A theme block inside mobile app sets the visual identity for every screen. Colors, typography, and shape are inherited by all widgets automatically:

zornux
mobile app "TaskFlow"

theme
    primary   "#1B5E20"
    secondary "#66BB6A"
    background "#FAFAFA"
    surface    "#FFFFFF"
    error      "#B00020"
    on_primary   "#FFFFFF"
    on_secondary "#000000"

    font_family "Inter"
    corner_radius 12
end

screen Main
    column
        text "TaskFlow" style heading
        button "Get Started"
            when tapped
                go to Tasks
            end
        end
    end
end

start with Main
PropertyControls
primaryButtons, active states, links.
secondaryAccent elements, FABs, toggles.
backgroundThe screen background behind surfaces.
surfaceCards, dialogs, bottom sheets.
errorError text, validation borders.
on_primary / on_secondaryText and icons on top of the primary/secondary color.
font_familyThe typeface for all text widgets.
corner_radiusDefault corner rounding for buttons and cards (in dp).
Omit what you don't need

Every theme property has a Material Design 3 default. Declare only what you want to override — a two-line theme block is perfectly valid.

Dark mode

Add a dark sub-block to provide dark-mode colors. Android switches automatically based on the system setting:

zornux
theme
    primary    "#1B5E20"
    background "#FAFAFA"
    surface    "#FFFFFF"

    dark
        primary    "#81C784"
        background "#121212"
        surface    "#1E1E1E"
    end
end
Automatic, not manual

You do not toggle dark mode yourself. The generated Compose code reads the system theme and selects the right palette. If no dark block is declared, Material 3 derives dark colors automatically from the light palette.

Text styles

The text widget accepts a style modifier to control its typographic role:

zornux
column
    text "Welcome back" style heading
    text "You have 3 new tasks" style subheading
    text "Tap a task to begin working on it." style body
    text "Last updated: 2 minutes ago" style caption
end
StyleCompose equivalentTypical use
headingheadlineMediumPage titles.
subheadingtitleMediumSection headers, card titles.
body (default)bodyLargeParagraph text.
captionbodySmallTimestamps, footnotes, labels.
labellabelLargeButton text, input labels.

Images

Display an image from a URL or a local asset:

zornux
screen Profile receives userId
    state avatar = ""

    when screen opens
        avatar = http.get("https://api.example.com/users/" + userId + "/avatar")
    end

    column
        image avatar
            width 120
            height 120
            corner_radius 60
        end
        text "User " + userId style heading
    end
end

Images accept size and shape modifiers:

ModifierEffect
width NFixed width in dp.
height NFixed height in dp.
corner_radius NRounded corners (set to half the dimension for a circle).
fit cover / fit containHow the image fills the bounds.

Cards

A card wraps content in an elevated surface with rounded corners:

zornux
screen TaskList
    state tasks = ["Design login", "Build API", "Write tests"]

    column
        text "My Tasks" style heading
        for each task in tasks
            card
                row
                    text task
                    button "Done"
                        when tapped
                            show task + " completed!"
                        end
                    end
                end
            end
        end
    end
end

Cards inherit the surface color from the theme and pick up corner_radius automatically.

Layout modifiers

Both column and row accept modifiers that control spacing, alignment, and padding:

zornux
screen Settings
    column padding 24 spacing 16
        text "Settings" style heading

        row spacing 12 align center
            text "Notifications"
            switch notifications_enabled
        end

        row spacing 12 align center
            text "Dark mode"
            switch dark_mode
        end

        spacer

        button "Log out" style destructive
            when tapped
                go to Login
            end
        end
    end
end
ModifierApplies toEffect
padding Ncolumn, row, cardInternal padding in dp.
spacing Ncolumn, rowGap between children in dp.
align center / start / endrowCross-axis alignment.
scrollcolumnMake the column vertically scrollable.

The spacer widget pushes content apart — it fills all available space along the parent's axis.

Advanced widgets

Beyond text, button, and input, the widget toolkit includes:

WidgetSyntaxRenders
Switchswitch stateNameA toggle bound to a boolean state.
Checkboxcheckbox stateName "Label"A labeled checkbox.
Sliderslider stateName from 0 to 100A range slider.
Progressprogress / progress valueIndeterminate or determinate progress indicator.
DividerdividerA horizontal line separator.
SpacerspacerFlexible empty space.
Iconicon "name"A Material icon by name.
zornux
screen Preferences
    state volume = 50
    state wifi_enabled = true
    state agree = false

    column padding 24 spacing 16
        text "Preferences" style heading

        row spacing 12 align center
            text "Volume"
            slider volume from 0 to 100
        end

        row spacing 12 align center
            icon "wifi"
            text "Wi-Fi"
            switch wifi_enabled
        end

        checkbox agree "I accept the terms and conditions"

        if agree
            button "Continue"
                when tapped
                    go to Home
                end
            end
        end
    end
end

Button styles

Buttons support a style modifier for common visual roles:

zornux
column spacing 12
    button "Save changes" style primary
        when tapped
            save()
        end
    end
    button "Cancel" style outline
        when tapped
            go back
        end
    end
    button "Delete account" style destructive
        when tapped
            show "Are you sure?"
        end
    end
end
StyleAppearance
primary (default)Filled with the primary color.
outlineBordered, transparent background.
textNo border, no background — just the label.
destructiveFilled with the error color.

Forms and validation

Build forms by combining input widgets with state and conditional rendering. Validate before submission:

zornux
screen Register
    state name = ""
    state email = ""
    state password = ""
    state error_message = ""

    column padding 24 spacing 16
        text "Create Account" style heading

        input name "Full name"
        input email "Email address"
        input password "Password" secure

        if error_message is not ""
            text error_message style caption
        end

        button "Sign Up"
            when tapped
                if name is "" or email is "" or password is ""
                    error_message = "All fields are required"
                else if length of password is less than 8
                    error_message = "Password must be at least 8 characters"
                else
                    error_message = ""
                    create result = http.post("https://api.example.com/register")
                    show "Account created!"
                    go to Login
                end
            end
        end
    end
end
Secure input

Adding secure after the label turns an input into a password field — the characters are masked and the keyboard disables autocomplete.

Scrollable lists with sections

For long content, add the scroll modifier to a column. Combine it with for each for data-driven lists:

zornux
screen Contacts
    state contacts = []

    when screen opens
        contacts = http.get("https://api.example.com/contacts")
    end

    column scroll padding 16 spacing 8
        text "Contacts" style heading

        if length of contacts is 0
            progress
            text "Loading..." style caption
        else
            for each contact in contacts
                card
                    row padding 16 spacing 12 align center
                        image contact.avatar
                            width 48
                            height 48
                            corner_radius 24
                        end
                        column spacing 4
                            text contact.name style subheading
                            text contact.email style caption
                        end
                    end
                end
            end
        end
    end
end

Accessibility

The generated Compose code inherits Android's accessibility framework. Zornux adds a label modifier for widgets that need an explicit content description:

zornux
button "X" label "Close dialog"
    when tapped
        go back
    end
end

image avatar label "User profile photo"
    width 64
    height 64
    corner_radius 32
end

icon "delete" label "Delete this item"
Text widgets are self-describing

A text widget's content is already its accessible label. Adding a redundant label creates a double announcement — only add one when the visible text does not describe the purpose (like an icon-only button labeled "X").

What's next