Skip to content

Sticky Toggle Buttons

A sticky toggle button pops down (valueUp) or stays pressed down (valueDown) — pressing it while up latches it down, and pressing it again while down releases it back up. This is the "record button" or "power button" pattern: unlike Momentary Buttons, releasing the pointer does not undo the state change. scenerystack/sun exports RectangularStickyToggleButton<T> and RoundStickyToggleButton<T>, both built on the shared StickyToggleButtonModel<T>.

ts
import { RoundStickyToggleButton } from 'scenerystack/sun';
import { Path } from 'scenerystack/scenery';
import { BooleanProperty } from 'scenerystack/axon';
import { Shape } from 'scenerystack/kite';

const pressedProperty = new BooleanProperty( false );

const button = new RoundStickyToggleButton( pressedProperty, false, true, {
  content: new Path( Shape.circle( 10 ), { fill: '#C0392B' } ),
  baseColor: 'white'
} );

RectangularStickyToggleButton has the identical (valueProperty, valueUp, valueDown, options?) constructor — only the button shape differs.

StickyToggleButtonModel

Both classes construct a StickyToggleButtonModel<T> internally (extending the shared ButtonModel base documented on PushButtonModel), which owns the latch logic: pressing while valueProperty.value === valueUp immediately toggles it to valueDown; releasing while down and not part of the same press-release action toggles it back to valueUp. Property.valueComparisonStrategy must be 'reference' — the model asserts this at construction, since it compares against valueUp/valueDown with ===.

Options

Both classes accept the same appearance options as their non-sticky counterparts (RectangularButtonOptions / RoundButtonOptions) plus:

OptionEffect
soundPlayerSound played on every toggle, in either direction (default: shared 'pushButton' player)

Both classes default ariaRole to 'switch' and set both aria-pressed and aria-checked PDOM attributes to reflect valueProperty.value === valueDown — screen readers announce these as a switch rather than a momentary button.

Prefer OnOffSwitch for a plain boolean on/off control

If your state is just boolean and you want the classic iOS-style sliding switch rather than a button that visually "sticks down," see OnOffSwitch instead — it's a different visual metaphor for a similar two-state idea.

Releasing during the same press doesn't release the latch

StickyToggleButtonModel tracks whether the button was pressed while already down as part of the same continuous action — releasing right after the press that latched it down does not immediately un-latch it. You have to press and release again to pop it back up. This is what makes the interaction feel like a real latch rather than a simple boolean toggle-on-tap.