Skip to content

Theming

The framework includes a ThemeManager that applies a set of design tokens to the entire UI at once via CSS custom properties. Theme switches happen in a single function call and take effect immediately — no re-render needed.

Quick start

typescript
import { ThemeManager, DefaultTheme, DarkTheme } from '@jimka/typescript-ui/core';
ThemeManager.setTheme(DefaultTheme); // light
ThemeManager.setTheme(DarkTheme);    // dark

Two built-in themes ship with the package: DefaultTheme (light) and DarkTheme. Custom themes are created by spreading one of them and overriding tokens — see Custom themes below.

How it works

setTheme does three things:

  1. Writes each token as a CSS custom property on :root (e.g. --ts-ui-body-bg). Because CSS variables cascade, any component that references a variable in its style rule updates automatically — no re-render needed.
  2. Sets color-scheme on :root so the browser renders native form elements (checkboxes, scrollbars, <select>) in the matching light or dark style.
  3. Sets color and background-color on both <html> and <body>. The <html> target is necessary because Window components are appended to document.documentElement rather than document.body, so text inside floating windows must inherit from <html>.

Theme keys

The Theme interface uses nested objects grouped by component. All keys are required; spread DefaultTheme and override only what you need (see Custom themes below).

Key pathCSS variableAffects
colorScheme(set directly as color-scheme)Browser rendering of native controls (checkboxes, scrollbars). Use 'light' or 'dark'.
font.family--ts-ui-font-familyFont family for the entire UI (cascades from <html>)
font.size--ts-ui-font-sizeBase font size for the entire UI
font.lineHeight--ts-ui-line-heightUnitless line-height multiplier applied document-wide. Drives the row-height of Text components and the baseline alignment math in HBox/Column/Grid
text.color--ts-ui-text-colorDefault text color for all components
body.background--ts-ui-body-bgPage background; also the background of Window
border.color--ts-ui-border-colorDefault border color for Window and other bordered components
border.radius--ts-ui-border-radiusCorner radius applied to Button and text-input components
button.background--ts-ui-button-bgBackground of Button, window title bars, and table headers
button.border--ts-ui-button-borderOutline of Button and ToggleButton
button.shadow--ts-ui-button-shadowDrop shadow on unpressed buttons
button.padding--ts-ui-button-paddingPadding inside Button
button.font.size--ts-ui-button-font-sizeFont size of Button labels
button.pressed.background--ts-ui-button-pressed-bgBackground while a button is held down
button.pressed.foreground--ts-ui-button-pressed-fgText color while a button is held down
button.pressed.shadow--ts-ui-button-pressed-shadowInset shadow on a pressed button
button.hover.background--ts-ui-button-hover-bgBackground while the pointer is over a button (but not pressed)
button.hover.foreground--ts-ui-button-hover-fgText color while the pointer is over a button (default inherit)
button.hover.shadow--ts-ui-button-hover-shadowDrop shadow while the pointer is over a button
toggle.selected.background--ts-ui-toggle-selected-bgBackground of a selected ToggleButton or RadioButton
toggle.selected.shadow--ts-ui-toggle-selected-shadowInset shadow on a selected toggle / radio
input.background--ts-ui-input-bgBackground of text inputs, password fields, text areas, checkboxes, and the table body
gutter.background--ts-ui-gutter-bgBackground of the Split drag gutter; also used as the scrollbar track color
tab.toolbar.background--ts-ui-tab-toolbar-bgBackground of the tab button toolbar in the Tab layout
tab.toolbar.border--ts-ui-tab-toolbar-borderBottom border of the tab button toolbar
tab.button.background--ts-ui-tab-button-bgBackground of inactive tab buttons
window.shadow--ts-ui-window-shadowDrop shadow on floating Window components
header.font.size--ts-ui-header-font-sizeFont size of window and panel title-bar labels
table.header.border--ts-ui-table-header-borderBottom border separating the table header from the body
table.header.font.size--ts-ui-table-header-font-sizeFont size of table column header cells
table.row.selected--ts-ui-table-row-selectedBackground tint of the currently selected table row
table.row.new--ts-ui-table-row-newBackground tint of unsaved new records
table.row.dirty--ts-ui-table-row-dirtyBackground tint of locally modified records
contextMenu.background--ts-ui-context-menu-bgBackground of the Menu panel in rebuild mode (right-click)
contextMenu.border--ts-ui-context-menu-borderBorder color of the rebuild-mode Menu panel
contextMenu.shadow--ts-ui-context-menu-shadowDrop shadow of the rebuild-mode Menu panel
contextMenu.item.hoverBackground--ts-ui-context-menu-item-hover-bgBackground of a rebuild-mode MenuItem on hover
contextMenu.item.disabledColor--ts-ui-context-menu-item-disabled-colorText color of a disabled rebuild-mode MenuItem
contextMenu.separatorColor--ts-ui-context-menu-separator-colorColor of the rebuild-mode MenuSeparator line
tooltip.background--ts-ui-tooltip-bgBackground of the Tooltip panel
tooltip.color--ts-ui-tooltip-colorText color inside the Tooltip
tooltip.border--ts-ui-tooltip-borderBorder color of the Tooltip panel
tooltip.shadow--ts-ui-tooltip-shadowDrop shadow of the Tooltip panel
notification.shadow--ts-ui-notification-shadowDrop shadow applied to all Notification toasts
notification.info.background--ts-ui-notification-info-bgBackground of 'info' notifications
notification.info.border--ts-ui-notification-info-borderBorder color of 'info' notifications
notification.success.background--ts-ui-notification-success-bgBackground of 'success' notifications
notification.success.border--ts-ui-notification-success-borderBorder color of 'success' notifications
notification.warning.background--ts-ui-notification-warning-bgBackground of 'warning' notifications
notification.warning.border--ts-ui-notification-warning-borderBorder color of 'warning' notifications
notification.error.background--ts-ui-notification-error-bgBackground of 'error' notifications
notification.error.border--ts-ui-notification-error-borderBorder color of 'error' notifications

