Material design cards test and study.
A Pen by Mattia Astorino on CodePen.
| // Controllers | |
| div.controller-container | |
| span#big.controller.is_current BIG CARD | |
| span#medium.controller MEDIUM CARD | |
| span#small.controller SMALL CARD | |
| // Cards | |
| .card.card--big | |
| div.card__image(style='background-image: url(http://i.imgur.com/8ki2fuE.jpg)') | |
| h2.card__title Kangaroo Valley Safari | |
| span.card__subtitle By Mattia Astorino | |
| p.card__text Located two hours south of Sydney in the Southern Highland of New South Wales... | |
| div.card__action-bar | |
| button.card__button SHARE | |
| button.card__button LEARN MORE |
Material design cards test and study.
A Pen by Mattia Astorino on CodePen.
| (function(){ | |
| $(".controller").click(function(){ | |
| id = $(this).attr("id"); | |
| $(".controller-container").find(".is_current").removeClass("is_current"); | |
| $(this).addClass("is_current"); | |
| $(".card").attr('class', 'card card--' + id); | |
| $("html").attr('class', 'bg--' + id); | |
| }); | |
| })(); | |
| // Ripple function | |
| (function(){ | |
| "use strict"; | |
| var colour = "#FF1744"; | |
| var opacity = 0.1; | |
| var ripple_within_elements = ['input', 'button', 'a']; | |
| var ripple_without_diameter = 0; | |
| var overlays = { | |
| items: [], | |
| get: function(){ | |
| var $element; | |
| for(var i = 0; i < overlays.items.length; i++){ | |
| $element = overlays.items[i]; | |
| if($element.transition_phase === false) { | |
| $element.transition_phase = 0; | |
| return $element; | |
| } | |
| } | |
| $element = document.createElement("div"); | |
| $element.style.position = "absolute"; | |
| $element.style.opacity = opacity; | |
| //$element.style.outline = "10px solid red"; | |
| $element.style.pointerEvents = "none"; | |
| $element.style.background = "-webkit-radial-gradient(" + colour + " 64%, rgba(0,0,0,0) 65%) no-repeat"; | |
| $element.style.background = "radial-gradient(" + colour + " 64%, rgba(0,0,0,0) 65%) no-repeat"; | |
| $element.style.transform = "translateZ(0)"; | |
| $element.transition_phase = 0; | |
| $element.rid = overlays.items.length; | |
| $element.next_transition = overlays.next_transition_generator($element); | |
| document.body.appendChild($element); | |
| overlays.items.push($element); | |
| return $element; | |
| }, | |
| next_transition_generator: function($element){ | |
| return function(){ | |
| $element.transition_phase++; | |
| switch($element.transition_phase){ | |
| case 1: | |
| $element.style[transition] = "all 500ms cubic-bezier(0.165, 0.840, 0.440, 1.000)"; | |
| $element.style.backgroundSize = $element.ripple_backgroundSize; | |
| $element.style.backgroundPosition = $element.ripple_backgroundPosition; | |
| setTimeout($element.next_transition, 0.2 * 1000); //now I know transitionend is better but it fires multiple times when multiple properties are animated, so this is simpler code and (imo) worth tiny delays | |
| break; | |
| case 2: | |
| $element.style[transition] = "opacity 0.15s ease-in-out"; | |
| $element.style.opacity = 0; | |
| setTimeout($element.next_transition, 0.15 * 1000); | |
| break; | |
| case 3: | |
| overlays.recycle($element); | |
| break; | |
| } | |
| }; | |
| }, | |
| recycle: function($element){ | |
| $element.style.display = "none"; | |
| $element.style[transition] = "none"; | |
| if($element.timer) clearTimeout($element.timer); | |
| $element.transition_phase = false; | |
| } | |
| }; | |
| var transition = function(){ | |
| var i, | |
| el = document.createElement('div'), | |
| transitions = { | |
| 'WebkitTransition':'webkitTransition', | |
| 'transition':'transition', | |
| 'OTransition':'otransition', | |
| 'MozTransition':'transition' | |
| }; | |
| for (i in transitions) { | |
| if (transitions.hasOwnProperty(i) && el.style[i] !== undefined) { | |
| return transitions[i]; | |
| } | |
| } | |
| }(); | |
| var click = function(event){ | |
| var $element = overlays.get(), | |
| touch, | |
| x, | |
| y; | |
| touch = event.touches ? event.touches[0] : event; | |
| $element.style[transition] = "none"; | |
| $element.style.backgroundSize = "3px 3px"; | |
| $element.style.opacity = opacity; | |
| if(ripple_within_elements.indexOf(touch.target.nodeName.toLowerCase()) > -1) { | |
| x = touch.offsetX; | |
| y = touch.offsetY; | |
| var dimensions = touch.target.getBoundingClientRect(); | |
| if(!x || !y){ | |
| x = (touch.clientX || touch.x) - dimensions.left; | |
| y = (touch.clientY || touch.y) - dimensions.top; | |
| } | |
| $element.style.backgroundPosition = x + "px " + y + "px"; | |
| $element.style.width = dimensions.width + "px"; | |
| $element.style.height = dimensions.height + "px"; | |
| $element.style.left = (dimensions.left) + "px"; | |
| $element.style.top = (dimensions.top + document.body.scrollTop + document.documentElement.scrollTop) + "px"; | |
| var computed_style = window.getComputedStyle(event.target); | |
| for (var key in computed_style) { | |
| if (key.toString().indexOf("adius") > -1) { | |
| if(computed_style[key]) { | |
| $element.style[key] = computed_style[key]; | |
| } | |
| } else if(parseInt(key, 10).toString() === key && computed_style[key].indexOf("adius") > -1){ | |
| $element.style[computed_style[key]] = computed_style[computed_style[key]]; | |
| } | |
| } | |
| $element.style.backgroundPosition = x + "px " + y + "px"; | |
| $element.ripple_backgroundPosition = (x - dimensions.width) + "px " + (y - dimensions.width) + "px"; | |
| $element.ripple_backgroundSize = (dimensions.width * 2) + "px " + (dimensions.width * 2) + "px"; | |
| } else { //click was outside of ripple element | |
| x = touch.clientX || touch.x || touch.pageX; | |
| y = touch.clientY || touch.y || touch.pageY; | |
| $element.style.borderRadius = "0px"; | |
| $element.style.left = (x - ripple_without_diameter / 2) + "px"; | |
| $element.style.top = (document.body.scrollTop + document.documentElement.scrollTop + y - ripple_without_diameter / 2) + "px"; | |
| $element.ripple_backgroundSize = ripple_without_diameter + "px " + ripple_without_diameter + "px"; | |
| $element.style.width = ripple_without_diameter + "px"; | |
| $element.style.height = ripple_without_diameter + "px"; | |
| $element.style.backgroundPosition = "center center"; | |
| $element.ripple_backgroundPosition = "center center"; | |
| $element.ripple_backgroundSize = ripple_without_diameter + "px " + ripple_without_diameter + "px"; | |
| } | |
| $element.ripple_x = x; | |
| $element.ripple_y = y; | |
| $element.style.display = "block"; | |
| setTimeout($element.next_transition, 20); | |
| }; | |
| if('ontouchstart' in window || 'onmsgesturechange' in window){ | |
| document.addEventListener("touchstart", click, false); | |
| } else { | |
| document.addEventListener("click", click, false); | |
| } | |
| }()); |
| <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> |
| // SETTINGS | |
| // ######################################## | |
| @import url(http://fonts.googleapis.com/css?family=Roboto:400,700,300); | |
| @cta-color: #FF1744; | |
| // BASIC STYLE | |
| //######################################## | |
| :root { | |
| transition: all 300ms; | |
| &.bg--big { | |
| background-color: #00BCD4; } | |
| &.bg--medium { | |
| background-color: #E53935; } | |
| &.bg--small { | |
| background-color: #4527A0; } | |
| } | |
| body { | |
| height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| flex-direction: column; | |
| font-family: 'Roboto', sans-serif; | |
| transform: translateZ(0); | |
| font-weight: 400; } | |
| // button reset | |
| button { | |
| border: none; | |
| background: transparent; } | |
| // controllers bar | |
| .controller-container { | |
| margin-bottom: 5%; | |
| text-align: center; } | |
| // controller element | |
| .controller { | |
| user-select: none; | |
| display: inline-block; | |
| margin: 0 20px; | |
| font-size: 22px; | |
| padding: 10px 0; | |
| color: #FFF; | |
| position: relative; | |
| transition: all 100ms cubic-bezier(0.165, 0.840, 0.440, 1.000); | |
| &:after { | |
| content: ""; | |
| height: 3px; | |
| display: block; | |
| position: absolute; | |
| bottom: 0; | |
| width: 0; | |
| transition: all 300ms cubic-bezier(0.165, 0.840, 0.440, 1.000); | |
| background-color: #FFF; } | |
| &.is_current:after { | |
| width: 100%; } | |
| &:hover, &.is_current { | |
| transform: translate3d( 0, -5px, 0 ); | |
| cursor: pointer; } | |
| } | |
| // DEFAULT CARD AND ELEMENTS | |
| //######################################## | |
| // basic card block | |
| .card { | |
| will-change: transform; | |
| margin: 8px; | |
| position: relative; | |
| border-radius: 2px; | |
| overflow: hidden; | |
| background-color: #fafafa; | |
| height: 35%; | |
| width: 344px; | |
| transition: all 400ms cubic-bezier(0.165, 0.840, 0.440, 1.000); | |
| .z-1; | |
| // hover status | |
| &:hover { | |
| cursor: pointer; } | |
| } | |
| // default card image element | |
| .card__image { | |
| position: absolute; | |
| background-size: cover; | |
| background-position: center bottom; | |
| background-repeat: no-repat; | |
| width: 100%; | |
| height: 100%; | |
| overflow: hidden; | |
| display: block; | |
| opacity: 0; | |
| transition: all 200ms cubic-bezier(0.075, 0.820, 0.165, 1.000); | |
| // image overlay | |
| &:after { | |
| content: ""; | |
| display: block; | |
| position: absolute; | |
| background-color: rgba(0,0,0,0.1); | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| transition: all 500ms; | |
| bottom: 0; } | |
| } | |
| // default card title element | |
| .card__title { | |
| user-select: none; | |
| font-size: 24px; | |
| color: #FFF; | |
| margin: 0; | |
| position: absolute; | |
| left: 0; | |
| right: 0; | |
| padding: 0 16px; | |
| font-weight: 400; | |
| white-space: nowrap; | |
| text-overflow: ellipsis; | |
| overflow: hidden; | |
| transition: all 200ms cubic-bezier(0.075, 0.820, 0.165, 1.000); } | |
| // default card subtitle element | |
| .card__subtitle { | |
| user-select: none; | |
| font-size: 14px; | |
| display: block; | |
| white-space: nowrap; | |
| text-overflow: ellipsis; | |
| overflow: hidden; | |
| color: #000; | |
| left: 88px; | |
| right: 0; | |
| top: 45px; | |
| position: absolute; | |
| padding: 0 16px; | |
| opacity: 0; | |
| transition: all 200ms cubic-bezier(0.075, 0.820, 0.165, 1.000); } | |
| // default card text element | |
| .card__text { | |
| user-select: none; | |
| font-size: 14px; | |
| display: block; | |
| left: 0; | |
| right: 0; | |
| top: 100px; | |
| padding: 16px; | |
| margin: 0; | |
| line-height: 1.6; | |
| position: absolute; | |
| color: #000; | |
| overflow: hidden; | |
| transition: width 200ms cubic-bezier(0.075, 0.820, 0.165, 1.000); | |
| transition: delay 0.1s; } | |
| // default card action bar element | |
| .card__action-bar { | |
| user-select: none; | |
| position: absolute; | |
| bottom: 0; | |
| top: auto; | |
| left: 0; | |
| right: 0; | |
| padding: 0 8px; | |
| border-top: 1px solid #E0E0E0; | |
| boz-sizing: border-box; | |
| height: 52px; | |
| transition: left 200ms cubic-bezier(0.075, 0.820, 0.165, 1.000); } | |
| // default card button element | |
| .card__button { | |
| outline: none; | |
| position: relative; | |
| display: inline-block; | |
| line-height: 52px; | |
| padding: 0 16px; | |
| color: @cta-color; } | |
| // CARDS MODIFIERS | |
| //######################################## | |
| // Big modifier | |
| .card--big { | |
| .z-5; | |
| height: 304px; | |
| min-height: 304px; | |
| // image element | |
| .card__image { | |
| border-radius: 1px 1px 0 0; | |
| left: 0; | |
| right: 0; | |
| top: 0; | |
| opacity: 1; | |
| max-height: 176px; } | |
| // title element | |
| .card__title { | |
| top: 135px; } | |
| // text element | |
| .card__text { | |
| top: 176px; } | |
| .card__action-bar { | |
| left: 0; } | |
| } | |
| // Medium modifier | |
| .card--medium { | |
| .z-3; | |
| height: 208px; | |
| min-height: 208px; | |
| // image element | |
| .card__image { | |
| border-radius: 1px 1px 0 0; | |
| left: 0; | |
| right: 0; | |
| top: 0; | |
| max-height: 0; | |
| opacity: 0; | |
| transition-duration: 300ms; } | |
| // title element | |
| .card__title { | |
| color: @cta-color; | |
| top: 16px; } | |
| // text element | |
| .card__text { | |
| font-size: 16px; | |
| top: 50px; } | |
| .card__action-bar { | |
| left: 0; } | |
| } | |
| // Small modifier | |
| .card--small { | |
| .z-2; | |
| height: 136px; | |
| min-height: 136px; | |
| // image element | |
| .card__image { | |
| border-radius: 1px 0 0 1px; | |
| left: 0; | |
| top: 0; | |
| width: 88px; | |
| opacity: 1; | |
| max-height: 136px; | |
| // image overlay | |
| &:after { | |
| opacity: 0; } | |
| } | |
| // title element | |
| .card__title { | |
| color: #000; | |
| left: 88px; | |
| top: 8px; } | |
| .card__subtitle { | |
| opacity: 1; | |
| left: 88px; } | |
| // text element | |
| .card__text { | |
| top: 30px; | |
| opacity: 0; } | |
| // actionbar element | |
| .card__action-bar { | |
| left: 88px; } | |
| } | |
| // MIXINS | |
| //######################################## | |
| // z-depth official shadows | |
| // (https://www.polymer-project.org/components/paper-elements/demo.html#paper-shadow) | |
| .z-1() { box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.37); } | |
| .z-2() { box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.3), 0 2px 2px 0 rgba(0, 0, 0, 0.2); } | |
| .z-3() { box-shadow: 0 13px 25px 0 rgba(0, 0, 0, 0.3), 0 11px 7px 0 rgba(0, 0, 0, 0.19); } | |
| .z-4() { box-shadow: 0 20px 40px 0 rgba(0, 0, 0, 0.3), 0 14px 12px 0 rgba(0, 0, 0, 0.17); } | |
| .z-5() { box-shadow: 0 27px 55px 0 rgba(0, 0, 0, 0.3), 0 17px 17px 0 rgba(0, 0, 0, 0.15); } |