#app
View Compiled
@import url('https://fonts.googleapis.com/css?family=Lato')

*
*:after
*:before
  box-sizing border-box

body
  margin 0
  padding 0
  min-height 100vh
  display flex
  align-items center
  justify-content center
  font-family 'Lato', sans-serif
  background linear-gradient(28deg, #c5eff7, #fff)

form
  display grid
  background #fafafa
  grid-template-rows repeat(3, auto)
  grid-template-columns repeat(2, 1fr)
  grid-gap 2rem 0
  min-width 160px
  align-content center
  justify-content center
  align-items center
  justify-items center
  margin 0
  padding 2rem 1rem
  filter drop-shadow(20px 20px 20px #aaa)
  position relative

h1
  grid-column 1 / -1
  margin 0
  font-size 1.25rem
  color #fff
  background #89c4f4
  position absolute
  width 248px
  padding 1rem
  z-index 3
  top 0
  left -1rem
  text-align center
  transform-origin top left
  transform rotate(-90deg) translate(-100%, -100%)

label
  cursor pointer


.check
  display inline-block
  height 40px
  width 80px
  position relative
  overflow hidden
  border-radius 20px
  overflow hidden
  cursor pointer

  &__check
    z-index 2
    opacity 0
    cursor pointer

    &:checked ~ .check__indicator
      background #7befb2

    &:checked ~ .check__indicator:after
      transform translate(40px, 0)

  &__check
  &__indicator
    height 100%
    width 100%
    position absolute
    top 0
    right 0
    bottom 0
    left 0
    margin 0

  &__indicator
    background #ececec
    transition background .25s ease

    &:after
      height 30px
      width 30px
      background #fff
      content ''
      position absolute
      border-radius 100%
      top 5px
      left 5px
      transition transform 0.25s ease




View Compiled
const { React, ReactDOM, PropTypes } = window
const { Component, Fragment } = React
const { render } = ReactDOM
const rootNode = document.getElementById('app')

class MultiRadio extends Component {
  static defaultProps = {
    limit: 2,
    options: [
      {
        label: 'Cheap',
        checked: false,
      },
      {
        label: 'Good',
        checked: false,
      },
      {
        label: 'Fast',
        checked: true,
      },
    ],
  }
  static propTypes = {
    options: PropTypes.array,
    limit: PropTypes.number,
  }
  state = {
    options: this.props.options,
  }
  onChange = e => {
    const invert =
      this.state.options.filter(o => o.checked && o.label !== e.target.id)
        .length === this.props.limit

    this.setState({
      options: this.state.options.reduce(
        (arr, opt) => [
          ...arr,
          {
            ...opt,
            checked:
              e.target.id === opt.label
                ? e.target.checked
                : invert
                  ? !opt.checked
                  : opt.checked,
          },
        ],
        []
      ),
    })
  }
  render = () => {
    return (
      <form>
        <h1>Services on Offer</h1>
        {this.state.options.map(o => (
          <Fragment key={`option--${o.label}`}>
            <label htmlFor={o.label}>{o.label}</label>
            <div className="check">
              <input
                onChange={this.onChange}
                type="checkbox"
                className="check__check"
                checked={o.checked}
                value={o.label}
                id={o.label}
              />
              <div className="check__indicator" />
            </div>
          </Fragment>
        ))}
      </form>
    )
  }
}

render(<MultiRadio />, rootNode)
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.com/react@16.4.0/umd/react.development.js
  2. https://unpkg.com/react-dom@16.4.0/umd/react-dom.development.js
  3. https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.6.1/prop-types.min.js