Skip to main content

Themes

ChartTheme is the base visual skin for the chart and standalone widgets. It is accepted at construction and at runtime.

TradeScript chart in the shipped dark theme
Dark preset: near-black chart and chrome with cool neutral text and high-contrast market colors.
The same TradeScript chart in the shipped light theme
Light preset: paper-white surfaces and slate text, with chart state unchanged. One setTheme('light') call separates the two states.

Choose a preset

If you need…Start with
Default trading terminaldark
Bright application surfacelight
Low-color institutional UImodern-monochrome or minimalist-neutrals
Brand-forward dark surfaceocean-depths, retro-sunset, vibrant-tropics, or electric-neon
Warm or soft light surfaceearth-tones, pastel-paradise, soft-scandinavian, or desert-sunset
sdk.chart.mount({
mount: '#chart',
symbol: 'AAPL',
interval: '1D',
datafeed,
theme: 'dark',
});
chart.customization().setTheme('dark');

Minimal custom theme

The shortest custom-theme path is one object with a base. Set base to any preset id and override only what you need — the preset supplies every token you leave out.

chart.customization().setTheme({
name: 'broker-ocean',
base: 'ocean-depths',
ui: {
semantic: { accent: '#f59e0b' },
},
});

Visible result: the whole chart adopts the ocean-depths skin, and every accent-colored control turns amber. Nothing else changes.

Crosshair labels

Each scale carries its own crosshair label, and each preset colors them separately. The time-scale label takes a chip derived from that preset's accent — its own color in every one of the twelve. The price-scale label stays neutral, so it reads apart from the direction-colored last-price mark sharing that gutter.

Set either scale from a theme. Naming a scale colors that label's background and its border together; crosshairLabelBackground colors whichever scale is left unnamed.

chart.customization().setTheme({
name: 'broker-dark',
base: 'dark',
colors: {
crosshairTimeLabelBackground: '#1677ff',
crosshairPriceLabelBackground: '#e2e8f0',
},
});

Visible result: the label under the pointer on the time scale turns blue with near-white text, and the price-scale label turns pale with near-black text.

Label text follows its chip. Each label takes whichever of colors.text and colors.background reads on its own background, so recoloring one label flips its text and leaves the other alone. Pin a label's text with crosshairTimeLabelTextColor or crosshairPriceLabelTextColor when a brand color has to win over the readable one.

For a label's font, border width, radius, or padding, use styleOverrides.crosshair — see Background and crosshair. Overrides sit above the theme, so a value set there wins for as long as it is applied.

Shipped Presets

Twelve first-party presets ship with the SDK. Any id can be passed wherever a theme is accepted, applied at runtime, or used as a base layer to build on. None requires prior registration.

Dark-ground presets

Preset idCharacter
darkThe default. Near-black panels, cool neutral text.
ocean-depthsDeep teal panels with a bright cyan accent.
retro-sunsetWarm brown panels with an amber accent.
vibrant-tropicsDeep jungle green with a cyan accent and lime gains.
modern-monochromeGreyscale throughout. See the note below.
electric-neonNear-black violet with saturated cyan, lime and magenta.

Light-ground presets

Preset idCharacter
lightPaper-white panels with high-contrast slate text.
earth-tonesWarm parchment and clay with an olive accent.
pastel-paradiseSoft blush panels with a violet accent.
minimalist-neutralsAchromatic greys, no colour beyond direction.
soft-scandinavianWarm off-white panels with a muted sage accent.
desert-sunsetSand panels with a terracotta accent.
chart.customization().applyTheme('ocean-depths');

Every preset passes the built-in contrast audit at 4.5:1 for text and 3:1 for non-text chrome. Verify any theme, including your own, with auditChartThemeContrast.

Inspect the catalog at runtime:

import {
CHART_THEME_PRESETS,
CHART_THEME_PRESET_NAMES,
getChartThemePreset,
isChartThemeName,
} from '@tradescript/pro/sdk';

CHART_THEME_PRESET_NAMES; // ['dark', 'light', 'earth-tones', ...]
getChartThemePreset('retro-sunset')?.label; // 'Retro Sunset'
isChartThemeName('earth-tones'); // true — exact catalog membership
isChartThemeName('earth'); // false — never inferred from a name pattern

applyTheme throws an SdkError with code theme.not-found for an unknown id, and its details.available lists every id that would have worked — shipped presets plus anything you registered.

Registering a theme under a preset's own name overrides that preset for your chart.

Direction must not rely on colour alone

modern-monochrome is greyscale by design, so gains and losses are not distinguishable by hue. Pair it with a non-colour cue — an arrow, a sign, or a label — wherever your UI shows direction. This applies to any low-chroma theme, including your own.

Themes are visual only

A theme never changes order behavior, market-data behavior, permissions, or agent authority. Anything that would change what an action does belongs in overrides or features, not in a theme.

Runtime theme switching and persistence

Runtime theme changes are synchronous SDK calls:

const ui = chart.customization();

ui.setTheme('light');
ui.setTheme(brokerDarkTheme);

const activeTheme = ui.getTheme();

The chart also emits a customization-change event whose customization payload has type theme-change.

