How to Customize the UI Theming in Stirling-PDF Using the @app Import Path Pattern
Override the default Mantine theme by creating a custom theme file in frontend/src/core/theme/ and importing it via the @app/ alias, which Vite resolves to the core directory, enabling consistent theming across web, desktop, and proprietary builds.
Stirling-PDF’s frontend is built with React, Vite, Mantine, and Tailwind CSS, with all UI-related code centralized under frontend/src/core/. Customizing the UI theming using the @app/ import path pattern ensures your modifications remain portable across the web application, desktop (Tauri) builds, and proprietary distributions without requiring path refactoring.
How the @app Import Alias Works
The @app/ pattern is a Vite resolve alias defined in frontend/vite.config.ts that maps imports to the absolute path of the core directory:
// frontend/vite.config.ts
import path from 'path';
export default {
resolve: {
alias: {
'@app': path.resolve(__dirname, 'src/core')
}
}
};
When you write import { mantineTheme } from '@app/theme/mantineTheme', Vite rewrites this to <repository-root>/frontend/src/core/theme/mantineTheme.ts. This abstraction eliminates relative path gymnastics (e.g., ../../../theme/) and guarantees that modules resolve correctly regardless of whether the code runs in the web build, desktop wrapper, or proprietary fork.
Locating the Default Theme Files
The theming system relies on four key files within frontend/src/core/:
theme/mantineTheme.ts– Contains theextendThemedefinition including color palettes, component defaults, and global stylesconstants/theme.ts– Exports theThemeModetype ('light' | 'dark' | 'rainbow') and detects OS-level theme preferenceshooks/useRainbowTheme.ts– Implements the special "rainbow" animated theme modeservices/preferencesService.ts– Persists user theme selections tolocalStoragevia thePreferencesinterface
These files are imported throughout the application using the @app/ prefix. For example, the root provider typically instantiates Mantine with:
import { mantineTheme } from '@app/theme/mantineTheme';
<MantineProvider theme={mantineTheme}>
{/* Application components */}
</MantineProvider>
Creating Custom Theme Overrides
To customize the UI theming, create a new file at frontend/src/core/theme/customTheme.ts that extends the base configuration while preserving the original file untouched.
Overriding Color Palettes
Define new color scales and assign them as the primary palette:
// frontend/src/core/theme/customTheme.ts
import { mantineTheme } from '@app/theme/mantineTheme';
export const customTheme = {
...mantineTheme,
colors: {
...mantineTheme.colors,
brand: ['#e0f2ff', '#b3e5ff', '#80d4ff', '#4dc2ff', '#1ab1ff', '#0099e6', '#0077b3', '#005580', '#00334d', '#001122'],
primary: ['#e6fffa', '#b3fff0', '#80ffe6', '#4dffdc', '#1affd2', '#00e6b8', '#00b38f', '#008066', '#004d3c', '#001913'],
},
primaryColor: 'brand',
};
Implementing Dark-Mode Specific Styles
For dynamic dark-mode adjustments, export a theme factory function that receives the current color scheme:
// frontend/src/core/theme/customTheme.ts
import { mantineTheme } from '@app/theme/mantineTheme';
export const customTheme = (colorScheme: 'light' | 'dark') => ({
...mantineTheme,
colors: {
...mantineTheme.colors,
dark: colorScheme === 'dark'
? ['#0d0d0d', '#1a1a1a', '#262626', '#333333', '#404040', '#4d4d4d', '#595959', '#666666', '#737373', '#808080']
: mantineTheme.colors.dark,
},
});
Customizing Component Styles
Modify default props for all instances of a component by extending the components key:
export const customTheme = {
...mantineTheme,
components: {
...mantineTheme.components,
Button: {
styles: (theme) => ({
root: {
borderRadius: theme.radius.md,
padding: `${theme.spacing.xs} ${theme.spacing.sm}`,
},
}),
},
},
};
Registering Your Custom Theme
Update the root application file (typically frontend/src/App.tsx) to import your custom theme using the @app/ alias:
import { MantineProvider } from '@mantine/core';
import { customTheme } from '@app/theme/customTheme';
import { usePreferences } from '@app/contexts/PreferencesContext';
function App() {
const { theme } = usePreferences(); // Returns 'light', 'dark', or 'rainbow'
return (
<MantineProvider theme={customTheme(theme)} colorScheme={theme}>
{/* Rest of application */}
</MantineProvider>
);
}
Using the @app/theme/customTheme path ensures the import resolves correctly in all build targets without modification.
Adding New Theme Modes
To introduce a custom mode such as highContrast:
- Extend the type definition in
frontend/src/core/constants/theme.ts:
export type ThemeMode = 'light' | 'dark' | 'rainbow' | 'highContrast';
-
Update the preferences service at
frontend/src/core/services/preferencesService.tsto include a default value for the new mode (the service already handleslocalStorageread/write operations via thePreferencesinterface). -
Add a UI toggle in
frontend/src/core/components/rightRail/ThemeSwitcher.tsx(or equivalent) that callsupdatePreference('theme', 'highContrast'). TheusePreferenceshook propagates this change automatically, causing the theme provider to re-render with the new mode.
Summary
- The
@app/alias resolves tofrontend/src/core/via Vite configuration invite.config.ts, enabling consistent imports across web, desktop, and proprietary builds - Base theme configuration resides in
theme/mantineTheme.tsand should remain unmodified to facilitate updates - Custom themes are created by extending the base theme object and importing via
@app/theme/customTheme - Dynamic theming is achieved by exporting theme factory functions that accept the current
colorScheme - New theme modes require extending the
ThemeModetype inconstants/theme.tsand updating the preferences service interface
Frequently Asked Questions
What is the @app import path in Stirling-PDF?
The @app/ path is a Vite resolve alias configured in frontend/vite.config.ts that maps to frontend/src/core/. It allows developers to import core modules using absolute-style paths (e.g., @app/theme/mantineTheme) rather than relative paths, ensuring code portability between the web application, desktop (Tauri) builds, and proprietary distributions.
Where is the default Mantine theme defined?
The default theme is defined in frontend/src/core/theme/mantineTheme.ts using Mantine's extendTheme function. This file exports the color palettes, component default props, and global styles that drive the application's visual appearance. Constants and types supporting this theme are located in frontend/src/core/constants/theme.ts.
How do I add a new theme mode like high contrast?
Extend the ThemeMode type in frontend/src/core/constants/theme.ts to include your new mode (e.g., 'highContrast'). The preferencesService.ts already persists theme selections to localStorage, so you only need to ensure your new mode is handled in the theme factory function and add a UI control that calls updatePreference('theme', 'highContrast').
Will custom themes work in the desktop application?
Yes. Because the @app/ alias resolves to the same src/core/ directory in all build configurations (web, desktop, and proprietary), custom themes imported via @app/theme/customTheme will function identically across all platforms without requiring path adjustments or conditional imports.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →