UI Customization

Add custom UI elements to the game

The UI API lets you add buttons, panels, notifications, and custom React components to the game interface.

Notifications#

showNotification#

Display a toast notification.

javascript
window.SubwayBuilderAPI.ui.showNotification('Mod loaded successfully!', 'success');
window.SubwayBuilderAPI.ui.showNotification('Something went wrong', 'error');
window.SubwayBuilderAPI.ui.showNotification('Check this out', 'info');
window.SubwayBuilderAPI.ui.showNotification('Be careful!', 'warning');

UI Primitives#

Add simple UI elements without needing React knowledge.

addButton#

Add a button to a UI placement.

javascript
api.ui.addButton('settings-menu', {
    id: 'my-button',
    label: 'Click Me',
    icon: 'Zap', // Lucide icon name (optional)
    onClick: () => console.log('Button clicked!')
});

addToggle#

Add a toggle switch.

javascript
api.ui.addToggle('settings-menu', {
    id: 'my-toggle',
    label: 'Enable Feature',
    defaultValue: false,
    onChange: (enabled) => console.log('Toggle:', enabled)
});

addSlider#

Add a range slider.

javascript
api.ui.addSlider('settings-menu', {
    id: 'speed-slider',
    label: 'Speed',
    min: 1,
    max: 10,
    step: 1,
    defaultValue: 5,
    onChange: (value) => console.log('Speed:', value)
});

addSelect#

Add a dropdown select.

javascript
api.ui.addSelect('settings-menu', {
    id: 'mode-select',
    label: 'Mode',
    options: [
        { value: 'easy', label: 'Easy' },
        { value: 'normal', label: 'Normal' },
        { value: 'hard', label: 'Hard' }
    ],
    defaultValue: 'normal',
    onChange: (value) => console.log('Mode:', value)
});

addText#

Add static text/label.

javascript
api.ui.addText('settings-menu', {
    id: 'info-text',
    text: 'My Mod v1.0.0',
    className: 'text-sm text-muted-foreground' // optional
});

addSeparator#

Add a visual separator line.

javascript
api.ui.addSeparator('settings-menu', { id: 'my-separator' });

Toolbar Buttons#

Add buttons to the top toolbar.

addToolbarButton#

javascript
api.ui.addToolbarButton({
    id: 'my-button',
    icon: 'Zap', // Lucide icon name
    tooltip: 'Do Thing',
    onClick: () => console.log('clicked!'),
    isActive: () => false // Optional: return true to show "active" state
});

addToolbarPanel#

Add a toolbar button that opens a floating panel.

javascript
api.ui.addToolbarPanel({
    id: 'my-dashboard',
    icon: 'BarChart3',
    tooltip: 'My Dashboard',
    title: 'Dashboard',
    width: 400,
    render: () => {
        var h = api.utils.React.createElement;
        return h('div', null, 'Hello from my panel!');
    }
});

The panel automatically includes:

  • Styled button matching native UI
  • Click-outside-to-close
  • Close button (X)
  • Header with title
  • Proper z-index and backdrop

Add buttons to the main menu with the game's signature style.

javascript
api.ui.addMainMenuButton({
    id: 'my-mod-button',
    text: 'My Mod',
    onClick: () => console.log('Clicked!'),
    description: 'Optional description text below button',
    arrowBearing: 45 // optional: 0, 45, 90, 135, 180, 225, 270, 315
});

Styled Components#

Use the game's actual UI components for a polished look.

addStyledButton#

javascript
api.ui.addStyledButton('settings-menu', {
    id: 'my-styled-button',
    label: 'Save Changes',
    icon: 'Save',
    variant: 'default', // 'default', 'destructive', 'outline', 'secondary', 'ghost', 'link'
    size: 'default', // 'default', 'sm', 'lg', 'icon'
    onClick: () => console.log('Saved!')
});

addStyledToggle#

javascript
api.ui.addStyledToggle('settings-menu', {
    id: 'my-styled-toggle',
    label: 'Enable Feature',
    defaultValue: false,
    onChange: (enabled) => console.log('Toggled:', enabled)
});

addStyledSlider#

javascript
api.ui.addStyledSlider('settings-menu', {
    id: 'my-styled-slider',
    label: 'Speed Multiplier',
    min: 0.5,
    max: 3.0,
    step: 0.1,
    defaultValue: 1.0,
    showValue: true,
    unit: 'x',
    onChange: (value) => console.log('Speed:', value)
});

Floating Panels#

Create resizable, draggable floating panels that dock to the side of the screen.

javascript
const { React, components } = window.SubwayBuilderAPI.utils;
const h = React.createElement;

window.SubwayBuilderAPI.ui.addFloatingPanel({
    id: 'my-analytics',
    icon: 'BarChart3', // Curated Lucide icon name
    tooltip: 'My Analytics',
    title: 'Analytics Dashboard',
    defaultWidth: 300,
    defaultHeight: 400,
    minWidth: 200,
    minHeight: 200,
    render: ({ width, height }) =>
        h('div', { style: { padding: 8 } },
            h('p', null, `Panel is ${width}x${height}`)
        )
});
PropertyTypeDefaultDescription
idstringrequiredUnique panel identifier
iconstringrequiredCurated Lucide icon name for the dock button
tooltipstringoptionalTooltip on hover
titlestringoptionalPanel title bar text
defaultWidthnumber300Initial width in pixels
defaultHeightnumber400Initial height in pixels
minWidthnumber200Minimum resize width
minHeightnumber200Minimum resize height
renderComponentrequiredReact component receiving { width, height } props

