#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
This Pen doesn't use any external CSS resources.