@@ -12,6 +12,7 @@ The IntroJS theme system allows you to easily customize the look and feel of you
1212- ✅ ** Custom Themes** - Load any CSS file as a theme
1313- ✅ ** Theme Registration** - Register custom themes for easy reuse
1414- ✅ ** Tour Options Integration** - Set theme directly in tour options
15+ - ✅ ** Live Theme Switching** - Change the theme of a running tour with ` tour.setTheme() `
1516
1617## Quick Start
1718
@@ -23,22 +24,22 @@ The simplest way to use themes is by setting the `theme` option when creating a
2324import introJs from ' intro.js' ;
2425
2526// Use dark theme
26- introJs ().setOptions ({
27+ introJs . tour ().setOptions ({
2728 theme: ' dark'
2829}).start ();
2930
3031// Use light theme
31- introJs ().setOptions ({
32+ introJs . tour ().setOptions ({
3233 theme: ' light'
3334}).start ();
3435
3536// Use system preference (default)
36- introJs ().setOptions ({
37+ introJs . tour ().setOptions ({
3738 theme: ' auto'
3839}).start ();
3940
4041// Use any pre-registered theme
41- introJs ().setOptions ({
42+ introJs . tour ().setOptions ({
4243 theme: ' modern'
4344}).start ();
4445```
@@ -51,13 +52,13 @@ You can load a custom CSS file by providing both `theme` and `themePath` options
5152import introJs from ' intro.js' ;
5253
5354// Load a custom theme from a CSS file
54- introJs ().setOptions ({
55+ introJs . tour ().setOptions ({
5556 theme: ' ocean' ,
5657 themePath: ' path/to/themes/introjs-ocean.css'
5758}).start ();
5859
5960// Load from CDN
60- introJs ().setOptions ({
61+ introJs . tour ().setOptions ({
6162 theme: ' custom' ,
6263 themePath: ' https://cdn.example.com/intro-custom-theme.css'
6364}).start ();
@@ -69,79 +70,79 @@ The following themes are available out of the box:
6970
7071| Theme Name | Description | CSS Path |
7172| ------------| -------------| ----------|
72- | ` light ` | Light theme (default style) | ` themes/introjs-light.css ` |
73- | ` dark ` | Dark theme | ` themes/introjs-dark.css ` |
74- | ` auto ` | System preference ( switches automatically) | N/A ( uses classes only) |
73+ | ` light ` | Light theme (default style) | N/A - uses CSS custom properties, no file to load |
74+ | ` dark ` | Dark theme | N/A - uses CSS custom properties, no file to load |
75+ | ` auto ` | Follows the system preference, switches automatically | N/A - uses CSS custom properties, no file to load |
7576| ` modern ` | Modern theme | ` themes/introjs-modern.css ` |
7677| ` flattener ` | Flat design theme | ` themes/introjs-flattener.css ` |
7778| ` nassim ` | Nassim theme | ` themes/introjs-nassim.css ` |
7879| ` nazanin ` | Nazanin theme (RTL support) | ` themes/introjs-nazanin.css ` |
7980| ` royal ` | Royal theme | ` themes/introjs-royal.css ` |
8081
82+ > Note: ` light ` , ` dark ` and ` auto ` are implemented purely with CSS classes/custom properties on the tour's root element, so no extra network request happens when you use them. The other themes are loaded on demand from the CSS file shown above.
83+
8184## Advanced Usage
8285
8386### 1. Registering Custom Themes
8487
8588You can register custom themes globally so they can be used by name:
8689
8790``` javascript
88- import { registerTheme , registerThemes } from ' intro.js/theme' ;
8991import introJs from ' intro.js' ;
9092
9193// Register a single theme
92- registerTheme (' ocean' , ' themes/introjs-ocean.css' );
94+ introJs . registerTheme (' ocean' , ' themes/introjs-ocean.css' );
9395
9496// Now you can use it by name
95- introJs ().setOptions ({
97+ introJs . tour ().setOptions ({
9698 theme: ' ocean'
9799}).start ();
98100
99101// Register multiple themes at once
100- registerThemes ([
102+ introJs . registerThemes ([
101103 { name: ' sunset' , cssPath: ' themes/introjs-sunset.css' },
102104 { name: ' forest' , cssPath: ' themes/introjs-forest.css' },
103105 { name: ' corporate' , cssPath: ' themes/introjs-corporate.css' }
104106]);
105107```
106108
107- ### 2. Using Theme API Directly
109+ ### 2. Changing the Theme of a Running Tour
108110
109- For more control, you can use the theme API directly :
111+ Every ` Tour ` instance exposes ` setTheme() ` / ` getTheme() ` so you can react to in-app theme toggles without restarting the tour :
110112
111113``` javascript
112- import { applyTheme , getTheme } from ' intro.js/theme ' ;
114+ import introJs from ' intro.js' ;
113115
114- // Apply a theme to a specific root element
115- const root = document .getElementById (' my-tour-container' );
116- applyTheme ({
117- root: root,
118- theme: ' dark'
119- });
116+ const tour = introJs .tour ().setOptions ({ theme: ' light' });
117+ await tour .start ();
120118
121- // Get current theme instance
122- const theme = getTheme ();
123- console .log (theme .currentTheme ); // 'dark'
124- console .log (theme .value ); // 'dark' or 'light'
119+ console .log (tour .getTheme ()); // 'light'
125120
126- // Change theme dynamically
127- await theme .setTheme (' light' );
121+ // Switch the running tour to dark mode
122+ await tour .setTheme (' dark' );
123+ console .log (tour .getTheme ()); // 'dark'
128124
129- // Clean up when done
130- theme . destroy ( );
125+ // Switch to a custom CSS theme
126+ await tour . setTheme ( ' ocean ' , ' themes/introjs-ocean.css ' );
131127```
132128
129+ Calling ` setTheme() ` before the tour has started simply stores the choice; it's applied the next time ` start() ` runs.
130+
133131### 3. Getting Theme Information
134132
135133``` javascript
136- import { getThemePath , getRegisteredThemes } from ' intro.js/theme ' ;
134+ import introJs from ' intro.js' ;
137135
138- // Get CSS path for a specific theme
139- const darkPath = getThemePath (' dark' );
140- console .log (darkPath); // 'themes/introjs-dark.css'
136+ // Get CSS path for a registered theme
137+ const modernPath = introJs .getThemePath (' modern' );
138+ console .log (modernPath); // 'themes/introjs-modern.css'
139+
140+ // Built-in themes (light/dark/auto) are not file-based, so this is undefined
141+ console .log (introJs .getThemePath (' dark' )); // undefined
141142
142143// Get all registered theme names
143- const allThemes = getRegisteredThemes ();
144- console .log (allThemes); // ['dark ', 'light ', 'modern ', ...]
144+ const allThemes = introJs . getRegisteredThemes ();
145+ console .log (allThemes); // ['modern ', 'flattener ', 'nassim', 'nazanin', 'royal ', ...]
145146```
146147
147148## Complete Examples
@@ -151,7 +152,7 @@ console.log(allThemes); // ['dark', 'light', 'modern', ...]
151152``` javascript
152153import introJs from ' intro.js' ;
153154
154- introJs ().setOptions ({
155+ introJs . tour ().setOptions ({
155156 steps: [
156157 {
157158 element: ' #step1' ,
@@ -170,13 +171,12 @@ introJs().setOptions({
170171
171172``` javascript
172173import introJs from ' intro.js' ;
173- import { registerTheme } from ' intro.js/theme' ;
174174
175175// Register your custom theme
176- registerTheme (' mycompany' , ' assets/css/intro-mycompany-theme.css' );
176+ introJs . registerTheme (' mycompany' , ' assets/css/intro-mycompany-theme.css' );
177177
178178// Use it in your tour
179- introJs ().setOptions ({
179+ introJs . tour ().setOptions ({
180180 steps: [
181181 {
182182 element: ' #welcome' ,
@@ -193,8 +193,9 @@ introJs().setOptions({
193193import introJs from ' intro.js' ;
194194
195195// This tour will automatically use dark theme in dark mode
196- // and light theme in light mode
197- introJs ().setOptions ({
196+ // and light theme in light mode, and keeps reacting if the
197+ // user's OS theme changes while the tour is open
198+ introJs .tour ().setOptions ({
198199 steps: [
199200 {
200201 intro: ' This tour adapts to your system theme!'
@@ -209,7 +210,7 @@ introJs().setOptions({
209210``` javascript
210211import introJs from ' intro.js' ;
211212
212- const tour = introJs ().setOptions ({
213+ const tour = introJs . tour ().setOptions ({
213214 steps: [
214215 {
215216 intro: ' Starting with light theme'
@@ -221,15 +222,10 @@ const tour = introJs().setOptions({
221222 theme: ' light'
222223});
223224
224- tour .onBeforeChange (function (targetElement ) {
225+ tour .onBeforeChange (async function (targetElement ) {
225226 if (this .getCurrentStep () === 1 ) {
226- // Switch to dark theme on step 2
227- this .setOption (' theme' , ' dark' );
228- // Re-initialize theme
229- this .exit ().then (() => {
230- this .setCurrentStep (1 );
231- this .start ();
232- });
227+ // Switch the already-running tour to dark theme on step 2
228+ await this .setTheme (' dark' );
233229 }
234230});
235231
@@ -285,7 +281,7 @@ Then use it:
285281``` javascript
286282import introJs from ' intro.js' ;
287283
288- introJs ().setOptions ({
284+ introJs . tour ().setOptions ({
289285 theme: ' ocean' ,
290286 themePath: ' themes/introjs-ocean.css'
291287}).start ();
@@ -296,17 +292,16 @@ introJs().setOptions({
296292The theme system is fully typed:
297293
298294``` typescript
299- import introJs from ' intro.js' ;
300- import { ThemeType , registerTheme , applyTheme } from ' intro.js/theme' ;
295+ import introJs , { type ThemeType } from ' intro.js' ;
301296
302297// Theme types
303298const theme: ThemeType = ' dark' ; // 'light' | 'dark' | 'auto' | string
304299
305300// Register theme with types
306- registerTheme (' custom' , ' path/to/custom.css' );
301+ introJs . registerTheme (' custom' , ' path/to/custom.css' );
307302
308303// Use in tour with types
309- introJs ().setOptions ({
304+ introJs . tour ().setOptions ({
310305 theme: ' dark' ,
311306 themePath: ' path/to/theme.css' // optional
312307}).start ();
@@ -316,15 +311,15 @@ introJs().setOptions({
316311
3173121 . ** Use ` auto ` for Better UX** : Let users' system preferences determine the theme
318313 ``` javascript
319- introJs ().setOptions ({ theme: ' auto' }).start ();
314+ introJs . tour ().setOptions ({ theme: ' auto' }).start ();
320315 ```
321316
3223172 . ** Register Themes Early** : Register all custom themes at app initialization
323318 ``` javascript
324319 // app-init.js
325- import { registerThemes } from ' intro.js/theme ' ;
326-
327- registerThemes ([
320+ import introJs from ' intro.js' ;
321+
322+ introJs . registerThemes ([
328323 { name: ' brand' , cssPath: ' themes/brand.css' },
329324 { name: ' seasonal' , cssPath: ' themes/seasonal.css' }
330325 ]);
@@ -353,7 +348,7 @@ introJs().setOptions({
353348** Problem** : Theme is registered but not applying
354349
355350** Solutions** :
356- - Make sure you call ` registerTheme() ` before starting the tour
351+ - Make sure you call ` introJs. registerTheme()` before starting the tour
357352- Verify the theme name matches exactly
358353- Check that the CSS selectors in your theme file are correct
359354- Ensure no other CSS is overriding your theme styles
@@ -363,9 +358,9 @@ introJs().setOptions({
363358** Problem** : Multiple theme CSS files are being loaded
364359
365360** Solutions** :
366- - The system automatically prevents duplicate loading
361+ - The system automatically prevents duplicate loading of the same CSS file
362+ - When you call ` tour.setTheme() ` with a different custom theme, the previous theme's ` <link> ` is removed automatically
367363- Use the same theme name consistently
368- - Call ` theme.destroy() ` when switching themes programmatically
369364
370365## API Reference
371366
@@ -376,11 +371,18 @@ introJs().setOptions({
376371| ` theme ` | ` ThemeType ` | ` 'auto' ` | Theme name ('light', 'dark', 'auto', or custom) |
377372| ` themePath ` | ` string ` | ` undefined ` | Path to custom CSS file |
378373
379- ### Theme Functions
374+ ### Tour Instance Methods
375+
376+ | Method | Parameters | Returns | Description |
377+ | --------| ------------| ---------| -------------|
378+ | ` tour.setTheme(theme, themePath?) ` | ` theme: ThemeType, themePath?: string ` | ` Promise<Tour> ` | Change the theme; applies immediately if the tour is running |
379+ | ` tour.getTheme() ` | - | ` 'light' \| 'dark' \| undefined ` | The currently resolved theme, or ` undefined ` before the tour starts |
380+
381+ ### ` introJs ` Theme Functions
380382
381383| Function | Parameters | Returns | Description |
382384| ----------| ------------| ---------| -------------|
383- | ` registerTheme(name, cssPath) ` | ` name: string, cssPath: string ` | ` void ` | Register a custom theme |
384- | ` registerThemes(themes) ` | ` themes: ThemeRegistration[] ` | ` void ` | Register multiple themes |
385- | ` getThemePath(name) ` | ` name: string ` | ` string \| undefined ` | Get CSS path for a theme |
386- | ` getRegisteredThemes() ` | -
385+ | ` introJs. registerTheme(name, cssPath)` | ` name: string, cssPath: string ` | ` void ` | Register a custom theme |
386+ | ` introJs. registerThemes(themes)` | ` themes: ThemeRegistration[] ` | ` void ` | Register multiple themes |
387+ | ` introJs. getThemePath(name)` | ` name: string ` | ` string \| undefined ` | Get the CSS path for a theme (built-in themes return ` undefined ` ) |
388+ | ` introJs. getRegisteredThemes()` | - | ` string[] ` | List the names of all registered themes |
0 commit comments