Button

A button component for audio applications supporting both toggle (latch) and momentary modes with proper event handling.

The Button component provides a customizable button for audio applications. It shares the same layout, parameter model, interaction, and accessibility as other vector components; see the Vector introduction.

  • Two operation modes: Latch (toggle) and momentary
  • Full theming support: color, roundness customization
  • Drag interactions: Proper handling of global mouse events to ensure reliable release behavior, and allow easy implementations of, for example, step sequencers and pads

Props

NameTypeDefaultRequiredDescription
valueboolean—RequiredCurrent value of the button (true = pressed/active, false = released/inactive)
onChange(event: AudioControlEvent<boolean>) => void—OptionalHandler for value changes
latchbooleanfalseOptionalWhether the button should latch (toggle between states) or momentary (only active while pressed). Ad-hoc mode only, ignored if parameter provided.
labelstring—OptionalLabel displayed below the component
colorstring—OptionalComponent primary color - any valid CSS color value
roundnessnumber—OptionalRoundness for component corners (normalized 0.0-1.0)
adaptiveSizebooleanfalseOptionalWhether the component stretches to fill its container
size"xsmall" | "small" | "normal" | "large" | "xlarge"normalOptionalSize of the component
displayMode"scaleToFit" | "fill"scaleToFitOptionalLayout mode for the control
labelMode"none" | "hidden" | "visible"visibleOptionalVisibility of the label row
labelPosition"above" | "below"belowOptionalVertical position of the label relative to the control
labelAlign"start" | "center" | "end"centerOptionalHorizontal alignment of the label text
labelOverflow"ellipsis" | "abbreviate" | "auto"autoOptionalHow to handle label text overflow
labelHeightUnitsnumber—OptionalLabel height in the same units as SVG viewBox height
parameterBooleanParameter—OptionalAudio Parameter definition (Model). If provided, overrides label/latch from ad-hoc props
paramIdstring—OptionalIdentifier for the parameter this control represents
midiResolutionMidiResolution7OptionalMIDI resolution in bits (ad-hoc mode, ignored if parameter provided)
onClickReact.MouseEventHandler—OptionalClick event handler
onMouseDownReact.MouseEventHandler—OptionalMouse down event handler
onMouseUpReact.MouseEventHandler—OptionalMouse up event handler
onMouseEnterReact.MouseEventHandler—OptionalMouse enter event handler
onMouseLeaveReact.MouseEventHandler—OptionalMouse leave event handler
classNamestring—OptionalAdditional CSS classes
styleReact.CSSProperties—OptionalAdditional inline styles

Basic Usage

The Button component requires a value (boolean) and onChange handler. The button operates in two modes: latch (toggle) or momentary.

Latch Mode (Toggle)

In latch mode, the button toggles between pressed and released states:

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function LatchButtonExample() {
  const [isOn, setIsOn] = useState(false);
 
  return (
    <Button
      label="Power"
      latch={true}
      value={isOn}
      onChange={(e) => setIsOn(e.value)}
      size="large"
    />
  );
}
 

Momentary Mode

In momentary mode, the button is only active while being pressed:

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function MomentaryButtonExample() {
  const [isPressed, setIsPressed] = useState(false);
 
  return (
    <Button
      label="Record"
      latch={false}
      value={isPressed}
      onChange={(e) => setIsPressed(e.value)}
      size="large"
    />
  );
}
 

Modes of Operation

The Button component supports two modes of operation:

Ad-Hoc Mode (Props Only)

In ad-hoc mode, you provide individual props (label, latch, etc.) and the component creates the parameter model internally:

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function AdHocButtonExample() {
  const [isOn, setIsOn] = useState(false);
 
  return (
    <Button
      label="Power"
      latch={true}
      value={isOn}
      onChange={(e) => setIsOn(e.value)}
      size="large"
    />
  );
}

Strict Mode (Parameter Only)

In strict mode, you provide a full parameter model via the parameter prop. The following example uses ad-hoc props; with the full library you would use createBooleanParameter and the parameter prop:

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function StrictModeButtonExample() {
  const [isOn, setIsOn] = useState(false);
 
  return (
    <Button
      label="Power"
      latch={true}
      value={isOn}
      onChange={(e) => setIsOn(e.value)}
      size="large"
    />
  );
}

Theming

Vector components share common theming features; see Theming Features in the Vector introduction for an overview. The Button component supports theming through the color and roundness props:

