@import "nib"
//
// Colors
//-----------------------------------------------------
$grey = #69747e
$grey-light = #ecf0f1
$grey-medium = #6c7a89
$grey-dark = #22313F
$blue = #3498db
$red = #c0392b
$green = #27ae60
$purple = #9b59b6
$orange = #f39c12
//
// Typography
//-----------------------------------------------------
@import url('https://fonts.googleapis.com/css?family=Lato:100,300,400,700')
//
// Globals
//-----------------------------------------------------
* {
box-sizing border-box
margin 0
padding 0
}
html, body {
background $grey-light
font-family 'Lato', sans-serif
}
ul, ol, li {
list-style-type none
}
a {
text-decoration none
}
h1, h2, h3, h4, h5, h6 {
margin 0
padding 0
font-weight normal
}
//
// Variables & animations
//-----------------------------------------------------
$card-width = 300px
$card-height = 400px
$masthead-height = 220px
$easing = cubic-bezier(0.645, 0.045, 0.355, 1)
@keyframes shake {
0% {
transform translateX(7px)
}
15% {
transform translateX(-7px)
}
30% {
transform translateX(5px)
}
45% {
transform translateX(-5px)
}
60% {
transform translateX(3px)
}
75% {
transform translateX(-3px)
}
100% {
transform translateX(0px)
}
}
.shake {
animation-name shake
animation-duration 450ms
}
//
// Component UI
//-----------------------------------------------------
.ui-card {
width $card-width
height $card-height
background white
border 2px solid darken($grey-light, 5%)
position relative
border-radius 8px
overflow hidden
// For codepen
position absolute
top 50%
left 50%
margin-top -($card-height / 2)
margin-left -($card-width / 2)
}
.ui-card .toggle {
position absolute
top 10px
right 10px
}
.ui-card section {
width 100%
height 100%
position absolute
transition all 200ms ease-in-out
}
.ui-card .content {
left 0
}
//
// Side bar toggle switch
//
.ui-card .toggle i {
color white
transition all 300ms $easing
}
.ui-card.sidebar-open .toggle i {
color $grey-dark
transform rotateZ(180deg)
}
//
// Sidebar styles
//
.ui-card .sidebar {
left 100%
background white
box-shadow none
transition all 300ms $easing
overflow hidden
}
//
// Sidebar toggled animation
//
.ui-card.sidebar-open .content {
left -10%
}
.ui-card.sidebar-open .sidebar {
left 10%
box-shadow 0px 0px $card-width 100px rgba(0, 0, 0, 0.7)
}
.ui-card nav {
width 90% // this accounts for the 10% 'margin' on the right when shown
padding-top 40px
}
.ui-card nav li {
border-bottom 1px solid $grey-light
}
.ui-card nav li i {
display inline-block
width 30px
}
.ui-card nav li a {
display block
padding 20px
padding-left 30px
color $grey-medium
}
.ui-card nav li a:hover {
border-left 4px solid $blue
padding-left 26px
color $grey-dark
}
.ui-card .disconnect {
line-height 117px
left 0px
}
.ui-card .disconnect,
.ui-card .reauth {
width 90%
height 117px // Arbitrary height... should refactor this area to flex box
text-align center
position absolute
bottom 0px
transition all 300ms $easing
}
.ui-card .btn {
font-size .8em
}
.ui-card .btn-disconnect {
color $red
padding 10px 30px
}
.ui-card .btn-disconnect:hover {
color white
background-color $red
border-radius 6px // This would be in a global
}
//
// Re-auth area
//
.ui-card .reauth {
padding-top 20px
left 100%
}
.ui-card .reauth h3 {
font-size 15px
margin-bottom 7px
color $red
}
.ui-card .reauth input {
border-radius 4px
border 1px solid lighten($grey-medium, 50%)
padding 5px
margin-bottom 7px
}
.ui-card .reauth input:focus {
outline none
border 2px solid $blue
margin-top -1px
margin-bottom 6px
}
.ui-card .btn-cancel {
color darken($grey-light, 20%)
}
//
// Reauth toggled state
//
.ui-card .sidebar.reauth-toggled .disconnect {
left -100%
}
.ui-card .sidebar.reauth-toggled .reauth {
left 0px
}
//
// Card content area
//
.ui-card .masthead {
width 100%
height $masthead-height
position relative
background $grey-dark
}
.ui-card .masthead h1 {
height $masthead-height
color white
line-height ($masthead-height - 20)
text-align center
font-weight 300
font-size 28px
}
.ui-card .device-icon {
background $blue
width 90px
height 90px
border-radius 50%
padding 4.5%
position absolute
left 50%
margin-left -45px
bottom -45px
text-align center
}
.ui-card .device-info {
width 100%
position absolute
top ($masthead-height + 30%)
}
.ui-card .device-icon i {
color white
font-size 66px
}
.ui-card .device-space {
width 100%
height 60px
text-align center
}
.ui-card .device-space h3 {
color $grey-medium
font-size 17px
margin-bottom 15px
}
.ui-card .available-space {
background $grey-light
width 80%
margin 0 auto
height 10px
position relative
border-radius 20px
overflow hidden
}
// Hardcoded for prototype
.ui-card .used-space--photos,
.ui-card .used-space--videos {
height 100%
position absolute
border-radius 20px
}
.ui-card .used-space--photos {
width 70%
background $green
}
.ui-card .used-space--videos {
width 30%
background $orange
}
.ui-card .device-legend {
text-align center
font-size 11px
}
.ui-card .device-legend li {
display inline-block
margin-right 20px
color $grey-medium
}
.ui-card .device-legend li:last-child {
margin-right 0px
}
.ui-card .device-legend i {
display inline-block
position relative
top -1px
font-size 9px
margin-right 3px
}
.ui-card .device-legend i.photos {
color $green
}
.ui-card .device-legend i.videos {
color $orange
}
View Compiled
/** @jsx React.DOM */
//
// Main device wrapper
// NOTE: Holding menu toggle state here (eventually)
//--------------------------------------------------------
var DeviceCard = React.createClass({
render: function() {
return (
<div className='ui-card'>
<Device_section__info data={this.props.data} />
<Device_section__sideBarMenu />
<Device_action__toggleMenu />
</div>
);
}
});
//
// Device information section wrapper
//--------------------------------------------------------
var Device_section__info = React.createClass({
render: function() {
return (
<section className='content'>
<Device_section__mastHead data={this.props.data} />
<Device_section__freeSpace />
</section>
);
}
});
//
// Device side bar menu wrapper
//--------------------------------------------------------
var Device_section__sideBarMenu = React.createClass({
render: function() {
return (
<section className='sidebar'>
<nav>
<Device_section__sideBarNav />
<Device_section__sideBarRemoveConnection />
</nav>
</section>
);
}
});
//
// Device side bar nav list
//--------------------------------------------------------
var Device_section__sideBarNav = React.createClass({
render: function() {
return (
// TODO: Each item here should be visible based on flags
// I.e No photos? Should we show edit photos?
<ul>
<li>
<a href="#"><i className='fa fa-wifi'></i>Wireless Settings</a>
</li>
<li>
<a href="#"><i className='fa fa-info-circle'></i>Device Information</a>
</li>
<li>
<a href="#"><i className='fa fa-photo'></i>Edit Photos</a>
</li>
<li>
<a href="#"><i className='fa fa-video-camera'></i>Edit Videos</a>
</li>
</ul>
);
}
});
//
// Device side bar delete connection \ reauth
//--------------------------------------------------------
var Device_section__sideBarRemoveConnection = React.createClass({
render: function() {
return (
<span>
<div className='disconnect'>
<a href='#' className='btn btn-disconnect'>Disconnect Device</a>
</div>
<div className='reauth'>
<h3>Re-enter your password</h3>
<input type='password' />
<p>
<a href="#" className='btn btn-cancel js-reAuth'>Cancel</a>
</p>
</div>
</span>
);
}
});
//
// Device actions toggle switch
//--------------------------------------------------------
var Device_action__toggleMenu = React.createClass({
handleClick: function() {
// TODO: Do this in the React way.
$(this.getDOMNode()).closest('.ui-card').toggleClass('sidebar-open');
},
render: function() {
return (
<div className='toggle'>
<a href='#' onClick={this.handleClick}><i className="fa fa-cog"></i></a>
</div>
);
}
});
//
// Masthead
//
var Device_section__mastHead = React.createClass({
render: function() {
return (
<header className='masthead'>
<h1>{this.props.data.deviceName}</h1>
<div className='device-icon'>
<i className='fa fa-mobile-phone'></i>
</div>
</header>
);
}
});
//
// Free space module
//
var Device_section__freeSpace = React.createClass({
render: function() {
return (
// This is all static for now
// Todo: Populate this with data, and animate accordingly.
// Adding some actual values probably wouldn't hurt either.
<article className='device-info'>
<div className='device-space'>
<h3>Free Space</h3>
<div className='available-space'>
<div className="used-space--photos"></div>
<div className="used-space--videos"></div>
</div>
</div>
<ul className='device-legend'>
<li><i className="fa fa-circle videos"></i> Videos</li>
<li><i className="fa fa-circle photos"></i> Photos</li>
<li><i className="fa fa-circle available"></i> Available</li>
</ul>
</article>
);
}
});
//
// Fake and totally arbitrary data
//--------------------------------------------------------
var device_data = {
deviceType : 'iphone',
deviceName : 'iPhone 6',
availableSpace : 2048, // megabytes
photosTotalSpace : 768, // megabytes
videosTotalSpace : 384 // megabytes
}
//
// Render this ish
//--------------------------------------------------------
React.renderComponent(
<DeviceCard data={device_data}/>,
document.getElementById('device-cards')
);