Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Reusable single-screen SceneryStack template. Run `npm run rename` to fork it to
| `src/sim-screen/view/SimScreenSummaryContent.ts` | Accessible screen summary (reference a11y pattern) |
| `src/sim-screen/view/SimKeyboardHelpContent.ts` | Keyboard-help dialog content |
| `src/common/SimPanel.ts` | Pre-themed `Panel` wrapper (uses `SimColors` automatically) |
| `src/common/SimButtonOptions.ts` | Flat button-appearance option bundles + light-control-surface combo-box options |
| `src/common/TimeModel.ts` | Composable play/pause + elapsed-time model for animated sims |
| `scripts/generate-icons.ts` | PNG icons from `public/icons/icon.svg` |
| `scripts/rename-sim.ts` | Automated fork/rename across all files and folders |
Expand Down Expand Up @@ -58,6 +59,30 @@ export class FrictionModel implements TModel {
Wire the view to `TimeControlNode` from `scenerystack/scenery-phet` binding on
`model.timer.isPlayingProperty`.

### SimButtonOptions

SceneryStack's push/round buttons default to a 3-D/beveled look; every button in the sim
should be flat instead. Spread these into the relevant options object:

```typescript
import { FLAT_RESET_ALL_BUTTON_OPTIONS, FLAT_RECTANGULAR_BUTTON_OPTIONS } from "../../common/SimButtonOptions.js";

const resetAllButton = new ResetAllButton({ ...FLAT_RESET_ALL_BUTTON_OPTIONS, listener: () => {...} });
const exampleButton = new RectangularPushButton({ ...FLAT_RECTANGULAR_BUTTON_OPTIONS, content, listener });
```

`FLAT_PLAY_PAUSE_STEP_BUTTON_OPTIONS` spreads into `TimeControlNode`'s `playPauseStepButtonOptions`;
`TIME_CONTROL_SPEED_RADIO_OPTIONS` fixes `TimeControlNode`'s speed-radio label color, which
otherwise defaults to black text on the sim's dark default-mode panels. `SIM_COMBO_BOX_OPTIONS`
themes a `ComboBox`'s button/list chrome to the light control surface below; pair item labels
with `LIGHT_SURFACE_TEXT_FILL` (not `SimColors.textColorProperty`, which is for panel-fill text).

`SimColors.ts` backs this with a "light control surfaces" section —
`controlSurfaceColorProperty`, `controlSurfaceDisabledColorProperty`,
`controlSurfaceTextColorProperty` — identical white/dark-text values in both default and
projector profiles, so any component that must stay light regardless of theme (combo boxes,
flat buttons, editable fields) keeps readable contrast automatically.

## Accessibility

This template is the **canonical accessibility reference** for OpenPhysics sims. It ships with
Expand Down
23 changes: 23 additions & 0 deletions src/SimColors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,29 @@ const SimColors = {
default: "#e0e0e0",
projector: "#1a1a1a",
}),

// ── Light control surfaces ───────────────────────────────────────────────────
// White chrome (combo boxes, flat push buttons, editable input fields) stays light
// in both profiles; its text stays dark. Same values in default and projector mode,
// but defined here so every color lives in one themeable place.

/** Fill of light control surfaces: combo-box button/list, editable input fields. */
controlSurfaceColorProperty: new ProfileColorProperty(SimNamespace, "controlSurface", {
default: "#ffffff",
projector: "#ffffff",
}),

/** Fill of a disabled control surface (grayed-out editable input field). */
controlSurfaceDisabledColorProperty: new ProfileColorProperty(SimNamespace, "controlSurfaceDisabled", {
default: "#cccccc",
projector: "#cccccc",
}),

/** Text on light control surfaces: combo items, flat-button labels, field values, preferences. */
controlSurfaceTextColorProperty: new ProfileColorProperty(SimNamespace, "controlSurfaceText", {
default: "#1a1a1a",
projector: "#1a1a1a",
}),
};

export default SimColors;
52 changes: 52 additions & 0 deletions src/common/SimButtonOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* SimButtonOptions.ts
*
* Shared flat button appearance for the sim. Rectangular and round push buttons
* default to SceneryStack's 3-D appearance; pass these options (or spread them
* into nested button options) for a flat look everywhere.
*/