Color Customization

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function ButtonColor() {
  const [v1, setV1] = useState(false);
  const [v2, setV2] = useState(false);
 
  return (
    <div className="flex gap-8">
      <Button
        label="Power"
        latch={true}
        value={v1}
        onChange={(e) => setV1(e.value)}
        color="#3b82f6"
        size="large"
      />
      <Button
        label="Record"
        latch={false}
        value={v2}
        onChange={(e) => setV2(e.value)}
        color="#ef4444"
        size="large"
      />
    </div>
  );
}
 

Roundness

Control the roundness of the button corners:

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function ButtonRoundness() {
  const [v1, setV1] = useState(false);
  const [v2, setV2] = useState(false);
  const [v3, setV3] = useState(false);
 
  return (
    <div className="flex gap-8">
      <Button
        label="Square"
        latch={true}
        value={v1}
        onChange={(e) => setV1(e.value)}
        roundness={0.0}
        size="large"
      />
      <Button
        label="Rounded"
        latch={true}
        value={v2}
        onChange={(e) => setV2(e.value)}
        roundness={0.5}
        size="large"
      />
      <Button
        label="Fully Rounded"
        latch={true}
        value={v3}
        onChange={(e) => setV3(e.value)}
        roundness={1.0}
        size="large"
      />
    </div>
  );
}
 

Adaptive Sizing

The Button component supports both fixed sizes and adaptive sizing. By default, the size of the component is driven by the Size System and their size attribute:

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function ButtonSizing() {
  const [v1, setV1] = useState(false);
  const [v2, setV2] = useState(false);
  const [v3, setV3] = useState(false);
  const [v4, setV4] = useState(false);
 
  return (
    <div className="flex gap-4 items-center flex-wrap">
      <Button
        label="Small"
        latch={true}
        value={v1}
        onChange={(e) => setV1(e.value)}
        size="small"
      />
      <Button
        label="Normal"
        latch={true}
        value={v2}
        onChange={(e) => setV2(e.value)}
        size="normal"
      />
      <Button
        label="Large"
        latch={true}
        value={v3}
        onChange={(e) => setV3(e.value)}
        size="large"
      />
      <Button
        label="XLarge"
        latch={true}
        value={v4}
        onChange={(e) => setV4(e.value)}
        size="xlarge"
      />
    </div>
  );
}
 

Adaptive sizing allows the component to adapt to the size of its parent container, adjusting its dimensions intelligently without causing distortion (scaleToFit display mode). The fill display mode makes the component fill the entire container, potentially distorting it.

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function ButtonAdaptiveSize() {
  const [v1, setV1] = useState(false);
  const [v2, setV2] = useState(false);
  const [v3, setV3] = useState(false);
 
  return (
    <div className="flex gap-6 items-center flex-wrap">
      <div className="w-32 h-8 border rounded-lg p-2">
        <Button
          label="Wide"
          latch={true}
          value={v1}
          onChange={(e) => setV1(e.value)}
          adaptiveSize={true}
        />
      </div>
      <div className="w-12 h-32 border rounded-lg p-2">
        <Button
          label="Tall"
          latch={true}
          value={v2}
          onChange={(e) => setV2(e.value)}
          adaptiveSize={true}
        />
      </div>
      <div className="w-24 h-16 border rounded-lg p-2">
        <Button
          label="Distorted"
          latch={true}
          value={v3}
          onChange={(e) => setV3(e.value)}
          adaptiveSize={true}
          displayMode="fill"
        />
      </div>
    </div>
  );
}
 

Label Configuration

Control how the label is displayed:

Label Position

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function LabelPositionExample() {
  const [aboveOn, setAboveOn] = useState(false);
  const [belowOn, setBelowOn] = useState(false);
 
  return (
    <div className="flex gap-4 items-center flex-wrap">
      <Button
        label="Above"
        latch={true}
        value={aboveOn}
        onChange={(e) => setAboveOn(e.value)}
        labelPosition="above"
        size="large"
      />
      <Button
        label="Below"
        latch={true}
        value={belowOn}
        onChange={(e) => setBelowOn(e.value)}
        labelPosition="below"
        size="large"
      />
    </div>
  );
}

Label Alignment

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function LabelAlignmentExample() {
  const [startOn, setStartOn] = useState(false);
  const [centerOn, setCenterOn] = useState(false);
  const [endOn, setEndOn] = useState(false);
 
  return (
    <div className="flex gap-4 items-center flex-wrap">
      <Button
        label="Start"
        latch={true}
        value={startOn}
        onChange={(e) => setStartOn(e.value)}
        labelAlign="start"
        size="large"
      />
      <Button
        label="Center"
        latch={true}
        value={centerOn}
        onChange={(e) => setCenterOn(e.value)}
        labelAlign="center"
        size="large"
      />
      <Button
        label="End"
        latch={true}
        value={endOn}
        onChange={(e) => setEndOn(e.value)}
        labelAlign="end"
        size="large"
      />
    </div>
  );
}