Background tokens accept gradients

button.background, button.pressed.background, button.hover.background, and toggle.selected.background accept either a plain colour (rgb(200, 200, 200)) or any CSS background-image value (linear-gradient(...), radial-gradient(...), etc.). The framework applies the token to both background-color and background-image; CSS's "invalid at computed-value time" rule routes the value to whichever property it is valid for.

Custom themes

Implement the Theme interface and pass it to setTheme. Spread DefaultTheme and override only the keys you care about:

typescript
import { Theme, ThemeManager, DefaultTheme } from '@jimka/typescript-ui/core';
const MyTheme: Theme = {
    ...DefaultTheme,
    body: { background: 'rgb(240, 248, 255)' },
    text: { color: 'rgb(10, 30, 60)' },
    button: {
        ...DefaultTheme.button,
        background: 'linear-gradient(rgb(200, 220, 255), rgb(160, 190, 240))',
    },
};

ThemeManager.setTheme(MyTheme);

Components that need a theme value at construction time (rather than via a CSS variable) can call ThemeManager.getTheme() to read the currently active theme.

Theme change listeners

ThemeManager.onThemeChange subscribes a callback that fires after every setTheme call, once all CSS variables have been written. Text-based components (Label, Header labels, table column headers) automatically recalculate their preferred size on each theme change so layout managers see updated dimensions.

typescript
const unsubscribe = ThemeManager.onThemeChange(() => {
    console.log('theme changed:', ThemeManager.getTheme().colorScheme);
});

// Later, to stop listening:
unsubscribe();

Memory leaks

Custom components that create Text instances and are removed from the page should call text.dispose() to detach the listener and avoid memory leaks. The framework does this automatically for built-in components, but a Text you create yourself is your responsibility.