---
title: Add Reusable Custom Themes
product: Survey Creator
description: Theme Editor is a form styling tool integrated into SurveyJS Form Builder, enabling form creators to add custom themes and store them in a shared repository for collaborative use. With Theme Editor, designers can easily create custom themes and save them for future reuse. Try out a live demo for JavaScript with a step-by-step guide on how to create and manage custom themes in SurveyJS Form Builder.
framework: Vanilla JS
source: https://surveyjs.io/survey-creator/examples/save-custom-theme/vanillajs
index: https://surveyjs.io/survey-creator/examples/overview.md
---

# Add Reusable Custom Themes (Vanilla JS)

Theme Editor is a powerful form styling tool fully integrated into Survey Creator. It enables form creators to add and manage reusable custom themes using a set of UI controls. Once saved in a shared repository, themes become available for collaborative use, ensuring consistency of design across various departments within an organization. In this example, users can utilize the intuitive interface of the Theme Editor to capture the current theme configuration as a custom theme by clicking the "Add custom theme to the list" toolbar button. The "Delete theme" button allows users to remove unwanted custom themes.

## Themes in SurveyJS

A SurveyJS theme is a JSON object that specifies CSS variables and other theme settings, such as the color palette (light or dark), question appearance with or without question boxes, and survey header settings. SurveyJS includes several [predefined themes](/documentation/themes-and-custom-styles#predefined-themes) and allows you to add custom themes. 

## Add a Custom Theme

Theme Editor displays a list of available themes within the Theme drop-down menu. To add a custom theme to this menu, follow the steps below:

1. [Specify a theme JSON schema](#specify-a-theme-json-schema).
2. [Add a custom theme to the list of available themes](#add-a-custom-theme-to-the-list-of-available-themes).
3. [Apply the custom theme at application startup](#apply-the-custom-theme-at-application-startup).

### Specify a theme JSON schema

Declare a JSON object that defines a custom theme. Within this object, specify a unique [`themeName`](https://surveyjs.io/form-library/documentation/api-reference/itheme#themeName) property: 

```js
// theme_json.js
export const customTheme = {
  "backgroundImage": "",
  "backgroundImageFit": "cover",
  "backgroundImageAttachment": "scroll",
  "backgroundOpacity": 1,
  "cssVariables": {
    //...
  },
  "headerView": "advanced",
  "header": {
    //...
  },
  "themeName": "custom",
  "colorPalette": "light",
  "isPanelless": true
};
```

If you want to create a theme with dark/light color variations, define two JSON objects that differ only in the [`colorPalette`](https://surveyjs.io/form-library/documentation/api-reference/itheme#colorPalette) property:

```js
// theme_variations.js
const lightTheme = {
  "themeName": "custom-with-color-variations",
  "colorPalette": "light",
  // ...
  // Other properties are the same as `darkTheme` object properties
  // ...
};

const darkTheme = {
  "themeName": "custom-with-color-variations",
  "colorPalette": "dark",
  // ...
  // Other properties are the same as `lightTheme` object properties
  // ...
};
```

Similarly, you can create a theme with visible/invisible question boxes by setting different [`isPanelless`](https://surveyjs.io/form-library/documentation/api-reference/itheme#isPanelless) property values:

```js
const panellessTheme = {
  "themeName": "custom-with-question-box-variations",
  "isPanelless": true,
  // ...
  // Other properties are the same as `themeWithPanels` object properties
  // ...
};

const themeWithPanels = {
  "themeName": "custom-with-question-box-variations",
  "isPanelless": false,
  // ...
  // Other properties are the same as `panellessTheme` object properties
  // ...
};
```

### Add a custom theme to the list of available themes

Access a [`ThemeTabPlugin`](https://surveyjs.io/survey-creator/documentation/api-reference/themetabplugin) using Survey Creator's [`themeEditor`](https://surveyjs.io/survey-creator/documentation/api-reference/survey-creator#themeEditor) property. Call the plugin's [`addTheme()`](https://surveyjs.io/survey-creator/documentation/api-reference/themetabplugin#addTheme) method and pass a theme JSON schema to it. If you have theme variations, call this method once for each variation. The variations will be collected into one entry in the theme list.

```js
import { customTheme } from "./theme_json";
import { lightTheme, darkTheme } from "./theme_variations";
// ...
const themeTabPlugin = creator.themeEditor;
themeTabPlugin.addTheme(customTheme);
themeTabPlugin.addTheme(lightTheme);
themeTabPlugin.addTheme(darkTheme);
```

For a more user-friendly interface, specify a theme title that will be displayed in the theme list. Use [Survey Creator's localization capabilities](https://surveyjs.io/survey-creator/documentation/survey-localization-translate-surveys-to-different-languages#override-individual-translations) to do this:

```js
import { getLocaleStrings } from "survey-creator-core";

const enLocale = getLocaleStrings("en");
enLocale.theme.names[theme.themeName] = "Custom Theme";
```

### Apply a custom theme to a survey being designed

Assign the theme JSON schema to Survey Creator's [`theme`](/survey-creator/documentation/api-reference/survey-creator#theme) property:

```js
creator.theme = customTheme;
```
    
## Save a Theme

To let users save current theme modifications as a new theme, add an [action button](https://surveyjs.io/form-library/documentation/api-reference/iaction) to the toolbar. This button should call a function that obtains a JSON object with the current theme, saves it to a required storage, and updates the theme list in Theme Editor:

```js
import { Action } from "survey-core";
// ...
function saveCustomTheme() {
  // Get the current theme
  const currentTheme = creator.theme;
  // ...
  // Save the theme to a custom storage
  // ...
  // Update the theme list using the `ThemeTabPlugin` API
  // ...
  // Refer to the Code tab for a full code listing
};

const saveThemeAction = new Action({
  id: "svd-save-custom-theme",
  tooltip: "Add custom theme to the list",
  action: saveCustomTheme,
  iconName: "icon-saveas"
});
creator.toolbar.actions.push(saveThemeAction);
```

## Delete a Theme

To implement a UI for theme deletion, add another toolbar action. Use `ThemeTabPlugin`'s [`removeTheme()`](https://surveyjs.io/survey-creator/documentation/api-reference/themetabplugin#removeTheme) method to update the theme list:

```js
import { Action } from "survey-core";
// ...
function deleteCurrentCustomTheme() {
  // Get the current theme
  const currentTheme = creator.theme;
  // ...
  // Remove the theme from a custom storage
  // ...
  // Update the theme list using the `ThemeTabPlugin` API
  // ...
  // Refer to the Code tab for a full code listing
};

const deleteThemeAction = new Action({
  id: "svd-delete-custom-theme",
  tooltip: "Delete theme",
  action: deleteCurrentCustomTheme,
  iconName: "icon-delete"
});
creator.toolbar.actions.push(deleteThemeAction);
```

In this demo, the "Delete theme" toolbar action is available only for custom themes.

## Files

### `public/index.html`

```html
<!-- Uncomment the following lines to enable Ace Editor in the JSON Editor tab -->
<!-- 
<script src="https://unpkg.com/ace-builds/src-min-noconflict/ace.js"></script>
<script src="https://unpkg.com/ace-builds/src-min-noconflict/ext-searchbox.js"></script>
<script src="https://unpkg.com/ace-builds/src-min-noconflict/theme-clouds_midnight.js"></script>
-->

<div id="surveyCreatorContainer" style="position: absolute; height: 100%; width: 100%"></div>
```

### `src/index.css`

```css
.my-dialog .sd-root-modern {
    background-color: transparent;
}

.my-dialog .sv-popup__body-header {
    color: var(--sjs2-color-component-input-default-value, var(--sjs2-color-fg-basic-primary, rgba(0, 0, 0, 0.91)));
    margin-bottom: 0;
    font-family: var(--sjs2-typography-font-family-text);
    font-size: var(--sjs2-typography-font-size-default, 16px);
    font-style: normal;
    font-weight: var(--sjs2-typography-font-weight-basic, 400);
    line-height: var(--sjs2-typography-line-height-default, 24px);
}

.my-dialog .sv-components-row > .sv-components-column--expandable {
    width: auto;
    padding-bottom: var(--sjs2-spacing-x025, 2px);
}
```

### `src/survey_json.js`

```js
export const formJSON = {
  "title": "Online Check-in",
  "description": "Check-in is available 2 to 24 hours prior to departure for all destinations. To complete the check-in process, please fill out the form below.",
  "logo": "https://api.surveyjs.io/private/Surveys/files?name=ee96dc76-ecfb-4b17-8589-493015f1132a",
  "logoWidth": "auto",
  "logoHeight": "40",
  "completedHtml": "<div style=\"max-width:640px;text-align:center;margin:16px auto;\">\n\n<div style=\"padding:0 24px;\">\n<h4>Check-in complete.</h4>\n<p>Thank you for checking in. Your journey with us is all set. Have a great flight.</p>\n</div>\n\n</div>\n",
  "pages": [
    {
      "name": "page1",
      "elements": [
        {
          "type": "paneldynamic",
          "name": "passengers",
          "width": "100%",
          "minWidth": "256px",
          "titleLocation": "hidden",
          "templateElements": [
            {
              "type": "text",
              "name": "first-name",
              "width": "35%",
              "minWidth": "208px",
              "title": "PASSENGER #{panelIndex} INFO",
              "titleLocation": "top",
              "placeholder": "First name"
            },
            {
              "type": "text",
              "name": "last-name",
              "width": "30%",
              "minWidth": "172px",
              "startWithNewLine": false,
              "title": " ",
              "titleLocation": "top",
              "placeholder": "Last name"
            },
            {
              "type": "dropdown",
              "name": "prefix",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "title": " ",
              "titleLocation": "top",
              "choices": [
                "Mr.",
                "Mrs.",
                "Ms."
              ],
              "choicesOrder": "random",
              "placeholder": "Prefix",
              "allowClear": false
            },
            {
              "type": "multipletext",
              "name": "birthdate",
              "width": "65%",
              "minWidth": "256px",
              "items": [
                {
                  "name": "date",
                  "inputType": "date",
                  "title": "Date of birth"
                }
              ]
            },
            {
              "type": "text",
              "name": "nationality",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "placeholder": "Nationality"
            },
            {
              "type": "text",
              "name": "passport-number",
              "width": "100%",
              "minWidth": "256px",
              "title": "PASSENGER #{panelIndex} ID",
              "titleLocation": "top",
              "placeholder": "Passport #"
            },
            {
              "type": "dropdown",
              "name": "passport-issue-country",
              "width": "35%",
              "minWidth": "208px",
              "choicesByUrl": {
                "url": "https://surveyjs.io/api/CountriesExample",
                "valueName": "name"
              },
              "placeholder": "Country of issue",
              "allowClear": false
            },
            {
              "type": "multipletext",
              "name": "passport-exp-date",
              "width": "65%",
              "minWidth": "256px",
              "startWithNewLine": false,
              "items": [
                {
                  "name": "date",
                  "inputType": "date",
                  "title": "Exp. date"
                }
              ]
            }
          ],
          "panelCount": 1,
          "minPanelCount": 1,
          "confirmDeleteText": "Do you want to delete the passenger?",
          "addPanelText": "ADD PASSENGER",
          "removePanelText": "REMOVE",
          "showProgressBar": false,
          "templateTitleLocation": "hidden"
        },
        {
          "type": "panel",
          "name": "person-to-notify",
          "elements": [
            {
              "type": "text",
              "name": "person-to-notify-first-name",
              "width": "35%",
              "minWidth": "208px",
              "title": "PERSON TO NOTIFY",
              "titleLocation": "top",
              "setValueIf": "{passengers[0].first-name} notempty",
              "setValueExpression": "{passengers[0].first-name}",
              "placeholder": "First name"
            },
            {
              "type": "text",
              "name": "person-to-notify-last-name",
              "width": "30%",
              "minWidth": "172px",
              "startWithNewLine": false,
              "title": " ",
              "titleLocation": "top",
              "setValueIf": "{passengers[0].last-name} notempty",
              "setValueExpression": "{passengers[0].last-name}",
              "placeholder": "Last name"
            },
            {
              "type": "dropdown",
              "name": "person-to-notify-prefix",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "title": " ",
              "titleLocation": "top",
              "setValueIf": "{passengers[0].prefix} notempty",
              "setValueExpression": "{passengers[0].prefix}",
              "choices": [
                "Mr.",
                "Mrs.",
                "Ms."
              ],
              "choicesOrder": "random",
              "placeholder": "Prefix",
              "allowClear": false
            },
            {
              "type": "text",
              "name": "person-to-notify-email",
              "width": "65%",
              "minWidth": "256px",
              "inputType": "email",
              "placeholder": "Email"
            },
            {
              "type": "text",
              "name": "person-to-notify-phone",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "placeholder": "Phone"
            },
            {
              "type": "text",
              "name": "person-to-notify-address",
              "width": "100%",
              "minWidth": "256px",
              "title": "ADDRESS",
              "titleLocation": "top",
              "placeholder": "Address line 1"
            },
            {
              "type": "text",
              "name": "person-to-notify-city",
              "width": "35%",
              "minWidth": "208px",
              "placeholder": "City"
            },
            {
              "type": "text",
              "name": "person-to-notify-state",
              "width": "30%",
              "minWidth": "172px",
              "startWithNewLine": false,
              "placeholder": "State"
            },
            {
              "type": "text",
              "name": "person-to-notify-zip",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "placeholder": "Zip Code"
            },
            {
              "type": "dropdown",
              "name": "person-to-notify-country",
              "width": "100%",
              "minWidth": "256px",
              "choicesByUrl": {
                "url": "https://surveyjs.io/api/CountriesExample",
                "valueName": "name"
              },
              "placeholder": "Country",
              "allowClear": false
            }
          ],
          "questionTitleLocation": "hidden",
          "width": "100%",
          "minWidth": "256px"
        },
        {
          "type": "panel",
          "name": "flight",
          "elements": [
            {
              "type": "text",
              "name": "departure-booking-number",
              "width": "65%",
              "minWidth": "256px",
              "title": "DEPARTURE",
              "titleLocation": "top",
              "validators": [
                {
                  "type": "regex",
                  "text": "Your booking number must consist of exactly 6 digits.",
                  "regex": "^\\d{6}$"
                }
              ],
              "maxLength": 6,
              "placeholder": "Please enter your 6-digit booking number"
            },
            {
              "type": "text",
              "name": "departure-flight-number",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "title": " ",
              "titleLocation": "top",
              "placeholder": "Flight #"
            },
            {
              "type": "multipletext",
              "name": "departure-date",
              "width": "65%",
              "minWidth": "256px",
              "items": [
                {
                  "name": "date",
                  "inputType": "date",
                  "title": "Date"
                }
              ]
            },
            {
              "type": "multipletext",
              "name": "departure-time",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "items": [
                {
                  "name": "time",
                  "inputType": "time",
                  "title": "Time"
                }
              ]
            },
            {
              "type": "dropdown",
              "name": "departure-country",
              "width": "65%",
              "minWidth": "256px",
              "title": "DEPARTING FROM",
              "choicesByUrl": {
                "url": "https://surveyjs.io/api/CountriesExample"
              },
              "placeholder": "Country",
              "allowClear": false
            },
            {
              "type": "text",
              "name": "departure-city",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "title": " ",
              "placeholder": "City"
            },
            {
              "type": "dropdown",
              "name": "destination-country",
              "width": "65%",
              "minWidth": "256px",
              "title": "DESTINATION",
              "titleLocation": "top",
              "choicesByUrl": {
                "url": "https://surveyjs.io/api/CountriesExample"
              },
              "placeholder": "Country",
              "allowClear": false
            },
            {
              "type": "text",
              "name": "destination-city",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "title": " ",
              "titleLocation": "top",
              "placeholder": "City"
            },
            {
              "type": "checkbox",
              "name": "connecting-flight",
              "width": "100%",
              "minWidth": "256px",
              "choices": [
                {
                  "value": "true",
                  "text": "I have a connecting flight"
                }
              ]
            },
            {
              "type": "multipletext",
              "name": "connecting-flight-date",
              "visibleIf": "{connecting-flight} = ['true']",
              "width": "65%",
              "minWidth": "256px",
              "items": [
                {
                  "name": "date",
                  "inputType": "date",
                  "title": "Date"
                }
              ]
            },
            {
              "type": "multipletext",
              "name": "connecting-flight-time",
              "visibleIf": "{connecting-flight} = ['true']",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "items": [
                {
                  "name": "time",
                  "inputType": "time",
                  "title": "Time"
                }
              ]
            },
            {
              "type": "text",
              "name": "connecting-flight-booking-number",
              "visibleIf": "{connecting-flight} = ['true']",
              "width": "65%",
              "minWidth": "256px",
              "title": "DEPARTURE",
              "validators": [
                {
                  "type": "regex",
                  "text": "Your booking number must consist of exactly 6 digits.",
                  "regex": "^\\d{6}$"
                }
              ],
              "maxLength": 6,
              "placeholder": "Please enter your 6-digit booking number"
            },
            {
              "type": "text",
              "name": "connecting-flight-number",
              "visibleIf": "{connecting-flight} = ['true']",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "placeholder": "Flight #"
            }
          ],
          "questionTitleLocation": "hidden",
          "width": "100%",
          "minWidth": "256px"
        }
      ]
    }
  ],
  "questionDescriptionLocation": "underInput",
  "questionErrorLocation": "bottom",
  "completeText": "Check In",
  "widthMode": "static",
  "width": "860"
}
```

### `src/theme_json.js`

```js
export const customTheme = {
  "backgroundImage": "",
  "backgroundImageFit": "cover",
  "backgroundImageAttachment": "scroll",
  "backgroundOpacity": 1,
  "isPanelless": true,
  "cssVariables": {
    "--sjs2-color-bg-basic-primary": "rgba(246, 248, 250, 1)",
    "--sjs2-color-utility-property-grid": "rgba(246, 248, 250, 1)",
    "--sjs2-color-utility-tabs": "rgba(246, 248, 250, 1)",
    "--sjs2-color-utility-toolbox": "rgba(246, 248, 250, 1)",
    "--sjs2-color-bg-basic-primary-dim": "rgba(248, 248, 248, 1)",
    "--sjs2-color-bg-neutral-tertiary-dim": "rgba(255, 255, 255, 1)",
    "--sjs2-color-utility-surface-survey-panelless": "rgba(255, 255, 255, 1)",
    "--sjs2-color-utility-surface-survey": "rgba(255, 255, 255, 1)",
    "--sjs2-color-bg-basic-secondary": "rgba(246, 248, 250, 1)",
    "--sjs2-color-bg-basic-secondary-dim": "rgba(243, 243, 243, 1)",
    "--sjs2-color-fg-basic-primary": "rgba(0, 0, 0, 0.91)",
    "--sjs2-color-fg-basic-secondary": "rgba(0, 0, 0, 0.45)",
    "--sjs2-color-project-brand-600": "rgba(9, 105, 218, 1)",
    "--sjs2-color-bg-brand-secondary": "rgba(9, 105, 218, 0.1)",
    "--sjs2-color-bg-brand-primary-dim": "rgba(8, 98, 203, 1)",
    "--sjs2-color-fg-brand-on-primary": "rgba(255, 255, 255, 1)",
    "--sjs2-color-fg-brand-primary-disabled": "rgba(255, 255, 255, 0.25)",
    "--sjs2-base-unit-size": "6px",
    "--sjs2-base-unit-spacing": "6px",
    "--sjs2-base-unit-radius": "4px",
    "--sjs2-color-bg-accent-primary": "rgba(255, 152, 20, 1)",
    "--sjs2-color-bg-accent-secondary": "rgba(255, 152, 20, 0.1)",
    "--sjs2-color-bg-accent-secondary-dim": "rgba(255, 152, 20, 0.25)",
    "--sjs2-color-fg-accent-on-primary": "rgba(255, 255, 255, 1)",
    "--sjs2-color-fg-accent-primary-disabled": "rgba(255, 255, 255, 0.25)",
    "--sjs2-border-effect-surface-default": "0px 0px 0px 1px rgba(101, 109, 118, 0.25), 0px 2px 0px 0px rgba(101, 109, 118, 0.05), inset 0px 1px 0px 0px rgba(255, 255, 255, 0.5)",
    "--sjs2-border-effect-floating-default": "0px 2px 6px 0px rgba(0, 0, 0, 0.1),0px 8px 16px 0px rgba(0, 0, 0, 0.1)",
    "--sjs2-border-effect-component-formbox-default": "inset 0px 0px 0px 1px rgba(101, 109, 118, 0.25), inset 0px 2px 0px 0px rgba(101, 109, 118, 0.05)",
    "--sjs2-border-effect-component-check-true-default": "inset 0px 0px 0px 1px rgba(101, 109, 118, 0.25), inset 0px 2px 0px 0px rgba(101, 109, 118, 0.05)",
    "--sjs2-border-effect-component-check-false-default": "inset 0px 0px 0px 1px rgba(101, 109, 118, 0.25), inset 0px 2px 0px 0px rgba(101, 109, 118, 0.05)",
    "--sjs2-color-border-basic-secondary": "rgba(216, 222, 228, 1)",
    "--sjs2-color-component-input-default-line": "rgba(216, 222, 228, 1)",
    "--sjs2-color-border-basic-secondary-overlay": "rgba(0, 0, 0, 0.16)",
    "--sjs2-color-bg-alert-primary": "rgba(229, 10, 62, 1)",
    "--sjs2-color-bg-alert-secondary": "rgba(229, 10, 62, 0.1)",
    "--sjs2-color-fg-alert-on-primary": "rgba(255, 255, 255, 1)",
    "--sjs2-color-bg-positive-primary": "rgba(25, 179, 148, 1)",
    "--sjs2-color-bg-positive-secondary": "rgba(25, 179, 148, 0.1)",
    "--sjs2-color-fg-positive-on-primary": "rgba(255, 255, 255, 1)",
    "--sjs2-color-bg-note-primary": "rgba(67, 127, 217, 1)",
    "--sjs2-color-bg-note-secondary": "rgba(67, 127, 217, 0.1)",
    "--sjs2-color-fg-note-on-primary": "rgba(255, 255, 255, 1)",
    "--sjs2-color-bg-warning-primary": "rgba(255, 152, 20, 1)",
    "--sjs2-color-bg-warning-secondary": "rgba(255, 152, 20, 0.1)",
    "--sjs2-color-fg-warning-on-primary": "rgba(255, 255, 255, 1)",
    "--sjs2-radius-form": "6px",
    "--sjs2-color-component-formbox-default-bg": "rgba(246, 248, 250, 1)",
    "--sjs2-color-component-check-false-hovered-bg": "rgba(243, 244, 246, 1)",
    "--sjs2-radius-component-panel": "6px",
    "--sjs2-color-component-panel-default-bg": "rgba(246, 248, 250, 1)",
    "--sjs2-color-unknown-variable-001": "rgba(243, 244, 246, 1)",
    "--sjs2-typography-font-family-text": "Open Sans",
    "--sjs2-typography-font-family-component-page-title": "Arial, sans-serif",
    "--sjs2-typography-font-weight-component-page-title": "600",
    "--sjs2-typography-font-size-component-page-title": "22px",
    "--sjs2-color-component-page-default-title": "rgba(31, 35, 40, 1)",
    "--sjs2-typography-font-family-component-page-description": "Arial, sans-serif",
    "--sjs2-typography-font-size-component-page-description": "13px",
    "--sjs2-color-component-page-default-description": "rgba(101, 109, 118, 1)",
    "--sjs2-typography-font-family-component-question-title": "Arial, sans-serif",
    "--sjs2-typography-font-size-component-question-title": "14px",
    "--sjs2-color-component-question-default-title": "rgba(31, 35, 40, 1)",
    "--sjs2-color-fg-basic-primary": "rgba(31, 35, 40, 1)",
    "--sjs2-typography-font-family-component-question-description": "Arial, sans-serif",
    "--sjs2-typography-font-size-component-question-description": "13px",
    "--sjs2-color-component-question-default-description": "rgba(101, 109, 118, 1)",
    "--sjs2-color-component-header-default-title": "rgba(255, 255, 255, 1)",
    "--sjs2-color-component-header-default-description": "rgba(255, 255, 255, 1)",
    "--sjs2-color-component-header-default-bg": "var(--sjs2-color-project-brand-600)",
    "--sjs2-typography-font-weight-component-header-title": "700",
    "--sjs2-typography-font-size-component-header-description": "20px",
    "--sjs2-typography-font-family-component-input-content": "Arial, sans-serif",
    "--sjs2-typography-font-size-component-input-content": "14px",
    "--sjs2-typography-line-height-component-input-content": "21px",
    "--sjs2-color-component-input-default-value": "rgba(33, 37, 42, 1)",
    "--sjs2-color-component-input-default-placeholder": "rgba(110, 119, 129, 1)",
    "--sjs2-color-component-boolean-item-false-default-value": "rgba(110, 119, 129, 1)",
    "--sjs2-color-component-input-default-label": "rgba(110, 119, 129, 1)",
    "--sjs2-typography-line-height-component-page-title": "29.26px",
    "--sjs2-typography-line-height-component-page-description": "19.5px",
    "--sjs2-typography-line-height-component-question-title": "21px",
    "--sjs2-typography-line-height-component-question-description": "19.5px"
   },
  "themeName": "custom",
  "colorPalette": "light",
  "headerView": "advanced",
  "header": {
    "height": 256,
    "inheritWidthFrom": "container",
    "textAreaWidth": 512,
    "logoPositionX": "right",
    "logoPositionY": "top",
    "titlePositionX": "left",
    "titlePositionY": "bottom",
    "descriptionPositionX": "left",
    "descriptionPositionY": "bottom"
  }
};
```

### `src/theme_variations.js`

```js
export const lightTheme = {
  "backgroundImage": "",
  "backgroundImageFit": "cover",
  "backgroundImageAttachment": "scroll",
  "backgroundOpacity": 1,
  "cssVariables": {
    "--sjs2-color-bg-basic-primary": "rgba(254, 247, 255, 1)",
    "--sjs2-color-utility-property-grid": "rgba(254, 247, 255, 1)",
    "--sjs2-color-utility-tabs": "rgba(254, 247, 255, 1)",
    "--sjs2-color-utility-toolbox": "rgba(254, 247, 255, 1)",
    "--sjs2-color-bg-basic-primary-dim": "rgba(248, 248, 248, 1)",
    "--sjs2-color-bg-neutral-tertiary-dim": "#FEF7FF",
    "--sjs2-color-utility-surface-survey-panelless": "#FEF7FF",
    "--sjs2-color-utility-surface-survey": "#FEF7FF",
    "--sjs2-color-bg-basic-secondary": "rgba(254, 247, 255, 1)",
    "--sjs2-color-bg-basic-secondary-dim": "rgba(243, 243, 243, 1)",
    "--sjs2-color-fg-basic-primary": "rgba(0, 0, 0, 0.91)",
    "--sjs2-color-fg-basic-secondary": "rgba(0, 0, 0, 0.45)",
    "--sjs2-color-project-brand-600": "rgba(104, 81, 164, 1)",
    "--sjs2-color-bg-brand-secondary": "rgba(104, 81, 164, 0.1)",
    "--sjs2-color-bg-brand-primary-dim": "rgba(115, 94, 171, 1)",
    "--sjs2-color-fg-brand-on-primary": "rgba(255, 255, 255, 1)",
    "--sjs2-color-fg-brand-primary-disabled": "rgba(255, 255, 255, 0.25)",
    "--sjs2-base-unit-size": "8px",
    "--sjs2-base-unit-spacing": "8px",
    "--sjs2-base-unit-radius": "4px",
    "--sjs2-color-bg-accent-primary": "rgba(255, 152, 20, 1)",
    "--sjs2-color-bg-accent-secondary": "rgba(255, 152, 20, 0.1)",
    "--sjs2-color-bg-accent-secondary-dim": "rgba(255, 152, 20, 0.25)",
    "--sjs2-color-fg-accent-on-primary": "rgba(255, 255, 255, 1)",
    "--sjs2-color-fg-accent-primary-disabled": "rgba(255, 255, 255, 0.25)",
    "--sjs2-border-effect-surface-default": "0px 0px 0px 1px rgba(121, 116, 126, 1)",
    "--sjs2-border-effect-floating-default": "0px 2px 6px 0px rgba(0, 0, 0, 0.1),0px 8px 16px 0px rgba(0, 0, 0, 0.1)",
    "--sjs2-border-effect-component-formbox-default": "inset 0px 0px 0px 1px rgba(121, 116, 126, 1)",
    "--sjs2-border-effect-component-check-true-default": "inset 0px 0px 0px 1px rgba(121, 116, 126, 1)",
    "--sjs2-border-effect-component-check-false-default": "inset 0px 0px 0px 1px rgba(121, 116, 126, 1)",
    "--sjs2-color-border-basic-secondary": "rgba(230, 224, 233, 1)",
    "--sjs2-color-component-input-default-line": "rgba(202, 196, 208, 1)",
    "--sjs2-color-border-basic-secondary-overlay": "rgba(0, 0, 0, 0.16)",
    "--sjs2-color-bg-alert-primary": "rgba(229, 10, 62, 1)",
    "--sjs2-color-bg-alert-secondary": "rgba(229, 10, 62, 0.1)",
    "--sjs2-color-fg-alert-on-primary": "rgba(255, 255, 255, 1)",
    "--sjs2-color-bg-positive-primary": "rgba(25, 179, 148, 1)",
    "--sjs2-color-bg-positive-secondary": "rgba(25, 179, 148, 0.1)",
    "--sjs2-color-fg-positive-on-primary": "rgba(255, 255, 255, 1)",
    "--sjs2-color-bg-note-primary": "rgba(67, 127, 217, 1)",
    "--sjs2-color-bg-note-secondary": "rgba(67, 127, 217, 0.1)",
    "--sjs2-color-fg-note-on-primary": "rgba(255, 255, 255, 1)",
    "--sjs2-color-bg-warning-primary": "rgba(255, 152, 20, 1)",
    "--sjs2-color-bg-warning-secondary": "rgba(255, 152, 20, 0.1)",
    "--sjs2-color-fg-warning-on-primary": "rgba(255, 255, 255, 1)",
    "--sjs2-color-component-formbox-default-bg": "rgba(254, 247, 255, 1)",
    "--sjs2-color-component-check-false-hovered-bg": "rgba(242, 234, 248, 1)",
    "--sjs2-color-component-panel-default-bg": "rgba(254, 247, 255, 1)",
    "--sjs2-color-unknown-variable-001": "rgba(242, 234, 248, 1)",
    "--sjs2-typography-font-weight-component-header-title": "700",
    "--sjs2-typography-font-size-component-header-description": "20px",
    "--sjs2-typography-font-weight-component-page-title": "600",
    "--sjs2-typography-font-size-component-page-title": "22px",
    "--sjs2-color-component-page-default-title": "rgba(29, 27, 32, 1)",
    "--sjs2-color-component-page-default-description": "rgba(73, 69, 79, 1)",
    "--sjs2-typography-font-weight-component-question-title": "400",
    "--sjs2-color-component-question-default-title": "rgba(29, 27, 32, 1)",
    "--sjs2-color-fg-basic-primary": "rgba(29, 27, 32, 1)",
    "--sjs2-typography-font-size-component-question-description": "14px",
    "--sjs2-color-component-question-default-description": "rgba(73, 69, 79, 1)",
    "--sjs2-color-component-input-default-value": "rgba(29, 27, 32, 1)",
    "--sjs2-color-component-input-default-placeholder": "rgba(73, 69, 79, 1)",
    "--sjs2-color-component-boolean-item-false-default-value": "rgba(73, 69, 79, 1)",
    "--sjs2-color-component-input-default-label": "rgba(73, 69, 79, 1)",
    "--sjs2-typography-line-height-component-page-title": "29.26px",
    "--sjs2-typography-line-height-component-question-description": "21px"
  },
  "themeName": "custom_with_color_variations",
  "colorPalette": "light",
  "isPanelless": true
};

export const darkTheme = {
  "backgroundImage": "",
  "backgroundImageFit": "cover",
  "backgroundImageAttachment": "scroll",
  "backgroundOpacity": 1,
  "cssVariables": {
    "--sjs2-color-bg-basic-primary": "rgba(20, 18, 24, 1)",
    "--sjs2-color-utility-property-grid": "rgba(20, 18, 24, 1)",
    "--sjs2-color-utility-tabs": "rgba(20, 18, 24, 1)",
    "--sjs2-color-utility-toolbox": "rgba(20, 18, 24, 1)",
    "--sjs2-color-bg-basic-primary-dim": "rgba(52, 52, 52, 1)",
    "--sjs2-color-bg-neutral-tertiary-dim": "#141218",
    "--sjs2-color-utility-surface-survey-panelless": "#141218",
    "--sjs2-color-utility-surface-survey": "#141218",
    "--sjs2-color-bg-basic-secondary": "rgba(20, 18, 24, 1)",
    "--sjs2-color-bg-basic-secondary-dim": "rgba(46, 46, 46, 1)",
    "--sjs2-color-fg-basic-primary": "rgba(255, 255, 255, 0.78)",
    "--sjs2-color-fg-basic-secondary": "rgba(255, 255, 255, 0.42)",
    "--sjs2-color-project-brand-600": "rgba(208, 188, 255, 1)",
    "--sjs2-color-bg-brand-secondary": "rgba(208, 188, 255, 0.12)",
    "--sjs2-color-bg-brand-primary-dim": "rgba(196, 175, 244, 1)",
    "--sjs2-color-fg-brand-on-primary": "rgba(56, 30, 114, 1)",
    "--sjs2-color-fg-brand-primary-disabled": "rgba(56, 30, 114, 0.25)",
    "--sjs2-base-unit-size": "8px",
    "--sjs2-base-unit-spacing": "8px",
    "--sjs2-base-unit-radius": "4px",
    "--sjs2-color-bg-accent-primary": "rgba(255, 152, 20, 1)",
    "--sjs2-color-bg-accent-secondary": "rgba(255, 152, 20, 0.1)",
    "--sjs2-color-bg-accent-secondary-dim": "rgba(255, 152, 20, 0.25)",
    "--sjs2-color-fg-accent-on-primary": "rgba(48, 48, 48, 1)",
    "--sjs2-color-fg-accent-primary-disabled": "rgba(48, 48, 48, 0.25)",
    "--sjs2-border-effect-surface-default": "0px 0px 0px 1px rgba(147, 143, 153, 1)",
    "--sjs2-border-effect-floating-default": "0px 2px 6px 0px rgba(0, 0, 0, 0.2),0px 8px 16px 0px rgba(0, 0, 0, 0.2)",
    "--sjs2-border-effect-component-formbox-default": "inset 0px 0px 0px 1px rgba(147, 143, 153, 1)",
    "--sjs2-border-effect-component-check-true-default": "inset 0px 0px 0px 1px rgba(147, 143, 153, 1)",
    "--sjs2-border-effect-component-check-false-default": "inset 0px 0px 0px 1px rgba(147, 143, 153, 1)",
    "--sjs2-color-border-basic-secondary": "rgba(54, 52, 59, 1)",
    "--sjs2-color-component-input-default-line": "rgba(73, 69, 79, 1)",
    "--sjs2-color-border-basic-secondary-overlay": "rgba(255, 255, 255, 0.08)",
    "--sjs2-color-bg-alert-primary": "rgba(254, 76, 108, 1)",
    "--sjs2-color-bg-alert-secondary": "rgba(254, 76, 108, 0.1)",
    "--sjs2-color-fg-alert-on-primary": "rgba(48, 48, 48, 1)",
    "--sjs2-color-bg-positive-primary": "rgba(36, 197, 164, 1)",
    "--sjs2-color-bg-positive-secondary": "rgba(36, 197, 164, 0.1)",
    "--sjs2-color-fg-positive-on-primary": "rgba(48, 48, 48, 1)",
    "--sjs2-color-bg-note-primary": "rgba(91, 151, 242, 1)",
    "--sjs2-color-bg-note-secondary": "rgba(91, 151, 242, 0.1)",
    "--sjs2-color-fg-note-on-primary": "rgba(48, 48, 48, 1)",
    "--sjs2-color-bg-warning-primary": "rgba(255, 152, 20, 1)",
    "--sjs2-color-bg-warning-secondary": "rgba(255, 152, 20, 0.1)",
    "--sjs2-color-fg-warning-on-primary": "rgba(48, 48, 48, 1)",
    "--sjs2-color-component-formbox-default-bg": "rgba(20, 18, 24, 1)",
    "--sjs2-color-component-check-false-hovered-bg": "rgba(208, 188, 255, 0.08)",
    "--sjs2-color-component-panel-default-bg": "rgba(20, 18, 24, 1)",
    "--sjs2-color-unknown-variable-001": "rgba(208, 188, 255, 0.08)",
    "--sjs2-typography-font-weight-component-header-title": "700",
    "--sjs2-typography-font-size-component-header-description": "20px",
    "--sjs2-typography-font-weight-component-page-title": "600",
    "--sjs2-typography-font-size-component-page-title": "22px",
    "--sjs2-color-component-page-default-title": "rgba(230, 224, 233, 1)",
    "--sjs2-color-component-page-default-description": "rgba(202, 196, 208, 1)",
    "--sjs2-color-component-question-default-title": "rgba(230, 224, 233, 1)",
    "--sjs2-color-fg-basic-primary": "rgba(230, 224, 233, 1)",
    "--sjs2-typography-font-size-component-question-description": "14px",
    "--sjs2-color-component-question-default-description": "rgba(202, 196, 208, 1)",
    "--sjs2-color-component-input-default-value": "rgba(230, 224, 233, 1)",
    "--sjs2-color-component-input-default-placeholder": "rgba(202, 196, 208, 1)",
    "--sjs2-color-component-boolean-item-false-default-value": "rgba(202, 196, 208, 1)",
    "--sjs2-color-component-input-default-label": "rgba(202, 196, 208, 1)",
    "--sjs2-typography-line-height-component-page-title": "29.26px",
    "--sjs2-typography-line-height-component-question-description": "21px"
  },
  "themeName": "custom_with_color_variations",
  "colorPalette": "dark",
  "isPanelless": true
};
```

### `src/index.js`

```js
import { SurveyCreator } from "survey-creator-js";
import "survey-core/survey.i18n";
import "survey-creator-core/survey-creator-core.i18n";
import { formJSON } from "./survey_json";
import { customTheme } from "./theme_json";
import { lightTheme, darkTheme } from "./theme_variations";
import { SurveyModel, Action, settings, surveyLocalization, SvgRegistry } from "survey-core";
import { getLocaleStrings, PredefinedThemes } from "survey-creator-core";
import "survey-core/survey-core.css";
import "survey-creator-core/survey-creator-core.css";
import "./index.css";
import SurveyTheme from "survey-core/themes";
import { registerCreatorTheme, registerSurveyTheme } from "survey-creator-core";

registerSurveyTheme(SurveyTheme); // Add predefined Form Library UI themes
registerCreatorTheme(SurveyTheme); // Add predefined Survey Creator UI themes

const enLocale = getLocaleStrings("en");

const creator = new SurveyCreator({ showThemeTab: true });

// Register a custom SVG icon for the Save Theme action
const saveAsIcon = '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d = "M24 11H22V13H20V11H18V9H20V7H22V9H24V11ZM20 14H22V20C22 21.1 21.1 22 20 22H4C2.9 22 2 21.1 2 20V4L4 2H20C21.1 2 22 2.9 22 4V6H20V4H17V8H7V4H4.83L4 4.83V20H6V13H18V20H20V14ZM9 6H15V4H9V6ZM16 15H8V20H16V15Z" fill = "black" fill-opacity="0.45" /></svg>';
SvgRegistry.registerIcon("icon-saveas", saveAsIcon);

const themeTabPlugin = creator.themeEditor;

function addCustomTheme(theme, userFriendlyThemeName) {
    // Add a localized user-friendly theme name
    enLocale.theme.names[theme.themeName] = userFriendlyThemeName;
    // Add the theme to the theme list
    themeTabPlugin.addTheme(theme);
}

// Add a custom theme to the Theme Editor
addCustomTheme(customTheme, "Custom Theme");

// Set the custom theme as an initial survey theme
creator.theme = customTheme;

// Register a custom theme with Dark and Light variations
addCustomTheme(lightTheme, "Custom Theme with Dark/Light Variations");
addCustomTheme(darkTheme, "Custom Theme with Dark/Light Variations");

function askForThemeName(title, text, initialValue, callback) {
    const survey = new SurveyModel({
        showNavigationButtons: false,
        questionErrorLocation: "bottom",
        questions: [{
            type: "text",
            name: "title",
            title: text,
            defaultValue: initialValue.title,
            isRequired: true,
            requiredErrorText: "Theme title is required"
        }]
    });
    survey.isCompact = true;
    const popupViewModel = settings.showDialog({
        componentName: "survey",
        data: { model: survey},
        onApply: () => {
            if (survey.tryComplete()) {
                callback(true, survey.data);
                return true;
            }
        },
        onCancel: () => {
            callback(false);
            return false;
        },
        title: title,
        displayMode: "popup",
        isFocusedContent: true,
        cssClass: "my-dialog"
    }, settings.environment.popupMountContainer);

    const toolbar = popupViewModel.footerToolbar;
    const applyBtn = toolbar.getActionById("apply");
    const cancelBtn = toolbar.getActionById("cancel");
    cancelBtn.title = surveyLocalization.getString("cancel");
    applyBtn.title = surveyLocalization.getString("ok");
}

let themeId = 1;
function saveCustomTheme() {
    // Get the current theme
    const currentTheme = creator.theme;
    // Generate a unique theme name
    currentTheme.themeName += "_modified_" + themeId;
    // Generate a human-friendly theme name
    const themeTitle = "My Custom Theme " + themeId;
    askForThemeName("Do you want to save the current theme configuration?", "Enter a theme title", { title: themeTitle }, (confirm, data) => {
        if (confirm) {
            addCustomTheme(currentTheme, data.title);
            // Set the theme as a current theme; update the theme list and theme options
            const themeModel = themeTabPlugin.themeModel;
            themeModel.setTheme(currentTheme);
            themeId++;
            updateCustomActions();
            // ...
            // (Optional) Save the theme to an external storage here
            // ...
        }
    });
};
function deleteCurrentCustomTheme() {
    const currentTheme = creator.theme;
    const builtInThemeIndex = PredefinedThemes.indexOf(currentTheme.themeName);
    if (builtInThemeIndex === -1) { // A custom theme
        const enLocale = getLocaleStrings("en");
        settings.confirmActionAsync("Do you want to delete the following theme: \"" + enLocale.theme.names[currentTheme.themeName] + "\"?", (confirm) => {
            if (confirm) {
                themeTabPlugin.removeTheme(currentTheme, true);
                const themeModel = themeTabPlugin.themeModel;
                themeModel.setTheme({ themeName: "default" });
                updateCustomActions();
                // ...
                // (Optional) Delete the theme from an external storage here
                // ...
            }
        });
    }
};

// Register custom actions to save and delete a current theme modification
const saveThemeAction = new Action({
    id: "svd-save-custom-theme",
    tooltip: "Add custom theme to the list",
    action: saveCustomTheme,
    iconName: "icon-saveas"
});
creator.toolbar.actions.push(saveThemeAction);

const deleteThemeAction = new Action({
    id: "svd-delete-custom-theme",
    tooltip: "Delete theme",
    action: deleteCurrentCustomTheme,
    iconName: "icon-delete"
});
creator.toolbar.actions.push(deleteThemeAction);

function updateCustomActions() {
    const isThemeTab = creator.activeTab === "theme";
    saveThemeAction.visible = isThemeTab;
    const currentTheme = creator.theme;
    const isCustomTheme = PredefinedThemes.indexOf(currentTheme.themeName) === -1;
    deleteThemeAction.visible = isThemeTab && isCustomTheme;
}

// Update the Save and Delete action visibility when a user selects a theme or activates another tab
updateCustomActions();
themeTabPlugin.onThemeSelected.add(updateCustomActions);
creator.onActiveTabChanged.add(updateCustomActions);
themeTabPlugin.advancedModeEnabled = true;

// Initialize a survey JSON
creator.JSON = formJSON;

// Activate the Themes Tab
creator.activeTab = "theme";

creator.render("surveyCreatorContainer");
```

### `package.json`

```json
{
  "dependencies": {
    "survey-core": "latest",
    "survey-js-ui": "latest",
    "survey-creator-core": "latest",
    "survey-creator-js": "latest"
  },
  "devDependencies": {
    "react-scripts": "latest"
  }
}
```

## Other Frameworks

- [Angular](https://surveyjs.io/survey-creator/examples/save-custom-theme/angular.md)
- [React](https://surveyjs.io/survey-creator/examples/save-custom-theme/reactjs.md)
- [Vue 3](https://surveyjs.io/survey-creator/examples/save-custom-theme/vue3js.md)
- [jQuery](https://surveyjs.io/survey-creator/examples/save-custom-theme/jquery.md)
