Skip to Content
⚠️ Neuron is our internal UI component library. For internal use only.
DocumentationGuideControl State

Control State

Neuron’s control state system manages how UI elements respond to user interactions. It tracks the current interaction state of a component, triggering visual changes and callbacks.

Control States

The system defines several states:

  • Initialize: Before any interaction.
  • Default: Normal, idle state.
  • Pressed: When the primary input is actively pressing the element.
  • Hover: When the pointer hovers over the element.
  • Selected: When the element is selected via gamepad or keyboard navigation.
  • SelectedPressed: When a selected element is pressed via gamepad or keyboard.
  • Disabled: When the element is inactive.

State Layer Affordance & Mode

Neuron also supports state layer styling:

  • Affordance: Determines which part of the element (background, border, or none) is affected.
  • Mode: Adjusts the style presentation (e.g., Default, Inverse, Light, Dark).

From a UX perspective, state affordance determines which part of your component — like the background or border — is used to signal interaction. It’s all about providing clear visual cues that a component is interactive. For example, a button might have a highlighted border or a subtle background change on hover.

State mode defines the style of these visual cues. Whether it’s Default, Light, Dark, or Inverse, mode helps match the feedback to your overall design theme while ensuring the interaction feels natural.

Note

Avoid setting the affordance to None. Visual feedback is crucial for letting users know their input is recognized, so always keep some form of affordance enabled. Neuron’s affordance system has built-in support for all interaction methods, so breaking out of it and rolling your own might not work across all devices or input methods.

How It Works

Components (like the Interactable component) use hooks (e.g., useGuiControlState) to monitor interaction events. When an event occurs, a callback updates the control state, which can then drive style changes via design tokens.

Examples

Basic Interaction

A simple button only needs an onActivated callback. When you pass this prop, View switches to using Interactable under the hood.

local React = require(...) local Neuron = require(...) local View = Neuron.View local function BasicButton() return React.createElement(View, { onActivated = function() print("Button activated!") end, tag = "bg-primary radius-medium", size = UDim2.new(0, 100, 0, 50), }, { React.createElement("TextLabel", { Text = "Click Me", Size = UDim2.new(1, 0, 1, 0), }) }) end return BasicButton

Reacting to State Changes

For more control, use the onStateChanged callback to react to transitions — say, to update styles based on hover or press events.

⚠️
Warning

Performance Notice

Do not declare the onStateChanged callback inline.

For functional components, wrap it in useCallback and minimize dependencies. In class components, declare it in the Init function. Use bindings wherever possible to reduce re-renders.

local React = require(...) local Neuron = require(...) local View = Neuron.View local ControlState = Neuron.Enums.ControlState local function HoverButton() local controlState, setControlState = React.useBinding(ControlState.Initialize) local handleStateChanged = React.useCallback(function(newState) setControlState(newState) print("New state:", newState) end) -- Dynamically choose a background based on state. local backgroundTag = controlState:map(function(state) if state == ControlState.Hover then return "bg-hover" elseif state == ControlState.Pressed then return "bg-pressed" else return "bg-default" end end) return React.createElement(View, { onStateChanged = handleStateChanged, tag = backgroundTag, size = UDim2.new(0, 120, 0, 60), }, { React.createElement("TextLabel", { Text = "Hover Over Me", Size = UDim2.new(1, 0, 1, 0), }) }) end return HoverButton

Customizing Visual Feedback with StateLayer

You can enhance visual feedback using the stateLayer prop. This allows you to set an affordance (e.g. "Background", "Border", or "None") and a mode (e.g. "Default", "Inverse", "Light", "Dark") for state transitions.

local React = require(...) local Neuron = require(...) local View = Neuron.View local function StateLayerButton() return React.createElement(View, { onActivated = function() print("StateLayer button activated!") end, onStateChanged = function(newState) print("State changed to:", newState) end, stateLayer = { mode = "Dark", -- Choose the visual style affordance = "Border", -- Apply changes to the border inset = true, -- Optionally, inset the effect for depth }, tag = "bg-surface-100 radius-small", size = UDim2.new(0, 150, 0, 75), }, { React.createElement("TextLabel", { Text = "StateLayer Button", Size = UDim2.new(1, 0, 1, 0), }) }) end return StateLayerButton

Handling Disabled Components

Mark a component as disabled by passing isDisabled = true. The control state system automatically transitions to "Disabled", and any interaction callbacks (like onActivated) won’t fire.

local React = require(...) local Neuron = require(...) local View = Neuron.View local function DisabledButton() return React.createElement(View, { isDisabled = true, tag = "bg-disabled radius-small", onActivated = function() print("This won't trigger because the button is disabled.") end, size = UDim2.new(0, 120, 0, 60), }, { React.createElement("TextLabel", { Text = "Disabled", Size = UDim2.new(1, 0, 1, 0), }) }) end return DisabledButton
Last updated on