<div id="app"></div>
// Fonts
@import 'https://fonts.googleapis.com/css?family=Lato:100,300,400,700,900';
@import 'https://fonts.googleapis.com/css?family=Playfair+Display:400,700,900';

// Colors
$goldfusion: #7B7554;
$yankeesblue: #17183B;
$darkmagenta: #A11692;
$fieryrose: #FF4F79;
$peachorange: #FFB49A;

body {
	background: $yankeesblue;
	color: $yankeesblue;
	font-family: 'Lato', sans-serif;
}

.App {
	width: 100vw;
	height: 100vh;
	position: absolute;
	top: 0;
	left: 0;
	background: $yankeesblue;
	
	.Overlay {
		width: 100vw;
		height: 100vh;
		position: absolute;
		top: 0;
		left: 0;
		background-size: cover;
		filter: blur(10px);
		opacity: .2;
	}
}

// Header
header {
	position: fixed;
	top: 0;
	left: 0;
	width: 100vw;
	background: white;
	z-index: 10;
	padding: 20px;
}

// Container
.Container {
	display: flex;
	height: 532px;
	max-width: 650px;
	z-index: 3;
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	margin: auto;
}

// ImagePreview
.ImagePreview {
	background-size: cover;
	display: flex;
	align-items: flex-end;
	position: relative;
	width: 50%;
	
	&::before {
		width: 100%;
		content: '';
		display: block;
		position: absolute;
		top: 0;
		left: 0;
		height: 100%;
		background: -moz-linear-gradient(top, rgba($yankeesblue,0) 0%, rgba($yankeesblue,0.45) 100%); /* FF3.6-15 */
		background: -webkit-linear-gradient(top, rgba($yankeesblue,0) 0%,rgba($yankeesblue,0.45) 100%); /* Chrome10-25,Safari5.1-6 */
		background: linear-gradient(to bottom, rgba($yankeesblue,0) 0%,rgba($yankeesblue,0.45) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
		filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00000000', endColorstr='#a6000000',GradientType=0 ); /* IE6-9 */
	}
	
	.WorkspaceOverview {
		color: white;
		position: relative;
		z-index: 3;
		width: 100%;
		padding: 20px;
		
		.WorkspaceInformation {
			
			.WorkspaceName {
				font-family: 'Playfair Display';
				font-weight: 600;
				font-size: 36px;
			}
			
			.WorkspacePrice {
				display: flex;
				align-items: flex-end;
				margin-top: 20px;
				
				.Price {
					font-size: 26px;
					font-weight: 200;
				}
				
				.Duration {
					font-size: 16px;
					font-weight: 300;
					position: relative;
					bottom: 2px;
					margin-left: 5px;
				}
			}
		}
		
		.WorkspaceMeta {
			color: white;
			line-height: 1.6;
			font-size: 13px;
			font-weight: 300;
			margin-top: 20px;
			
			strong {
				font-weight: 600;
			}
		}
	}
}

// Checkout
.Checkout {
	background: white;
	width: 50%;
	
	.OrderSummary {
		border-bottom: 1px solid darken(white, 5);
		padding-bottom: 10px;
		
		table {
			font-size: 12px;
			margin: 0 20px;
			margin-bottom: 10px;
			line-height: 1.6;
			width: calc(100% - 40px);
			opacity: .8;
			font-weight: 400;
			
			tr {
				td:last-child {
					text-align: right;
					font-weight: 600;
				}
			}
		}
		
		.Total {
			display: flex;
			padding: 0 20px;
			margin-bottom: 10px;
			align-items: flex-end;
			
			.TotalLabel {
				display: block;
				text-transform: uppercase;
				margin-bottom: 5px;
				font-size: 11px;
				font-weight: 600;
			}
			
			.Amount {
				font-size: 18px;
				margin-left: auto;
			}
		}
	}
	
	.Title {
		font-family: 'Playfair Display';
		font-size: 18px;
		padding: 20px;
	}
}

// Inputs
.BasicInput {
	padding: 0 20px;
	margin-bottom: 20px;
	
	label {
		display: block;
		text-transform: uppercase;
		margin-bottom: 5px;
		font-size: 11px;
		font-weight: 600;
	}
	
	input {
		width: 100%;
		border: 0;
		border-bottom: 1px solid $yankeesblue;
		font-family: 'Lato', sans-serif;
		padding: 5px 0;
		font-size: 18px;
		font-weight: 300;
		outline: none;
		
		&:focus {
			border-bottom: 1px solid $peachorange;
		}
	}
}

// Expiry Date
.ExpiryDate {
	display: flex;
	margin-bottom: 20px;
	padding: 0 20px;
	width: 100%;
	box-sizing: border-box;
	flex-wrap: nowrap;
	
	label {
		display: block;
		text-transform: uppercase;
		margin-bottom: 5px;
		font-size: 11px;
		font-weight: 600;
	}
	
	div:first-child {
		flex: 1 0 auto;
	}
	
	.Expiry {
		display: flex;
	}
	
	input {
		width: 100%;
		border: 0;
		border-bottom: 1px solid $yankeesblue;
		font-family: 'Lato', sans-serif;
		padding: 5px 0;
		font-size: 18px;
		font-weight: 300;
		outline: none;
	}

	select {
		appearance: none;
		flex: 1 0 auto;
		padding: 10px;
		border-radius: 0;
		border: 0;
		background: transparent;
		border-bottom: 1px solid $yankeesblue;
		border-bottom: 1px solid $yankeesblue;
		font-family: 'Lato', sans-serif;
		padding: 5px 0;
		font-size: 18px;
		font-weight: 300;
		outline: none;
		margin-right: 10px;
	}
	
	.CVCField {
		width: 20%;
	}
}

// CVC
.CVC {
	display: flex;
	margin-bottom: 20px;
	padding: 0 20px;
	
	label {
		display: block;
		text-transform: uppercase;
		margin-bottom: 5px;
		font-size: 11px;
		font-weight: 600;
	}
	
	input {
		width: 100%;
		border: 0;
		border-bottom: 1px solid $yankeesblue;
		font-family: 'Lato', sans-serif;
		padding: 5px 0;
		font-size: 18px;
		font-weight: 300;
		outline: none;
		
		&:focus {
			border-bottom: 1px solid $peachorange;
		}
	}
	
	span {
		display: block;
		padding-top: 20px;
		margin-left: 10px;
		color: $peachorange;
		font-size: 11px;
		border-bottom: 1px dotted $peachorange;
	}
}

// Checkout Button
.CheckoutButton {
	padding: 0 20px;
	text-align: center;
	
	button {
		background: darken($peachorange, 5);
		color: white;
		border: 0;
		border-radius: 2px;
		font-family: 'Lato', sans-serif;
		width: 100%;
		font-size: 13px;
		text-transform: uppercase;
		font-weight: 300;
		outline: none;
		padding: 10px 0;
		margin-bottom: 10px;
		
		&:hover {
			background: darken($peachorange, 10);
		}
		
		&:active {
			transform: scale(.99);
		}
	}
	
	span {
		font-size: 11px;
		color: darken(white, 25);
		font-weight: 300;
		text-align: center;
	}
}

// Animations
.overlay-enter {
  opacity: 0.01;
}

.overlay-enter.overlay-enter-active {
  opacity: .2;
  transition: opacity .5s ease .5s;
}

.container-enter {
  opacity: 0.01;
}

.container-enter.container-enter-active {
  opacity: 1;
  transition: opacity .5s ease;
}
View Compiled
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;

var Overlay = React.createClass({
	render: function() {
		return (
			<div className="Overlay" style={{'backgroundImage':'url(' + this.props.image + ')'}}>
				Something
			</div>
		);
	}
});

var Container = React.createClass({
	render: function() {
		return (
			<div className="Container">
				{this.props.children}
			</div>
		)
	}
});

var WorkspaceInformation = React.createClass({
	render: function() {
		
		if(this.props.duration > 1) {
			var duration = this.props.duration + ' days';
		} else {
			var duration = this.props.duration + ' day';
		}
		
		return (
			<div className="WorkspaceInformation">
				<div className="WorkspaceName">{this.props.name}</div>
				<div className="WorkspacePrice">
					<div className="Price">{this.props.price} GBP</div>
					<div className="Duration">/ {duration}</div>
				</div>
			</div>
		);
	}
});

var WorkspaceMeta = React.createClass({
	render: function() {
		
		if(this.props.people > 1) {
			var people = this.props.people + ' people';
		} else {
			var people = this.props.people + ' person';
		}
		
		
		return (
			<div className="WorkspaceMeta">
				<div className="Description">Entire office for <strong>{people}</strong></div>	
				<div className="Dates"><strong>Mon, Aug 22, 2016</strong> to <strong>Fri, Aug 29, 2016</strong></div>
			</div>
		);
	}
});

var ImagePreview = React.createClass({
	render: function() {
		return (
			<div className="ImagePreview" style={{'backgroundImage': 'url('+ this.props.image +')'}}>
				<div className="WorkspaceOverview">
					<WorkspaceInformation name="Coworking Space, South Korea" price={this.props.price} duration="1" />
					<WorkspaceMeta people={this.props.people} />
				</div>
			</div>
		);
	}
});

var OrderSummary = React.createClass({
	render: function() {
		
		// Duration pluralisation
		if(this.props.duration > 1) {
			var duration = this.props.duration + ' days';
		} else {
			var duration = this.props.duration + ' day';
		}
		
		// Initial total Calculation
		var initialTotal = this.props.duration * this.props.price;
		
		// Discount Calculation
		var discount = Math.floor((initialTotal / 100) * this.props.discount);
		
		// Subtotal (with Discount)
		var subTotal = initialTotal - discount;
		
		// Calculate tax
		var tax = Math.floor((subTotal / 100) * this.props.tax);
		
		// Total
		var total = subTotal + tax;
		
		return (
			<div className="OrderSummary">
				<div className="Title">Order Summary</div>
				<table>
					<tr>
						<td>{this.props.price} x {duration}</td>
						<td>{initialTotal} GBP</td>
					</tr>
					<tr>
						<td>Discount</td>
						<td>{discount} GBP</td>
					</tr>
					<tr>
						<td>Subtotal</td>
						<td>{subTotal} GBP</td>
					</tr>
					<tr>
						<td>Tax</td>
						<td>{tax} GBP</td>
					</tr>
				</table>
				<div className="Total">
					<div className="TotalLabel">Total</div>
					<div className="Amount">
						{total} <small>GBP</small>
					</div>
				</div>
			</div>
		);
	}
});

var PaymentForm = React.createClass({
	render: function() {
		return (
			<div className="PaymentForm">
				<form onSubmit={this.props.onSubmit}>
					<div className="Title">Payment information</div>
					<BasicInput name="name" label="Name on credit card" type="text" placeholder="John Smith" />
					<BasicInput name="card" label="Credit card number" type="number" placeholder="0000 0000 0000 0000" />
					<ExpiryDate />
					<CheckoutButton />
				</form>
			</div>
		);
	}
});

var CheckoutButton = React.createClass({
	render: function() {
		return (
			<div className="CheckoutButton">
				<button>Book securely</button>
				<span><i className="fa fa-fw fa-lock"></i> Your credit card information is encrypted</span>
			</div>
		);
	}
});

var ExpiryDate = React.createClass({
	render: function() {
		return (
			<div className="ExpiryDate">
				<div>
					<label>Expires on</label>
					<div className="Expiry">
						<select>
							<option value="">January</option>
							<option value="">February</option>
							<option value="">March</option>
							<option value="">April</option>
							<option value="">May</option>
							<option value="">June</option>
							<option value="">July</option>
							<option value="">August</option>
							<option value="">September</option>
							<option value="">October</option>
							<option value="">November</option>
							<option value="">December</option>
						</select>
						<select name="" id="">
							<option value="">2016</option>
							<option value="">2017</option>
							<option value="">2018</option>
							<option value="">2019</option>
							<option value="">2020</option>
							<option value="">2021</option>
						</select>
					</div>
				</div>
				<div className="CVCField">
					<label>CVC</label>
					<input placeholder="000" type="number" />
				</div>
			</div>
		);
	}
});

var BasicInput = React.createClass({
	render: function() {
		return (
			<div className="BasicInput">
				<label for={this.props.name}>{this.props.label}</label>
				<input id={this.props.name} type={this.props.type} placeholder={this.props.placeholder} />
			</div>
		);
	}
});

var Checkout = React.createClass({
	render: function() {
		return (
			<div className="Checkout">
				<OrderSummary discount={this.props.discount} tax={this.props.tax} price={this.props.price} duration={this.props.duration} />
				<PaymentForm onSubmit={this.props.onSubmit} />
			</div>
		);
	}
});

var Header = React.createClass({
	
	render: function() {
		return (
			<header>
				<input onChange={this.props.onChange} type="range" max="100" min="1" step="1" />
			</header>
		);
	}
});

var App = React.createClass({
	
	getInitialState: function() {
		return ({
			mounted: false,
			people: 1,
			price: 320.00,
			tax: 20,
			duration: 5,
			discount: 5
		});
	},
	
	componentDidMount: function() {
		this.setState({ mounted: true });
	},
	
	handleSubmit: function(e) {
		e.preventDefault();
	},
	
	handleChange: function(e) {
		console.log(e.target.value);
		this.setState({ duration: e.target.value });
	},

	render: function() {
		
		var overlay, container;
		if(this.state.mounted) {
			overlay = (
				<Overlay image="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/jj-2.jpg" />
			);
			container = (
				<Container>
					<ImagePreview price={this.state.price} duration={this.state.duration} people={this.state.people} image="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/jj-2.jpg" />
					<Checkout discount={this.state.discount} tax={this.state.tax} price={this.state.price} duration={this.state.duration} onSubmit={this.handleSubmit} />
				</Container>
			);
		}
		
		return(
			<div className="App">
				<ReactCSSTransitionGroup transitionName="overlay" transitionEnterTimeout={500} transitionLeaveTimeout={300}>
					{overlay}
				</ReactCSSTransitionGroup>
				<ReactCSSTransitionGroup transitionName="container" transitionEnterTimeout={500} transitionLeaveTimeout={300}>
					{container}
				</ReactCSSTransitionGroup>
				<Header onChange={this.handleChange} />
			</div>
		);
	}
});

ReactDOM.render(
	<App />,
	document.getElementById('app')
);
View Compiled

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css

External JavaScript

  1. https://fb.me/react-with-addons-0.14.7.js
  2. https://npmcdn.com/react-dom@15.3.0/dist/react-dom.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/zepto/1.1.6/zepto.min.js