Hypen
Guide

Basics

Fundamental concepts of the Hypen language

Basics

This guide covers the fundamental concepts of the Hypen language.

Components

Components are the building blocks of Hypen UIs. They're declared by name followed by optional arguments and children.

// Component with no arguments
Spacer()

// Component with arguments
Text("Hello World")

// Component with children
Column {
    Text("First")
    Text("Second")
}

// Component with arguments AND children
Button {
    Text("Submit")
}.onClick(@actions.submit)

Component Names

Component names are PascalCase (start with uppercase):

// Correct
Column { }
Text("Hi")
MyCustomComponent()

// Wrong - will be treated as unknown
column { }
text("Hi")

Arguments

Arguments pass data to components. They can be positional or named.

Positional Arguments

Text("Hello")           // Single positional
Image("photo.jpg")      // URL as positional

Named Arguments

Text(text: "Hello")
Input(placeholder: "Enter name")
    .value(${state.name})

Argument Types

// Strings
Text("Hello")
Text(text: "Hello")

// Numbers
.padding(16)
.fontSize(24)
.opacity(0.5)

// Booleans
Button(disabled: true)
Input(multiline: false)

// Lists
Row(items: ["a", "b", "c"])

// Maps
Card(style: { elevation: 4, rounded: true })

// State bindings
Text("Count: ${state.count}")

// Actions (via applicators)
Button { Text("Save") }
    .onClick(@actions.save)

Applicators

Applicators modify a component's appearance or behavior. They're chained with dot notation.

Text("Styled text")
    .fontSize(18)
    .fontWeight("bold")
    .color("#3B82F6")
    .padding(16)

Chaining

Applicators are applied in order, from top to bottom:

Box { }
    .backgroundColor("blue")    // Applied first
    .padding(16)                // Applied second
    .borderRadius(8)            // Applied third

Common Applicators

// Spacing
.padding(16)
.margin(8)
.gap(12)

// Size
.width(200)
.height(100)
.fillMaxWidth(true)

// Colors
.color("#333333")
.backgroundColor("#F3F4F6")

// Typography
.fontSize(18)
.fontWeight("bold")
.textAlign("center")

// Border
.borderRadius(8)
.borderWidth(1)
.borderColor("#E5E7EB")

// Layout
.horizontalAlignment("center")
.verticalAlignment("center")

State Bindings

State bindings connect your UI to reactive data using ${state.path} syntax.

// Simple binding
Text("Hello, ${state.name}!")

// Nested paths
Text("${state.user.profile.displayName}")

// In string interpolation
Text("You have ${state.messages.length} messages")

When the referenced state changes, the UI automatically updates.

Actions

Actions trigger handlers in your module using @actions.actionName syntax via event applicators.

// Basic action
Button { Text("Save") }
    .onClick(@actions.save)

// Action with additional data
Button { Text("Delete") }
    .onClick(@actions.deleteItem, id: ${state.currentId})

// Multiple events
Input(placeholder: "Type here")
    .onInput(@actions.updateText)
    .onFocus(@actions.inputFocused)
    .onBlur(@actions.inputBlurred)

Children

Components can contain other components as children using braces { }.

Column {
    Text("First child")
    Text("Second child")
    Row {
        Text("Nested child")
    }
}

Nesting

There's no limit to nesting depth:

Column {
    Card {
        Row {
            Image("avatar.png")
            Column {
                Text("John Doe")
                Text("@johndoe")
            }
        }
    }
}

Control Flow

Hypen provides first-class control flow components for conditionals and iteration.

ForEach

Iterates over a collection and renders children for each item. Use @item to reference each item.

Column {
    ForEach(items: @state.todos) {
        Row {
            Text(@item.title)
            Text(@item.status)
        }
    }
}

Arguments:

  • items: The collection to iterate over
  • key (optional): Property to use as unique key for efficient updates

When

Pattern matching on a value with multiple cases.

When(value: @state.status) {
    Case(match: "loading") {
        Spinner()
    }
    Case(match: "error") {
        Text("Something went wrong")
    }
    Case(match: "success") {
        Text("Done!")
    }
    Else {
        Text("Unknown status")
    }
}

Match Patterns:

  • Exact values: Case(match: "loading")
  • Multiple values: Case(match: [200, 201, 204])
  • Wildcards: Case(match: "_") or Case(match: "*")
  • Expressions: Case(match: "${value >= 1 && value <= 100}")

If

Boolean conditional for simple true/false branching.

If(condition: @state.isLoggedIn) {
    Text("Welcome back!")
    Else {
        Text("Please sign in")
    }
}

Comments

// Single line comment

/*
   Multi-line
   comment
*/

Column {
    Text("Visible")
    // Text("Hidden - commented out")
}

Modules

Modules are components with state. Declare them with the module keyword.

module Counter {
    Column {
        Text("Count: ${state.count}")
        Button { Text("+") }
            .onClick(@actions.increment)
    }
}

The module name must match the TypeScript file that defines its state and actions.

Complete Example

module UserProfile {
    Column {
        // Header
        Row {
            Image(${state.user.avatar})
                .size(64)
                .borderRadius(32)

            Column {
                Text(${state.user.name})
                    .fontSize(20)
                    .fontWeight("bold")

                Text("@${state.user.username}")
                    .color("#6B7280")
            }
            .gap(4)
        }
        .gap(16)
        .padding(16)

        Divider()

        // Stats
        Row {
            Column {
                Text("${state.user.posts}")
                    .fontWeight("bold")
                Text("Posts")
                    .fontSize(12)
            }

            Column {
                Text("${state.user.followers}")
                    .fontWeight("bold")
                Text("Followers")
                    .fontSize(12)
            }

            Column {
                Text("${state.user.following}")
                    .fontWeight("bold")
                Text("Following")
                    .fontSize(12)
            }
        }
        .horizontalAlignment("space-around")
        .padding(16)

        // Actions
        Button {
            Text(${state.isFollowing} ? "Unfollow" : "Follow")
        }
        .onClick(@actions.follow)
        .backgroundColor(${state.isFollowing} ? "#E5E7EB" : "#3B82F6")
        .padding(horizontal: 24, vertical: 12)
        .borderRadius(8)
        .fillMaxWidth(true)
        .margin(16)
    }
}

Next Steps