diff --git a/packages/web/src/scss/components/UNSTABLE_Toggle/README.md b/packages/web/src/scss/components/UNSTABLE_Toggle/README.md
new file mode 100644
index 0000000000..0fd2759a89
--- /dev/null
+++ b/packages/web/src/scss/components/UNSTABLE_Toggle/README.md
@@ -0,0 +1,193 @@
+# UNSTABLE Toggle
+
+> ⚠️ This component is UNSTABLE. It may significantly change at any point in the future.
+> Please use it with caution.
+
+Toggle is a form control that allows users to switch between two states.
+
+## Basic Usage
+
+The Toggle component implements the HTML [checkbox input][mdn-checkbox] element. It uses
+the native input element and styles it to look like a toggle switch.
+
+```html
+
+```
+
+## Indicators
+
+If you need to indicate the state of the toggle, you can add the `UNSTABLE_Toggle__input--indicators`
+modifier class to the input. This will add a visual indicators to the toggle switch.
+
+```html
+
+```
+
+## Required
+
+Add the `required` attribute to the input to mark it as required and add the
+`UNSTABLE_Toggle__label--required` modifier class to the label to indicate the state.
+
+```html
+
+```
+
+## Hidden Label
+
+```html
+
+```
+
+## Fluid
+
+```html
+
+```
+
+## Helper Text
+
+```html
+
+```
+
+## Validation States
+
+Validation states can be presented either by adding a CSS modifier class
+(`UNSTABLE_Toggle--success`, `UNSTABLE_Toggle--warning`, `UNSTABLE_Toggle--danger`), or by adding
+a JS interaction class when controlled by JavaScript (`has-success`,
+`has-warning`, `has-danger`). See Validation state [dictionary][dictionary-validation].
+
+```html
+
+
+
+
+
+
+
+
+
First validation text
+
Second validation text
+
+
+
+
+```
+
+### JavaScript-Controlled Validation Text
+
+When implementing client-side form validation, use JS interaction state classes
+(`has-success`, `has-warning`, `has-danger`) on the wrapping `
` element and
+render validation texts in a `
` or `
` with `data-spirit-element="validation_text"`
+attribute. This way your JS remains disconnected from CSS that may or may not be
+[prefixed][prefixed].
+
+**Remember this approach is only valid for vanilla JS implementation. React
+components mix CSS with JS by design and handle prefixes their own way.**
+
+```html
+
+```
+
+## Disabled State
+
+On top of adding the `disabled` attribute to the input, disabled Toggle needs to
+be marked by adding `UNSTABLE_Toggle--disabled` modifier class, or with `is-disabled`
+JS interaction class when controlled by JavaScript:
+
+```html
+
+```
+
+[dictionary-validation]: https://github.com/lmc-eu/spirit-design-system/blob/main/docs/DICTIONARIES.md#validation
+[mdn-checkbox]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox
+[prefixed]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web/README.md#prefixing-css-class-names
diff --git a/packages/web/src/scss/components/UNSTABLE_Toggle/_UNSTABLE_Toggle.scss b/packages/web/src/scss/components/UNSTABLE_Toggle/_UNSTABLE_Toggle.scss
new file mode 100644
index 0000000000..4e5b7ea55f
--- /dev/null
+++ b/packages/web/src/scss/components/UNSTABLE_Toggle/_UNSTABLE_Toggle.scss
@@ -0,0 +1,128 @@
+@use 'theme';
+@use '../../settings/cursors';
+@use '../../theme/form-fields' as form-fields-theme;
+@use '../../tools/form-fields' as form-fields-tools;
+@use '../../tools/svg';
+
+$_field-name: 'UNSTABLE_Toggle';
+
+.UNSTABLE_Toggle {
+ display: flex;
+ align-items: start;
+ justify-content: space-between;
+ max-width: theme.$max-width;
+ margin-block: form-fields-theme.$gap;
+ cursor: cursors.$form-fields;
+}
+
+.UNSTABLE_Toggle--fluid {
+ max-width: none;
+}
+
+.UNSTABLE_Toggle__text {
+ margin-right: form-fields-theme.$gap;
+}
+
+.UNSTABLE_Toggle__label {
+ @include form-fields-tools.inline-field-label();
+}
+
+.UNSTABLE_Toggle__label--hidden {
+ @include form-fields-tools.label-hidden();
+}
+
+.UNSTABLE_Toggle__label--required::after {
+ @include form-fields-tools.label-required();
+}
+
+.UNSTABLE_Toggle__input {
+ @include form-fields-tools.inline-field-input();
+
+ flex-shrink: 0;
+ width: theme.$input-width;
+ height: theme.$input-height;
+ border: theme.$input-border-width solid form-fields-theme.$inline-field-input-color-unchecked;
+ border-radius: theme.$input-border-radius;
+ background-image: svg.escape(theme.$input-mark);
+ background-position-x: theme.$input-mark-position-x;
+ background-position-y: theme.$input-mark-position-y;
+ background-size: theme.$input-mark-width theme.$input-mark-height;
+ background-repeat: no-repeat;
+ background-color: form-fields-theme.$inline-field-input-color-unchecked;
+
+ @media (prefers-reduced-motion: no-preference) {
+ transition-property: border-color, background-color, background-position;
+ transition-duration: theme.$input-transition-duration;
+ transition-timing-function: theme.$input-transition-timing;
+ }
+
+ &:focus {
+ background-color: form-fields-theme.$inline-field-input-color-unchecked-active;
+ box-shadow: theme.$input-focus-shadow;
+ }
+
+ &:checked {
+ border-color: form-fields-theme.$inline-field-input-color-checked;
+ background-position-x: theme.$input-mark-position-x-checked;
+ background-color: form-fields-theme.$inline-field-input-color-checked;
+ }
+
+ @media (hover: hover) {
+ &:hover {
+ background-color: form-fields-theme.$inline-field-input-color-unchecked-hover;
+ }
+
+ &:checked:hover {
+ background-color: form-fields-theme.$inline-field-input-color-checked-hover;
+ }
+ }
+}
+
+.UNSTABLE_Toggle__input--indicators {
+ background-image: svg.escape(theme.$input-mark-indicators);
+}
+
+.UNSTABLE_Toggle__validationText,
+.UNSTABLE_Toggle > .UNSTABLE_Toggle__text > [data-spirit-element='validation_text'] {
+ @include form-fields-tools.validation-text();
+}
+
+.UNSTABLE_Toggle__helperText {
+ @include form-fields-tools.helper-text();
+}
+
+@include form-fields-tools.input-field-validation-states($_field-name);
+
+.UNSTABLE_Toggle--disabled {
+ @include form-fields-tools.inline-field-root-disabled();
+}
+
+.UNSTABLE_Toggle--disabled .UNSTABLE_Toggle__label {
+ @include form-fields-tools.label-disabled();
+}
+
+.UNSTABLE_Toggle--disabled > .UNSTABLE_Toggle__input,
+.UNSTABLE_Toggle > .UNSTABLE_Toggle__input:disabled {
+ @include form-fields-tools.input-disabled();
+
+ border-color: theme.$input-border-color-disabled;
+ background-image: svg.escape(theme.$input-mark-disabled);
+ background-color: transparent;
+}
+
+.UNSTABLE_Toggle__input:checked:focus {
+ background-color: form-fields-theme.$inline-field-input-color-checked-active;
+}
+
+.UNSTABLE_Toggle--disabled > .UNSTABLE_Toggle__input:checked,
+.UNSTABLE_Toggle > .UNSTABLE_Toggle__input:disabled:checked {
+ background-image: svg.escape(theme.$input-mark-disabled-checked);
+}
+
+.UNSTABLE_Toggle--disabled .UNSTABLE_Toggle__validationText {
+ @include form-fields-tools.validation-text-disabled();
+}
+
+.UNSTABLE_Toggle--disabled .UNSTABLE_Toggle__helperText {
+ @include form-fields-tools.helper-text-disabled();
+}
diff --git a/packages/web/src/scss/components/UNSTABLE_Toggle/_theme.scss b/packages/web/src/scss/components/UNSTABLE_Toggle/_theme.scss
new file mode 100644
index 0000000000..c850ae200b
--- /dev/null
+++ b/packages/web/src/scss/components/UNSTABLE_Toggle/_theme.scss
@@ -0,0 +1,21 @@
+@use '@tokens' as tokens;
+@use '../../settings/transitions';
+
+$max-width: 21rem;
+$input-border-width: tokens.$border-width-100;
+$input-border-radius: 12px;
+$input-width: 44px;
+$input-height: 24px;
+$input-mark-height: 44px;
+$input-mark-width: 52px;
+$input-mark-position-y: -7px; // Position of the mark is offset because it has a shadow
+$input-mark-position-x: -15px; // Position of the mark in non-checked state
+$input-mark-position-x-checked: $input-mark-position-x + 20px; // Position of the mark in checked state
+$input-mark: url('data:image/svg+xml,');
+$input-mark-indicators: url('data:image/svg+xml,');
+$input-mark-disabled: url('data:image/svg+xml,');
+$input-mark-disabled-checked: url('data:image/svg+xml,');
+$input-transition-duration: transitions.$duration-100;
+$input-transition-timing: transitions.$timing-eased-in-out;
+$input-border-color-disabled: tokens.$border-primary-disabled;
+$input-focus-shadow: tokens.$focus;
diff --git a/packages/web/src/scss/components/UNSTABLE_Toggle/index.html b/packages/web/src/scss/components/UNSTABLE_Toggle/index.html
new file mode 100644
index 0000000000..a9d56826f2
--- /dev/null
+++ b/packages/web/src/scss/components/UNSTABLE_Toggle/index.html
@@ -0,0 +1,189 @@
+{{#> web/layout/plain }}
+
+
+