Skip to content

RectangularButton

RectangularButton (from scenerystack/sun) is the visual base class every rectangular button in sun is built from — RectangularPushButton, RectangularToggleButton/BooleanRectangularToggleButton, RectangularStickyToggleButton, RectangularMomentaryButton, RectangularRadioButton, ArrowButton, and CarouselButton all extend it. It in turn extends ButtonNode, the shared base of both rectangular and round buttons. You won't construct either class directly in normal sim code — they're both protected constructors — but understanding the split matters when you need a fully custom button that none of the concrete subclasses quite covers, and when you're deciphering an options type like RectangularPushButtonOptions that's actually assembled from several layers.

The layering

ClassOwnsNotes
ButtonModelNon-visual interaction state (overProperty, downProperty, enabledProperty, looksPressedProperty)See PushButtonModel for the concrete subclasses
ButtonNodeContent layout, baseColor, buttonAppearanceStrategy, contentAppearanceStrategy, PDOM/Voicing wiringShape-agnostic; takes a pre-built buttonBackground Path
RectangularButtonThe rectangular buttonBackground shape itself, corner radii, size/minWidth/minHeight, pointer-area dilationThis page
RectangularPushButton, etc.Wires a specific ButtonModel subclass to RectangularButtonThe classes you actually construct

Every concrete rectangular button constructs its own ButtonModel subclass and a TReadOnlyProperty<ButtonInteractionState>, then passes both to super() — that's the seam RectangularButton's protected constructor exposes to its subclasses:

ts
import { RectangularButton, ButtonModel, ButtonInteractionState } from 'scenerystack/sun';
import type { TReadOnlyProperty } from 'scenerystack/axon';
import { Tandem } from 'scenerystack/tandem';

// Sketch of the seam RectangularPushButton, RectangularToggleButton, etc. all use internally —
// write a subclass like this only when none of the built-in ones fit.
class MyCustomRectangularButton extends RectangularButton {
  public constructor( buttonModel: ButtonModel, interactionStateProperty: TReadOnlyProperty<ButtonInteractionState> ) {
    super( buttonModel, interactionStateProperty, {
      baseColor: 'orange',
      cornerRadius: 8,
      tandem: Tandem.REQUIRED
    } );
  }
}

In practice, reach for PushButtonModel (or ToggleButtonModel, StickyToggleButtonModel, MomentaryButtonModel) plus a matching *InteractionStateProperty only when building a genuinely new interaction pattern — for anything that fires once, toggles, latches, or is momentary, the ready-made subclasses in RectangularPushButton, Toggle Buttons, Sticky Toggle Buttons, and Momentary Buttons already cover it.

Options

RectangularButtonOptions (shape) combines with ButtonNodeOptions (appearance/content), and this combination is what every concrete RectangularButtonOptions-based type (RectangularPushButtonOptions, RectangularToggleButtonOptions, …) is built from:

OptionLayerEffect
contentButtonNodeThe Node shown on the button
xMargin / yMarginButtonNodeMargin between content and the button's edge (defaults 8/5, deliberately asymmetric)
baseColorButtonNodeBackground color other colors are derived from
buttonAppearanceStrategyButtonNodeRectangularButton.ThreeDAppearanceStrategy (default, gradient/shaded look) or ButtonNode.FlatAppearanceStrategy (flat, color-swap-only look used by e.g. CarouselButton)
sizeRectangularButtonFixed Dimension2; content is scaled to fit instead of sizing the button to content
cornerRadiusRectangularButtonApplied to all four corners (default 4)
leftTopCornerRadius / rightTopCornerRadius / leftBottomCornerRadius / rightBottomCornerRadiusRectangularButtonPer-corner overrides of cornerRadius — this is how CarouselButton gets square inner corners and rounded outer ones
stroke / lineWidthRectangularButtonWhen stroke is null (default), one is derived from baseColor
touchAreaXDilation / touchAreaYDilation / mouseAreaXDilation / mouseAreaYDilationRectangularButtonExpand the pointer-area rectangle beyond the visual bounds

buttonAppearanceStrategy decides the whole look

Swapping buttonAppearanceStrategy from the default ThreeDAppearanceStrategy to ButtonNode.FlatAppearanceStrategy changes far more than "flat vs. gradient" — it also changes which sub-options (overStroke, selectedLineWidth, deselectedFill, …) inside buttonAppearanceStrategyOptions actually apply, since each strategy defines its own set. CarouselButton uses the flat strategy so its arrow buttons read as part of the carousel's chrome rather than as a shaded, standalone button.