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

// Colors
$white: #FFFFFF;
$sunsetorange: #F05D5E;
$irresistable: #AF3B6E;
$yankeesblue: #252D3F;
$mediumspringgreen: #21FA90;

// Mixins
@mixin mq($width) {
	@media screen and (max-width: $width) {
		@content;
	}
}

// Styles
.App {
	align-items: center;
	font-family: 'Lato', sans-serif;
	display: flex;
	font-weight: 300;
	width: 100vw;
	height: 100vh;
	justify-content: center;
	background: $yankeesblue;
	overflow: hidden;
	position: absolute;
	top: 0;
	left: 0;
}

.ImageBG {
	position: absolute;
	top: -5vh;
	left: -5vw;
	width: 110vw;
	height: 110vh;
	background-size: cover;
	filter: blur(15px);
	opacity: 1;
	mix-blend-mode: multiply;
}

.Settings {
	width: 90vw;
	height: 90vh;
	position: relative;
	z-index: 2;
}

.MainWrapper {
	display: flex;
	width: 100%;
	height: 85%;

	.Sidebar {
		width: 30%;
		max-width: 350px;
		height: 100%;
		background: rgba(lighten($yankeesblue, 5), .85);
		
		@include mq(880px) {
			display: none;
		}
		
		.Title {
			width: 100%;
			padding: 20px;
			box-sizing: border-box;
			border-bottom: 1px solid rgba($white, .1);
			color: $white;
			text-transform: uppercase;
			font-weight: 600;
			letter-spacing: .2em;
			font-size: 12px;
		}
		
		.Setting {
			padding: 0 20px;
			margin-top: 20px;
			border-bottom: 1px solid rgba($white, .05);
			
			label {
				font-weight: 400;
				text-transform: uppercase;
				color: $white;
				display: flex;
				justify-content: space-between;
				margin-bottom: 5px;
				font-size: 12px;
				letter-spacing: .2em;
				opacity: .5;
			}
			
			input {
				width: 100%;
				margin-bottom: 20px;
			}
		}
	}
	
	.ImageContainer {
		width: 50%;
		background: rgba(darken($yankeesblue, 5), .5);
		display: flex;
		flex: 1 0 auto;
		justify-content: center;
		align-items: center;
		
		.Image {
			height: 90%;
			width: 90%;
			background-size: cover;
			box-shadow: 0 10px 20px rgba(black, .5);
			background-position: center;
			position: relative;
			transition: filter .125s ease;
			
			&::before {
				content: '';
				width: 100%;
				height: 100%;
				position: absolute;
				top: 0;
				left: 0;
				// background: blue;
				// mix-blend-mode: color;
				opacity: .3;
			}
			
			&::after {
				width: 100%;
				height: 100%;
				position: absolute;
				top: 0;
				mix-blend-mode: overlay;
				left: 0;
				z-index: 3;
				content: '';
				// background: -moz-radial-gradient(center, ellipse cover,  rgba(0,0,0,0) 0%, rgba(0,0,0,0.65) 100%); /* FF3.6-15 */
				// background: -webkit-radial-gradient(center, ellipse cover,  rgba(0,0,0,0) 0%,rgba(0,0,0,0.65) 100%); /* Chrome10-25,Safari5.1-6 */
				// background: radial-gradient(ellipse at center,  rgba(0,0,0,0) 0%,rgba(0,0,0,0.65) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
				// filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00000000', endColorstr='#a6000000',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
			}
		}
	}
}
.FilterList {
	width: 100%;
	height: calc(15% - 10px);
	background: rgba(lighten($yankeesblue, 5), .45);
	box-sizing: border-box;
	padding: 20px;
	display: flex;
	overflow-x: auto;
	
	.Filter {
		// min-width: 150px;
		// width: 150px;
		width: 25%;
		height: 100%;
		position: relative;
		flex: 1 0 1;
		margin-right: 20px;
		background: darken($yankeesblue, 2);
		box-shadow: 0 10px 20px 0 rgba(black, .25);
		transition: box-shadow .25s ease, transform .25s ease;
		
		&:hover {
			cursor:pointer;
			box-shadow: 0 10px 20px -10px rgba(black, 1);
			transform: scale(1.1);
		}
		
		&:last-child {
			margin-right: 0px;
		}
		
		// FilterImage
		.Image {
			position: absolute;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
			background-size: cover;
			background-position: center;
			transition: filter .25s ease .25s;
		}
	}
}
View Compiled
// Data
var data = {
	image: 'https://unsplash.it/1080/800',
	settings: [
		{
			name: 'contrast',
			value: '100%',
		},
		{
			name: 'hue',
			value: '0deg'
		},
		{
			name: 'brightness',
			value: '100%'
		},
		{
			name: 'saturate',
			value: '100%'
		},
		{
			name: 'sepia',
			value: '0%'
		}
	],
	filters: [
		{
			id: 0,
			name: 'Noir',
			settings: [
				{
					name: 'contrast',
					value: '138%',
				},
				{
					name: 'hue',
					value: '0deg'
				},
				{
					name: 'brightness',
					value: '122%'
				},
				{
					name: 'saturate',
					value: '0%'
				},
				{
					name: 'sepia',
					value: '0%'
				}
			]
		},
		{
			id: 1,
			name: 'Aged',
			settings: [
				{
					name: 'contrast',
					value: '94%',
				},
				{
					name: 'hue',
					value: '-54deg'
				},
				{
					name: 'brightness',
					value: '92%'
				},
				{
					name: 'saturate',
					value: '100%'
				},
				{
					name: 'sepia',
					value: '44%'
				}
			]
		},
		{
			id: 2,
			name: 'Whiteout',
			settings: [
				{
					name: 'contrast',
					value: '32%',
				},
				{
					name: 'hue',
					value: '0deg'
				},
				{
					name: 'brightness',
					value: '173%'
				},
				{
					name: 'saturate',
					value: '0%'
				},
				{
					name: 'sepia',
					value: '0%'
				}
			]
		},
		{
			id: 3,
			name: 'Vintage',
			settings: [
				{
					name: 'contrast',
					value: '164%',
				},
				{
					name: 'hue',
					value: '0deg'
				},
				{
					name: 'brightness',
					value: '47%'
				},
				{
					name: 'saturate',
					value: '0%'
				},
				{
					name: 'sepia',
					value: '100%'
				}
			]
		}
	]
};

