<div id="app"></div>
// Fonts
@import 'https://fonts.googleapis.com/css?family=Lato:100,300,400,700,900';
// Variables
$font: 'Lato', sans-serif;
// Colors
$red: #e50914;
$black: #221f1f;
$blackblack: #000000;
$grey: #f5f5f1;
$white: #ffffff;
// General
body {
background: $black;
color: $white;
font-family: $font;
}
// Typography
h2 {
font-size: 24px;
font-weight: 600;
line-height: 1.4;
margin-bottom: 1em;
}
p {
font-size: 15px;
font-weight: 300;
line-height: 1.6;
margin-bottom: 1em;
}
////////////
// MIXINS //
////////////
@mixin generate-gradient($color,$amount) {
background: -moz-linear-gradient(top, rgba($color,$amount) 0%, rgba($color,0) 100%); /* FF3.6-15 */
background: -webkit-linear-gradient(top, rgba($color,$amount) 0%,rgba($color,0) 100%); /* Chrome10-25,Safari5.1-6 */
background: linear-gradient(to bottom, rgba($color,$amount) 0%,rgba($color,0) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#a6000000', endColorstr='#00000000',GradientType=0 ); /* IE6-9 */
}
////////////////
// COMPONENTS //
////////////////
// Header
.Header {
@include generate-gradient($blackblack, 1);
display: flex;
padding: 40px ;
position: fixed;
top: 0;
left: 0;
width: 100vw;
box-sizing: border-box;
z-index: 5;
}
// Logo
.Logo {
width: 151px;
height: 41px;
margin-right: 10px;
transition: margin .125s ease;
svg {
transform: scale(.5);
transform-origin: 0 0;
path {
fill: $red;
transition: fill .125s ease;
}
}
&:hover {
cursor: pointer;
margin-top: 2px;
svg {
path {
fill: $white !important;
}
}
}
}
// Navigation
.Navigation {
ul {
display: flex;
height: 37px;
align-items: center;
padding: 0 10px;
li {
font-weight: 400;
padding: 7px 10px;
font-size: 14px;
transition: background .125s ease;
border-radius: 3px;
&:hover {
background: $red;
cursor: pointer;
}
}
}
}
// Search
.Search {
align-items: center;
justify-content: center;
width: 100%;
position: absolute;
display: flex;
justify-content: center;
pointer-events: none;
input {
font-family: $font;
font-weight: 300;
font-size: 14px;
height: 37px;
width: 20%;
background: transparent;
color: $white;
appearance: none;
border: 0;
border: 2px solid rgba($grey, .1);
outline: none;
border-radius: 37px;
padding: 0 10px;
pointer-events: all;
transition: border .25s ease, width .125s ease .125s;
&:focus {
border: 2px solid $red;
width: 30%;
}
}
}
// UserProfile
.UserProfile {
margin-left: auto;
// User
.User {
display: flex;
.image {
border-radius: 44px;
margin-left: 10px;
overflow: hidden;
width: 44px;
height: 44px;
box-sizing: border-box;
border: 3px solid transparent;
transition: border .125s ease;
img {
width: 100%;
display: block;
}
}
.name {
align-items: center;
display: flex;
font-size: 18px;
font-weight: 300;
height: 44px;
}
&:hover {
cursor: pointer;
.image {
border: 3px solid $red;
}
}
}
// UserProfile-menu
.UserProfile-menu {
display: none;
}
}
// Hero Image
.Hero {
width: 100%;
position: relative;
background-size: cover;
min-height: 800px;
.content {
position: relative;
z-index: 4;
width: 500px;
left: 10vw;
top: 10vw;
.logo {
max-width: 400px;
display: block;
position: relative;
left: -5px;
}
p {
width: 100%;
}
.button-wrapper {
display: flex;
width: 400px;
margin-top: 30px;
.Button:first-child {
margin-right: 10px;
}
}
}
.overlay {
@include generate-gradient($black, 1);
height: 100%;
position: absolute;
z-index: 3;
top: 0;
left: 0;
width: 100%;
transform: rotate(180deg);
}
}
// Button
.Button {
background: transparent;
display: flex;
height: 44px;
align-items: center;
justify-content: center;
text-decoration: none;
color: $white;
padding: 20px;
box-sizing: border-box;
border: 2px solid rgba($grey, .2);
border-radius: 44px;
font-size: 14px;
font-weight: 600;
transition: border .125s ease, background .125s ease;
&:hover {
border: 2px solid $grey;
}
&[data-primary='true'] {
border: 2px solid $red;
&:hover {
background: $red;
}
}
}
// TitleList
.Hero ~ .TitleList:nth-child(3) {
margin-top: -10vw;
position: relative;
z-index: 4;
}
// Hack for first after Hero
.TitleList {
padding: 20px 40px;
box-sizing: border-box;
transition: opacity 3s ease;
opacity: 0;
.Title {
@extend h2;
}
.titles-wrapper {
display: flex;
flex-wrap: wrap;
margin: 20px 0;
width: calc(100vw - 80px);
box-sizing: border-box;
justify-content: space-between;
}
// Animation
&[data-loaded='true'] {
opacity: 1;
}
}
// Item
.Item {
width: calc(20% - 10px);
min-width: calc(20% - 10px);
background-color: $blackblack;
flex: 1 0 auto;
background-position: center;
background-size: 100%;
height: 200px;
background-repeat: no-repeat;
overflow: hidden;
margin-right: 10px;
transition: background 1s ease;
&:nth-child(n+6) {
margin-top: 20px;
}
&:last-child {
margin-right: 0;
}
.overlay {
@include generate-gradient($blackblack, .75);
padding: 20px;
position: relative;
height: 100%;
pointer-events: none;
opacity: 0;
transition: opacity .125s ease;
.title {
font-size: 22px;
font-weight: 200;
opacity: 0;
transition: opacity .25s ease;
}
.rating {
font-size: 14px;
opacity: 0;
transition: opacity .25s ease .125s;
}
.plot {
$lines-to-show: 3;
font-size: 14px;
display: -webkit-box;
margin-top: 100px;
font-weight: 300;
line-height: 1.6;
opacity: 0;
-webkit-line-clamp: $lines-to-show;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
transition: margin .25s ease .125s, opacity .25s ease .25s;
}
}
&:hover {
background-size: 150%;
.overlay {
opacity: 1;
pointer-events: all;
.plot {
margin-top: 10px;
opacity: 1;
}
.title {
opacity: 1;
}
.rating {
opacity: 1;
}
.ListToggle {
opacity: 1;
}
}
}
}
// Item [ANIMATION]
// ListToggle
.ListToggle {
$size: 32px;
border: 2px solid rgba($white, .2);
width: $size;
height: $size;
font-size: $size / 2;
border-radius: $size;
overflow: hidden;
position: absolute;
right: 20px;
top: 20px;
opacity: 0;
&:hover {
border: 2px solid $white;
div {
top: - $size;
}
}
&[data-toggled="true"] {
background: $red;
border: 2px solid $red;
div {
top: - $size;
}
}
div {
position: absolute;
top: 0;
left: 0;
height: $size * 2;
width: $size;
transition: .125s ease;
i {
display: flex;
align-items: center;
justify-content: center;
height: $size;
width: $size;
}
}
}
View Compiled
/////////////////
/// COMPONENTS //
/////////////////
// Container
var App = React.createClass({
apiKey: '87dfa1c669eea853da609d4968d294be',
getInitialState: function() {
return {data: []};
},
performSearch: function(e) {
// stop form from submitting
e.preventDefault();
// get the value
var val = $('.Search input').val();
// Do the thing
var requestUrl = 'https://api.themoviedb.org/3/search/multi?query=' + val + '&api_key=' + this.apiKey;
$.ajax({
url: requestUrl,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
componentDidUpdate: function() {
},
render: function() {
if(this.state.data.results) {
console.log(this.state.data);
}
return (
<div>
<Header onSubmit={this.performSearch} />
<Hero />
<TitleList title="Top TV picks for Jack" url='discover/tv?sort_by=popularity.desc&page=1' />
<TitleList title="Trending now" url='discover/movie?sort_by=popularity.desc&page=1' />
<TitleList title="Most watched in Horror" url='genre/27/movies?sort_by=popularity.desc&page=1' />
<TitleList title="Sci-Fi greats" url='genre/878/movies?sort_by=popularity.desc&page=1' />
<TitleList title="Comedy magic" url='genre/35/movies?sort_by=popularity.desc&page=1' />
</div>
);
}
});
////////////
// Header //
////////////
var Header = React.createClass({
render: function() {
return (
<header className="Header">
<Logo />
<Navigation />
<Search onSubmit={this.props.onSubmit} />
<UserProfile />
</header>
);
}
});
// Logo
var Logo = React.createClass({
render: function() {
return (
<div id="logo" className="Logo">
<svg version="1.1" width="300" height="81.386726" id="svg3262">
<g transform="translate(-384.28572,-428.81172)" id="layer1">
<g transform="matrix(2.5445375,0,0,2.5445375,1157.1714,-1457.8678)" id="g3235">
<path d="m -203.09972,771.41415 c 1.6425,0.15875 3.2825,0.33 4.92,0.5075 l 3.615,-8.92625 3.43625,9.74875 c 1.76375,0.22125 3.525,0.4525 5.2825,0.695 l -6.02375,-17.09625 6.02625,-14.88 -5.10375,0 -0.0525,0.0725 -3.255,8.03875 -2.8575,-8.11125 -5.03875,0 5.2025,14.7625 -6.15125,15.18875 z" id="path3015" style={{fill:'#b81d24', fillOpacity: 1, fillRule:'nonzero', stroke:'none'}} />
<path d="m -206.91147,771.06478 0,-29.60125 -5.0375,0 0,29.18625 c 1.68125,0.12875 3.36125,0.26875 5.0375,0.415" id="path3019" style={{fill:'#b81d24', fillOpacity: 1, fillRule:'nonzero', stroke:'none'}} />
<path d="m -244.7486,769.4089 c 1.36,0 2.7175,0.01 4.07375,0.0213 l 0,-10.875 6.05125,0 0,-4.63125 -6.05125,0 0,-7.825 6.96875,0 0,-4.63625 -12.02625,0 0,27.95 c 0.3275,0 0.655,-0.004 0.98375,-0.004" id="path3023" style={{fill:'#b81d24', fillOpacity: 1, fillRule:'nonzero', stroke:'none'}} />
<path d="m -260.3881,769.69191 c 1.6775,-0.06 3.3575,-0.11 5.04,-0.15125 l 0,-23.44125 4.7075,0 0,-4.63625 -14.45625,0 0,4.63625 4.70875,0 0,23.5925 z" id="path3035" style={{fill:'#b81d24', fillOpacity: 1, fillRule:'nonzero', stroke:'none'}} />
<path d="m -298.91059,772.81378 0,-17.63625 5.96375,16.92375 c 1.83375,-0.20625 3.67125,-0.4 5.5125,-0.5825 l 0,-30.055 -4.8325,0 0,18.2675 -6.43625,-18.2675 -0.2075,0 -4.8325,0 0,31.98375 0.03,0 c 1.5975,-0.22125 3.19875,-0.43125 4.8025,-0.63375" id="path3039" style={{fill:'#b81d24', fillOpacity: 1, fillRule:'nonzero', stroke:'none'}} />
<path d="m -269.95297,746.09903 0,-4.63625 -12.0275,0 0,24.9125 0,4.6375 0,0.004 c 3.99125,-0.345 7.99625,-0.63375 12.0175,-0.86875 l 0,-0.004 0,-1.33625 0,-3.3 c -2.325,0.135 -4.645,0.29125 -6.96,0.46375 l 0,-7.415 6.05125,0 0,-4.63375 -6.05125,0 0,-7.82375 6.97,0 z" id="path3051" style={{fill:'#b81d24', fillOpacity: 1, fillRule:'nonzero', stroke:'none'}} />
<path d="m -223.72272,765.2864 0,-23.82375 -5.05875,0 0,23.605 0,4.63625 0,0.005 c 4.02375,0.1475 8.0325,0.35375 12.0275,0.6125 l 0,-0.006 0,-1.4975 0,-3.13875 c -2.31875,-0.15 -4.64125,-0.28 -6.96875,-0.3925" id="path3055" style={{fill:'#b81d24', fillOpacity: 1, fillRule:'nonzero', stroke:'none'}} />
</g>
</g>
</svg>
</div>
);
}
});
// Navigation
var Navigation = React.createClass({
render: function() {
return (
<div id="navigation" className="Navigation">
<nav>
<ul>
<li>Browse</li>
<li>My list</li>
<li>Top picks</li>
<li>Recent</li>
</ul>
</nav>
</div>
);
}
});
// Search
var Search = React.createClass({
render: function() {
return (
<form onSubmit={this.props.onSubmit} id="search" className="Search">
<input type="search" placeholder="Search for a title..." />
</form>
);
}
});
// User Profile
var UserProfile = React.createClass({
render: function() {
return (
<div className="UserProfile">
<div className="User">
<div className="name">Jack Oliver</div>
<div className="image"><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/profile/profile-512_1.jpg" /></div>
</div>
<div className="UserProfile-menu">
<div className="UserProfileSwitch">
<ul>
<li>
<div className="UserProfile-image">
<img src="http://lorempixel.com/96/96" />
</div>
<div className="UserProfile-name">
Alexander
</div>
</li>
<li>
<div className="UserProfile-image">
<img src="http://lorempixel.com/96/96" />
</div>
<div className="UserProfile-name">
Mattias
</div>
</li>
</ul>
</div>
<div className="UserNavigation">
<ul>
<li>Your Account</li>
<li>Help Center</li>
<li>Sign out of Netflix</li>
</ul>
</div>
</div>
</div>
);
}
});
//////////
// Hero //
//////////
var Hero = React.createClass({
render: function() {
return (
<div id="hero" className="Hero" style={{backgroundImage: 'url(https://images.alphacoders.com/633/633643.jpg)'}}>
<div className="content">
<img className="logo" src="http://www.returndates.com/backgrounds/narcos.logo.png" alt="" />
<h2>Season 2 now available</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Doloremque id quam sapiente unde voluptatum alias vero debitis, magnam quis quod.</p>
<div className="button-wrapper">
<HeroButton primary={true} text="Watch now" />
<HeroButton primary={false} text="+ My list" />
</div>
</div>
<div className="overlay"></div>
</div>
);
}
})
// Hero Button
var HeroButton = React.createClass({
render: function() {
return (
<a href="#" className="Button" data-primary={this.props.primary}>{this.props.text}</a>
);
}
})
////////////////
// Title List //
////////////////
// Title List Container
var TitleList = React.createClass({
apiKey: '87dfa1c669eea853da609d4968d294be',
getInitialState: function() {
return {data: [], mounted: false};
},
loadContent: function() {
var requestUrl = 'https://api.themoviedb.org/3/' + this.props.url + '&api_key=' + this.apiKey;
$.ajax({
url: requestUrl,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
// console.log(data);
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
componentDidMount: function() {
this.loadContent();
this.setState({ mounted: true });
},
componentWillUnmount: function() {
this.setState({ mounted: false });
},
render: function() {
if(this.state.data.results) {
var titles = this.state.data.results.map(function(title, i) {
if(i < 5) {
var backDrop = 'http://image.tmdb.org/t/p/original' + title.backdrop_path;
if(!title.name) {
var name = title.original_title;
} else {
var name = title.name;
}
return (
<Item key={title.id} title={name} score={title.vote_average} overview={title.overview} backdrop={backDrop} />
);
}
});
} else {
var titles = '';
}
return (
<div ref="titlecategory" className="TitleList" data-loaded={this.state.mounted}>
<div className="Title">
<h1>{this.props.title}</h1>
<div className="titles-wrapper">
{titles}
</div>
</div>
</div>
);
}
});
// Title List Item
var Item = React.createClass({
render: function() {
return (
<div className="Item" style={{backgroundImage: 'url(' + this.props.backdrop + ')'}} >
<div className="overlay">
<div className="title">{this.props.title}</div>
<div className="rating">{this.props.score} / 10</div>
<div className="plot">{this.props.overview}</div>
<ListToggle />
</div>
</div>
);
}
});
// ListToggle
var ListToggle = React.createClass({
getInitialState: function() {
return({ toggled: false })
},
handleClick: function() {
if(this.state.toggled === true) {
this.setState({ toggled: false });
} else {
this.setState({ toggled: true });
}
},
render: function() {
return (
<div onClick={this.handleClick} data-toggled={this.state.toggled} className="ListToggle">
<div>
<i className="fa fa-fw fa-plus"></i>
<i className="fa fa-fw fa-check"></i>
</div>
</div>
);
}
});
ReactDOM.render(
<App />,
document.getElementById('app')
);
View Compiled
This Pen doesn't use any external CSS resources.