Constants File Pattern
A single, project-wide module — conventionally named MySimConstants.ts — holds every shared magic number, color, and font a simulation's view code depends on, exported as one plain object and imported by name everywhere that value is needed. Spacing and Sizing Constants already covers the layout-dimension slice of this convention in depth; this page is the general pattern it's one application of — the same "one file, referenced by name, never retyped" rule applies just as much to colors, fonts, and any other sim-wide constant, not spacing alone.
The pattern, generalized beyond spacing
// MySimConstants.ts
import { PhetFont } from 'scenerystack/scenery-phet';
const MySimConstants = {
// Spacing/sizing - see Spacing and Sizing Constants for this slice in depth
SCREEN_MARGIN: 15,
PANEL_CORNER_RADIUS: 10,
// Color
CONTROL_PANEL_FILL: '#f0f0f0',
ACCENT_COLOR: '#3182ce',
// Font
LABEL_FONT: new PhetFont( 16 ),
TITLE_FONT: new PhetFont( { size: 22, weight: 'bold' } )
};
export default MySimConstants;// Anywhere in view code - reference by name, never retype a literal color/font/number.
import MySimConstants from '../MySimConstants.js';
import { Panel } from 'scenerystack/sun';
import { Text } from 'scenerystack/scenery';
const panel = new Panel( content, {
cornerRadius: MySimConstants.PANEL_CORNER_RADIUS,
fill: MySimConstants.CONTROL_PANEL_FILL
} );
const title = new Text( 'My Sim', { font: MySimConstants.TITLE_FONT } );The value of centralizing this goes up, not down, once colors and fonts are included alongside spacing: a color used in five places that's supposed to be "the sim's one accent color" is exactly the kind of value that silently drifts (one call site gets nudged during a design review, the other four don't) if it's retyped as a literal at each site, the same drift Spacing and Sizing Constants describes for margins.
Precedent: this is how SceneryStack's own libraries are structured
This isn't a convention invented for individual sim repos in isolation — it mirrors how scenery-phet and sun structure their own shared, cross-component constants: a SceneryPhetConstants-style module holds values common-code components across scenery-phet share (for instance, the default button radius ResetAllButton falls back to, referenced as SceneryPhetConstants.DEFAULT_BUTTON_RADIUS in that button's own option defaults — see ResetAllButton), and a parallel SunConstants-style module plays the same role for sun (e.g. the default placeholder token NumberDisplay substitutes a value into, referenced as SunConstants.VALUE_NAMED_PLACEHOLDER — see NumberDisplay). A project's own MySimConstants.ts is the same idea applied one level down: instead of a library centralizing values shared across its own components, a sim centralizes values shared across its own screens.
What belongs in the constants file
The same test Spacing and Sizing Constants applies generalized beyond dimensions: a value belongs in the constants file if a design review would refer to it as "the project's standard X" — its accent color, its label font, its panel margin — regardless of whether X is a number, a color string, or a Font instance. A value that's genuinely local to one Node (the exact shade of one decorative flourish, a one-off font size nothing else shares) stays where it's used; promoting every literal in a codebase into the constants file regardless of whether it's actually shared just relocates the clutter instead of removing it.
One file per project, organized by category - not one file per screen
As Multi-Screen Sim Structure establishes for shared code generally, a constants module belongs in common/, imported by every screen that needs it — never duplicated per-screen, and never one screen's constants file imported by a second screen out of convenience. Structure the file's contents by kind (spacing, color, font) rather than by which screen first needed a given value, the same guidance Spacing and Sizing Constants gives for the spacing-only case.