// App Container
var App = React.createClass({
	getDefaultProps: function() {
		return(data);
	},
	handleChange: function(e) {
		var value = e.target.value;
		var name = e.target.id;
		switch (name) {
			case 'contrast':
				this.props.settings[0].value = value + '%';
				break;
			case 'hue':
				this.props.settings[1].value = value + 'deg';
				break;
			case 'brightness':
				this.props.settings[2].value = value + '%';
				break;
			case 'saturate':
				this.props.settings[3].value = value + '%';
				break;
			case 'sepia':
				this.props.settings[4].value = value + '%';
				break;
		}
		this.forceUpdate();
	},
	handleClick: function(e) {
		var index = e.target.id.replace('filter-','');
		// console.log(this.props.filters[index].settings);
		this.props.settings[0].value = this.props.filters[index].settings[0].value;
		this.props.settings[1].value = this.props.filters[index].settings[1].value;
		this.props.settings[2].value = this.props.filters[index].settings[2].value;
		this.props.settings[3].value = this.props.filters[index].settings[3].value;
		this.props.settings[4].value = this.props.filters[index].settings[4].value;
		this.forceUpdate();
		
	},
	render: function() {
		return(
			<div className="App">
				<ImageBG image={this.props.image} />
				<Settings onClick={this.handleClick} onChange={this.handleChange} data={this.props} />
			</div>	
		)
	}
});

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

// Settings Container
var Settings = React.createClass({
	render: function() {
		return (
			<div className="Settings">
				<div className="MainWrapper">
					<Sidebar onChange={this.props.onChange} settings={this.props.data.settings} />
					<ImageContainer settings={this.props.data.settings} image={this.props.data.image} />
				</div>
				<FilterList onClick={this.props.onClick} filters={this.props.data.filters} image={this.props.data.image} />
			</div>
		)
	}
});

// Sidebar
var Sidebar = React.createClass({
	render: function() {
		var onChange = this.props.onChange;
		var settings = this.props.settings.map(function(setting, i) {
			return <Setting onChange={onChange} name={setting.name} value={setting.value} />;
		});
		
		return (
			<div className="Sidebar">
				<div className="Title">Reactagram v1.0</div>
				{settings}
			</div>
		);
	}
});

var Setting = React.createClass({
	render: function() {
		
		if(this.props.name == 'hue') {
			
			var value = this.props.value.replace('deg','');
			
			return (
				<div className="Setting">
					<label><div>{this.props.name}</div><div>{value}</div></label>
					<input refs={this.props.name} min="-360" max="360" step="1" onChange={this.props.onChange} id={this.props.name} type="range" defaultValue={this.props.value} />
				</div>
			);
			
		} else if(this.props.name == 'contrast' || this.props.name == 'brightness') {
			
			var value = this.props.value.replace('%','');
			
			return (
				<div className="Setting">
					<label><div>{this.props.name}</div><div>{value}</div></label>
					<input refs={this.props.name} min="0" max="200" step="1" onChange={this.props.onChange} id={this.props.name} type="range" defaultValue={this.props.value} />
				</div>
			);
			
		} else {
			
			var value = this.props.value.replace('%','');
			
			return (
				<div className="Setting">
					<label><div>{this.props.name}</div><div>{value}</div></label>
					<input refs={this.props.name} min="0" max="100" step="1" onChange={this.props.onChange} id={this.props.name} type="range" defaultValue={this.props.value} />
				</div>
			);
			
		}
		
	}
});

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

// Image
var Image = React.createClass({
	render: function() {
		
		if(!this.props.settings == []) {
			var filterString = "";
			var filters = this.props.settings.map(function(filter, i) {
				
				if(filter.name == 'hue') {
					filterString = filterString + 'hue-rotate(' + filter.value + ') ';
				} else {
					filterString = filterString + filter.name + '(' + filter.value + ') ';
				}

				return filterString;
			});
		}
		
		
		var style = {
			backgroundImage: 'url(' + this.props.image + ')',
			webkitFilter: filterString
		};
		
		if(!this.props.id) {
			var id = 'filter-image';
		} else {
			var id = this.props.id;
		}
		
		return (
			<div id={id} className="Image" style={style}></div>
		);

	}
});

// FilterList
var FilterList = React.createClass({	
	render: function() {
		var image = this.props.image;
		var onClick = this.props.onClick;
		
		var filters = this.props.filters.map(function(filter, i) {
			return <Filter onClick={onClick} id={filter.id} image={image} settings={filter.settings} />
		});
		
		return (
			<div className="FilterList">
				{filters}
			</div>
		);
	}
});

// Filter
var Filter = React.createClass({
	render: function() {
		return (
			<div className="Filter" onClick={this.props.onClick}>
				<Image id={'filter-' + this.props.id} settings={this.props.settings} image={this.props.image} />
			</div>
		);
	}
});

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

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://npmcdn.com/[email protected]/dist/react.min.js
  2. https://npmcdn.com/[email protected]/dist/react-dom.min.js