Hypen
Guide

Inputs

Form elements, buttons, and handling user interactions in Hypen

Inputs

Input components allow users to interact with your application through forms, buttons, and other controls.

Button

A clickable element that triggers an action when pressed.

Button { Text("Submit") }
    .onClick(@actions.submit)

Styling Buttons

// Primary button
Button { Text("Primary Action") }
    .onClick(@actions.primary)
    .backgroundColor("#3B82F6")
    .padding(horizontal: 24, vertical: 12)
    .borderRadius(8)

// Secondary/outline button
Button { Text("Secondary") }
    .onClick(@actions.secondary)
    .backgroundColor("transparent")
    .borderWidth(1)
    .borderColor("#3B82F6")
    .padding(horizontal: 24, vertical: 12)
    .borderRadius(8)

Button with Icon

Button {
    Row {
        Image("cart-icon.svg").size(20)
        Text("Add to Cart")
    }
    .gap(8)
}
.onClick(@actions.addToCart)

Disabled State

Button { Text("Submit") }
    .onClick(@actions.submit)
    .disabled(${state.isSubmitting})
    .opacity(${state.isSubmitting} ? 0.5 : 1)

Platform Support: Web, Android, iOS

Input

A single-line text input field.

Input(placeholder: "Enter your name")
    .onInput(@actions.updateName)

Input Events

Input(placeholder: "Email address")
    .value(${state.email})
    .onInput(@actions.updateEmail)
    .onFocus(@actions.emailFocused)
    .onBlur(@actions.emailBlurred)

Enter Key Handling

Input(placeholder: "Search...")
    .onInput(@actions.updateSearch)
    .onKey(@actions.submitSearch)

The onKey event fires when the Enter key is pressed.

Styling Inputs

Input(placeholder: "Username")
    .padding(12)
    .borderWidth(1)
    .borderColor("#E5E7EB")
    .borderRadius(6)
    .fontSize(16)

Platform Support: Web, Android, iOS

Textarea

A multi-line text input field for longer content.

Textarea(placeholder: "Enter your message...")
    .onInput(@actions.updateMessage)
    .height(120)
    .padding(12)
    .borderRadius(8)

Textarea with Character Limit

Column {
    Textarea(placeholder: "Bio (max 200 characters)")
        .value(${state.bio})
        .onInput(@actions.updateBio)
        .height(100)

    Text("${state.bio.length}/200")
        .fontSize(12)
        .color("#6B7280")
        .textAlign("right")
}

Platform Support: Web, Android, iOS

Checkbox

A toggleable checkbox for boolean selections.

Checkbox {}
    .checked(${state.agreeToTerms})
    .onChange(@actions.toggleTerms)

Checkbox with Label

Row {
    Checkbox {}
        .checked(${state.rememberMe})
        .onChange(@actions.toggleRememberMe)
    Text("Remember me")
}
.gap(8)
.alignItems("center")

Platform Support: Web, Android, iOS

Switch

A toggle switch for binary on/off settings.

Switch {}
    .checked(${state.darkMode})
    .onChange(@actions.toggleDarkMode)

Switch with Label

Row {
    Text("Dark Mode")
    Spacer()
    Switch {}
        .checked(${state.darkMode})
        .onChange(@actions.toggleDarkMode)
}
.padding(16)

Platform Support: Web, Android, iOS

Select

A dropdown selection component.

Select {}
    .value(${state.country})
    .onChange(@actions.updateCountry)

Platform Support: Web, Android, iOS

Slider

A range slider for numeric input.

Slider {}
    .value(${state.volume})
    .onChange(@actions.updateVolume)

Slider with Value Display

Column {
    Text("Volume: ${state.volume}%")
    Slider {}
        .value(${state.volume})
        .onChange(@actions.updateVolume)
}
.gap(8)

Platform Support: Web, Android, iOS

Form Example

A complete form with multiple input types:

module ContactForm {
    Column {
        Text("Contact Us")
            .fontSize(24)
            .fontWeight("bold")

        Input(placeholder: "Name")
            .value(${state.name})
            .onInput(@actions.updateName)
            .padding(12)
            .borderRadius(6)
            .borderWidth(1)
            .borderColor("#E5E7EB")

        Input(placeholder: "Email")
            .value(${state.email})
            .onInput(@actions.updateEmail)
            .padding(12)
            .borderRadius(6)
            .borderWidth(1)
            .borderColor("#E5E7EB")

        Textarea(placeholder: "Message")
            .value(${state.message})
            .onInput(@actions.updateMessage)
            .height(120)
            .padding(12)
            .borderRadius(6)
            .borderWidth(1)
            .borderColor("#E5E7EB")

        Row {
            Checkbox {}
                .checked(${state.subscribe})
                .onChange(@actions.toggleSubscribe)
            Text("Subscribe to newsletter")
        }
        .gap(8)

        Button { Text("Send Message") }
            .onClick(@actions.submit)
            .backgroundColor("#3B82F6")
            .padding(horizontal: 24, vertical: 12)
            .borderRadius(8)
            .fillMaxWidth(true)
    }
    .gap(16)
    .padding(24)
    .maxWidth(500)
}

Handling Form State

import { app } from "@hypen-space/core";

interface FormState {
  name: string;
  email: string;
  message: string;
  subscribe: boolean;
  isSubmitting: boolean;
}

export default app
  .defineState<FormState>({
    name: "",
    email: "",
    message: "",
    subscribe: false,
    isSubmitting: false
  })

  .onAction("updateName", async ({ action, state }) => {
    state.name = action.payload.value;
  })

  .onAction("updateEmail", async ({ action, state }) => {
    state.email = action.payload.value;
  })

  .onAction("updateMessage", async ({ action, state }) => {
    state.message = action.payload.value;
  })

  .onAction("toggleSubscribe", async ({ state }) => {
    state.subscribe = !state.subscribe;
  })

  .onAction("submit", async ({ state }) => {
    state.isSubmitting = true;

    try {
      await fetch("/api/contact", {
        method: "POST",
        body: JSON.stringify({
          name: state.name,
          email: state.email,
          message: state.message,
          subscribe: state.subscribe
        })
      });
      // Reset form
      state.name = "";
      state.email = "";
      state.message = "";
      state.subscribe = false;
    } finally {
      state.isSubmitting = false;
    }
  });

Next Steps