Label Visibility

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function LabelVisibilityExample() {
  const [visibleOn, setVisibleOn] = useState(false);
  const [hiddenOn, setHiddenOn] = useState(false);
  const [noneOn, setNoneOn] = useState(false);
 
  return (
    <div className="flex gap-4 items-center flex-wrap">
      <Button
        label="Visible"
        latch={true}
        value={visibleOn}
        onChange={(e) => setVisibleOn(e.value)}
        labelMode="visible"
        size="large"
      />
      <Button
        label="Hidden"
        latch={true}
        value={hiddenOn}
        onChange={(e) => setHiddenOn(e.value)}
        labelMode="hidden"
        size="large"
      />
      <Button
        label="None"
        latch={true}
        value={noneOn}
        onChange={(e) => setNoneOn(e.value)}
        labelMode="none"
        size="large"
      />
    </div>
  );
}

Event Handling

The Button component provides standard mouse event handlers:

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function ButtonEventsExample() {
  const [isOn, setIsOn] = useState(false);
 
  return (
    <Button
      label="Interactive"
      latch={true}
      value={isOn}
      onChange={(e) => setIsOn(e.value)}
      size="large"
      onClick={() => console.log("Clicked")}
      onMouseDown={() => console.log("Mouse down")}
      onMouseUp={() => console.log("Mouse up")}
      onMouseEnter={() => console.log("Mouse enter")}
      onMouseLeave={() => console.log("Mouse leave")}
    />
  );
}

Global Mouse Event Handling

The Button component handles global mouse events for momentary buttons to ensure reliable release behavior. This means that even if you drag the mouse outside the button before releasing, the button will still properly detect the release event.

This is particularly important for momentary buttons used in audio applications where precise timing is critical.

Common Patterns

Power Button

A typical power toggle button:

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function PowerButtonExample() {
  const [isPowered, setIsPowered] = useState(false);
 
  return (
    <Button
      label="Power"
      latch={true}
      value={isPowered}
      onChange={(e) => setIsPowered(e.value)}
      color={isPowered ? "#10b981" : "#6b7280"}
      size="large"
    />
  );
}

Record Button

A momentary record button:

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function RecordButtonExample() {
  const [isRecording, setIsRecording] = useState(false);
 
  return (
    <Button
      label="Record"
      latch={false}
      value={isRecording}
      onChange={(e) => setIsRecording(e.value)}
      color={isRecording ? "#ef4444" : "#6b7280"}
      size="large"
    />
  );
}

Button Group

Multiple buttons in a group:

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function ButtonGroupExample() {
  const [power, setPower] = useState(false);
  const [record, setRecord] = useState(false);
  const [play, setPlay] = useState(false);
 
  return (
    <div className="flex gap-4 items-center flex-wrap">
      <Button
        label="Power"
        latch={true}
        value={power}
        onChange={(e) => setPower(e.value)}
        size="large"
      />
      <Button
        label="Record"
        latch={false}
        value={record}
        onChange={(e) => setRecord(e.value)}
        size="large"
      />
      <Button
        label="Play"
        latch={true}
        value={play}
        onChange={(e) => setPlay(e.value)}
        size="large"
      />
    </div>
  );
}
 

Parameter Model

Use a parameter model for integration with audio parameter systems. The following example uses ad-hoc props; with the full library you would use createBooleanParameter and the parameter prop:

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function ButtonParameterModelExample() {
  const [value, setValue] = useState(false);
 
  return (
    <Button
      label="Power"
      latch={true}
      value={value}
      onChange={(e) => setValue(e.value)}
      size="large"
    />
  );
}

Common Use Cases

Power Switch

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function PowerSwitchExample() {
  const [isOn, setIsOn] = useState(false);
 
  return (
    <Button
      value={isOn}
      latch={true}
      label="Power"
      onChange={(e) => setIsOn(e.value)}
      size="large"
    />
  );
}

Record Button

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function RecordButtonUseCaseExample() {
  const [isRecording, setIsRecording] = useState(false);
 
  return (
    <Button
      value={isRecording}
      latch={false}
      label="Record"
      onChange={(e) => setIsRecording(e.value)}
      size="large"
    />
  );
}

Effect Bypass

import { useState } from "react";
import { Button } from "@cutoff/audio-ui-react";
 
export default function EffectBypassExample() {
  const [isBypassed, setIsBypassed] = useState(false);
 
  return (
    <Button
      value={isBypassed}
      latch={true}
      label="Bypass"
      onChange={(e) => setIsBypassed(e.value)}
      size="large"
    />
  );
}

Next Steps