Custom React Components#

For complex UIs, register custom React components.

javascript
const { React, components } = window.SubwayBuilderAPI.utils;
const h = React.createElement;
const { Card, CardHeader, CardTitle, CardContent, Button, Switch, Label } = components;

window.SubwayBuilderAPI.ui.registerComponent('settings-menu', {
    id: 'my-custom-panel',
    component: () =>
        h(Card, { className: 'p-4' }, [
            h(CardHeader, { key: 'header' },
                h(CardTitle, null, 'My Panel')
            ),
            h(CardContent, { key: 'content' }, [
                h(Button, {
                    key: 'btn',
                    onClick: () => alert('Hi!')
                }, 'Click Me'),
                h('div', {
                    key: 'toggle',
                    className: 'flex items-center gap-2 mt-2'
                }, [
                    h(Label, { key: 'label' }, 'Enable'),
                    h(Switch, { key: 'switch' })
                ])
            ])
        ])
});

Available Components#

Components available in api.utils.components:

  • Button - Styled button with variants
  • Card, CardHeader, CardTitle, CardContent, CardDescription - Card layout
  • Switch - Toggle switch
  • Slider - Range slider
  • Label - Form label
  • Input - Text input
  • Badge - Status badge
  • Progress - Progress bar
  • Tooltip, TooltipTrigger, TooltipContent, TooltipProvider - Tooltips
  • SubwayButton - The main menu-style arrow button
  • MainMenuButton - Pre-configured SubwayButton for main menu

UI Placements#

Available placements for UI elements:

PlacementDescription
settings-menuMod settings in Settings panel
escape-menuCustom menu items in escape menu
escape-menu-buttonsButtons in the escape/pause menu
main-menuItems in the main menu
bottom-barBottom UI bar (next to clock, money)
top-barTop UI bar (right side toolbar)
debug-panelDebug info panel (when debug mode enabled)

Theme Control#

Control the app's light/dark theme.

javascript
// Set theme
window.SubwayBuilderAPI.ui.setTheme('dark');
window.SubwayBuilderAPI.ui.setTheme('light');
window.SubwayBuilderAPI.ui.setTheme('system'); // Follow OS preference

// Get current theme setting
const themeSetting = window.SubwayBuilderAPI.ui.getTheme();

// Get actual resolved theme (useful when set to 'system')
const actualTheme = window.SubwayBuilderAPI.ui.getResolvedTheme();

Color Customization#

Customize accent and primary colors.

javascript
// Set accent color
window.SubwayBuilderAPI.ui.setAccentColor('#8b5cf6'); // Purple

// Set primary color
window.SubwayBuilderAPI.ui.setPrimaryColor('#0ea5e9'); // Sky blue

// Set any CSS variable
window.SubwayBuilderAPI.ui.setCSSVariable('--radius', '0.75rem');

// Reset all colors to defaults
window.SubwayBuilderAPI.ui.resetColors();

Data Visualization with Charts#

Build custom dashboards using the built-in Recharts library:

javascript
const { React, charts, components } = window.SubwayBuilderAPI.utils;
const { ResponsiveContainer, PieChart, Pie, Cell, BarChart, Bar, XAxis, YAxis, Tooltip } = charts;
const { Card, CardContent, CardHeader, CardTitle } = components;
const h = React.createElement;

// Get mode choice data
const modes = window.SubwayBuilderAPI.gameState.getModeChoiceStats();
const pieData = [
    { name: 'Transit', value: modes.transit, color: '#22c55e' },
    { name: 'Driving', value: modes.driving, color: '#ef4444' },
    { name: 'Walking', value: modes.walking, color: '#3b82f6' }
];

// Create a pie chart component
const ModeShareChart = () =>
    h(Card, null, [
        h(CardHeader, null, h(CardTitle, null, 'Mode Share')),
        h(CardContent, null,
            h(ResponsiveContainer, { width: '100%', height: 200 },
                h(PieChart, null,
                    h(Pie, {
                        data: pieData,
                        dataKey: 'value',
                        nameKey: 'name',
                        cx: '50%',
                        cy: '50%',
                        outerRadius: 60
                    },
                    pieData.map((entry, i) =>
                        h(Cell, { key: i, fill: entry.color })
                    ))
                )
            )
        )
    ]);

// Register it in the UI
window.SubwayBuilderAPI.ui.registerComponent('settings-menu', {
    id: 'my-mode-chart',
    component: ModeShareChart
});

Available Chart Types#

Access all Recharts components via api.utils.charts:

  • LineChart - Time series and trends
  • BarChart - Comparisons
  • AreaChart - Cumulative data
  • PieChart - Proportions
  • RadarChart - Multi-dimensional data
  • ComposedChart - Mixed chart types

Plus all standard Recharts components: XAxis, YAxis, Tooltip, Legend, ResponsiveContainer, Cell, etc.

See the Recharts documentation for full API details.

Available Icons#

api.utils.icons exposes a curated Lucide set (PascalCase names). For the full guaranteed list, see the UI Components guide.

Access them via:

javascript
const { Train, Settings, MapPin } = api.utils.icons;