diff --git a/docs/toggle.md b/docs/toggle.md
new file mode 100644
index 000000000..e59b5b027
--- /dev/null
+++ b/docs/toggle.md
@@ -0,0 +1,24 @@
+# Toggle
+### Usage
+
+```jsx
+import { Toggle } from 'radiance-ui';
+
+
+```
+
+
+
+### Proptypes
+| prop | propType | required | default | description
+|-----------------------|------------------|----------|-----------|------------------------------------------------------------------------------------------------------------------------------|
+| checked | bool | yes | - | controls the toggle state |
+| label | string | no | '' | the toggle label |
+| onChange | function | yes | - | the toggle handler function, this usually toggle the checked prop value |
+
+### Notes
+The `` component is usually wrapped in a `container` element (with a fixed `width` style for example). The toggle and label are spread in the container (`space-between`) from edge to edge.
\ No newline at end of file
diff --git a/package.json b/package.json
index b093a0f8c..0a36a57df 100644
--- a/package.json
+++ b/package.json
@@ -113,6 +113,7 @@
"react": "^16.6.0",
"react-modal": "^3.8.1",
"react-slick": "^0.23.2",
+ "react-toggle-button": "^2.2.0",
"react-transition-group": "^2.9.0",
"tinycolor2": "^1.4.1"
},
diff --git a/src/shared-components/index.js b/src/shared-components/index.js
index 8720fc798..597bf17e7 100644
--- a/src/shared-components/index.js
+++ b/src/shared-components/index.js
@@ -23,6 +23,7 @@ export { default as OptionButton } from './optionButton';
export { default as ProgressBar } from './progressBar';
export { default as RadioButton } from './radioButton';
export { default as Tabs } from './tabs';
+export { default as Toggle } from './toggle';
export { default as Tooltip } from './tooltip';
export { default as Typography, style as TYPOGRAPHY_STYLE } from './typography';
export { FadeInContainer, opacityInAnimationStyle } from './transitions';
diff --git a/src/shared-components/toggle/__snapshots__/test.js.snap b/src/shared-components/toggle/__snapshots__/test.js.snap
new file mode 100644
index 000000000..63093c9a1
--- /dev/null
+++ b/src/shared-components/toggle/__snapshots__/test.js.snap
@@ -0,0 +1,191 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` UI snapshot renders the component 1`] = `
+.emotion-2 {
+ position: relative;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-flex-flow: row nowrap;
+ -ms-flex-flow: row nowrap;
+ flex-flow: row nowrap;
+ -webkit-box-pack: justify;
+ -webkit-justify-content: space-between;
+ -ms-flex-pack: justify;
+ justify-content: space-between;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ width: 100%;
+ padding: 1rem 0;
+}
+
+.emotion-2 > input {
+ display: none;
+}
+
+.emotion-0 {
+ color: #524D6E;
+ margin: 0;
+ font-size: 1rem;
+ line-height: 1.5rem;
+ text-align: left;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ margin-right: 0.5rem;
+}
+
+
+`;
diff --git a/src/shared-components/toggle/index.js b/src/shared-components/toggle/index.js
new file mode 100644
index 000000000..6aedc4aef
--- /dev/null
+++ b/src/shared-components/toggle/index.js
@@ -0,0 +1,51 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import ToggleButton from 'react-toggle-button';
+
+import { COLORS } from '../../constants';
+import { Container, Label, trackStyle, thumbStyle } from './style';
+
+const propTypes = {
+ checked: PropTypes.bool,
+ label: PropTypes.string,
+ onChange: PropTypes.func,
+};
+
+const defaultProps = {
+ checked: false,
+ label: '',
+};
+
+const Toggle = ({ checked, label, onChange }) => (
+
+ {label && }
+
+
+);
+
+Toggle.propTypes = propTypes;
+Toggle.defaultProps = defaultProps;
+
+export default Toggle;
diff --git a/src/shared-components/toggle/style.js b/src/shared-components/toggle/style.js
new file mode 100644
index 000000000..82afed4e5
--- /dev/null
+++ b/src/shared-components/toggle/style.js
@@ -0,0 +1,43 @@
+import styled from '@emotion/styled';
+
+import { COLORS, SPACING } from '../../constants';
+
+export const Container = styled.div`
+ position: relative;
+ display: flex;
+ flex-flow: row nowrap;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ padding: ${SPACING.small} 0;
+
+ & > input {
+ display: none;
+ }
+`;
+
+export const Label = styled.span`
+ color: ${COLORS.primaryTint1};
+ margin: 0;
+ font-size: ${SPACING.small};
+ line-height: ${SPACING.base};
+ text-align: left;
+ cursor: pointer;
+ user-select: none;
+ margin-right: ${SPACING.xsmall};
+`;
+
+export const trackStyle = {
+ borderRadius: 100,
+ height: 24,
+ width: 40,
+};
+
+export const thumbStyle = {
+ height: 22,
+ width: 22,
+ border: `1px solid ${COLORS.border}`,
+ boxShadow: `none`,
+ background: COLORS.white,
+ backgroundColor: COLORS.white,
+};
diff --git a/src/shared-components/toggle/test.js b/src/shared-components/toggle/test.js
new file mode 100644
index 000000000..b668dc8e0
--- /dev/null
+++ b/src/shared-components/toggle/test.js
@@ -0,0 +1,45 @@
+import React from 'react';
+import { shallow, mount } from 'enzyme';
+import renderer from 'react-test-renderer';
+
+import Toggle from './index';
+
+describe('', () => {
+ const labelText = 'Label Text';
+
+ describe('UI snapshot', () => {
+ it('renders the component', () => {
+ const component = renderer.create(
+
+ );
+
+ const tree = component.toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+ });
+
+ describe('when label is undefined', () => {
+ test('does not render a label component', () => {
+ const wrapper = shallow();
+ expect(wrapper.html().indexOf('label') === -1).toBe(true);
+ });
+ });
+
+ describe('when label is a string', () => {
+ test('renders a text component', () => {
+ const wrapper = shallow();
+
+ expect(wrapper.html().indexOf(labelText) > 0).toBe(true);
+ });
+ });
+
+ describe('when checkbox is clicked', () => {
+ test('fires onChange function with correct argument when function exists', () => {
+ const spy = jest.fn();
+ const wrapper = mount();
+
+ wrapper.find('[type="checkbox"]').simulate('click');
+ expect(spy).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/stories/toggle/index.js b/stories/toggle/index.js
new file mode 100644
index 000000000..2423f50cf
--- /dev/null
+++ b/stories/toggle/index.js
@@ -0,0 +1,40 @@
+import React from 'react';
+import { storiesOf } from '@storybook/react';
+import { withDocs } from 'storybook-readme';
+import styled from '@emotion/styled';
+
+import ToggleReadme from 'docs/toggle.md';
+import { Toggle, Typography } from 'src/shared-components';
+import { SPACING } from 'src/constants';
+
+import ToggleExample from './toggleExample';
+
+const MainContainer = styled.div`
+ text-align: left;
+`;
+
+const ToggleContainer = styled.div`
+ width: 300px;
+ margin: ${SPACING.base} 0;
+`;
+
+const stories = storiesOf('Toggle', module);
+
+stories.add(
+ 'Usage',
+ withDocs(ToggleReadme, () => (
+
+ Examples:
+
+
+
+
+
+ {}} />
+
+
+ {}} />
+
+
+ ))
+);
diff --git a/stories/toggle/toggleExample.js b/stories/toggle/toggleExample.js
new file mode 100644
index 000000000..0bb6a2cc6
--- /dev/null
+++ b/stories/toggle/toggleExample.js
@@ -0,0 +1,30 @@
+import React from 'react';
+
+import { Toggle } from 'src/shared-components';
+
+class ToggleExample extends React.Component {
+ state = {
+ checked: false,
+ };
+
+ onChange = () => {
+ const { checked } = this.state;
+ this.setState({
+ checked: !checked,
+ });
+ };
+
+ render() {
+ const { checked } = this.state;
+
+ return (
+
+ );
+ }
+}
+
+export default ToggleExample;
diff --git a/yarn.lock b/yarn.lock
index caff6f196..0f37e6aff 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7419,6 +7419,11 @@ pbkdf2@^3.0.3:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
+performance-now@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
+ integrity sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=
+
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
@@ -7762,7 +7767,7 @@ querystringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.0.tgz#7ded8dfbf7879dcc60d0a644ac6754b283ad17ef"
-raf@^3.4.0:
+raf@^3.1.0, raf@^3.4.0:
version "3.4.1"
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
dependencies:
@@ -7949,6 +7954,15 @@ react-modal@^3.8.1:
react-lifecycles-compat "^3.0.0"
warning "^3.0.0"
+react-motion@^0.5.2:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/react-motion/-/react-motion-0.5.2.tgz#0dd3a69e411316567927917c6626551ba0607316"
+ integrity sha512-9q3YAvHoUiWlP3cK0v+w1N5Z23HXMj4IF4YuvjvWegWqNPfLXsOBE/V7UvQGpXxHFKRQQcNcVQE31g9SB/6qgQ==
+ dependencies:
+ performance-now "^0.2.0"
+ prop-types "^15.5.8"
+ raf "^3.1.0"
+
react-slick@^0.23.2:
version "0.23.2"
resolved "https://registry.yarnpkg.com/react-slick/-/react-slick-0.23.2.tgz#8d8bdbc77a6678e8ad36f50c32578c7c0f1c54f6"
@@ -7991,6 +8005,14 @@ react-textarea-autosize@^7.0.4:
dependencies:
prop-types "^15.6.0"
+react-toggle-button@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/react-toggle-button/-/react-toggle-button-2.2.0.tgz#a1b92143aa0df414642fcb141f0879f545bc5a89"
+ integrity sha1-obkhQ6oN9BRkL8sUHwh59UW8Wok=
+ dependencies:
+ prop-types "^15.6.0"
+ react-motion "^0.5.2"
+
react-transition-group@^2.0.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.5.0.tgz#70bca0e3546102c4dc5cf3f5f57f73447cce6874"