<div class="marvel-device nexus5">
<div class="top-bar"></div>
<div class="sleep"></div>
<div class="volume"></div>
<div class="camera"></div>
<div class="screen">
<div class="screen-scroll">
<h3 class="title">Results</h3>
<div class="loader">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" aria-hidden="true">
<circle cx="50" cy="37" r="7"/>
<circle cx="62.5" cy="43.5" r="7"/>
<circle cx="62.5" cy="56.5" r="7"/>
<circle cx="50" cy="65" r="7"/>
<circle cx="37.5" cy="56.5" r="7"/>
<circle cx="37.5" cy="43.5" r="7"/>
</svg>
</div>
<ul class="users"></ul>
</div>
<div class="modal">
<div class="avatar"></div>
<div class="profile"></div>
</div>
</div>
</div>
// ==========================================================
// DEMO STYLES
// ==========================================================
$bg: #91999f;
html,body {height: 100%;}body {display:flex;flex-direction:column;align-items:center;justify-content:center;width: 100%;background:$bg;}
// ==========================================================
// REQUIRED STYLES
// ==========================================================
:root {
--primary-color: #2C3942;
--secondary-color: #1192FF;
--tertiary-color: #997AC0;
--button-bg: var(--tertiary-color);
--material-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
--screen-height: 568px;
--screen-width: 320px;
}
* {
&:before,
&:after {
box-sizing: inherit;
}
}
html,
input {
box-sizing: border-box;
}
button {
appearance: none;
border: 2px solid var(--button-bg);
border-radius: 100px;
margin: 10px 0;
padding: 10px 0;
transition: 200ms background cubic-bezier(.4, 0, .2, 1);
font-weight: 400;
background: transparent;
color: white;
&:hover,
&:focus {
cursor: pointer;
background: var(--button-bg);
}
&:focus {
outline: none;
}
}
.screen {
position: relative;
background: var(--primary-color);
}
.screen-scroll {
height: 100%;
overflow: scroll;
}
.title {
font-family: 'Roboto', sans-serif;
font-size: 1em;
font-weight: 300;
text-transform: uppercase;
color: white;
}
.users {
display: flex;
flex-wrap: wrap;
list-style-type: none;
margin: 0;
padding: 0;
li {
padding: 5px;
width: 30%;
opacity: 0;
}
img {
border-radius: 80%;
box-shadow: var(--material-shadow);
&:hover {
cursor: pointer;
}
}
}
.modal {
border-radius: 100%;
height: var(--screen-height);
pointer-events: none;
position: absolute;
top: 0;
left: 0;
overflow: scroll;
transform: scale(0) translateZ(0);
transition-duration: 320ms;
transition-timing-function: cubic-bezier(.4, 0, .2, 1);
transition-property: transform, opacity, border-radius;
width: var(--screen-width);
background: var(--primary-color);
opacity: 0;
}
.avatar {
position: relative;
&::after {
content: '';
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, var(--primary-color) 98%);
}
img {
display: inline-block;
width: 100%;
max-width: 100%;
}
}
.user-name {
display: block;
font-family: 'Roboto', sans-serif;
font-weight: 300;
text-align: center;
font-size: 0.875em;
text-transform: capitalize;
}
.profile {
display: flex;
flex-direction: column;
padding: 0 1rem;
transition: 200ms transform 100ms cubic-bezier(.4, 0, .2, 1);
transform: translateY(-100%);
font-family: 'Roboto', sans-serif;
font-weight: 400;
}
.profile__name {
margin: 0;
font-family: 'Roboto', sans-serif;
font-weight: 300;
text-transform: capitalize;
}
.profile__email {
display: inline-block;
margin: 5px 0;
font-family: 'Roboto', sans-serif;
font-weight: 300;
text-decoration: none;
color: inherit;
}
.profile__info {
font-family: 'Roboto', sans-serif;
font-weight: 300;
}
// ==========================================================
// LOADER
// ==========================================================
$loader-count: 6;
$loader-proportion: 200px;
$loader-color: #00AABB;
$stagger: 0.1875s;
$animation_config: (
name: expand-out,
duration: 600ms,
timing: cubic-bezier(.66,.14,.83,.67),
iteration: infinite,
direction: alternate,
fill-mode: both
);
@function sh-setup($config) {
@return zip(map-values($config)...);
}
.loader {
position: absolute;
top: 50%;
left: 0;
right: 0;
bottom: 0;
transform: translateY(-50%);
}
.loader svg {
position: relative;
width: $loader-proportion;
height: $loader-proportion;
circle {
animation: sh-setup($animation_config);
position: absolute;
transform: scale(0);
transform-origin: center center;
fill: $loader-color;
}
}
@for $i from 1 through $loader-count {
.loader circle:nth-of-type(#{$i}) {
animation-delay: $i * $stagger;
fill: lighten($loader-color, $i * 3%);
}
}
// ==========================================================
// STATES
// ==========================================================
$user-count: 30;
$duration: 200ms;
$stagger_delay: 0.0125s;
$easing: cubic-bezier(.66, .14, .83, .67);
.loader.hide {
display: none;
}
.users.show {
> * {
animation-duration: $duration;
animation-name: fade-in;
animation-fill-mode: both;
animation-timing-function: $easing;
opacity: 1;
> * {
animation-duration: $duration;
animation-name: expand-out;
animation-fill-mode: both;
animation-timing-function: $easing;
}
@for $i from 1 through $user-count {
&:nth-of-type(#{$i}) {
animation-delay: ($stagger_delay * $i);
> * {
animation-delay: ($stagger_delay * $i);
}
}
}
}
}
.screen.active {
.screen-scroll {
overflow: hidden;
}
.modal {
border-radius: 0;
pointer-events: auto;
transform: scale(1) translateZ(0);
opacity: 1;
}
.profile {
transform: translateY(0);
}
}
// ==========================================================
// KEYFRAMES
// ==========================================================
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes expand-out {
from { transform: scale(0); }
to { transform: scale(1); }
}
View Compiled
// ==========================================================
// UTILITIES
// ==========================================================
function select(s) {
return document.querySelector(s);
}
function selectAll(s) {
return document.querySelectorAll(s);
}
function hide(el) {
el.classList.toggle('hide');
}
function show(el) {
el.classList.toggle('show');
}
function activeToggle(el) {
el.classList.toggle('active');
}
// ==========================================================
// GLOBALS
// ==========================================================
const users = select('.users'),
loader = select('.loader'),
modal = select('.modal'),
screen = select('.screen'),
screen_scroll = select('.screen-scroll'),
avatar = select('.avatar'),
profile = select('.profile');
const transOriginNames = {
webkitTransformOrigin : 'webkitTransformOrigin',
MozTransformOrigin : 'MozTransformOrigin',
msTransformOrigin : 'msTransformOrigin',
transformOrigin : 'transformOrigin'
};
// ==========================================================
// AJAX
// ==========================================================
let request = new XMLHttpRequest();
request.open('GET', 'https://randomuser.me/api/?results=30', true);
request.onload = function() {
if (request.status >= 200 && request.status < 400) {
hide(loader);
show(users);
let data = JSON.parse(request.responseText),
l = data.results.length;
for(var i = 0; i < l; i++) {
users.insertAdjacentHTML('beforeend', '<li><img src="'+
data.results[i].picture.medium +'" data-pic="'+
data.results[i].picture.large +'" data-name="'+
data.results[i].name.first + ' ' +
data.results[i].name.last + '" data-email="'+
data.results[i].email +'"><span class="user-name">'+
data.results[i].name.first +'</span></li>');
}
} else {
alert('We reached our target server, but there was an error');
}
};
request.onerror = function() {
alert('There was a connection error of some sort')
};
request.send();
// ==========================================================
// EVENTS
// ==========================================================
users.addEventListener('click', function(e) {
let target = e.target,
data_src = target.getAttribute('data-pic'),
data_name = target.getAttribute('data-name'),
data_email = target.getAttribute('data-email'),
target_coords = target.getBoundingClientRect();
if(target.nodeName === 'IMG') {
activeToggle(screen);
avatar.innerHTML = '<img src="' + data_src + '" alt="">';
profile.innerHTML = '<h3 class="profile__name">'+ data_name +'</h3>';
profile.innerHTML += '<a href="#" class="profile__email">'+ data_email +'</a>';
profile.innerHTML += '<button>Follow</button>';
profile.innerHTML += '<p class="profile__info">Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.</p>';
for(let name in transOriginNames) {
modal.style[name] = (target.offsetLeft + (target_coords.width/2)) +'px ' + ((target.offsetTop + (target_coords.height/2)) - screen_scroll.scrollTop) + 'px';
}
}
});
modal.addEventListener('click', function(e) {
activeToggle(screen);
});
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.