import type { PlayPauseStepButtonGroupOptions, TimeControlNodeOptions } from "scenerystack/scenery-phet";
import { ButtonNode, type ComboBoxOptions } from "scenerystack/sun";
import SimColors from "../SimColors.js";

export const FLAT_BUTTON_APPEARANCE_OPTIONS = {
buttonAppearanceStrategy: ButtonNode.FlatAppearanceStrategy,
} as const;

/** Text on flat push buttons and combo-box items (always on a light control surface). */
export const LIGHT_SURFACE_TEXT_FILL = SimColors.controlSurfaceTextColorProperty;

/**
* Combo-box chrome for panels. Item labels must use {@link LIGHT_SURFACE_TEXT_FILL}, not
* {@link SimColors.textColorProperty} — that color is for labels on the dark panel fill.
*/
export const SIM_COMBO_BOX_OPTIONS = {
buttonFill: SimColors.controlSurfaceColorProperty,
listFill: SimColors.controlSurfaceColorProperty,
buttonStroke: SimColors.panelBorderColorProperty,
listStroke: SimColors.panelBorderColorProperty,
} satisfies Pick<ComboBoxOptions, "buttonFill" | "listFill" | "buttonStroke" | "listStroke">;

/** Options for RectangularPushButton and NumberControl arrow buttons. */
export const FLAT_RECTANGULAR_BUTTON_OPTIONS = FLAT_BUTTON_APPEARANCE_OPTIONS;

/** Options for ResetAllButton (extends RoundPushButton). */
export const FLAT_RESET_ALL_BUTTON_OPTIONS = FLAT_BUTTON_APPEARANCE_OPTIONS;

/** Nested options for TimeControlNode play / pause / step round buttons. */
export const FLAT_PLAY_PAUSE_STEP_BUTTON_OPTIONS = {
playPauseButtonOptions: FLAT_BUTTON_APPEARANCE_OPTIONS,
stepForwardButtonOptions: FLAT_BUTTON_APPEARANCE_OPTIONS,
stepBackwardButtonOptions: FLAT_BUTTON_APPEARANCE_OPTIONS,
} satisfies PlayPauseStepButtonGroupOptions;

/**
* Speed radio labels for TimeControlNode. SceneryStack Text defaults to black, which
* is low-contrast on the sim's dark Default-mode panels.
*/
export const TIME_CONTROL_SPEED_RADIO_OPTIONS = {
speedRadioButtonGroupOptions: {
labelOptions: { fill: SimColors.textColorProperty },
},
} satisfies Pick<TimeControlNodeOptions, "speedRadioButtonGroupOptions">;
3 changes: 3 additions & 0 deletions src/sim-screen/view/SimScreenView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { Node, Rectangle, Text } from "scenerystack/scenery";
import { ResetAllButton } from "scenerystack/scenery-phet";
import type { ScreenViewOptions } from "scenerystack/sim";
import { ScreenView } from "scenerystack/sim";
import { FLAT_RESET_ALL_BUTTON_OPTIONS } from "../../common/SimButtonOptions.js";
import SimColors from "../../SimColors.js";
import { SCREEN_VIEW_MARGIN } from "../../SimConstants.js";
import type { SimModel } from "../model/SimModel.js";
Expand Down Expand Up @@ -67,6 +68,7 @@ export class SimScreenView extends ScreenView {
//
// const a11y = StringManager.getInstance().getA11yStrings();
// const exampleButton = new RectangularPushButton({
// ...FLAT_RECTANGULAR_BUTTON_OPTIONS, // flat appearance, not SceneryStack's default 3-D look
// content: someIcon,
// listener: () => model.doSomething(),
// accessibleName: a11y.controls.exampleControlStringProperty,
Expand All @@ -76,6 +78,7 @@ export class SimScreenView extends ScreenView {
// ── Reset All button ──────────────────────────────────────────────────────
// Always position at bottom-right (PhET convention).
const resetAllButton = new ResetAllButton({
...FLAT_RESET_ALL_BUTTON_OPTIONS,
listener: () => {
model.reset();
this.reset();
Expand Down
Loading