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
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
value | boolean | — | Required | Current value of the button (true = pressed/active, false = released/inactive) |
onChange | (event: AudioControlEvent<boolean>) => void | — | Optional | Handler for value changes |
latch | boolean | false | Optional | Whether the button should latch (toggle between states) or momentary (only active while pressed). Ad-hoc mode only, ignored if parameter provided. |
label | string | — | Optional | Label displayed below the component |
color | string | — | Optional | Component primary color - any valid CSS color value |
roundness | number | — | Optional | Roundness for component corners (normalized 0.0-1.0) |
adaptiveSize | boolean | false | Optional | Whether the component stretches to fill its container |
size | "xsmall" | "small" | "normal" | "large" | "xlarge" | normal | Optional | Size of the component |
displayMode | "scaleToFit" | "fill" | scaleToFit | Optional | Layout mode for the control |
labelMode | "none" | "hidden" | "visible" | visible | Optional | Visibility of the label row |
labelPosition | "above" | "below" | below | Optional | Vertical position of the label relative to the control |
labelAlign | "start" | "center" | "end" | center | Optional | Horizontal alignment of the label text |
labelOverflow | "ellipsis" | "abbreviate" | "auto" | auto | Optional | How to handle label text overflow |
labelHeightUnits | number | — | Optional | Label height in the same units as SVG viewBox height |
parameter | BooleanParameter | — | Optional | Audio Parameter definition (Model). If provided, overrides label/latch from ad-hoc props |
paramId | string | — | Optional | Identifier for the parameter this control represents |
midiResolution | MidiResolution | 7 | Optional | MIDI resolution in bits (ad-hoc mode, ignored if parameter provided) |
onClick | React.MouseEventHandler | — | Optional | Click event handler |
onMouseDown | React.MouseEventHandler | — | Optional | Mouse down event handler |
onMouseUp | React.MouseEventHandler | — | Optional | Mouse up event handler |
onMouseEnter | React.MouseEventHandler | — | Optional | Mouse enter event handler |
onMouseLeave | React.MouseEventHandler | — | Optional | Mouse leave event handler |
className | string | — | Optional | Additional CSS classes |
style | React.CSSProperties | — | Optional | Additional 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
- Explore the CycleButton component for discrete option selection
- Learn about Knob and Slider for continuous controls
- Check out FilmStripBooleanControl or ImageSwitch for bitmap-based buttons
- Visit the playground for interactive examples