Glow Button

glow-buttonControls

One component covers large performance controls and compact vintage console buttons. Round and pill caps use an LED bezel with a layered halo; console-round and console-square use a machined bezel, recessed face and restrained indicator bloom. Every style can be momentary or latching with real button semantics underneath. The glow prop can still be fed from an audio level, and onInteraction fires synchronously inside the first gesture, which makes it the natural place to resume a suspended AudioContext.

Hold the first, latch the second, or use the smaller console faces for vintage hardware panels. Space and Enter work too.

Install

Copies the source into your project. Anything it depends on comes with it.

$ npx shadcn add @syntho-ui/glow-button

Pulls in

control-themeuse-first-interaction

First time? Add the registry to your components.json once:

components.json
{
  "registries": {
    "@syntho-ui": "https://engine.syntho.app/r/{name}.json"
  }
}

Usage

Controlled or uncontrolled, whichever suits the surrounding state.

example.tsx
import { GlowButton } from "@/components/ui/glow-button"

// The audio-start button: resume the context synchronously inside the
// first gesture — that is the only place browsers allow it.
<GlowButton
  mode="toggle"
  size={160}
  onInteraction={() => audioContext.resume()}
  onPressedChange={(on) => (on ? transport.start() : transport.stop())}
>
  Play
</GlowButton>

// Side-chain the light to the music: feed the halo an audio level.
const [level, setLevel] = useState(0) // 0..1, from an AnalyserNode loop

<GlowButton mode="toggle" glow={playing ? level : undefined} pulse={{ bpm: 124 }}>
  Drop
</GlowButton>

Props

PropTypeDefaultDescription
mode"momentary" | "toggle""momentary"Lit while held and firing onPress/onRelease, or latching with aria-pressed.
pressedbooleanControlled latched state (toggle mode). Omit for uncontrolled operation.
defaultPressedbooleanfalseInitial latched state when uncontrolled (toggle mode).
onPressedChange(pressed: boolean) => voidFires on every latch change (toggle mode), controlled or not.
onPress / onRelease(event) => voidMomentary mode: the push landed / ended — including a pointer dragged off the cap.
onInteraction(event) => voidFires once, on the first gesture of any kind, synchronously inside the event — the place to resume a suspended AudioContext.
glownumberExternal halo intensity, 0..1. Takes over the bloom completely — pass an audio level every frame and the light follows the music. Overrides pulse and the lit/idle logic; clamped, so a hot level cannot blow the layers up.
pulse{ bpm: number }Make the idle halo hit on the beat. rAF-driven with no re-renders, off by default, and skipped under prefers-reduced-motion.
childrenReactNodeText or glyph centred on the cap. Doubles as the accessible name.
variant"round" | "pill" | "console-round" | "console-square""round"Large round or pill performance controls, plus compact round or square machined-console faces.
sizenumber140 / 52Cap diameter (round/square) or height (pill), in pixels. Console variants default to 52; performance variants default to 140.
colorstringHalo and LED colour. Defaults to the --uikit-accent token.
disabledbooleanfalseBlock interaction, kill the glow and dim the control.

Interaction

  • Drag with pointer capture, so the gesture keeps tracking past the edge of the control.
  • Shift while dragging for a fine pass; release it mid-gesture and coarse tracking resumes.
  • Double-click resets to defaultValue.
  • Arrow keys step normally, Shift+arrow steps finely, PageUp/PageDown jump by ten, Home/End go to the extremes.
  • Proper slider and switch roles with live aria-valuetext, so a screen reader announces the formatted value rather than the raw number.
  • The scroll wheel adjusts the value while the pointer is over the control; stepped ranges move at least one declared step per notch by default.