<div id="app">
<main class="ui-items">
<div class="ui-item -yesterday">
<i class="fa fa-heart"></i>
<div class="ui-date">Yesterday</div>
<div class="ui-heart-rate">90/320</div>
<div class="ui-footer">
<div class="ui-link">More</div>
<div class="ui-link">Settings</div>
</div>
</div>
<div class="ui-item -today">
<i class="fa fa-heart"></i>
<div class="ui-date">Today</div>
<div class="ui-heart-rate">120/280</div>
<div class="ui-footer">
<div class="ui-link">More</div>
<div class="ui-link">Settings</div>
</div>
</div>
<div class="ui-item -yesterday">
<i class="fa fa-heart"></i>
<div class="ui-date">Yesterday</div>
<div class="ui-heart-rate">90/320</div>
<div class="ui-footer">
<div class="ui-link">More</div>
<div class="ui-link">Settings</div>
</div>
</div>
<div class="ui-item -today">
<i class="fa fa-heart"></i>
<div class="ui-date">Today</div>
<div class="ui-heart-rate">120/280</div>
<div class="ui-footer">
<div class="ui-link">More</div>
<div class="ui-link">Settings</div>
</div>
</div>
</main>
</div>
@import url('https://fonts.googleapis.com/css?family=Lato|Oswald:300');
$color-primary: var(--color-primary, #F82462);
$color-secondary: var(--color-secondary, #1D2063);
$easing: cubic-bezier(.56,.01,.19,1.4);
:root {
--stopped: calc(-1 * var(--pan-moving) + 1);
--duration: calc(var(--stopped) * 0.6s);
--drag: calc(var(--pan-y) * var(--pan-y) / 300000);
font-size: 2.5vh;
}
#app {
font-family: Lato, sans-serif;
height: 90vh;
width: 100%;
max-width: 55vh;
text-transform: uppercase;
overflow: hidden;
border-radius: 1rem;
box-shadow: 0 0 2rem rgba(black, 0.2);
backface-visibility: hidden;
transform: translate3d(0,0,0);
background: white;
letter-spacing: 1px;
}
.ui-items {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
transform: translateY(calc(
var(--pan-y) * 0.5px
- var(--pan-item) * 50%
));
transition: transform var(--duration) $easing;
}
.ui-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 50%;
width: 100%;
&:before {
content: '';
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform:
translateY(calc(var(--pan-y) * 0.5px))
scaleY(calc(1 + var(--drag)))
skewY(calc(var(--pan-y) * 0.02deg));
transform-origin: bottom center;
transition:
transform var(--duration) $easing,
background-color 0.3s ease-in-out;
}
&.-yesterday {
color: white;
&:before {
background-color: $color-primary;
}
.fa-heart {
-webkit-text-stroke-color: white;
}
}
&.-today {
color: $color-primary;
&:before {
background-color: white;
}
.fa-heart {
-webkit-text-stroke-color: $color-primary;
}
}
> .ui-footer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 1rem;
}
}
.ui-date {
font-size: 0.8rem;
}
.ui-heart-rate {
font-family: Oswald, sans-serif;
margin: 0.5rem 0;
font-size: 2.5rem;
}
.ui-date, .ui-heart-rate {
transform: scale(calc(1 - var(--drag)));
transition: transform var(--duration) $easing;
}
.fa-heart {
transform:
scale(calc(1 - var(--drag)))
translateY(calc(var(--pan-y) * 0.1px))
;
transition: transform var(--duration) $easing;
-webkit-text-stroke-width: 2px;
margin-bottom: 1rem;
font-size: 2rem;
color: transparent;
}
.ui-footer {
opacity: var(--stopped);
transition: opacity 0.2s $easing;
}
*, *:before, *:after {
box-sizing: border-box;
position: relative;
}
body, html {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
&:before {
content: '';
position: absolute;
top: -150%;
left: calc(50% - 50vh);
width: 200%;
height: 200%;
border: 25vh solid white;
border-radius: 7rem;
background: $color-secondary;
box-shadow:
-25vh 25vh 0 $color-primary,
-50vh 50vh 0 $color-secondary,
;
transition: all 0.3s ease-in-out;
}
}
View Compiled
const { fromEventPattern } = rxjs;
const { scan } = rxjs.operators;
const app = document.getElementById('app');
const hearts = document.querySelectorAll('.fa-heart');
const styleVars = (vars, el = document.documentElement) => {
Object.keys(vars).forEach(key => {
el.style.setProperty(`--${key}`, vars[key]);
});
};
const hApp = new Hammer(app, {
direction: Hammer.DIRECTION_ALL,
threshold: 0,
});
hApp.get('pan')
.set({ direction: Hammer.DIRECTION_ALL });
const pan$ = fromEventPattern((cb) =>
hApp.on('panstart panend panup pandown', cb));
const initialState = {
item: 0,
y: 0,
moving: false,
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'panup':
case 'pandown':
return {
...state,
y: action.deltaY,
moving: true,
};
case 'panend': {
const newItem = action.deltaY > 100
? Math.max(state.item - 1, 0)
: action.deltaY < 100
? Math.min(state.item + 1, 2)
: state.item;
return {
y: 0,
moving: false,
item: newItem,
};
}
default:
return state;
}
}
const state$ = pan$.pipe(scan(reducer, initialState));
state$.subscribe(values => {
styleVars({
'pan-item': values.item,
'pan-y': values.y,
'pan-moving': +values.moving
});
});
// Since you've scrolled this far down,
// here's an easter egg!
const colorPairs = [
['#F82462', '#1D2063'],
['#24B8F8', '#24315A'],
['#FF5C43', '#343AA6'],
['#3EBB8C', '#FFD8C0'],
];
hearts.forEach((heart, i) => {
const [primary, secondary] = colorPairs[i];
heart.addEventListener('click', () => {
RxCSS.styledash(document.documentElement).set({
color: { primary, secondary },
});
});
});
View Compiled