diff --git a/.changeset/three-days-fold.md b/.changeset/three-days-fold.md
new file mode 100644
index 0000000..c3cdb5f
--- /dev/null
+++ b/.changeset/three-days-fold.md
@@ -0,0 +1,5 @@
+---
+"@openfun/cunningham-react": minor
+---
+
+add Switch component
diff --git a/packages/react/src/components/Forms/Switch/index.mdx b/packages/react/src/components/Forms/Switch/index.mdx
new file mode 100644
index 0000000..3fa5768
--- /dev/null
+++ b/packages/react/src/components/Forms/Switch/index.mdx
@@ -0,0 +1,137 @@
+import { Canvas, Story, Meta, ArgsTable, Source } from '@storybook/addon-docs';
+import { Switch } from './index';
+import * as Stories from './index.stories';
+
+
+
+# Switch
+
+Cunningham provides a Switch component that can be used in a variety of ways.
+
+> To better understand the Switch component, keep in mind that it is kind of a wrapper around the native HTML checkbox element.
+
+
+
+
+
+
+## Label
+
+The `label` props is optional, but you can use it to provide a description of the switch.
+
+**Without label**
+
+
+
+
+
+**With label**
+
+
+
+
+
+## Label Side
+
+You can decide where to place the label by using the `labelSide` prop.
+
+> By default the label is placed on the left.
+
+**Label on the left (default)**
+
+
+
+
+
+**Label on the right**
+
+
+
+
+
+## Disabled
+
+You can disable the switch by using the `disabled` prop.
+
+
+
+
+
+
+## States
+
+You can use the following props to change the state of the component by using the `state` props.
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Width
+
+By default, the Switch will automatically take the minimum needed width. But you can force it to take the full width of
+its container by using the `fullWidth` prop.
+
+
+
+
+
+
+
+
+
+## Controlled / Non Controlled
+
+In order to control the value of the switch, you can use the `checked` or `defaultChecked` props, as the native HTML checkbox element. You can't use both at the same time.
+
+> If you use the `checked` prop, you will need to handle the `onChange` event to update the value of the switch.
+
+> If you use the `defaultChecked` prop, the switch will be uncontrolled.
+
+
+
+
+
+## Props
+
+The props of this component are as close as possible to the native checkbox component. You can see the list of props below.
+
+
+
+## Design tokens
+
+Here are available custom design tokens.
+
+| Token | Description |
+|--------------- |----------------------------- |
+| accent-color | Color of the background |
+| rail-background-color | Color of the switch rail background |
+| rail-background-color--disabled | Color of the switch rail background when disabled |
+| rail-border-radius | Border radius of the switch rail |
+| handle-background-color | Background color of the switch handle |
+| handle-background-color--disabled | Background color of the switch handle when disabled |
+| handle-border-radius | Border radius of the switch handle when disabled |
+
+The design tokens `font-size`, `font-weight`, `color`, `width`, `height` are shared with [Checkbox](?path=/story/components-forms-checkbox-doc--page)
+
+See also [Field](?path=/story/components-forms-field-doc--page)
+
+
+##
+
+
+
+##
+
+
+
+##
+
+
diff --git a/packages/react/src/components/Forms/Switch/index.scss b/packages/react/src/components/Forms/Switch/index.scss
new file mode 100644
index 0000000..edc6381
--- /dev/null
+++ b/packages/react/src/components/Forms/Switch/index.scss
@@ -0,0 +1,89 @@
+.c__switch {
+
+ input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+ margin: 0;
+ // This is made to prevent a bug on Chromium were was the labelSide is set to right then it
+ // creates an artificial margin between successive switches.
+ position: absolute;
+ }
+
+ .c__checkbox__container {
+ justify-content: space-between;
+ }
+
+ input:checked + &__rail {
+ background-color: var(--c--components--forms-switch--accent-color);
+ }
+
+ input:checked + &__rail:before {
+ transform: translateX(20px);
+ }
+
+ &__rail__wrapper {
+ display: inline-flex;
+ }
+
+ &__rail {
+ position: relative;
+ cursor: pointer;
+ width: 2.8125rem;
+ height: 1.5rem;
+ background-color: var(--c--components--forms-switch--rail-background-color);
+ transition: var(--c--theme--transitions--duration) var(--c--theme--transitions--ease-out);
+ border-radius: var(--c--components--forms-switch--rail-border-radius);
+
+ &:before {
+ position: absolute;
+ content: "";
+ height: 1.125rem;
+ width: 1.125rem;
+ left: 4px;
+ top: 3px;
+ background-color: var(--c--components--forms-switch--handle-background-color);
+ transition: var(--c--theme--transitions--duration) var(--c--theme--transitions--ease-out);
+ border-radius: var(--c--components--forms-switch--handle-border-radius);
+ }
+ }
+
+ .c__field__footer {
+ padding: 0.25rem 0 0 0;
+ }
+
+ &.c__checkbox--disabled {
+
+ input:not(:checked) + .c__switch__rail {
+ background-color: var(--c--components--forms-switch--rail-background-color--disabled);
+ }
+
+ .c__switch__rail {
+ cursor: default;
+
+ &:before {
+ background-color: var(--c--components--forms-switch--handle-background-color--disabled);
+ }
+ }
+ }
+
+ &--right {
+ .c__checkbox__container {
+ flex-direction: row-reverse;
+ }
+
+ .c__field__footer {
+ padding: 0.25rem 0 0 3.3rem;
+ }
+
+ &.c__switch--full-width {
+ .c__field__footer {
+ flex-direction: row-reverse;
+ }
+ }
+ }
+
+ &--full-width {
+ width: 100%;
+ }
+}
diff --git a/packages/react/src/components/Forms/Switch/index.spec.tsx b/packages/react/src/components/Forms/Switch/index.spec.tsx
new file mode 100644
index 0000000..8470bba
--- /dev/null
+++ b/packages/react/src/components/Forms/Switch/index.spec.tsx
@@ -0,0 +1,135 @@
+import { render, screen } from "@testing-library/react";
+import React from "react";
+import userEvent from "@testing-library/user-event";
+import { Switch } from ":/components/Forms/Switch/index";
+
+describe(" ", () => {
+ it("renders and can be checked", async () => {
+ const user = userEvent.setup();
+ render( );
+ const input: HTMLInputElement = screen.getByRole("checkbox", {
+ name: "Newsletter",
+ });
+ expect(input.checked).toEqual(false);
+ await user.click(input);
+ expect(input.checked).toEqual(true);
+ });
+
+ it("renders with default value and can be unchecked", async () => {
+ const user = userEvent.setup();
+ render( );
+ const input: HTMLInputElement = screen.getByRole("checkbox", {
+ name: "Newsletter",
+ });
+ expect(input.checked).toEqual(true);
+ await user.click(input);
+ expect(input.checked).toEqual(false);
+ });
+
+ it("renders disabled", async () => {
+ render( );
+ expect(screen.getByRole("checkbox", { name: "Newsletter" })).toBeDisabled();
+ // Click and expect the checkbox does not get checked
+ const user = userEvent.setup();
+ const input: HTMLInputElement = screen.getByRole("checkbox", {
+ name: "Newsletter",
+ });
+ expect(input.checked).toEqual(false);
+ await user.click(input);
+ expect(input.checked).toEqual(false);
+ });
+
+ it("renders with text", async () => {
+ render( );
+ screen.getByText("Text");
+ });
+
+ it("renders with state=success", async () => {
+ render( );
+ screen.getByText("Success text");
+ expect(
+ document.querySelector(".c__field.c__field--success")
+ ).toBeInTheDocument();
+ });
+
+ it("renders with state=error", async () => {
+ render( );
+ screen.getByText("Error text");
+ expect(
+ document.querySelector(".c__field.c__field--error")
+ ).toBeInTheDocument();
+ });
+
+ it("renders multiple", async () => {
+ // make sure switching one does not switch the others.
+ const user = userEvent.setup();
+ render(
+
+
+
+
+
+ );
+ // expect all checkboxes to be unchecked
+ const newsletter: HTMLInputElement = screen.getByRole("checkbox", {
+ name: "Newsletter",
+ });
+ const notifications: HTMLInputElement = screen.getByRole("checkbox", {
+ name: "Notifications",
+ });
+ const phone: HTMLInputElement = screen.getByRole("checkbox", {
+ name: "Phone",
+ });
+ expect(newsletter.checked).toEqual(false);
+ expect(notifications.checked).toEqual(false);
+ expect(phone.checked).toEqual(false);
+
+ // Turn on only one checkbox.
+ await user.click(newsletter);
+ expect(newsletter.checked).toEqual(true);
+ expect(notifications.checked).toEqual(false);
+ expect(phone.checked).toEqual(false);
+
+ // Turn off only one checkbox.
+ await user.click(newsletter);
+ expect(newsletter.checked).toEqual(false);
+ expect(notifications.checked).toEqual(false);
+ expect(phone.checked).toEqual(false);
+ });
+
+ it("renders with label right", async () => {
+ render( );
+ const input: HTMLInputElement = screen.getByRole("checkbox", {
+ name: "Newsletter",
+ });
+ expect(input.closest(".c__switch")).toHaveClass("c__switch--right");
+ });
+
+ it("renders controlled", async () => {
+ const Wrapper = () => {
+ const [checked, setChecked] = React.useState(false);
+ return (
+
+
Value: {JSON.stringify(checked)}.
+
setChecked(e.target.checked)}
+ />
+
+ );
+ };
+ render( );
+ const input: HTMLInputElement = screen.getByRole("checkbox", {
+ name: "Newsletter",
+ });
+ expect(input.checked).toEqual(false);
+ screen.queryByText("Value: false.");
+ await userEvent.click(input);
+ expect(input.checked).toEqual(true);
+ screen.queryByText("Value: true.");
+ await userEvent.click(input);
+ expect(input.checked).toEqual(false);
+ screen.queryByText("Value: false.");
+ });
+});
diff --git a/packages/react/src/components/Forms/Switch/index.stories.tsx b/packages/react/src/components/Forms/Switch/index.stories.tsx
new file mode 100644
index 0000000..eefa847
--- /dev/null
+++ b/packages/react/src/components/Forms/Switch/index.stories.tsx
@@ -0,0 +1,191 @@
+import { Meta } from "@storybook/react";
+import React from "react";
+import { Switch } from ":/components/Forms/Switch/index";
+import { Button } from ":/components/Button";
+
+export default {
+ title: "Components/Forms/Switch",
+ component: Switch,
+} as Meta;
+
+export const Default = {
+ args: {},
+};
+
+export const Checked = {
+ args: {
+ checked: true,
+ },
+};
+
+export const WithLabel = {
+ args: {
+ label: "Label",
+ },
+};
+
+export const WithLabelChecked = {
+ args: {
+ label: "Label",
+ checked: true,
+ },
+};
+
+export const WithText = {
+ args: {
+ label: "Label",
+ text: "This is an optional text",
+ checked: true,
+ },
+};
+
+export const FullWidth = {
+ args: {
+ label: "Label",
+ text: "This is an optional text",
+ fullWidth: true,
+ },
+};
+
+export const WithLabelRight = {
+ args: {
+ label: "Label",
+ labelSide: "right",
+ },
+};
+
+export const WithLabelRightAndText = {
+ args: {
+ label: "Label",
+ labelSide: "right",
+ text: "This is an optional text",
+ },
+};
+
+export const WithLabelRightAndFullWidth = {
+ args: {
+ label: "Label",
+ text: "This is an optional text",
+ fullWidth: true,
+ labelSide: "right",
+ },
+};
+
+export const Disabled = {
+ args: {
+ label: "Label",
+ text: "This is an optional text",
+ disabled: true,
+ },
+};
+
+export const DisabledChecked = {
+ args: {
+ label: "Label",
+ text: "This is an optional text",
+ disabled: true,
+ defaultChecked: true,
+ },
+};
+
+export const Error = {
+ args: {
+ label: "Label",
+ text: "This is an optional text",
+ state: "error",
+ defaultChecked: true,
+ },
+};
+
+export const Success = {
+ args: {
+ label: "Label",
+ text: "This is an optional text",
+ state: "success",
+ defaultChecked: true,
+ },
+};
+
+export const Controlled = {
+ render: () => {
+ const [checked, setChecked] = React.useState(false);
+ return (
+
+
Value: {JSON.stringify(checked)}
+
setChecked(e.target.checked)}
+ />
+ setChecked(!checked)}>Toggle
+
+ );
+ },
+};
+
+export const FormExample = {
+ render: () => {
+ return (
+
+ );
+ },
+};
+
+export const FormExampleRight = {
+ render: () => {
+ return (
+
+ );
+ },
+};
diff --git a/packages/react/src/components/Forms/Switch/index.tsx b/packages/react/src/components/Forms/Switch/index.tsx
new file mode 100644
index 0000000..0a0202a
--- /dev/null
+++ b/packages/react/src/components/Forms/Switch/index.tsx
@@ -0,0 +1,43 @@
+import React, { InputHTMLAttributes } from "react";
+import classNames from "classnames";
+import { Field, FieldProps } from ":/components/Forms/Field";
+
+type Props = InputHTMLAttributes &
+ FieldProps & {
+ label?: string;
+ labelSide?: "left" | "right";
+ };
+
+export const Switch = ({
+ label,
+ text,
+ state,
+ fullWidth,
+ labelSide = "left",
+
+ ...props
+}: Props) => {
+ return (
+
+
+
+
+
+ );
+};
diff --git a/packages/react/src/components/Forms/Switch/resources/dd_1.svg b/packages/react/src/components/Forms/Switch/resources/dd_1.svg
new file mode 100644
index 0000000..22ef501
--- /dev/null
+++ b/packages/react/src/components/Forms/Switch/resources/dd_1.svg
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/react/src/components/Forms/Switch/resources/dd_2.svg b/packages/react/src/components/Forms/Switch/resources/dd_2.svg
new file mode 100644
index 0000000..5c2c279
--- /dev/null
+++ b/packages/react/src/components/Forms/Switch/resources/dd_2.svg
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/react/src/components/Forms/Switch/resources/dd_3.svg b/packages/react/src/components/Forms/Switch/resources/dd_3.svg
new file mode 100644
index 0000000..7328450
--- /dev/null
+++ b/packages/react/src/components/Forms/Switch/resources/dd_3.svg
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/react/src/components/Forms/Switch/tokens.ts b/packages/react/src/components/Forms/Switch/tokens.ts
new file mode 100644
index 0000000..3246b7c
--- /dev/null
+++ b/packages/react/src/components/Forms/Switch/tokens.ts
@@ -0,0 +1,11 @@
+import { DefaultTokens } from "@openfun/cunningham-tokens";
+
+export const tokens = (defaults: DefaultTokens) => ({
+ "accent-color": defaults.theme.colors["success-700"],
+ "rail-background-color": defaults.theme.colors["greyscale-500"],
+ "rail-background-color--disabled": defaults.theme.colors["greyscale-400"],
+ "rail-border-radius": "50vw",
+ "handle-background-color": "white",
+ "handle-background-color--disabled": defaults.theme.colors["greyscale-200"],
+ "handle-border-radius": "50%",
+});
diff --git a/packages/react/src/cunningham-tokens.css b/packages/react/src/cunningham-tokens.css
index 64e75c4..f86f82b 100644
--- a/packages/react/src/cunningham-tokens.css
+++ b/packages/react/src/cunningham-tokens.css
@@ -96,6 +96,13 @@
--c--theme--transitions--ease-out: cubic-bezier(0.33, 1, 0.68, 1);
--c--theme--transitions--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
--c--theme--transitions--duration: 250ms;
+ --c--components--forms-switch--accent-color: #419A14;
+ --c--components--forms-switch--rail-background-color: #9EA3AA;
+ --c--components--forms-switch--rail-background-color--disabled: #C2C6CA;
+ --c--components--forms-switch--rail-border-radius: 50vw;
+ --c--components--forms-switch--handle-background-color: white;
+ --c--components--forms-switch--handle-background-color--disabled: #F3F4F4;
+ --c--components--forms-switch--handle-border-radius: 50%;
--c--components--forms-select--border-color: #E7E8EA;
--c--components--forms-select--border-color--focus: #0556BF;
--c--components--forms-select--border-color--hover: #9EA3AA;
@@ -131,7 +138,7 @@
--c--components--forms-field--color: #79818A;
--c--components--forms-checkbox--background-color--hover: #F3F4F4;
--c--components--forms-checkbox--font-size: 0.8125rem;
- --c--components--forms-checkbox--font-weight: 400;
+ --c--components--forms-checkbox--font-weight: 500;
--c--components--forms-checkbox--color: #0C1A2B;
--c--components--forms-checkbox--border-color: #E7E8EA;
--c--components--forms-checkbox--border-radius: 2px;
diff --git a/packages/react/src/cunningham-tokens.js b/packages/react/src/cunningham-tokens.js
index e1080dd..f1faaa0 100644
--- a/packages/react/src/cunningham-tokens.js
+++ b/packages/react/src/cunningham-tokens.js
@@ -1 +1 @@
-export const tokens = {"theme":{"colors":{"primary-text":"#FFFFFF","primary-100":"#EBF2FC","primary-200":"#8CB5EA","primary-300":"#5894E1","primary-400":"#377FDB","primary-500":"#055FD2","primary-600":"#0556BF","primary-700":"#044395","primary-800":"#033474","primary-900":"#022858","secondary-text":"#555F6B","secondary-100":"#F2F7FC","secondary-200":"#EBF3FA","secondary-300":"#E2EEF8","secondary-400":"#DDEAF7","secondary-500":"#D4E5F5","secondary-600":"#C1D0DF","secondary-700":"#97A3AE","secondary-800":"#757E87","secondary-900":"#596067","greyscale-000":"#FFFFFF","greyscale-100":"#FAFAFB","greyscale-200":"#F3F4F4","greyscale-300":"#E7E8EA","greyscale-400":"#C2C6CA","greyscale-500":"#9EA3AA","greyscale-600":"#79818A","greyscale-700":"#555F6B","greyscale-800":"#303C4B","greyscale-900":"#0C1A2B","success-text":"#FFFFFF","success-100":"#EFFCD3","success-200":"#DBFAA9","success-300":"#BEF27C","success-400":"#A0E659","success-500":"#76D628","success-600":"#5AB81D","success-700":"#419A14","success-800":"#2C7C0C","success-900":"#1D6607","info-text":"#FFFFFF","info-100":"#EBF2FC","info-200":"#8CB5EA","info-300":"#5894E1","info-400":"#377FDB","info-500":"#055FD2","info-600":"#0556BF","info-700":"#044395","info-800":"#033474","info-900":"#022858","warning-text":"#FFFFFF","warning-100":"#FFF8CD","warning-200":"#FFEF9B","warning-300":"#FFE469","warning-400":"#FFDA43","warning-500":"#FFC805","warning-600":"#DBA603","warning-700":"#B78702","warning-800":"#936901","warning-900":"#7A5400","danger-text":"#FFFFFF","danger-100":"#F4B0B0","danger-200":"#EE8A8A","danger-300":"#E65454","danger-400":"#E13333","danger-500":"#DA0000","danger-600":"#C60000","danger-700":"#9B0000","danger-800":"#780000","danger-900":"#5C0000"},"font":{"sizes":{"h1":"1.75rem","h2":"1.375rem","h3":"1.125rem","h4":"0.8125rem","h5":"0.625rem","h6":"0.5rem","l":"1rem","m":"0.8125rem","s":"0.6875rem"},"weights":{"thin":100,"regular":300,"medium":400,"bold":500,"extrabold":700,"black":900},"families":{"base":"Roboto","accent":"Roboto"}},"spacings":{"xl":"4rem","l":"3rem","b":"1.625rem","s":"1rem","t":"0.5rem","st":"0.25rem"},"transitions":{"ease-in":"cubic-bezier(0.32, 0, 0.67, 0)","ease-out":"cubic-bezier(0.33, 1, 0.68, 1)","ease-in-out":"cubic-bezier(0.65, 0, 0.35, 1)","duration":"250ms"}},"components":{"forms-select":{"border-color":"#E7E8EA","border-color--focus":"#0556BF","border-color--hover":"#9EA3AA","border-radius":"8px","border-radius--focus":"2px","border-radius--hover":"2px","border-style":"solid","border-width":"2px","color":"#303C4B","font-size":"1rem","height":"3.5rem","item-background-color--hover":"#F3F4F4","item-background-color--selected":"#EBF2FC","item-color":"#303C4B","item-font-size":"1rem","background-color":"white","menu-background-color":"white"},"forms-radio":{"border-color":"#E7E8EA","accent-color":"#419A14"},"forms-input":{"font-weight":400,"font-size":"1rem","border-radius":"8px","border-radius--hover":"2px","border-radius--focus":"2px","border-width":"2px","border-color":"#E7E8EA","border-color--hover":"#9EA3AA","border-color--focus":"#0556BF","border-style":"solid","color":"#303C4B"},"forms-field":{"width":"292px","font-size":"0.6875rem","color":"#79818A"},"forms-checkbox":{"background-color--hover":"#F3F4F4","font-size":"0.8125rem","font-weight":400,"color":"#0C1A2B","border-color":"#E7E8EA","border-radius":"2px","accent-color":"#419A14","size":"1.5rem"},"button":{"border-radius":"8px","border-radius--active":"2px","medium-height":"48px","small-height":"32px","medium-font-size":"1rem","small-font-size":"0.8125rem","font-weight":400}}};
+export const tokens = {"theme":{"colors":{"primary-text":"#FFFFFF","primary-100":"#EBF2FC","primary-200":"#8CB5EA","primary-300":"#5894E1","primary-400":"#377FDB","primary-500":"#055FD2","primary-600":"#0556BF","primary-700":"#044395","primary-800":"#033474","primary-900":"#022858","secondary-text":"#555F6B","secondary-100":"#F2F7FC","secondary-200":"#EBF3FA","secondary-300":"#E2EEF8","secondary-400":"#DDEAF7","secondary-500":"#D4E5F5","secondary-600":"#C1D0DF","secondary-700":"#97A3AE","secondary-800":"#757E87","secondary-900":"#596067","greyscale-000":"#FFFFFF","greyscale-100":"#FAFAFB","greyscale-200":"#F3F4F4","greyscale-300":"#E7E8EA","greyscale-400":"#C2C6CA","greyscale-500":"#9EA3AA","greyscale-600":"#79818A","greyscale-700":"#555F6B","greyscale-800":"#303C4B","greyscale-900":"#0C1A2B","success-text":"#FFFFFF","success-100":"#EFFCD3","success-200":"#DBFAA9","success-300":"#BEF27C","success-400":"#A0E659","success-500":"#76D628","success-600":"#5AB81D","success-700":"#419A14","success-800":"#2C7C0C","success-900":"#1D6607","info-text":"#FFFFFF","info-100":"#EBF2FC","info-200":"#8CB5EA","info-300":"#5894E1","info-400":"#377FDB","info-500":"#055FD2","info-600":"#0556BF","info-700":"#044395","info-800":"#033474","info-900":"#022858","warning-text":"#FFFFFF","warning-100":"#FFF8CD","warning-200":"#FFEF9B","warning-300":"#FFE469","warning-400":"#FFDA43","warning-500":"#FFC805","warning-600":"#DBA603","warning-700":"#B78702","warning-800":"#936901","warning-900":"#7A5400","danger-text":"#FFFFFF","danger-100":"#F4B0B0","danger-200":"#EE8A8A","danger-300":"#E65454","danger-400":"#E13333","danger-500":"#DA0000","danger-600":"#C60000","danger-700":"#9B0000","danger-800":"#780000","danger-900":"#5C0000"},"font":{"sizes":{"h1":"1.75rem","h2":"1.375rem","h3":"1.125rem","h4":"0.8125rem","h5":"0.625rem","h6":"0.5rem","l":"1rem","m":"0.8125rem","s":"0.6875rem"},"weights":{"thin":100,"regular":300,"medium":400,"bold":500,"extrabold":700,"black":900},"families":{"base":"Roboto","accent":"Roboto"}},"spacings":{"xl":"4rem","l":"3rem","b":"1.625rem","s":"1rem","t":"0.5rem","st":"0.25rem"},"transitions":{"ease-in":"cubic-bezier(0.32, 0, 0.67, 0)","ease-out":"cubic-bezier(0.33, 1, 0.68, 1)","ease-in-out":"cubic-bezier(0.65, 0, 0.35, 1)","duration":"250ms"}},"components":{"forms-switch":{"accent-color":"#419A14","rail-background-color":"#9EA3AA","rail-background-color--disabled":"#C2C6CA","rail-border-radius":"50vw","handle-background-color":"white","handle-background-color--disabled":"#F3F4F4","handle-border-radius":"50%"},"forms-select":{"border-color":"#E7E8EA","border-color--focus":"#0556BF","border-color--hover":"#9EA3AA","border-radius":"8px","border-radius--focus":"2px","border-radius--hover":"2px","border-style":"solid","border-width":"2px","color":"#303C4B","font-size":"1rem","height":"3.5rem","item-background-color--hover":"#F3F4F4","item-background-color--selected":"#EBF2FC","item-color":"#303C4B","item-font-size":"1rem","background-color":"white","menu-background-color":"white"},"forms-radio":{"border-color":"#E7E8EA","accent-color":"#419A14"},"forms-input":{"font-weight":400,"font-size":"1rem","border-radius":"8px","border-radius--hover":"2px","border-radius--focus":"2px","border-width":"2px","border-color":"#E7E8EA","border-color--hover":"#9EA3AA","border-color--focus":"#0556BF","border-style":"solid","color":"#303C4B"},"forms-field":{"width":"292px","font-size":"0.6875rem","color":"#79818A"},"forms-checkbox":{"background-color--hover":"#F3F4F4","font-size":"0.8125rem","font-weight":500,"color":"#0C1A2B","border-color":"#E7E8EA","border-radius":"2px","accent-color":"#419A14","size":"1.5rem"},"button":{"border-radius":"8px","border-radius--active":"2px","medium-height":"48px","small-height":"32px","medium-font-size":"1rem","small-font-size":"0.8125rem","font-weight":400}}};
diff --git a/packages/react/src/cunningham-tokens.ts b/packages/react/src/cunningham-tokens.ts
index e1080dd..f1faaa0 100644
--- a/packages/react/src/cunningham-tokens.ts
+++ b/packages/react/src/cunningham-tokens.ts
@@ -1 +1 @@
-export const tokens = {"theme":{"colors":{"primary-text":"#FFFFFF","primary-100":"#EBF2FC","primary-200":"#8CB5EA","primary-300":"#5894E1","primary-400":"#377FDB","primary-500":"#055FD2","primary-600":"#0556BF","primary-700":"#044395","primary-800":"#033474","primary-900":"#022858","secondary-text":"#555F6B","secondary-100":"#F2F7FC","secondary-200":"#EBF3FA","secondary-300":"#E2EEF8","secondary-400":"#DDEAF7","secondary-500":"#D4E5F5","secondary-600":"#C1D0DF","secondary-700":"#97A3AE","secondary-800":"#757E87","secondary-900":"#596067","greyscale-000":"#FFFFFF","greyscale-100":"#FAFAFB","greyscale-200":"#F3F4F4","greyscale-300":"#E7E8EA","greyscale-400":"#C2C6CA","greyscale-500":"#9EA3AA","greyscale-600":"#79818A","greyscale-700":"#555F6B","greyscale-800":"#303C4B","greyscale-900":"#0C1A2B","success-text":"#FFFFFF","success-100":"#EFFCD3","success-200":"#DBFAA9","success-300":"#BEF27C","success-400":"#A0E659","success-500":"#76D628","success-600":"#5AB81D","success-700":"#419A14","success-800":"#2C7C0C","success-900":"#1D6607","info-text":"#FFFFFF","info-100":"#EBF2FC","info-200":"#8CB5EA","info-300":"#5894E1","info-400":"#377FDB","info-500":"#055FD2","info-600":"#0556BF","info-700":"#044395","info-800":"#033474","info-900":"#022858","warning-text":"#FFFFFF","warning-100":"#FFF8CD","warning-200":"#FFEF9B","warning-300":"#FFE469","warning-400":"#FFDA43","warning-500":"#FFC805","warning-600":"#DBA603","warning-700":"#B78702","warning-800":"#936901","warning-900":"#7A5400","danger-text":"#FFFFFF","danger-100":"#F4B0B0","danger-200":"#EE8A8A","danger-300":"#E65454","danger-400":"#E13333","danger-500":"#DA0000","danger-600":"#C60000","danger-700":"#9B0000","danger-800":"#780000","danger-900":"#5C0000"},"font":{"sizes":{"h1":"1.75rem","h2":"1.375rem","h3":"1.125rem","h4":"0.8125rem","h5":"0.625rem","h6":"0.5rem","l":"1rem","m":"0.8125rem","s":"0.6875rem"},"weights":{"thin":100,"regular":300,"medium":400,"bold":500,"extrabold":700,"black":900},"families":{"base":"Roboto","accent":"Roboto"}},"spacings":{"xl":"4rem","l":"3rem","b":"1.625rem","s":"1rem","t":"0.5rem","st":"0.25rem"},"transitions":{"ease-in":"cubic-bezier(0.32, 0, 0.67, 0)","ease-out":"cubic-bezier(0.33, 1, 0.68, 1)","ease-in-out":"cubic-bezier(0.65, 0, 0.35, 1)","duration":"250ms"}},"components":{"forms-select":{"border-color":"#E7E8EA","border-color--focus":"#0556BF","border-color--hover":"#9EA3AA","border-radius":"8px","border-radius--focus":"2px","border-radius--hover":"2px","border-style":"solid","border-width":"2px","color":"#303C4B","font-size":"1rem","height":"3.5rem","item-background-color--hover":"#F3F4F4","item-background-color--selected":"#EBF2FC","item-color":"#303C4B","item-font-size":"1rem","background-color":"white","menu-background-color":"white"},"forms-radio":{"border-color":"#E7E8EA","accent-color":"#419A14"},"forms-input":{"font-weight":400,"font-size":"1rem","border-radius":"8px","border-radius--hover":"2px","border-radius--focus":"2px","border-width":"2px","border-color":"#E7E8EA","border-color--hover":"#9EA3AA","border-color--focus":"#0556BF","border-style":"solid","color":"#303C4B"},"forms-field":{"width":"292px","font-size":"0.6875rem","color":"#79818A"},"forms-checkbox":{"background-color--hover":"#F3F4F4","font-size":"0.8125rem","font-weight":400,"color":"#0C1A2B","border-color":"#E7E8EA","border-radius":"2px","accent-color":"#419A14","size":"1.5rem"},"button":{"border-radius":"8px","border-radius--active":"2px","medium-height":"48px","small-height":"32px","medium-font-size":"1rem","small-font-size":"0.8125rem","font-weight":400}}};
+export const tokens = {"theme":{"colors":{"primary-text":"#FFFFFF","primary-100":"#EBF2FC","primary-200":"#8CB5EA","primary-300":"#5894E1","primary-400":"#377FDB","primary-500":"#055FD2","primary-600":"#0556BF","primary-700":"#044395","primary-800":"#033474","primary-900":"#022858","secondary-text":"#555F6B","secondary-100":"#F2F7FC","secondary-200":"#EBF3FA","secondary-300":"#E2EEF8","secondary-400":"#DDEAF7","secondary-500":"#D4E5F5","secondary-600":"#C1D0DF","secondary-700":"#97A3AE","secondary-800":"#757E87","secondary-900":"#596067","greyscale-000":"#FFFFFF","greyscale-100":"#FAFAFB","greyscale-200":"#F3F4F4","greyscale-300":"#E7E8EA","greyscale-400":"#C2C6CA","greyscale-500":"#9EA3AA","greyscale-600":"#79818A","greyscale-700":"#555F6B","greyscale-800":"#303C4B","greyscale-900":"#0C1A2B","success-text":"#FFFFFF","success-100":"#EFFCD3","success-200":"#DBFAA9","success-300":"#BEF27C","success-400":"#A0E659","success-500":"#76D628","success-600":"#5AB81D","success-700":"#419A14","success-800":"#2C7C0C","success-900":"#1D6607","info-text":"#FFFFFF","info-100":"#EBF2FC","info-200":"#8CB5EA","info-300":"#5894E1","info-400":"#377FDB","info-500":"#055FD2","info-600":"#0556BF","info-700":"#044395","info-800":"#033474","info-900":"#022858","warning-text":"#FFFFFF","warning-100":"#FFF8CD","warning-200":"#FFEF9B","warning-300":"#FFE469","warning-400":"#FFDA43","warning-500":"#FFC805","warning-600":"#DBA603","warning-700":"#B78702","warning-800":"#936901","warning-900":"#7A5400","danger-text":"#FFFFFF","danger-100":"#F4B0B0","danger-200":"#EE8A8A","danger-300":"#E65454","danger-400":"#E13333","danger-500":"#DA0000","danger-600":"#C60000","danger-700":"#9B0000","danger-800":"#780000","danger-900":"#5C0000"},"font":{"sizes":{"h1":"1.75rem","h2":"1.375rem","h3":"1.125rem","h4":"0.8125rem","h5":"0.625rem","h6":"0.5rem","l":"1rem","m":"0.8125rem","s":"0.6875rem"},"weights":{"thin":100,"regular":300,"medium":400,"bold":500,"extrabold":700,"black":900},"families":{"base":"Roboto","accent":"Roboto"}},"spacings":{"xl":"4rem","l":"3rem","b":"1.625rem","s":"1rem","t":"0.5rem","st":"0.25rem"},"transitions":{"ease-in":"cubic-bezier(0.32, 0, 0.67, 0)","ease-out":"cubic-bezier(0.33, 1, 0.68, 1)","ease-in-out":"cubic-bezier(0.65, 0, 0.35, 1)","duration":"250ms"}},"components":{"forms-switch":{"accent-color":"#419A14","rail-background-color":"#9EA3AA","rail-background-color--disabled":"#C2C6CA","rail-border-radius":"50vw","handle-background-color":"white","handle-background-color--disabled":"#F3F4F4","handle-border-radius":"50%"},"forms-select":{"border-color":"#E7E8EA","border-color--focus":"#0556BF","border-color--hover":"#9EA3AA","border-radius":"8px","border-radius--focus":"2px","border-radius--hover":"2px","border-style":"solid","border-width":"2px","color":"#303C4B","font-size":"1rem","height":"3.5rem","item-background-color--hover":"#F3F4F4","item-background-color--selected":"#EBF2FC","item-color":"#303C4B","item-font-size":"1rem","background-color":"white","menu-background-color":"white"},"forms-radio":{"border-color":"#E7E8EA","accent-color":"#419A14"},"forms-input":{"font-weight":400,"font-size":"1rem","border-radius":"8px","border-radius--hover":"2px","border-radius--focus":"2px","border-width":"2px","border-color":"#E7E8EA","border-color--hover":"#9EA3AA","border-color--focus":"#0556BF","border-style":"solid","color":"#303C4B"},"forms-field":{"width":"292px","font-size":"0.6875rem","color":"#79818A"},"forms-checkbox":{"background-color--hover":"#F3F4F4","font-size":"0.8125rem","font-weight":500,"color":"#0C1A2B","border-color":"#E7E8EA","border-radius":"2px","accent-color":"#419A14","size":"1.5rem"},"button":{"border-radius":"8px","border-radius--active":"2px","medium-height":"48px","small-height":"32px","medium-font-size":"1rem","small-font-size":"0.8125rem","font-weight":400}}};
diff --git a/packages/react/src/index.scss b/packages/react/src/index.scss
index 3efc16c..2b258d8 100644
--- a/packages/react/src/index.scss
+++ b/packages/react/src/index.scss
@@ -9,6 +9,7 @@
@import './components/Forms/Input';
@import './components/Forms/LabelledBox';
@import './components/Forms/Select';
+@import './components/Forms/Switch';
@import './components/Loader';
@import './components/Pagination';
diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts
index 1668444..f23518d 100644
--- a/packages/react/src/index.ts
+++ b/packages/react/src/index.ts
@@ -11,6 +11,7 @@ export * from "./components/Forms/Field";
export * from "./components/Forms/Input";
export * from "./components/Forms/Radio";
export * from "./components/Forms/Select";
+export * from "./components/Forms/Switch";
export * from "./components/Loader";
export * from "./components/Pagination";
export * from "./components/Provider";