Do runtime theme changes persist? The applied theme is stored as CustomizationState.theme, and customization state is included in chart state and layouts:

  • Within the session, the change persists until a later setTheme/applyTheme call or a restored state changes the theme.
  • chart.getState() and saved layouts capture the active theme, so restoring that state or layout restores that theme — including across page reloads, when the host saves and reapplies state through a storage adapter.
  • Nothing is written to browser storage automatically. Without a host-side save, a page reload starts again from the constructor theme option.

If a restored layout brings back a theme you no longer want, reapply the product theme after applyLayout(...) — see My restored layout uses the old theme.

Full theme object

Complete theme object: colors, UI token groups, variables, and scoped CSS
const brokerDarkTheme = {
base: 'dark',
name: 'broker-dark',
colors: {
background: '#071018',
grid: '#1f2937',
text: '#dbeafe',
up: '#16a34a',
down: '#dc2626',
},
variables: {
'--ts-chart-button-text': '#dbeafe',
'--ts-chart-marker-bg': '#f59e0b',
},
scopedVariables: {
'--ts-brand-accent': '#38bdf8',
'--ts-radius-control': '6px',
},
ui: {
button: {
background: '#071018',
selectedBackground: 'rgba(16, 185, 129, 0.2)',
selectedText: '#6ee7b7',
},
control: {
background: '#050b12',
focusBorder: '#38bdf8',
},
surface: {
text: '#dbeafe',
mutedText: '#94a3b8',
divider: 'rgba(148, 163, 184, 0.22)',
},
marker: {
background: '#f59e0b',
text: '#020617',
},
loader: {
color: '#38bdf8',
},
},
css: {
scoped: `
:host .broker-emphasis {
color: var(--ts-brand-accent);
}
`,
},
fontFamily: 'Inter, system-ui, sans-serif',
};

chart.customization().setTheme(brokerDarkTheme);
export interface ChartTheme {
base?: ChartThemeName;
name?: string;
colors?: Record<string, string>;
variables?: Record<string, string | number>;
scopedVariables?: Record<`--${string}`, string | number>;
ui?: ChartUiTheme;
css?: string | ChartCustomCssTheme;
fontFamily?: string;
metadata?: Record<string, unknown>;
}

What applies where

FieldApplies to
colors.backgroundChart background.
colors.gridChart grid.
colors.textTooltip and shared chart-text defaults.
colors.up / colors.downSemantic theme colors for host and widget styling. Use setCandleColors(...) for explicit candle colors.
colors.crosshairColorBoth crosshair guide lines.
colors.crosshairLabelBackgroundCrosshair axis-label background on whichever axis is not colored on its own.
colors.crosshairTimeLabelBackgroundTime-axis crosshair label, background and border.
colors.crosshairPriceLabelBackgroundPrice-axis crosshair label, background and border.
colors.crosshairTimeLabelTextColor / colors.crosshairPriceLabelTextColorCrosshair label text, pinned over the readable color each label would otherwise take.
baseBuilt-in light or dark token layer applied before this theme object.
variablesStable TradeScript UI-token overrides on the chart theme scope.
scopedVariablesHost-defined custom properties on the chart theme scope.
ui.buttonSDK buttons and toolbar-style controls.
ui.controlInputs, selects, and other form controls.
ui.surfacePanels, menus, and standalone widget surfaces.
ui.modalSDK modal surfaces.
ui.checkbox / ui.radioBinary and option controls.
ui.markerDefault datafeed marker colors when a mark does not provide explicit colors.
ui.loaderShared loading spinner color and track tokens.
css.scopedCSS scoped to one chart instance.
css.globalExplicit global CSS such as @font-face or shared keyframes.
fontFamilyChart and widget font family.

Scoped CSS

TradeScript is not iframe-isolated. That makes integration easier, but theme CSS must be scoped deliberately.

chart.customization().setTheme({
name: 'desk',
css: {
scoped: `
:host .ts-chart-control {
min-height: 32px;
}
`,
global: `
@font-face {
font-family: "Desk Sans";
src: url("/fonts/desk-sans.woff2") format("woff2");
}
`,
},
});

Use scoped for chart-instance styling. Use global only when the CSS is intentionally shared outside the chart.

Named theme registry

Use the native theme registry when a product has more than one named skin or needs to switch between branded themes without re-sending the full object each time.

const ui = chart.customization();

ui.setThemeRegistry({
deskDark: brokerDarkTheme,
deskLight: {
base: 'light',
name: 'desk-light',
colors: { background: '#ffffff', text: '#0f172a' },
},
});

ui.registerTheme('overnight', {
base: 'dark',
name: 'overnight',
colors: { background: '#020617', text: '#e5e7eb' },
});

ui.applyTheme('overnight');
const themes = ui.getThemeRegistry();
ui.unregisterTheme('deskLight');

Registry writes emit theme-registry-change. applyTheme(name) resolves a registered theme first and falls back to built-in light / dark names.

Standalone widgets

Standalone broker widgets can use the same ChartTheme UI tokens as the chart. For example, BrokerOrderTicket accepts theme, className, and style props, so an order ticket rendered beside TradeScriptWidget can share the same button, control, and surface tokens without being mounted inside the chart.

Use theme for the base widget skin. Use widget slot props such as slotClassNames and slotStyles when individual controls need product-specific styling, such as different entry, exit, long, short, quantity, or submit buttons.