├── .babelrc ├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── docs ├── images │ ├── mountain-1.jpg │ ├── mountain-2.jpg │ ├── mountain-3.jpg │ ├── mountain-4.jpg │ ├── mountain-5.jpg │ └── mountain-6.jpg ├── index.html ├── scripts │ ├── direction-reveal-umd.js │ ├── direction-reveal.js │ └── main.js └── styles │ ├── direction-reveal.css │ └── site.css ├── gulpfile.js ├── package-lock.json ├── package.json └── src ├── images ├── mountain-1.jpg ├── mountain-2.jpg ├── mountain-3.jpg ├── mountain-4.jpg ├── mountain-5.jpg └── mountain-6.jpg ├── index.html ├── scripts ├── direction-reveal-umd.js ├── direction-reveal.js └── main.js └── styles ├── direction-reveal.css ├── direction-reveal.scss ├── direction-reveal ├── _direction-reveal-animations.scss ├── _direction-reveal-variables.scss └── _direction-reveal.scss ├── site.css ├── site.scss └── site ├── _normalize.scss └── _site.scss /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/env" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # trim_trailing_whitespace = true NIGEL TO DO - Needed??? 12 | 13 | indent_style = space 14 | indent_size = 2 15 | 16 | # Matches multiple files with brace expansion notation 17 | # Set default charset 18 | [*.{js,html}] 19 | charset = utf-8 20 | 21 | # 2 space indentation 22 | [*.{js,html,css,scss}] 23 | indent_style = space 24 | indent_size = 2 25 | 26 | # Tab indentation (no size specified) 27 | [Makefile] 28 | indent_style = tab 29 | 30 | # Indentation override for all JS under lib directory 31 | [lib/**.js] 32 | indent_style = space 33 | indent_size = 2 34 | 35 | # Matches the exact files either package.json or .travis.yml 36 | [{package.json,.travis.yml}] 37 | indent_style = space 38 | indent_size = 2 39 | 40 | # Markdown files 41 | [*.md] 42 | trim_trailing_whitespace = false 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | 4 | # Compiled output 5 | /dist 6 | /.tmp 7 | /zip 8 | 9 | # System Files 10 | .DS_Store 11 | Thumbs.db 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Nigel O Toole 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Direction Reveal 2 | ### A plugin that detects the direction a user enters or leaves an element allowing you to reveal or hide content based on this direction. 3 | 4 | ### [View demo](http://nigelotoole.github.io/direction-reveal/) 5 | 6 | 7 | 8 | ## Installation 9 | ```javascript 10 | $ npm install direction-reveal --save-dev 11 | ``` 12 | 13 | 14 | ## Usage 15 | 16 | ### Import JS 17 | 18 | The script is an ES6(ES2015) module but the compiled version is included in the build as "src/scripts/direction-reveal-umd.js". You can also copy "src/scripts/direction-reveal.js" into your own site if your build process can accommodate ES6 modules. 19 | 20 | ```javascript 21 | import DirectionReveal from 'direction-reveal'; 22 | 23 | // Init with default setup 24 | const directionRevealDemo = DirectionReveal(); 25 | 26 | // Init with all options at default setting 27 | const directionRevealDefault = DirectionReveal({ 28 | selector: '.direction-reveal', 29 | itemSelector: '.direction-reveal__card', 30 | animationName: 'swing', 31 | animationPostfixEnter: 'enter', 32 | animationPostfixLeave: 'leave', 33 | enableTouch: true, 34 | touchThreshold: 250 35 | }); 36 | ``` 37 | 38 | ### Options 39 | | Property | Default | Type | Description | 40 | | ----------------------- | --------------------------- | ---------- | ------------------------------------------------------------------------------------------------- | 41 | | `selector` | '.direction-reveal' | String | Container element selector. | 42 | | `itemSelector` | '.direction-reveal\_\_card' | String | Item element selector. | 43 | | `animationName` | 'swing' | String | Animation class. | 44 | | `animationPostfixEnter` | 'enter' | String | Animation CSS class postfix for enter event. | 45 | | `animationPostfixLeave` | 'leave' | String | Animation CSS class postfix for leave event. | 46 | | `enableTouch` | true | Boolean | Adds touch event to show content on first click then follow link on the second click. | 47 | | `touchThreshold` | 250 | Number(ms) | The touch length in ms to trigger the reveal, this is to prevent triggering if user is scrolling. | 48 | 49 | 50 | ### Import SASS 51 | 52 | ```scss 53 | @import "node_modules/direction-reveal/src/styles/direction-reveal.scss"; 54 | ``` 55 | 56 | 57 | ### Markup 58 | 59 | ```html 60 |
61 | 62 | 63 | Image 64 | 65 |
66 |

Title

67 |

Description text.

68 |
69 |
70 | 71 | ... 72 |
73 | ``` 74 | 75 | 76 | ### Using other tags 77 | The demos use <a> tags for the "direction-reveal__card" but a <div> can be used as below, specifying the tabindex ensures keyboard navigation works as expected. They can all have a value of 0 and will follow the source order of the divs. 78 | 79 | ```html 80 |
81 | ... 82 |
83 | ``` 84 | 85 | ### Inverted animations 86 | 87 | Most of the animations above can be inverted so the overlay is visible by default and animates out on hover. Change the class 'direction-reveal__anim--enter' to 'direction-reveal__anim--leave' for this effect. 88 | 89 | You can also add the class 'direction-reveal__anim--enter' or 'direction-reveal__anim--leave' to the image to animate it at the same time as overlay. This effect can be seen in the Slide & Push demo. 90 | 91 | ## Events 92 | 93 | A 'directionChange' event is broadcast once a user enters/leaves an item with information about the action(enter,leave) and direction(top, right, bottom, left). 94 | 95 | ```javascript 96 | document.querySelector('#test').addEventListener('directionChange', (event) => { 97 | console.log(`Action: ${event.detail.action} Direction: ${event.detail.direction}`); 98 | }); 99 | ``` 100 | 101 | ## Compatibility 102 | 103 | ### Touch support 104 | The plugin will detect touch support and reveal the hidden content on first click then follow link on the second click. This can be disabled with the option enableTouch. 105 | 106 | 107 | ### Browser support 108 | Supports all modern browsers(Firefox, Chrome and Edge) released as of January 2018. For older browsers you may need to include polyfills for [Nodelist.forEach](https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach), [Element.classList](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList) and [Passive Event Listeners](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener). 109 | 110 | 111 | 112 | ## Demo site 113 | Clone or download from Github. 114 | 115 | ```javascript 116 | $ npm install 117 | $ gulp serve 118 | ``` 119 | 120 | ### Credits 121 | 122 | Inspired by a Codepen by [Noel Delgado](https://codepen.io/noeldelgado/pen/pGwFx), this [Stack overflow answer](https://stackoverflow.com/a/3647634), the article [Get an Element's position using javascript](https://www.kirupa.com/html5/get_element_position_using_javascript.htm) and [Images from Unsplash.](https://unsplash.com). 123 | 124 | 125 | ### License 126 | MIT © Nigel O Toole 127 | -------------------------------------------------------------------------------- /docs/images/mountain-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NigelOToole/direction-reveal/151a8d69af301c7a07cd3c4cd0960d20094f7686/docs/images/mountain-1.jpg -------------------------------------------------------------------------------- /docs/images/mountain-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NigelOToole/direction-reveal/151a8d69af301c7a07cd3c4cd0960d20094f7686/docs/images/mountain-2.jpg -------------------------------------------------------------------------------- /docs/images/mountain-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NigelOToole/direction-reveal/151a8d69af301c7a07cd3c4cd0960d20094f7686/docs/images/mountain-3.jpg -------------------------------------------------------------------------------- /docs/images/mountain-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NigelOToole/direction-reveal/151a8d69af301c7a07cd3c4cd0960d20094f7686/docs/images/mountain-4.jpg -------------------------------------------------------------------------------- /docs/images/mountain-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NigelOToole/direction-reveal/151a8d69af301c7a07cd3c4cd0960d20094f7686/docs/images/mountain-5.jpg -------------------------------------------------------------------------------- /docs/images/mountain-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NigelOToole/direction-reveal/151a8d69af301c7a07cd3c4cd0960d20094f7686/docs/images/mountain-6.jpg -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Direction Reveal - Direction aware content reveals 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 |

Direction Reveal

20 | 21 | 25 |
26 |
27 | 28 | 29 |
30 |
31 |

A plugin that detects the direction a user enters or leaves an element allowing you to reveal or hide content based on this direction.

32 |
33 |
34 | 35 | 36 |
37 |
38 | 39 |

Swing

40 | 41 | 98 | 99 |
100 |
101 | 102 | 103 |
104 |
105 |

Rotate

106 | 107 | 164 | 165 |
166 |
167 | 168 | 169 |
170 |
171 |

Flip

172 | 173 | 230 | 231 |
232 |
233 | 234 | 235 |
236 |
237 |

Slide

238 | 239 | 296 | 297 |
298 |
299 | 300 | 301 |
302 |
303 |

Slide inverted

304 | 305 | 335 | 336 |
337 |
338 | 339 | 340 |
341 |
342 |

Slide & Push

343 | 344 | 401 | 402 |
403 |
404 | 405 | 406 |
407 |
408 |

Roll out

409 | 410 | 467 | 468 |
469 |
470 | 471 | 472 |
473 |
474 |

CSS grid

475 | 476 | 506 | 507 |
508 |
509 | 510 | 511 |
512 |
513 |

Bootstrap 4 grid integration

514 | 515 | 555 | 556 |
557 |
558 | 559 | 560 | 561 |
562 |
563 | 564 |
565 |

Installation

566 | 567 |
568 | $ npm install direction-reveal --save-dev
569 | 
570 | 571 |
572 | 573 | 574 |
575 |

Usage

576 | 577 |

Import JS

578 | 579 |

The script is an ES6(ES2015) module but the compiled version is included in the build as "src/scripts/direction-reveal-umd.js". You can also copy "src/scripts/direction-reveal.js" into your own site if your build process can accommodate ES6 modules.

580 | 581 |
import DirectionReveal from 'direction-reveal';
582 | 
583 | // Init with default setup
584 | const directionRevealDemo = DirectionReveal();
585 | 
586 | // Init with all options at default setting
587 | const directionRevealDefault = DirectionReveal({
588 |   selector: '.direction-reveal',
589 |   itemSelector: '.direction-reveal__card',
590 |   animationName: 'swing',
591 |   animationPostfixEnter: 'enter',
592 |   animationPostfixLeave: 'leave',
593 |   enableTouch: true,
594 |   touchThreshold: 250
595 | });
596 | 
597 | 598 |

Options

599 | 600 |
601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 |
PropertyDefaultTypeDescription
selector'.direction-reveal'StringContainer element selector.
itemSelector'.direction-reveal__card'StringItem element selector.
animationName'swing'StringAnimation class.
animationPostfixEnter'enter'StringAnimation CSS class postfix for enter event.
animationPostfixLeave'leave'StringAnimation CSS class postfix for leave event.
enableTouchtrueBooleanAdds touch event to show content on first click then follow link on the second click.
touchThreshold250Number(ms)The touch length in ms to trigger the reveal, this is to prevent triggering if user is scrolling.
651 |
652 | 653 | 654 |

Import Sass

655 | 656 |
@import "node_modules/direction-reveal/src/styles/direction-reveal.scss";
657 | 658 | 659 |

Markup

660 | 661 |
<div class="direction-reveal">
662 | 
663 |   <a href="#" class="direction-reveal__card">
664 |     <img src="images/image.jpg" class="direction-reveal__img">
665 | 
666 |     <div class="direction-reveal__overlay direction-reveal__anim--enter">
667 |       <h3 class="direction-reveal__title">Title</h3>
668 |       <p class="direction-reveal__text">Description text.</p>
669 |     </div>
670 |   </a>
671 | 
672 |   ...
673 | 
674 | </div>
675 | 
676 | 677 | 678 |

Using other tags

679 | 680 |

The demos use <a> tags for the "direction-reveal__card" but <div> tags can be used as below, specifying the tabindex ensures keyboard navigation works as expected. They can all have a value of 0 and will follow the source order of the divs.

681 | 682 |
<div class="direction-reveal__card" tabindex="0">
683 |   ...
684 | </div>
685 | 
686 | 687 | 688 |

Inverted animations

689 | 690 |

Most of the animations above can be inverted so the overlay is visible by default and animates out on hover. Change the class 'direction-reveal__anim--enter' to 'direction-reveal__anim--leave' for this effect.

691 | 692 |

You can also add the class 'direction-reveal__anim--enter' or 'direction-reveal__anim--leave' to the image to animate it at the same time as overlay. This effect can be seen in the Slide & Push demo.

693 | 694 |
695 | 696 |
697 |

Events

698 | 699 |

A 'directionChange' event is broadcast once a user enters/leaves an item with information about the action(enter,leave) and direction(top, right, bottom, left).

700 | 701 |
document.querySelector('#test').addEventListener('directionChange', (event) => { 
702 |   console.log(`Action: ${event.detail.action} Direction: ${event.detail.direction}`);
703 | });
704 | 
705 |
706 | 707 |
708 |

Compatibility

709 | 710 |

Touch support

711 | 712 |

The plugin will detect touch support and reveal the hidden content on first click then follow link on the second click. This can be disabled with the option enableTouch.

713 | 714 |

Browser support

715 | 716 |

Supports all modern browsers(Firefox, Chrome and Edge) released as of January 2018. For older browsers you may need to include polyfills for Nodelist.forEach, Element.classList and Passive Event Listeners.

717 |
718 | 719 |
720 |

Demo site

721 |

Clone or download from Github.

722 | 723 |
$ npm install
724 | $ gulp serve
725 | 
726 | 727 |
728 | 729 |
730 |

Credits

731 |

Inspired by a Codepen by Noel Delgado, this Stack overflow answer, the article Get an Element's position using javascript and
images from Unsplash.

732 |
733 | 734 |
735 |
736 | 737 | 738 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | -------------------------------------------------------------------------------- /docs/scripts/direction-reveal-umd.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define(["exports"], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(exports); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(mod.exports); 11 | global.directionReveal = mod.exports; 12 | } 13 | })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports) { 14 | "use strict"; 15 | 16 | Object.defineProperty(_exports, "__esModule", { 17 | value: true 18 | }); 19 | _exports["default"] = void 0; 20 | 21 | /** 22 | Direction aware content reveals. 23 | 24 | @param {Object} object - Container for all options. 25 | @param {string} selector - Container element selector. 26 | @param {string} itemSelector - Item element selector. 27 | @param {string} animationName - Animation CSS class. 28 | @param {string} animationPostfixEnter - Animation CSS class postfix for enter event. 29 | @param {string} animationPostfixLeave - Animation CSS class postfix for leave event. 30 | @param {boolean} enableTouch - Adds touch event to show content on first click then follow link on the second click. 31 | @param {integer} touchThreshold - Touch length must be less than this to trigger reveal which prevents the event triggering if user is scrolling. 32 | */ 33 | var DirectionReveal = function DirectionReveal() { 34 | var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, 35 | _ref$selector = _ref.selector, 36 | selector = _ref$selector === void 0 ? '.direction-reveal' : _ref$selector, 37 | _ref$itemSelector = _ref.itemSelector, 38 | itemSelector = _ref$itemSelector === void 0 ? '.direction-reveal__card' : _ref$itemSelector, 39 | _ref$animationName = _ref.animationName, 40 | animationName = _ref$animationName === void 0 ? 'swing' : _ref$animationName, 41 | _ref$animationPostfix = _ref.animationPostfixEnter, 42 | animationPostfixEnter = _ref$animationPostfix === void 0 ? 'enter' : _ref$animationPostfix, 43 | _ref$animationPostfix2 = _ref.animationPostfixLeave, 44 | animationPostfixLeave = _ref$animationPostfix2 === void 0 ? 'leave' : _ref$animationPostfix2, 45 | _ref$enableTouch = _ref.enableTouch, 46 | enableTouch = _ref$enableTouch === void 0 ? true : _ref$enableTouch, 47 | _ref$touchThreshold = _ref.touchThreshold, 48 | touchThreshold = _ref$touchThreshold === void 0 ? 250 : _ref$touchThreshold; 49 | 50 | var containers = document.querySelectorAll(selector); 51 | var touchStart; // Utilities - https://hackernoon.com/rethinking-javascript-eliminate-the-switch-statement-for-better-code-5c81c044716d 52 | 53 | var addEventListenerMulti = function addEventListenerMulti(element, events, fn) { 54 | events.forEach(function (e) { 55 | return element.addEventListener(e, fn); 56 | }); 57 | }; 58 | 59 | var switchCase = function switchCase(cases) { 60 | return function (defaultCase) { 61 | return function (key) { 62 | return key in cases ? cases[key] : defaultCase; 63 | }; 64 | }; 65 | }; 66 | 67 | var fireEvent = function fireEvent(item, eventName, eventDetail) { 68 | var event = new CustomEvent(eventName, { 69 | bubbles: true, 70 | detail: eventDetail 71 | }); 72 | item.dispatchEvent(event); 73 | }; // Get direction data based on element and pointer positions 74 | 75 | 76 | var getDirection = function getDirection(e, item) { 77 | // Width and height of current item 78 | var w = item.offsetWidth; 79 | var h = item.offsetHeight; 80 | var position = getPosition(item); // Calculate the x/y value of the pointer entering/exiting, relative to the center of the item. 81 | 82 | var x = (e.pageX - position.x - w / 2) * (w > h ? h / w : 1); 83 | var y = (e.pageY - position.y - h / 2) * (h > w ? w / h : 1); // Calculate the angle the pointer entered/exited and convert to clockwise format (top/right/bottom/left = 0/1/2/3). - https://stackoverflow.com/a/3647634 84 | 85 | var d = Math.round(Math.atan2(y, x) / 1.57079633 + 5) % 4; // console.table([x, y, w, h, e.pageX, e.pageY, item.offsetLeft, item.offsetTop, position.x, position.y]); 86 | 87 | return d; 88 | }; // Gets an elements position - https://www.kirupa.com/html5/get_element_position_using_javascript.htm 89 | 90 | 91 | var getPosition = function getPosition(el) { 92 | var xPos = 0; 93 | var yPos = 0; 94 | 95 | while (el) { 96 | xPos += el.offsetLeft + el.clientLeft; 97 | yPos += el.offsetTop + el.clientTop; 98 | el = el.offsetParent; 99 | } 100 | 101 | return { 102 | x: xPos, 103 | y: yPos 104 | }; 105 | }; 106 | 107 | var translateDirection = switchCase({ 108 | 0: 'top', 109 | 1: 'right', 110 | 2: 'bottom', 111 | 3: 'left' 112 | })('top'); // Updates direction and toggles classes 113 | 114 | var updateDirection = function updateDirection(e, action) { 115 | var currentItem = e.currentTarget; 116 | var direction = getDirection(e, currentItem); 117 | var directionString = translateDirection(direction); // Remove current animation classes and adds current action/direction. 118 | 119 | var currentCssClasses = currentItem.className.split(' '); 120 | var filteredCssClasses = currentCssClasses.filter(function (cssClass) { 121 | return !cssClass.startsWith(animationName); 122 | }).join(' '); 123 | currentItem.className = filteredCssClasses; 124 | currentItem.classList.add("".concat(animationName, "--").concat(action, "-").concat(directionString)); 125 | var eventDetail = { 126 | action: action, 127 | direction: directionString 128 | }; 129 | fireEvent(currentItem, 'directionChange', eventDetail); 130 | }; 131 | 132 | var bindEvents = function bindEvents(containerItem) { 133 | var items = containerItem.querySelectorAll(itemSelector); 134 | items.forEach(function (item) { 135 | addEventListenerMulti(item, ['mouseenter', 'focus'], function (e) { 136 | updateDirection(e, animationPostfixEnter); 137 | }); 138 | addEventListenerMulti(item, ['mouseleave', 'blur'], function (e) { 139 | updateDirection(e, animationPostfixLeave); 140 | }); 141 | 142 | if (enableTouch) { 143 | item.addEventListener('touchstart', function (e) { 144 | touchStart = +new Date(); 145 | }, { 146 | passive: true 147 | }); 148 | item.addEventListener('touchend', function (e) { 149 | var touchTime = +new Date() - touchStart; 150 | 151 | if (touchTime < touchThreshold && !item.className.includes("".concat(animationName, "--").concat(animationPostfixEnter))) { 152 | e.preventDefault(); 153 | resetVisible(e, items, updateDirection(e, animationPostfixEnter)); 154 | } 155 | }); 156 | } 157 | }); 158 | }; 159 | 160 | var resetVisible = function resetVisible(e, items, callback) { 161 | items.forEach(function (item) { 162 | var currentCssClasses = item.className; 163 | 164 | if (currentCssClasses.includes("".concat(animationName, "--").concat(animationPostfixEnter)) && item !== e.currentTarget) { 165 | item.className = currentCssClasses.replace("".concat(animationName, "--").concat(animationPostfixEnter), "".concat(animationName, "--").concat(animationPostfixLeave)); 166 | } 167 | }); 168 | callback; 169 | }; 170 | 171 | var init = function init() { 172 | if (containers.length) { 173 | containers.forEach(function (containerItem) { 174 | bindEvents(containerItem); 175 | }); 176 | } else { 177 | return; 178 | } 179 | }; // Self init 180 | 181 | 182 | init(); // Reveal API 183 | 184 | return { 185 | init: init 186 | }; 187 | }; 188 | 189 | var _default = DirectionReveal; 190 | _exports["default"] = _default; 191 | }); -------------------------------------------------------------------------------- /docs/scripts/direction-reveal.js: -------------------------------------------------------------------------------- 1 | /** 2 | Direction aware content reveals. 3 | 4 | @param {Object} object - Container for all options. 5 | @param {string} selector - Container element selector. 6 | @param {string} itemSelector - Item element selector. 7 | @param {string} animationName - Animation CSS class. 8 | @param {string} animationPostfixEnter - Animation CSS class postfix for enter event. 9 | @param {string} animationPostfixLeave - Animation CSS class postfix for leave event. 10 | @param {boolean} enableTouch - Adds touch event to show content on first click then follow link on the second click. 11 | @param {integer} touchThreshold - Touch length must be less than this to trigger reveal which prevents the event triggering if user is scrolling. 12 | */ 13 | 14 | 15 | const DirectionReveal = function ({ 16 | selector: selector = '.direction-reveal', 17 | itemSelector: itemSelector = '.direction-reveal__card', 18 | animationName: animationName = 'swing', 19 | animationPostfixEnter: animationPostfixEnter = 'enter', 20 | animationPostfixLeave: animationPostfixLeave = 'leave', 21 | enableTouch: enableTouch = true, 22 | touchThreshold: touchThreshold = 250 23 | } = {}) { 24 | 25 | const containers = document.querySelectorAll(selector); 26 | let touchStart; 27 | 28 | 29 | // Utilities - https://hackernoon.com/rethinking-javascript-eliminate-the-switch-statement-for-better-code-5c81c044716d 30 | const addEventListenerMulti = function (element, events, fn) { 31 | events.forEach((e) => element.addEventListener(e, fn)); 32 | }; 33 | 34 | const switchCase = cases => defaultCase => key => key in cases ? cases[key] : defaultCase; 35 | 36 | const fireEvent = (item, eventName, eventDetail) => { 37 | const event = new CustomEvent(eventName, { 38 | bubbles: true, 39 | detail: eventDetail, 40 | }); 41 | 42 | item.dispatchEvent(event); 43 | }; 44 | 45 | 46 | // Get direction data based on element and pointer positions 47 | const getDirection = function (e, item) { 48 | // Width and height of current item 49 | let w = item.offsetWidth; 50 | let h = item.offsetHeight; 51 | let position = getPosition(item); 52 | 53 | // Calculate the x/y value of the pointer entering/exiting, relative to the center of the item. 54 | let x = (e.pageX - position.x - (w / 2)) * (w > h ? (h / w) : 1); 55 | let y = (e.pageY - position.y - (h / 2)) * (h > w ? (w / h) : 1); 56 | 57 | // Calculate the angle the pointer entered/exited and convert to clockwise format (top/right/bottom/left = 0/1/2/3). - https://stackoverflow.com/a/3647634 58 | let d = Math.round(Math.atan2(y, x) / 1.57079633 + 5) % 4; 59 | 60 | // console.table([x, y, w, h, e.pageX, e.pageY, item.offsetLeft, item.offsetTop, position.x, position.y]); 61 | 62 | return d; 63 | }; 64 | 65 | 66 | // Gets an elements position - https://www.kirupa.com/html5/get_element_position_using_javascript.htm 67 | const getPosition = function (el) { 68 | let xPos = 0; 69 | let yPos = 0; 70 | 71 | while (el) { 72 | xPos += (el.offsetLeft + el.clientLeft); 73 | yPos += (el.offsetTop + el.clientTop); 74 | 75 | el = el.offsetParent; 76 | } 77 | return { 78 | x: xPos, 79 | y: yPos 80 | }; 81 | }; 82 | 83 | 84 | const translateDirection = switchCase({ 85 | 0: 'top', 86 | 1: 'right', 87 | 2: 'bottom', 88 | 3: 'left' 89 | })('top'); 90 | 91 | 92 | // Updates direction and toggles classes 93 | const updateDirection = function (e, action) { 94 | let currentItem = e.currentTarget; 95 | let direction = getDirection(e, currentItem); 96 | let directionString = translateDirection(direction); 97 | 98 | // Remove current animation classes and adds current action/direction. 99 | let currentCssClasses = currentItem.className.split(' '); 100 | let filteredCssClasses = currentCssClasses.filter((cssClass) => (!cssClass.startsWith(animationName))).join(' '); 101 | currentItem.className = filteredCssClasses; 102 | currentItem.classList.add(`${animationName}--${action}-${directionString}`); 103 | 104 | let eventDetail = { action: action, direction: directionString }; 105 | fireEvent(currentItem, 'directionChange', eventDetail); 106 | }; 107 | 108 | 109 | const bindEvents = function (containerItem) { 110 | const items = containerItem.querySelectorAll(itemSelector); 111 | 112 | items.forEach((item) => { 113 | 114 | addEventListenerMulti(item, ['mouseenter', 'focus'], (e) => { 115 | updateDirection(e, animationPostfixEnter); 116 | }); 117 | 118 | addEventListenerMulti(item, ['mouseleave', 'blur'], (e) => { 119 | updateDirection(e, animationPostfixLeave); 120 | }); 121 | 122 | 123 | if (enableTouch) { 124 | 125 | item.addEventListener('touchstart', (e) => { 126 | touchStart = +new Date; 127 | }, { passive: true }); 128 | 129 | item.addEventListener('touchend', (e) => { 130 | let touchTime = +new Date - touchStart; 131 | 132 | if (touchTime < touchThreshold && !item.className.includes(`${animationName}--${animationPostfixEnter}`)) { 133 | e.preventDefault(); 134 | 135 | resetVisible(e, items, updateDirection(e, animationPostfixEnter)); 136 | } 137 | }); 138 | 139 | } 140 | 141 | }); 142 | }; 143 | 144 | 145 | const resetVisible = function (e, items, callback) { 146 | 147 | items.forEach((item) => { 148 | let currentCssClasses = item.className; 149 | 150 | if(currentCssClasses.includes(`${animationName}--${animationPostfixEnter}`) && item !== e.currentTarget) { 151 | item.className = currentCssClasses.replace(`${animationName}--${animationPostfixEnter}`, `${animationName}--${animationPostfixLeave}`); 152 | } 153 | }); 154 | 155 | callback; 156 | }; 157 | 158 | 159 | const init = function () { 160 | 161 | if (containers.length) { 162 | containers.forEach((containerItem) => { 163 | bindEvents(containerItem); 164 | }); 165 | } 166 | else { 167 | return; 168 | } 169 | 170 | }; 171 | 172 | 173 | // Self init 174 | init(); 175 | 176 | 177 | // Reveal API 178 | return { 179 | init 180 | }; 181 | }; 182 | 183 | export default DirectionReveal; 184 | -------------------------------------------------------------------------------- /docs/scripts/main.js: -------------------------------------------------------------------------------- 1 | import DirectionReveal from './direction-reveal.js'; 2 | 3 | // Swing (Default) 4 | const directionRevealSwing = DirectionReveal({ 5 | selector: '.direction-reveal--demo-swing' 6 | }); 7 | 8 | 9 | // Rotate (all options) 10 | const directionRevealRotate = DirectionReveal({ 11 | selector: '.direction-reveal--demo-rotate', 12 | itemSelector: '.direction-reveal__card', 13 | animationName: 'rotate', 14 | animationPostfixEnter: 'enter', 15 | animationPostfixLeave: 'leave', 16 | enableTouch: true, 17 | touchThreshold: 250 18 | }); 19 | 20 | 21 | // Flip 22 | const directionRevealFlip = DirectionReveal({ 23 | selector: '.direction-reveal--demo-flip', 24 | animationName: 'flip' 25 | }); 26 | 27 | 28 | // Slide 29 | const directionRevealSlide = DirectionReveal({ 30 | selector: '.direction-reveal--demo-slide', 31 | animationName: 'slide' 32 | }); 33 | 34 | 35 | // Slide & push 36 | const directionRevealSlidePush = DirectionReveal({ 37 | selector: '.direction-reveal--demo-slide-push', 38 | animationName: 'slide' 39 | }); 40 | 41 | 42 | // Roll out 43 | const directionRevealRollOut = DirectionReveal({ 44 | selector: '.direction-reveal--demo-roll-out', 45 | animationName: 'roll-out' 46 | }); 47 | 48 | 49 | 50 | // Add a listener to an item to monitor direction changes 51 | 52 | // document.querySelector('.direction-reveal--demo-swing .direction-reveal__card:first-child').addEventListener('directionChange', (event) => { 53 | // console.log(`Action: ${event.detail.action} Direction: ${event.detail.direction}`); 54 | // }); 55 | 56 | // let eventTargets = document.querySelectorAll('.direction-reveal--demo-swing .direction-reveal__card'); 57 | // eventTargets.forEach((item) => { 58 | // item.addEventListener('directionChange', (event) => { 59 | // console.log(`Action: ${event.detail.action} Direction: ${event.detail.direction}`); 60 | // console.log(item); 61 | // }); 62 | // }); 63 | -------------------------------------------------------------------------------- /docs/styles/direction-reveal.css: -------------------------------------------------------------------------------- 1 | .direction-reveal__card { 2 | display: inline-block; 3 | position: relative; 4 | overflow: hidden; 5 | } 6 | 7 | .direction-reveal__img { 8 | display: block; 9 | max-width: 100%; 10 | height: auto; 11 | } 12 | 13 | .direction-reveal__overlay { 14 | position: absolute; 15 | top: 0; 16 | left: 0; 17 | width: 100%; 18 | height: 100%; 19 | padding: 16px; 20 | color: #fff; 21 | overflow: hidden; 22 | background-color: rgba(0, 0, 0, 0.6); 23 | } 24 | 25 | .direction-reveal__anim--enter, .direction-reveal__anim--leave { 26 | -webkit-animation-duration: 0.6s; 27 | animation-duration: 0.6s; 28 | -webkit-animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 29 | animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 30 | -webkit-animation-fill-mode: forwards; 31 | animation-fill-mode: forwards; 32 | } 33 | 34 | .direction-reveal__anim--enter { 35 | transform: translate3d(0, -100%, 0); 36 | } 37 | 38 | .direction-reveal__title { 39 | margin-top: 0; 40 | } 41 | 42 | .direction-reveal__text { 43 | margin-bottom: 0; 44 | } 45 | 46 | .direction-reveal--3-grid { 47 | display: flex; 48 | flex-wrap: wrap; 49 | margin-right: -8px; 50 | margin-left: -8px; 51 | } 52 | 53 | .direction-reveal--3-grid .direction-reveal__card { 54 | border: 8px solid transparent; 55 | } 56 | 57 | @media (min-width: 576px) { 58 | .direction-reveal--3-grid .direction-reveal__card { 59 | flex: 0 0 33.333333%; 60 | max-width: 33.333333%; 61 | } 62 | } 63 | 64 | .direction-reveal--3-grid-cssgrid { 65 | display: grid; 66 | margin-right: -8px; 67 | margin-left: -8px; 68 | } 69 | 70 | @media (min-width: 576px) { 71 | .direction-reveal--3-grid-cssgrid { 72 | grid-template-columns: repeat(3, 1fr); 73 | } 74 | } 75 | 76 | .direction-reveal--3-grid-cssgrid .direction-reveal__card { 77 | border: 8px solid transparent; 78 | } 79 | 80 | .direction-reveal--grid-bootstrap .direction-reveal__card { 81 | margin-top: 15px; 82 | margin-bottom: 15px; 83 | } 84 | 85 | .swing--enter-top .direction-reveal__anim--enter { 86 | -webkit-animation-name: swing--enter-top; 87 | animation-name: swing--enter-top; 88 | } 89 | 90 | .swing--enter-top .direction-reveal__anim--leave { 91 | -webkit-animation-name: swing--leave-bottom; 92 | animation-name: swing--leave-bottom; 93 | } 94 | 95 | .swing--enter-bottom .direction-reveal__anim--enter { 96 | -webkit-animation-name: swing--enter-bottom; 97 | animation-name: swing--enter-bottom; 98 | } 99 | 100 | .swing--enter-bottom .direction-reveal__anim--leave { 101 | -webkit-animation-name: swing--leave-top; 102 | animation-name: swing--leave-top; 103 | } 104 | 105 | .swing--enter-left .direction-reveal__anim--enter { 106 | -webkit-animation-name: swing--enter-left; 107 | animation-name: swing--enter-left; 108 | } 109 | 110 | .swing--enter-left .direction-reveal__anim--leave { 111 | -webkit-animation-name: swing--leave-right; 112 | animation-name: swing--leave-right; 113 | } 114 | 115 | .swing--enter-right .direction-reveal__anim--enter { 116 | -webkit-animation-name: swing--enter-right; 117 | animation-name: swing--enter-right; 118 | } 119 | 120 | .swing--enter-right .direction-reveal__anim--leave { 121 | -webkit-animation-name: swing--leave-left; 122 | animation-name: swing--leave-left; 123 | } 124 | 125 | .swing--leave-top .direction-reveal__anim--enter { 126 | -webkit-animation-name: swing--leave-top; 127 | animation-name: swing--leave-top; 128 | } 129 | 130 | .swing--leave-top .direction-reveal__anim--leave { 131 | -webkit-animation-name: swing--enter-bottom; 132 | animation-name: swing--enter-bottom; 133 | } 134 | 135 | .swing--leave-bottom .direction-reveal__anim--enter { 136 | -webkit-animation-name: swing--leave-bottom; 137 | animation-name: swing--leave-bottom; 138 | } 139 | 140 | .swing--leave-bottom .direction-reveal__anim--leave { 141 | -webkit-animation-name: swing--enter-top; 142 | animation-name: swing--enter-top; 143 | } 144 | 145 | .swing--leave-left .direction-reveal__anim--enter { 146 | -webkit-animation-name: swing--leave-left; 147 | animation-name: swing--leave-left; 148 | } 149 | 150 | .swing--leave-left .direction-reveal__anim--leave { 151 | -webkit-animation-name: swing--enter-right; 152 | animation-name: swing--enter-right; 153 | } 154 | 155 | .swing--leave-right .direction-reveal__anim--enter { 156 | -webkit-animation-name: swing--leave-right; 157 | animation-name: swing--leave-right; 158 | } 159 | 160 | .swing--leave-right .direction-reveal__anim--leave { 161 | -webkit-animation-name: swing--enter-left; 162 | animation-name: swing--enter-left; 163 | } 164 | 165 | .direction-reveal [class*='swing--'] { 166 | perspective: 400px; 167 | } 168 | 169 | .direction-reveal [class*='swing--'] .direction-reveal__anim--enter, .direction-reveal [class*='swing--'] .direction-reveal__anim--leave { 170 | transform: rotate3d(0, 0, 0, 0); 171 | -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 172 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 173 | } 174 | 175 | .swing--enter-top, .swing--leave-top { 176 | perspective-origin: center top; 177 | } 178 | 179 | .swing--enter-top .direction-reveal__anim--enter, .swing--enter-top .direction-reveal__anim--leave, .swing--leave-top .direction-reveal__anim--enter, .swing--leave-top .direction-reveal__anim--leave { 180 | transform-origin: center top; 181 | } 182 | 183 | .swing--enter-bottom, .swing--leave-bottom { 184 | perspective-origin: center bottom; 185 | } 186 | 187 | .swing--enter-bottom .direction-reveal__anim--enter, .swing--enter-bottom .direction-reveal__anim--leave, .swing--leave-bottom .direction-reveal__anim--enter, .swing--leave-bottom .direction-reveal__anim--leave { 188 | transform-origin: center bottom; 189 | } 190 | 191 | .swing--enter-left, .swing--leave-left { 192 | perspective-origin: left center; 193 | } 194 | 195 | .swing--enter-left .direction-reveal__anim--enter, .swing--enter-left .direction-reveal__anim--leave, .swing--leave-left .direction-reveal__anim--enter, .swing--leave-left .direction-reveal__anim--leave { 196 | transform-origin: left center; 197 | } 198 | 199 | .swing--enter-right, .swing--leave-right { 200 | perspective-origin: right center; 201 | } 202 | 203 | .swing--enter-right .direction-reveal__anim--enter, .swing--enter-right .direction-reveal__anim--leave, .swing--leave-right .direction-reveal__anim--enter, .swing--leave-right .direction-reveal__anim--leave { 204 | transform-origin: right center; 205 | } 206 | 207 | @-webkit-keyframes swing--enter-top { 208 | 0% { 209 | transform: rotate3d(-1, 0, 0, 90deg); 210 | } 211 | } 212 | 213 | @keyframes swing--enter-top { 214 | 0% { 215 | transform: rotate3d(-1, 0, 0, 90deg); 216 | } 217 | } 218 | 219 | @-webkit-keyframes swing--leave-top { 220 | 100% { 221 | transform: rotate3d(-1, 0, 0, 90deg); 222 | } 223 | } 224 | 225 | @keyframes swing--leave-top { 226 | 100% { 227 | transform: rotate3d(-1, 0, 0, 90deg); 228 | } 229 | } 230 | 231 | @-webkit-keyframes swing--enter-bottom { 232 | 0% { 233 | transform: rotate3d(1, 0, 0, 90deg); 234 | } 235 | } 236 | 237 | @keyframes swing--enter-bottom { 238 | 0% { 239 | transform: rotate3d(1, 0, 0, 90deg); 240 | } 241 | } 242 | 243 | @-webkit-keyframes swing--leave-bottom { 244 | 100% { 245 | transform: rotate3d(1, 0, 0, 90deg); 246 | } 247 | } 248 | 249 | @keyframes swing--leave-bottom { 250 | 100% { 251 | transform: rotate3d(1, 0, 0, 90deg); 252 | } 253 | } 254 | 255 | @-webkit-keyframes swing--enter-left { 256 | 0% { 257 | transform: rotate3d(0, 1, 0, 90deg); 258 | } 259 | } 260 | 261 | @keyframes swing--enter-left { 262 | 0% { 263 | transform: rotate3d(0, 1, 0, 90deg); 264 | } 265 | } 266 | 267 | @-webkit-keyframes swing--leave-left { 268 | 100% { 269 | transform: rotate3d(0, 1, 0, 90deg); 270 | } 271 | } 272 | 273 | @keyframes swing--leave-left { 274 | 100% { 275 | transform: rotate3d(0, 1, 0, 90deg); 276 | } 277 | } 278 | 279 | @-webkit-keyframes swing--enter-right { 280 | 0% { 281 | transform: rotate3d(0, -1, 0, 90deg); 282 | } 283 | } 284 | 285 | @keyframes swing--enter-right { 286 | 0% { 287 | transform: rotate3d(0, -1, 0, 90deg); 288 | } 289 | } 290 | 291 | @-webkit-keyframes swing--leave-right { 292 | 100% { 293 | transform: rotate3d(0, -1, 0, 90deg); 294 | } 295 | } 296 | 297 | @keyframes swing--leave-right { 298 | 100% { 299 | transform: rotate3d(0, -1, 0, 90deg); 300 | } 301 | } 302 | 303 | .slide--enter-top .direction-reveal__anim--enter { 304 | -webkit-animation-name: slide--enter-top; 305 | animation-name: slide--enter-top; 306 | } 307 | 308 | .slide--enter-top .direction-reveal__anim--leave { 309 | -webkit-animation-name: slide--leave-bottom; 310 | animation-name: slide--leave-bottom; 311 | } 312 | 313 | .slide--enter-bottom .direction-reveal__anim--enter { 314 | -webkit-animation-name: slide--enter-bottom; 315 | animation-name: slide--enter-bottom; 316 | } 317 | 318 | .slide--enter-bottom .direction-reveal__anim--leave { 319 | -webkit-animation-name: slide--leave-top; 320 | animation-name: slide--leave-top; 321 | } 322 | 323 | .slide--enter-left .direction-reveal__anim--enter { 324 | -webkit-animation-name: slide--enter-left; 325 | animation-name: slide--enter-left; 326 | } 327 | 328 | .slide--enter-left .direction-reveal__anim--leave { 329 | -webkit-animation-name: slide--leave-right; 330 | animation-name: slide--leave-right; 331 | } 332 | 333 | .slide--enter-right .direction-reveal__anim--enter { 334 | -webkit-animation-name: slide--enter-right; 335 | animation-name: slide--enter-right; 336 | } 337 | 338 | .slide--enter-right .direction-reveal__anim--leave { 339 | -webkit-animation-name: slide--leave-left; 340 | animation-name: slide--leave-left; 341 | } 342 | 343 | .slide--leave-top .direction-reveal__anim--enter { 344 | -webkit-animation-name: slide--leave-top; 345 | animation-name: slide--leave-top; 346 | } 347 | 348 | .slide--leave-top .direction-reveal__anim--leave { 349 | -webkit-animation-name: slide--enter-bottom; 350 | animation-name: slide--enter-bottom; 351 | } 352 | 353 | .slide--leave-bottom .direction-reveal__anim--enter { 354 | -webkit-animation-name: slide--leave-bottom; 355 | animation-name: slide--leave-bottom; 356 | } 357 | 358 | .slide--leave-bottom .direction-reveal__anim--leave { 359 | -webkit-animation-name: slide--enter-top; 360 | animation-name: slide--enter-top; 361 | } 362 | 363 | .slide--leave-left .direction-reveal__anim--enter { 364 | -webkit-animation-name: slide--leave-left; 365 | animation-name: slide--leave-left; 366 | } 367 | 368 | .slide--leave-left .direction-reveal__anim--leave { 369 | -webkit-animation-name: slide--enter-right; 370 | animation-name: slide--enter-right; 371 | } 372 | 373 | .slide--leave-right .direction-reveal__anim--enter { 374 | -webkit-animation-name: slide--leave-right; 375 | animation-name: slide--leave-right; 376 | } 377 | 378 | .slide--leave-right .direction-reveal__anim--leave { 379 | -webkit-animation-name: slide--enter-left; 380 | animation-name: slide--enter-left; 381 | } 382 | 383 | .direction-reveal [class*='slide--'] .direction-reveal__anim--enter, .direction-reveal [class*='slide--'] .direction-reveal__anim--leave { 384 | transform: translate3d(0, 0, 0); 385 | -webkit-animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 386 | animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 387 | } 388 | 389 | @-webkit-keyframes slide--enter-top { 390 | 0% { 391 | transform: translate3d(0, -100%, 0); 392 | } 393 | } 394 | 395 | @keyframes slide--enter-top { 396 | 0% { 397 | transform: translate3d(0, -100%, 0); 398 | } 399 | } 400 | 401 | @-webkit-keyframes slide--leave-top { 402 | 100% { 403 | transform: translate3d(0, -100%, 0); 404 | } 405 | } 406 | 407 | @keyframes slide--leave-top { 408 | 100% { 409 | transform: translate3d(0, -100%, 0); 410 | } 411 | } 412 | 413 | @-webkit-keyframes slide--enter-bottom { 414 | 0% { 415 | transform: translate3d(0, 100%, 0); 416 | } 417 | } 418 | 419 | @keyframes slide--enter-bottom { 420 | 0% { 421 | transform: translate3d(0, 100%, 0); 422 | } 423 | } 424 | 425 | @-webkit-keyframes slide--leave-bottom { 426 | 100% { 427 | transform: translate3d(0, 100%, 0); 428 | } 429 | } 430 | 431 | @keyframes slide--leave-bottom { 432 | 100% { 433 | transform: translate3d(0, 100%, 0); 434 | } 435 | } 436 | 437 | @-webkit-keyframes slide--enter-left { 438 | 0% { 439 | transform: translate3d(-100%, 0, 0); 440 | } 441 | } 442 | 443 | @keyframes slide--enter-left { 444 | 0% { 445 | transform: translate3d(-100%, 0, 0); 446 | } 447 | } 448 | 449 | @-webkit-keyframes slide--leave-left { 450 | 100% { 451 | transform: translate3d(-100%, 0, 0); 452 | } 453 | } 454 | 455 | @keyframes slide--leave-left { 456 | 100% { 457 | transform: translate3d(-100%, 0, 0); 458 | } 459 | } 460 | 461 | @-webkit-keyframes slide--enter-right { 462 | 0% { 463 | transform: translate3d(100%, 0, 0); 464 | } 465 | } 466 | 467 | @keyframes slide--enter-right { 468 | 0% { 469 | transform: translate3d(100%, 0, 0); 470 | } 471 | } 472 | 473 | @-webkit-keyframes slide--leave-right { 474 | 100% { 475 | transform: translate3d(100%, 0, 0); 476 | } 477 | } 478 | 479 | @keyframes slide--leave-right { 480 | 100% { 481 | transform: translate3d(100%, 0, 0); 482 | } 483 | } 484 | 485 | .roll-out--enter-top .direction-reveal__anim--enter { 486 | -webkit-animation-name: roll-out--enter-top; 487 | animation-name: roll-out--enter-top; 488 | } 489 | 490 | .roll-out--enter-top .direction-reveal__anim--leave { 491 | -webkit-animation-name: roll-out--leave-bottom; 492 | animation-name: roll-out--leave-bottom; 493 | } 494 | 495 | .roll-out--enter-bottom .direction-reveal__anim--enter { 496 | -webkit-animation-name: roll-out--enter-bottom; 497 | animation-name: roll-out--enter-bottom; 498 | } 499 | 500 | .roll-out--enter-bottom .direction-reveal__anim--leave { 501 | -webkit-animation-name: roll-out--leave-top; 502 | animation-name: roll-out--leave-top; 503 | } 504 | 505 | .roll-out--enter-left .direction-reveal__anim--enter { 506 | -webkit-animation-name: roll-out--enter-left; 507 | animation-name: roll-out--enter-left; 508 | } 509 | 510 | .roll-out--enter-left .direction-reveal__anim--leave { 511 | -webkit-animation-name: roll-out--leave-right; 512 | animation-name: roll-out--leave-right; 513 | } 514 | 515 | .roll-out--enter-right .direction-reveal__anim--enter { 516 | -webkit-animation-name: roll-out--enter-right; 517 | animation-name: roll-out--enter-right; 518 | } 519 | 520 | .roll-out--enter-right .direction-reveal__anim--leave { 521 | -webkit-animation-name: roll-out--leave-left; 522 | animation-name: roll-out--leave-left; 523 | } 524 | 525 | .roll-out--leave-top .direction-reveal__anim--enter { 526 | -webkit-animation-name: roll-out--leave-top; 527 | animation-name: roll-out--leave-top; 528 | } 529 | 530 | .roll-out--leave-top .direction-reveal__anim--leave { 531 | -webkit-animation-name: roll-out--enter-bottom; 532 | animation-name: roll-out--enter-bottom; 533 | } 534 | 535 | .roll-out--leave-bottom .direction-reveal__anim--enter { 536 | -webkit-animation-name: roll-out--leave-bottom; 537 | animation-name: roll-out--leave-bottom; 538 | } 539 | 540 | .roll-out--leave-bottom .direction-reveal__anim--leave { 541 | -webkit-animation-name: roll-out--enter-top; 542 | animation-name: roll-out--enter-top; 543 | } 544 | 545 | .roll-out--leave-left .direction-reveal__anim--enter { 546 | -webkit-animation-name: roll-out--leave-left; 547 | animation-name: roll-out--leave-left; 548 | } 549 | 550 | .roll-out--leave-left .direction-reveal__anim--leave { 551 | -webkit-animation-name: roll-out--enter-right; 552 | animation-name: roll-out--enter-right; 553 | } 554 | 555 | .roll-out--leave-right .direction-reveal__anim--enter { 556 | -webkit-animation-name: roll-out--leave-right; 557 | animation-name: roll-out--leave-right; 558 | } 559 | 560 | .roll-out--leave-right .direction-reveal__anim--leave { 561 | -webkit-animation-name: roll-out--enter-left; 562 | animation-name: roll-out--enter-left; 563 | } 564 | 565 | .direction-reveal [class*='roll-out--'] .direction-reveal__anim--enter, .direction-reveal [class*='roll-out--'] .direction-reveal__anim--leave { 566 | transform: translate3d(0, 0, 0); 567 | -webkit-clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); 568 | clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); 569 | -webkit-animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 570 | animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 571 | } 572 | 573 | @-webkit-keyframes roll-out--enter-top { 574 | 0% { 575 | -webkit-clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 576 | clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 577 | } 578 | } 579 | 580 | @keyframes roll-out--enter-top { 581 | 0% { 582 | -webkit-clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 583 | clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 584 | } 585 | } 586 | 587 | @-webkit-keyframes roll-out--leave-top { 588 | 100% { 589 | -webkit-clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 590 | clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 591 | } 592 | } 593 | 594 | @keyframes roll-out--leave-top { 595 | 100% { 596 | -webkit-clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 597 | clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 598 | } 599 | } 600 | 601 | @-webkit-keyframes roll-out--enter-bottom { 602 | 0% { 603 | -webkit-clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 604 | clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 605 | } 606 | } 607 | 608 | @keyframes roll-out--enter-bottom { 609 | 0% { 610 | -webkit-clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 611 | clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 612 | } 613 | } 614 | 615 | @-webkit-keyframes roll-out--leave-bottom { 616 | 100% { 617 | -webkit-clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 618 | clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 619 | } 620 | } 621 | 622 | @keyframes roll-out--leave-bottom { 623 | 100% { 624 | -webkit-clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 625 | clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 626 | } 627 | } 628 | 629 | @-webkit-keyframes roll-out--enter-left { 630 | 0% { 631 | -webkit-clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 632 | clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 633 | } 634 | } 635 | 636 | @keyframes roll-out--enter-left { 637 | 0% { 638 | -webkit-clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 639 | clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 640 | } 641 | } 642 | 643 | @-webkit-keyframes roll-out--leave-left { 644 | 100% { 645 | -webkit-clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 646 | clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 647 | } 648 | } 649 | 650 | @keyframes roll-out--leave-left { 651 | 100% { 652 | -webkit-clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 653 | clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 654 | } 655 | } 656 | 657 | @-webkit-keyframes roll-out--enter-right { 658 | 0% { 659 | -webkit-clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 660 | clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 661 | } 662 | } 663 | 664 | @keyframes roll-out--enter-right { 665 | 0% { 666 | -webkit-clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 667 | clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 668 | } 669 | } 670 | 671 | @-webkit-keyframes roll-out--leave-right { 672 | 100% { 673 | -webkit-clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 674 | clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 675 | } 676 | } 677 | 678 | @keyframes roll-out--leave-right { 679 | 100% { 680 | -webkit-clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 681 | clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 682 | } 683 | } 684 | 685 | .rotate--enter-top .direction-reveal__anim--enter { 686 | -webkit-animation-name: rotate--enter-top; 687 | animation-name: rotate--enter-top; 688 | } 689 | 690 | .rotate--enter-top .direction-reveal__anim--leave { 691 | -webkit-animation-name: rotate--leave-bottom; 692 | animation-name: rotate--leave-bottom; 693 | } 694 | 695 | .rotate--enter-bottom .direction-reveal__anim--enter { 696 | -webkit-animation-name: rotate--enter-bottom; 697 | animation-name: rotate--enter-bottom; 698 | } 699 | 700 | .rotate--enter-bottom .direction-reveal__anim--leave { 701 | -webkit-animation-name: rotate--leave-top; 702 | animation-name: rotate--leave-top; 703 | } 704 | 705 | .rotate--enter-left .direction-reveal__anim--enter { 706 | -webkit-animation-name: rotate--enter-left; 707 | animation-name: rotate--enter-left; 708 | } 709 | 710 | .rotate--enter-left .direction-reveal__anim--leave { 711 | -webkit-animation-name: rotate--leave-right; 712 | animation-name: rotate--leave-right; 713 | } 714 | 715 | .rotate--enter-right .direction-reveal__anim--enter { 716 | -webkit-animation-name: rotate--enter-right; 717 | animation-name: rotate--enter-right; 718 | } 719 | 720 | .rotate--enter-right .direction-reveal__anim--leave { 721 | -webkit-animation-name: rotate--leave-left; 722 | animation-name: rotate--leave-left; 723 | } 724 | 725 | .rotate--leave-top .direction-reveal__anim--enter { 726 | -webkit-animation-name: rotate--leave-top; 727 | animation-name: rotate--leave-top; 728 | } 729 | 730 | .rotate--leave-top .direction-reveal__anim--leave { 731 | -webkit-animation-name: rotate--enter-bottom; 732 | animation-name: rotate--enter-bottom; 733 | } 734 | 735 | .rotate--leave-bottom .direction-reveal__anim--enter { 736 | -webkit-animation-name: rotate--leave-bottom; 737 | animation-name: rotate--leave-bottom; 738 | } 739 | 740 | .rotate--leave-bottom .direction-reveal__anim--leave { 741 | -webkit-animation-name: rotate--enter-top; 742 | animation-name: rotate--enter-top; 743 | } 744 | 745 | .rotate--leave-left .direction-reveal__anim--enter { 746 | -webkit-animation-name: rotate--leave-left; 747 | animation-name: rotate--leave-left; 748 | } 749 | 750 | .rotate--leave-left .direction-reveal__anim--leave { 751 | -webkit-animation-name: rotate--enter-right; 752 | animation-name: rotate--enter-right; 753 | } 754 | 755 | .rotate--leave-right .direction-reveal__anim--enter { 756 | -webkit-animation-name: rotate--leave-right; 757 | animation-name: rotate--leave-right; 758 | } 759 | 760 | .rotate--leave-right .direction-reveal__anim--leave { 761 | -webkit-animation-name: rotate--enter-left; 762 | animation-name: rotate--enter-left; 763 | } 764 | 765 | .direction-reveal [class*='rotate--'] .direction-reveal__anim--enter, .direction-reveal [class*='rotate--'] .direction-reveal__anim--leave { 766 | transform: rotate(0); 767 | -webkit-animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 768 | animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 769 | } 770 | 771 | .rotate--enter-top .direction-reveal__anim--enter, .rotate--enter-top .direction-reveal__anim--leave, .rotate--leave-top .direction-reveal__anim--enter, .rotate--leave-top .direction-reveal__anim--leave { 772 | transform-origin: left top; 773 | } 774 | 775 | .rotate--enter-bottom .direction-reveal__anim--enter, .rotate--enter-bottom .direction-reveal__anim--leave, .rotate--leave-bottom .direction-reveal__anim--enter, .rotate--leave-bottom .direction-reveal__anim--leave { 776 | transform-origin: left bottom; 777 | } 778 | 779 | .rotate--enter-left .direction-reveal__anim--enter, .rotate--enter-left .direction-reveal__anim--leave, .rotate--leave-left .direction-reveal__anim--enter, .rotate--leave-left .direction-reveal__anim--leave { 780 | transform-origin: left top; 781 | } 782 | 783 | .rotate--enter-right .direction-reveal__anim--enter, .rotate--enter-right .direction-reveal__anim--leave, .rotate--leave-right .direction-reveal__anim--enter, .rotate--leave-right .direction-reveal__anim--leave { 784 | transform-origin: right top; 785 | } 786 | 787 | @-webkit-keyframes rotate--enter-top { 788 | 0% { 789 | transform: rotate(-90deg); 790 | } 791 | } 792 | 793 | @keyframes rotate--enter-top { 794 | 0% { 795 | transform: rotate(-90deg); 796 | } 797 | } 798 | 799 | @-webkit-keyframes rotate--leave-top { 800 | 100% { 801 | transform: rotate(-90deg); 802 | } 803 | } 804 | 805 | @keyframes rotate--leave-top { 806 | 100% { 807 | transform: rotate(-90deg); 808 | } 809 | } 810 | 811 | @-webkit-keyframes rotate--enter-bottom { 812 | 0% { 813 | transform: rotate(90deg); 814 | } 815 | } 816 | 817 | @keyframes rotate--enter-bottom { 818 | 0% { 819 | transform: rotate(90deg); 820 | } 821 | } 822 | 823 | @-webkit-keyframes rotate--leave-bottom { 824 | 100% { 825 | transform: rotate(90deg); 826 | } 827 | } 828 | 829 | @keyframes rotate--leave-bottom { 830 | 100% { 831 | transform: rotate(90deg); 832 | } 833 | } 834 | 835 | @-webkit-keyframes rotate--enter-left { 836 | 0% { 837 | transform: rotate(90deg); 838 | } 839 | } 840 | 841 | @keyframes rotate--enter-left { 842 | 0% { 843 | transform: rotate(90deg); 844 | } 845 | } 846 | 847 | @-webkit-keyframes rotate--leave-left { 848 | 100% { 849 | transform: rotate(90deg); 850 | } 851 | } 852 | 853 | @keyframes rotate--leave-left { 854 | 100% { 855 | transform: rotate(90deg); 856 | } 857 | } 858 | 859 | @-webkit-keyframes rotate--enter-right { 860 | 0% { 861 | transform: rotate(-90deg); 862 | } 863 | } 864 | 865 | @keyframes rotate--enter-right { 866 | 0% { 867 | transform: rotate(-90deg); 868 | } 869 | } 870 | 871 | @-webkit-keyframes rotate--leave-right { 872 | 100% { 873 | transform: rotate(-90deg); 874 | } 875 | } 876 | 877 | @keyframes rotate--leave-right { 878 | 100% { 879 | transform: rotate(-90deg); 880 | } 881 | } 882 | 883 | .flip--enter-top .direction-reveal__anim--enter { 884 | -webkit-animation-name: flip--enter-top; 885 | animation-name: flip--enter-top; 886 | } 887 | 888 | .flip--enter-top .direction-reveal__anim--leave { 889 | -webkit-animation-name: flip--leave-bottom; 890 | animation-name: flip--leave-bottom; 891 | } 892 | 893 | .flip--enter-bottom .direction-reveal__anim--enter { 894 | -webkit-animation-name: flip--enter-bottom; 895 | animation-name: flip--enter-bottom; 896 | } 897 | 898 | .flip--enter-bottom .direction-reveal__anim--leave { 899 | -webkit-animation-name: flip--leave-top; 900 | animation-name: flip--leave-top; 901 | } 902 | 903 | .flip--enter-left .direction-reveal__anim--enter { 904 | -webkit-animation-name: flip--enter-left; 905 | animation-name: flip--enter-left; 906 | } 907 | 908 | .flip--enter-left .direction-reveal__anim--leave { 909 | -webkit-animation-name: flip--leave-right; 910 | animation-name: flip--leave-right; 911 | } 912 | 913 | .flip--enter-right .direction-reveal__anim--enter { 914 | -webkit-animation-name: flip--enter-right; 915 | animation-name: flip--enter-right; 916 | } 917 | 918 | .flip--enter-right .direction-reveal__anim--leave { 919 | -webkit-animation-name: flip--leave-left; 920 | animation-name: flip--leave-left; 921 | } 922 | 923 | .flip--leave-top .direction-reveal__anim--enter { 924 | -webkit-animation-name: flip--leave-top; 925 | animation-name: flip--leave-top; 926 | } 927 | 928 | .flip--leave-top .direction-reveal__anim--leave { 929 | -webkit-animation-name: flip--enter-bottom; 930 | animation-name: flip--enter-bottom; 931 | } 932 | 933 | .flip--leave-bottom .direction-reveal__anim--enter { 934 | -webkit-animation-name: flip--leave-bottom; 935 | animation-name: flip--leave-bottom; 936 | } 937 | 938 | .flip--leave-bottom .direction-reveal__anim--leave { 939 | -webkit-animation-name: flip--enter-top; 940 | animation-name: flip--enter-top; 941 | } 942 | 943 | .flip--leave-left .direction-reveal__anim--enter { 944 | -webkit-animation-name: flip--leave-left; 945 | animation-name: flip--leave-left; 946 | } 947 | 948 | .flip--leave-left .direction-reveal__anim--leave { 949 | -webkit-animation-name: flip--enter-right; 950 | animation-name: flip--enter-right; 951 | } 952 | 953 | .flip--leave-right .direction-reveal__anim--enter { 954 | -webkit-animation-name: flip--leave-right; 955 | animation-name: flip--leave-right; 956 | } 957 | 958 | .flip--leave-right .direction-reveal__anim--leave { 959 | -webkit-animation-name: flip--enter-left; 960 | animation-name: flip--enter-left; 961 | } 962 | 963 | .direction-reveal [class*='flip--'] { 964 | perspective: 800px; 965 | overflow: visible; 966 | } 967 | 968 | .direction-reveal [class*='flip--'] .direction-reveal__anim--enter, .direction-reveal [class*='flip--'] .direction-reveal__anim--leave { 969 | transform: rotateX(0) rotateY(0); 970 | -webkit-animation-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955); 971 | animation-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955); 972 | -webkit-animation-duration: 1.2s; 973 | animation-duration: 1.2s; 974 | -webkit-backface-visibility: hidden; 975 | backface-visibility: hidden; 976 | z-index: 1; 977 | } 978 | 979 | @-webkit-keyframes flip--enter-top { 980 | 0% { 981 | transform: rotateX(180deg) rotateY(0); 982 | } 983 | } 984 | 985 | @keyframes flip--enter-top { 986 | 0% { 987 | transform: rotateX(180deg) rotateY(0); 988 | } 989 | } 990 | 991 | @-webkit-keyframes flip--leave-top { 992 | 100% { 993 | transform: rotateX(180deg) rotateY(0); 994 | } 995 | } 996 | 997 | @keyframes flip--leave-top { 998 | 100% { 999 | transform: rotateX(180deg) rotateY(0); 1000 | } 1001 | } 1002 | 1003 | @-webkit-keyframes flip--enter-bottom { 1004 | 0% { 1005 | transform: rotateX(-180deg) rotateY(0); 1006 | } 1007 | } 1008 | 1009 | @keyframes flip--enter-bottom { 1010 | 0% { 1011 | transform: rotateX(-180deg) rotateY(0); 1012 | } 1013 | } 1014 | 1015 | @-webkit-keyframes flip--leave-bottom { 1016 | 100% { 1017 | transform: rotateX(-180deg) rotateY(0); 1018 | } 1019 | } 1020 | 1021 | @keyframes flip--leave-bottom { 1022 | 100% { 1023 | transform: rotateX(-180deg) rotateY(0); 1024 | } 1025 | } 1026 | 1027 | @-webkit-keyframes flip--enter-left { 1028 | 0% { 1029 | transform: rotateX(0) rotateY(-180deg); 1030 | } 1031 | } 1032 | 1033 | @keyframes flip--enter-left { 1034 | 0% { 1035 | transform: rotateX(0) rotateY(-180deg); 1036 | } 1037 | } 1038 | 1039 | @-webkit-keyframes flip--leave-left { 1040 | 100% { 1041 | transform: rotateX(0) rotateY(-180deg); 1042 | } 1043 | } 1044 | 1045 | @keyframes flip--leave-left { 1046 | 100% { 1047 | transform: rotateX(0) rotateY(-180deg); 1048 | } 1049 | } 1050 | 1051 | @-webkit-keyframes flip--enter-right { 1052 | 0% { 1053 | transform: rotateX(0) rotateY(180deg); 1054 | } 1055 | } 1056 | 1057 | @keyframes flip--enter-right { 1058 | 0% { 1059 | transform: rotateX(0) rotateY(180deg); 1060 | } 1061 | } 1062 | 1063 | @-webkit-keyframes flip--leave-right { 1064 | 100% { 1065 | transform: rotateX(0) rotateY(180deg); 1066 | } 1067 | } 1068 | 1069 | @keyframes flip--leave-right { 1070 | 100% { 1071 | transform: rotateX(0) rotateY(180deg); 1072 | } 1073 | } 1074 | -------------------------------------------------------------------------------- /docs/styles/site.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | /* Document 3 | ========================================================================== */ 4 | /** 5 | * 1. Correct the line height in all browsers. 6 | * 2. Prevent adjustments of font size after orientation changes in iOS. 7 | */ 8 | html { 9 | line-height: 1.15; 10 | /* 1 */ 11 | -webkit-text-size-adjust: 100%; 12 | /* 2 */ 13 | } 14 | 15 | /* Sections 16 | ========================================================================== */ 17 | /** 18 | * Remove the margin in all browsers. 19 | */ 20 | body { 21 | margin: 0; 22 | } 23 | 24 | /** 25 | * Render the `main` element consistently in IE. 26 | */ 27 | main { 28 | display: block; 29 | } 30 | 31 | /** 32 | * Correct the font size and margin on `h1` elements within `section` and 33 | * `article` contexts in Chrome, Firefox, and Safari. 34 | */ 35 | h1 { 36 | font-size: 2em; 37 | margin: 0.67em 0; 38 | } 39 | 40 | /* Grouping content 41 | ========================================================================== */ 42 | /** 43 | * 1. Add the correct box sizing in Firefox. 44 | * 2. Show the overflow in Edge and IE. 45 | */ 46 | hr { 47 | box-sizing: content-box; 48 | /* 1 */ 49 | height: 0; 50 | /* 1 */ 51 | overflow: visible; 52 | /* 2 */ 53 | } 54 | 55 | /** 56 | * 1. Correct the inheritance and scaling of font size in all browsers. 57 | * 2. Correct the odd `em` font sizing in all browsers. 58 | */ 59 | pre { 60 | font-family: monospace, monospace; 61 | /* 1 */ 62 | font-size: 1em; 63 | /* 2 */ 64 | } 65 | 66 | /* Text-level semantics 67 | ========================================================================== */ 68 | /** 69 | * Remove the gray background on active links in IE 10. 70 | */ 71 | a { 72 | background-color: transparent; 73 | } 74 | 75 | /** 76 | * 1. Remove the bottom border in Chrome 57- 77 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 78 | */ 79 | abbr[title] { 80 | border-bottom: none; 81 | /* 1 */ 82 | text-decoration: underline; 83 | /* 2 */ 84 | -webkit-text-decoration: underline dotted; 85 | text-decoration: underline dotted; 86 | /* 2 */ 87 | } 88 | 89 | /** 90 | * Add the correct font weight in Chrome, Edge, and Safari. 91 | */ 92 | b, 93 | strong { 94 | font-weight: bolder; 95 | } 96 | 97 | /** 98 | * 1. Correct the inheritance and scaling of font size in all browsers. 99 | * 2. Correct the odd `em` font sizing in all browsers. 100 | */ 101 | code, 102 | kbd, 103 | samp { 104 | font-family: monospace, monospace; 105 | /* 1 */ 106 | font-size: 1em; 107 | /* 2 */ 108 | } 109 | 110 | /** 111 | * Add the correct font size in all browsers. 112 | */ 113 | small { 114 | font-size: 80%; 115 | } 116 | 117 | /** 118 | * Prevent `sub` and `sup` elements from affecting the line height in 119 | * all browsers. 120 | */ 121 | sub, 122 | sup { 123 | font-size: 75%; 124 | line-height: 0; 125 | position: relative; 126 | vertical-align: baseline; 127 | } 128 | 129 | sub { 130 | bottom: -0.25em; 131 | } 132 | 133 | sup { 134 | top: -0.5em; 135 | } 136 | 137 | /* Embedded content 138 | ========================================================================== */ 139 | /** 140 | * Remove the border on images inside links in IE 10. 141 | */ 142 | img { 143 | border-style: none; 144 | } 145 | 146 | /* Forms 147 | ========================================================================== */ 148 | /** 149 | * 1. Change the font styles in all browsers. 150 | * 2. Remove the margin in Firefox and Safari. 151 | */ 152 | button, 153 | input, 154 | optgroup, 155 | select, 156 | textarea { 157 | font-family: inherit; 158 | /* 1 */ 159 | font-size: 100%; 160 | /* 1 */ 161 | line-height: 1.15; 162 | /* 1 */ 163 | margin: 0; 164 | /* 2 */ 165 | } 166 | 167 | /** 168 | * Show the overflow in IE. 169 | * 1. Show the overflow in Edge. 170 | */ 171 | button, 172 | input { 173 | /* 1 */ 174 | overflow: visible; 175 | } 176 | 177 | /** 178 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 179 | * 1. Remove the inheritance of text transform in Firefox. 180 | */ 181 | button, 182 | select { 183 | /* 1 */ 184 | text-transform: none; 185 | } 186 | 187 | /** 188 | * Correct the inability to style clickable types in iOS and Safari. 189 | */ 190 | button, 191 | [type="button"], 192 | [type="reset"], 193 | [type="submit"] { 194 | -webkit-appearance: button; 195 | } 196 | 197 | /** 198 | * Remove the inner border and padding in Firefox. 199 | */ 200 | button::-moz-focus-inner, 201 | [type="button"]::-moz-focus-inner, 202 | [type="reset"]::-moz-focus-inner, 203 | [type="submit"]::-moz-focus-inner { 204 | border-style: none; 205 | padding: 0; 206 | } 207 | 208 | /** 209 | * Restore the focus styles unset by the previous rule. 210 | */ 211 | button:-moz-focusring, 212 | [type="button"]:-moz-focusring, 213 | [type="reset"]:-moz-focusring, 214 | [type="submit"]:-moz-focusring { 215 | outline: 1px dotted ButtonText; 216 | } 217 | 218 | /** 219 | * Correct the padding in Firefox. 220 | */ 221 | fieldset { 222 | padding: 0.35em 0.75em 0.625em; 223 | } 224 | 225 | /** 226 | * 1. Correct the text wrapping in Edge and IE. 227 | * 2. Correct the color inheritance from `fieldset` elements in IE. 228 | * 3. Remove the padding so developers are not caught out when they zero out 229 | * `fieldset` elements in all browsers. 230 | */ 231 | legend { 232 | box-sizing: border-box; 233 | /* 1 */ 234 | color: inherit; 235 | /* 2 */ 236 | display: table; 237 | /* 1 */ 238 | max-width: 100%; 239 | /* 1 */ 240 | padding: 0; 241 | /* 3 */ 242 | white-space: normal; 243 | /* 1 */ 244 | } 245 | 246 | /** 247 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 248 | */ 249 | progress { 250 | vertical-align: baseline; 251 | } 252 | 253 | /** 254 | * Remove the default vertical scrollbar in IE 10+. 255 | */ 256 | textarea { 257 | overflow: auto; 258 | } 259 | 260 | /** 261 | * 1. Add the correct box sizing in IE 10. 262 | * 2. Remove the padding in IE 10. 263 | */ 264 | [type="checkbox"], 265 | [type="radio"] { 266 | box-sizing: border-box; 267 | /* 1 */ 268 | padding: 0; 269 | /* 2 */ 270 | } 271 | 272 | /** 273 | * Correct the cursor style of increment and decrement buttons in Chrome. 274 | */ 275 | [type="number"]::-webkit-inner-spin-button, 276 | [type="number"]::-webkit-outer-spin-button { 277 | height: auto; 278 | } 279 | 280 | /** 281 | * 1. Correct the odd appearance in Chrome and Safari. 282 | * 2. Correct the outline style in Safari. 283 | */ 284 | [type="search"] { 285 | -webkit-appearance: textfield; 286 | /* 1 */ 287 | outline-offset: -2px; 288 | /* 2 */ 289 | } 290 | 291 | /** 292 | * Remove the inner padding in Chrome and Safari on macOS. 293 | */ 294 | [type="search"]::-webkit-search-decoration { 295 | -webkit-appearance: none; 296 | } 297 | 298 | /** 299 | * 1. Correct the inability to style clickable types in iOS and Safari. 300 | * 2. Change font properties to `inherit` in Safari. 301 | */ 302 | ::-webkit-file-upload-button { 303 | -webkit-appearance: button; 304 | /* 1 */ 305 | font: inherit; 306 | /* 2 */ 307 | } 308 | 309 | /* Interactive 310 | ========================================================================== */ 311 | /* 312 | * Add the correct display in Edge, IE 10+, and Firefox. 313 | */ 314 | details { 315 | display: block; 316 | } 317 | 318 | /* 319 | * Add the correct display in all browsers. 320 | */ 321 | summary { 322 | display: list-item; 323 | } 324 | 325 | /* Misc 326 | ========================================================================== */ 327 | /** 328 | * Add the correct display in IE 10+. 329 | */ 330 | template { 331 | display: none; 332 | } 333 | 334 | /** 335 | * Add the correct display in IE 10. 336 | */ 337 | [hidden] { 338 | display: none; 339 | } 340 | 341 | *, 342 | *:before, 343 | *:after { 344 | box-sizing: border-box; 345 | } 346 | 347 | body { 348 | font-family: "Open Sans", -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; 349 | font-size: 1rem; 350 | font-weight: 400; 351 | line-height: 1.5; 352 | color: #333; 353 | background-color: #fff; 354 | } 355 | 356 | a { 357 | color: #1976D2; 358 | text-decoration: none; 359 | transition: color 0.3s; 360 | } 361 | 362 | a:hover, a:focus { 363 | color: #2196F3; 364 | } 365 | 366 | h1, h2, h3, h4, h5, h6 { 367 | line-height: 1.2; 368 | font-weight: 300; 369 | } 370 | 371 | h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { 372 | font-size: 60%; 373 | } 374 | 375 | h1, h2 { 376 | margin: 0 0 1.5rem 0; 377 | } 378 | 379 | h3 { 380 | margin: 0 0 1rem 0; 381 | } 382 | 383 | h4, h5, h6 { 384 | margin: 0 0 .5rem 0; 385 | font-weight: 400; 386 | } 387 | 388 | h1 { 389 | font-size: 2.5rem; 390 | } 391 | 392 | h2 { 393 | font-size: 2rem; 394 | } 395 | 396 | h3 { 397 | font-size: 1.75rem; 398 | } 399 | 400 | h4 { 401 | font-size: 1.5rem; 402 | } 403 | 404 | h5 { 405 | font-size: 1.25rem; 406 | } 407 | 408 | h6 { 409 | font-size: 1rem; 410 | } 411 | 412 | p { 413 | margin: 0 0 1rem 0; 414 | } 415 | 416 | strong, th { 417 | font-weight: 800; 418 | } 419 | 420 | pre, code { 421 | font-family: 'Cutive Mono', monospace; 422 | background-color: #f3f3f3; 423 | } 424 | 425 | pre { 426 | display: block; 427 | padding: .75rem 1rem; 428 | margin: 0 0 2rem 0; 429 | overflow: auto; 430 | font-size: 1rem; 431 | word-break: break-all; 432 | word-wrap: break-word; 433 | border: 1px solid #bbb; 434 | border-radius: 4px; 435 | } 436 | 437 | .table-wrapper { 438 | display: block; 439 | width: 100%; 440 | overflow-x: auto; 441 | } 442 | 443 | .table { 444 | width: 100%; 445 | margin: 0 0 1.5rem 0; 446 | border-collapse: collapse; 447 | } 448 | 449 | .table th, .table td { 450 | padding: .25rem .5rem; 451 | text-align: left; 452 | vertical-align: top; 453 | border: 1px solid #ddd; 454 | } 455 | 456 | .img-fluid { 457 | max-width: 100%; 458 | height: auto; 459 | } 460 | 461 | .fullwidth .container { 462 | width: 100%; 463 | padding-top: 2rem; 464 | padding-bottom: 2rem; 465 | } 466 | 467 | .fullwidth--sm .container { 468 | padding-top: 1rem; 469 | padding-bottom: 1rem; 470 | } 471 | 472 | .container { 473 | width: 100%; 474 | max-width: 1140px; 475 | margin: 0 auto; 476 | padding-left: 1rem; 477 | padding-right: 1rem; 478 | } 479 | 480 | .subsection { 481 | margin: 0 0 2.5rem 0; 482 | } 483 | 484 | .separator { 485 | border-bottom: 1px solid #ddd; 486 | } 487 | 488 | .header { 489 | position: -webkit-sticky; 490 | position: sticky; 491 | top: 0; 492 | z-index: 100; 493 | width: 100%; 494 | padding: .5rem 0; 495 | color: #fff; 496 | background-color: #1976D2; 497 | box-shadow: 0 0 6px 6px rgba(0, 0, 0, 0.1); 498 | } 499 | 500 | @media (min-width: 768px) { 501 | .header { 502 | padding: 1rem 0; 503 | } 504 | } 505 | 506 | .header .container { 507 | display: flex; 508 | } 509 | 510 | @media (max-width: 575px) { 511 | .header .container { 512 | flex-flow: column; 513 | } 514 | } 515 | 516 | @media (min-width: 576px) { 517 | .header .container { 518 | align-items: center; 519 | } 520 | } 521 | 522 | .logo-text { 523 | margin: 0; 524 | } 525 | 526 | @media (max-width: 767px) { 527 | .logo-text { 528 | font-size: 1.75rem; 529 | } 530 | } 531 | 532 | .header-links .btn-demo { 533 | margin: .5rem 0; 534 | } 535 | 536 | .header-links .btn-demo:last-child { 537 | margin-left: .5rem; 538 | } 539 | 540 | @media (min-width: 576px) { 541 | .header-links { 542 | margin-left: auto; 543 | } 544 | } 545 | 546 | .footer { 547 | text-align: center; 548 | } 549 | 550 | .btn-demo { 551 | display: inline-block; 552 | text-align: center; 553 | vertical-align: middle; 554 | cursor: pointer; 555 | border: 1px solid #bbb; 556 | padding: 4px 12px 6px 12px; 557 | font-size: 18px; 558 | background-color: #fff; 559 | border-radius: 4px; 560 | transition: background-color .3s, color .3s; 561 | } 562 | 563 | .btn-demo:hover, .btn-demo:focus, .btn-demo:active { 564 | background-color: #f3f3f3; 565 | } 566 | 567 | .btn-demo--white { 568 | color: #fff; 569 | border-color: #fff; 570 | background-color: transparent; 571 | } 572 | 573 | .m-0 { 574 | margin: 0; 575 | } 576 | 577 | .row { 578 | display: flex; 579 | flex-wrap: wrap; 580 | margin-right: -15px; 581 | margin-left: -15px; 582 | } 583 | 584 | .col-sm-4 { 585 | position: relative; 586 | width: 100%; 587 | padding-left: 15px; 588 | padding-right: 15px; 589 | min-height: 1px; 590 | } 591 | 592 | @media (min-width: 576px) { 593 | .col-sm-4 { 594 | flex: 0 0 33.333333%; 595 | max-width: 33.333333%; 596 | } 597 | } 598 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | // ----- Imports and variables ------ 2 | const { src, dest, watch, series, parallel, lastRun } = require('gulp'); 3 | const gulpLoadPlugins = require('gulp-load-plugins'); 4 | const $ = gulpLoadPlugins(); 5 | const browserSync = require('browser-sync'); 6 | const server = browserSync.create(); 7 | const del = require('del'); 8 | const autoprefixer = require('autoprefixer'); 9 | 10 | const paths = { 11 | src: 'src', 12 | dest: 'docs', 13 | tmp: '.tmp' 14 | }; 15 | 16 | 17 | // ----- Tasks ------ 18 | function styles() { 19 | return src(`${paths.src}/styles/*.scss`) 20 | .pipe($.plumber()) 21 | // .pipe($.sourcemaps.init()) 22 | .pipe($.sass.sync({ 23 | outputStyle: 'expanded', 24 | precision: 10, 25 | includePaths: ['.'] 26 | }) 27 | .on('error', $.sass.logError)) 28 | .pipe($.postcss([ 29 | autoprefixer() 30 | ])) 31 | // .pipe($.sourcemaps.write()) 32 | // .pipe(dest(`${paths.tmp}/styles`)) 33 | .pipe(dest(`${paths.src}/styles`)) 34 | .pipe(server.reload({stream: true})); 35 | }; 36 | 37 | exports.styles = styles; 38 | 39 | 40 | function scripts() { 41 | return src(`${paths.src}/scripts/direction-reveal.js`) 42 | .pipe($.plumber()) 43 | .pipe($.babel({ 44 | plugins: ['@babel/plugin-transform-modules-umd'] 45 | })) 46 | .pipe($.rename('direction-reveal-umd.js')) 47 | .pipe(dest(`${paths.src}/scripts/`)) 48 | }; 49 | 50 | exports.scripts = scripts; 51 | 52 | 53 | // ----- Serve tasks ------ 54 | function startAppServer() { 55 | server.init({ 56 | port: 9000, 57 | notify: false, 58 | ghostMode: false, 59 | server: { 60 | // baseDir: [`${paths.tmp}`, `${paths.src}`], 61 | baseDir: [`${paths.src}`], 62 | routes: { 63 | '/node_modules': 'node_modules' 64 | }, 65 | serveStaticOptions: { 66 | extensions: ['html'] 67 | } 68 | } 69 | }); 70 | 71 | watch([`${paths.src}/*.html`, `${paths.src}/**/*.js`]).on('change', server.reload); 72 | 73 | watch(`${paths.src}/**/*.scss`, styles); 74 | } 75 | 76 | 77 | const compile = series(clean, parallel(styles, scripts)); 78 | exports.compile = compile; 79 | 80 | let serve = series(compile, startAppServer); 81 | exports.serve = serve; 82 | 83 | 84 | 85 | // ----- Build tasks ------ 86 | function moveFiles() { 87 | return src([`${paths.src}/**/*.{html,css,js,jpg,gif,png,webp,mp4,webm}`]) 88 | .pipe(dest(`${paths.dest}`)); 89 | }; 90 | exports.moveFiles = moveFiles; 91 | 92 | 93 | function clean() { 94 | return del([`${paths.tmp}`, `${paths.dest}`]) 95 | } 96 | exports.clean = clean; 97 | 98 | 99 | const build = series(compile, moveFiles); 100 | exports.build = build; 101 | exports.default = build; 102 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "direction-reveal", 3 | "description": "A plugin that detects the direction a user enters or leaves an element allowing you to reveal or hide content based on this direction.", 4 | "version": "2.0.2", 5 | "main": "src/scripts/direction-reveal-umd.js", 6 | "esnext": "src/scripts/direction-reveal.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/NigelOToole/direction-reveal.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/NigelOToole/direction-reveal/issues" 13 | }, 14 | "homepage": "https://github.com/NigelOToole/direction-reveal#readme", 15 | "author": "Nigel O Toole (http://www.purestructure.com)", 16 | "license": "MIT", 17 | "keywords": [ 18 | "javascript", 19 | "es6", 20 | "module", 21 | "sass", 22 | "css", 23 | "reveal", 24 | "hover", 25 | "gallery", 26 | "grid" 27 | ], 28 | "engines": { 29 | "node": ">=4" 30 | }, 31 | "devDependencies": { 32 | "@babel/core": "^7.1.2", 33 | "@babel/plugin-transform-modules-umd": "^7.2.0", 34 | "@babel/preset-env": "^7.1.0", 35 | "autoprefixer": "^9.4.4", 36 | "browser-sync": "^2.26.7", 37 | "del": "^3.0.0", 38 | "gulp": "^4.0.0", 39 | "gulp-babel": "^8.0.0", 40 | "gulp-cli": "^2.2.1", 41 | "gulp-load-plugins": "^1.6.0", 42 | "gulp-plumber": "^1.0.1", 43 | "gulp-postcss": "^8.0.0", 44 | "gulp-rename": "^1.4.0", 45 | "gulp-sass": "^4.1.0", 46 | "gulp-sourcemaps": "^2.2.0" 47 | }, 48 | "scripts": { 49 | "start": "gulp serve", 50 | "build": "gulp" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/images/mountain-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NigelOToole/direction-reveal/151a8d69af301c7a07cd3c4cd0960d20094f7686/src/images/mountain-1.jpg -------------------------------------------------------------------------------- /src/images/mountain-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NigelOToole/direction-reveal/151a8d69af301c7a07cd3c4cd0960d20094f7686/src/images/mountain-2.jpg -------------------------------------------------------------------------------- /src/images/mountain-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NigelOToole/direction-reveal/151a8d69af301c7a07cd3c4cd0960d20094f7686/src/images/mountain-3.jpg -------------------------------------------------------------------------------- /src/images/mountain-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NigelOToole/direction-reveal/151a8d69af301c7a07cd3c4cd0960d20094f7686/src/images/mountain-4.jpg -------------------------------------------------------------------------------- /src/images/mountain-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NigelOToole/direction-reveal/151a8d69af301c7a07cd3c4cd0960d20094f7686/src/images/mountain-5.jpg -------------------------------------------------------------------------------- /src/images/mountain-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NigelOToole/direction-reveal/151a8d69af301c7a07cd3c4cd0960d20094f7686/src/images/mountain-6.jpg -------------------------------------------------------------------------------- /src/scripts/direction-reveal-umd.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define(["exports"], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(exports); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(mod.exports); 11 | global.directionReveal = mod.exports; 12 | } 13 | })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports) { 14 | "use strict"; 15 | 16 | Object.defineProperty(_exports, "__esModule", { 17 | value: true 18 | }); 19 | _exports["default"] = void 0; 20 | 21 | /** 22 | Direction aware content reveals. 23 | 24 | @param {Object} object - Container for all options. 25 | @param {string} selector - Container element selector. 26 | @param {string} itemSelector - Item element selector. 27 | @param {string} animationName - Animation CSS class. 28 | @param {string} animationPostfixEnter - Animation CSS class postfix for enter event. 29 | @param {string} animationPostfixLeave - Animation CSS class postfix for leave event. 30 | @param {boolean} enableTouch - Adds touch event to show content on first click then follow link on the second click. 31 | @param {integer} touchThreshold - Touch length must be less than this to trigger reveal which prevents the event triggering if user is scrolling. 32 | */ 33 | var DirectionReveal = function DirectionReveal() { 34 | var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, 35 | _ref$selector = _ref.selector, 36 | selector = _ref$selector === void 0 ? '.direction-reveal' : _ref$selector, 37 | _ref$itemSelector = _ref.itemSelector, 38 | itemSelector = _ref$itemSelector === void 0 ? '.direction-reveal__card' : _ref$itemSelector, 39 | _ref$animationName = _ref.animationName, 40 | animationName = _ref$animationName === void 0 ? 'swing' : _ref$animationName, 41 | _ref$animationPostfix = _ref.animationPostfixEnter, 42 | animationPostfixEnter = _ref$animationPostfix === void 0 ? 'enter' : _ref$animationPostfix, 43 | _ref$animationPostfix2 = _ref.animationPostfixLeave, 44 | animationPostfixLeave = _ref$animationPostfix2 === void 0 ? 'leave' : _ref$animationPostfix2, 45 | _ref$enableTouch = _ref.enableTouch, 46 | enableTouch = _ref$enableTouch === void 0 ? true : _ref$enableTouch, 47 | _ref$touchThreshold = _ref.touchThreshold, 48 | touchThreshold = _ref$touchThreshold === void 0 ? 250 : _ref$touchThreshold; 49 | 50 | var containers = document.querySelectorAll(selector); 51 | var touchStart; // Utilities - https://hackernoon.com/rethinking-javascript-eliminate-the-switch-statement-for-better-code-5c81c044716d 52 | 53 | var addEventListenerMulti = function addEventListenerMulti(element, events, fn) { 54 | events.forEach(function (e) { 55 | return element.addEventListener(e, fn); 56 | }); 57 | }; 58 | 59 | var switchCase = function switchCase(cases) { 60 | return function (defaultCase) { 61 | return function (key) { 62 | return key in cases ? cases[key] : defaultCase; 63 | }; 64 | }; 65 | }; 66 | 67 | var fireEvent = function fireEvent(item, eventName, eventDetail) { 68 | var event = new CustomEvent(eventName, { 69 | bubbles: true, 70 | detail: eventDetail 71 | }); 72 | item.dispatchEvent(event); 73 | }; // Get direction data based on element and pointer positions 74 | 75 | 76 | var getDirection = function getDirection(e, item) { 77 | // Width and height of current item 78 | var w = item.offsetWidth; 79 | var h = item.offsetHeight; 80 | var position = getPosition(item); // Calculate the x/y value of the pointer entering/exiting, relative to the center of the item. 81 | 82 | var x = (e.pageX - position.x - w / 2) * (w > h ? h / w : 1); 83 | var y = (e.pageY - position.y - h / 2) * (h > w ? w / h : 1); // Calculate the angle the pointer entered/exited and convert to clockwise format (top/right/bottom/left = 0/1/2/3). - https://stackoverflow.com/a/3647634 84 | 85 | var d = Math.round(Math.atan2(y, x) / 1.57079633 + 5) % 4; // console.table([x, y, w, h, e.pageX, e.pageY, item.offsetLeft, item.offsetTop, position.x, position.y]); 86 | 87 | return d; 88 | }; // Gets an elements position - https://www.kirupa.com/html5/get_element_position_using_javascript.htm 89 | 90 | 91 | var getPosition = function getPosition(el) { 92 | var xPos = 0; 93 | var yPos = 0; 94 | 95 | while (el) { 96 | xPos += el.offsetLeft + el.clientLeft; 97 | yPos += el.offsetTop + el.clientTop; 98 | el = el.offsetParent; 99 | } 100 | 101 | return { 102 | x: xPos, 103 | y: yPos 104 | }; 105 | }; 106 | 107 | var translateDirection = switchCase({ 108 | 0: 'top', 109 | 1: 'right', 110 | 2: 'bottom', 111 | 3: 'left' 112 | })('top'); // Updates direction and toggles classes 113 | 114 | var updateDirection = function updateDirection(e, action) { 115 | var currentItem = e.currentTarget; 116 | var direction = getDirection(e, currentItem); 117 | var directionString = translateDirection(direction); // Remove current animation classes and adds current action/direction. 118 | 119 | var currentCssClasses = currentItem.className.split(' '); 120 | var filteredCssClasses = currentCssClasses.filter(function (cssClass) { 121 | return !cssClass.startsWith(animationName); 122 | }).join(' '); 123 | currentItem.className = filteredCssClasses; 124 | currentItem.classList.add("".concat(animationName, "--").concat(action, "-").concat(directionString)); 125 | var eventDetail = { 126 | action: action, 127 | direction: directionString 128 | }; 129 | fireEvent(currentItem, 'directionChange', eventDetail); 130 | }; 131 | 132 | var bindEvents = function bindEvents(containerItem) { 133 | var items = containerItem.querySelectorAll(itemSelector); 134 | items.forEach(function (item) { 135 | addEventListenerMulti(item, ['mouseenter', 'focus'], function (e) { 136 | updateDirection(e, animationPostfixEnter); 137 | }); 138 | addEventListenerMulti(item, ['mouseleave', 'blur'], function (e) { 139 | updateDirection(e, animationPostfixLeave); 140 | }); 141 | 142 | if (enableTouch) { 143 | item.addEventListener('touchstart', function (e) { 144 | touchStart = +new Date(); 145 | }, { 146 | passive: true 147 | }); 148 | item.addEventListener('touchend', function (e) { 149 | var touchTime = +new Date() - touchStart; 150 | 151 | if (touchTime < touchThreshold && !item.className.includes("".concat(animationName, "--").concat(animationPostfixEnter))) { 152 | e.preventDefault(); 153 | resetVisible(e, items, updateDirection(e, animationPostfixEnter)); 154 | } 155 | }); 156 | } 157 | }); 158 | }; 159 | 160 | var resetVisible = function resetVisible(e, items, callback) { 161 | items.forEach(function (item) { 162 | var currentCssClasses = item.className; 163 | 164 | if (currentCssClasses.includes("".concat(animationName, "--").concat(animationPostfixEnter)) && item !== e.currentTarget) { 165 | item.className = currentCssClasses.replace("".concat(animationName, "--").concat(animationPostfixEnter), "".concat(animationName, "--").concat(animationPostfixLeave)); 166 | } 167 | }); 168 | callback; 169 | }; 170 | 171 | var init = function init() { 172 | if (containers.length) { 173 | containers.forEach(function (containerItem) { 174 | bindEvents(containerItem); 175 | }); 176 | } else { 177 | return; 178 | } 179 | }; // Self init 180 | 181 | 182 | init(); // Reveal API 183 | 184 | return { 185 | init: init 186 | }; 187 | }; 188 | 189 | var _default = DirectionReveal; 190 | _exports["default"] = _default; 191 | }); -------------------------------------------------------------------------------- /src/scripts/direction-reveal.js: -------------------------------------------------------------------------------- 1 | /** 2 | Direction aware content reveals. 3 | 4 | @param {Object} object - Container for all options. 5 | @param {string} selector - Container element selector. 6 | @param {string} itemSelector - Item element selector. 7 | @param {string} animationName - Animation CSS class. 8 | @param {string} animationPostfixEnter - Animation CSS class postfix for enter event. 9 | @param {string} animationPostfixLeave - Animation CSS class postfix for leave event. 10 | @param {boolean} enableTouch - Adds touch event to show content on first click then follow link on the second click. 11 | @param {integer} touchThreshold - Touch length must be less than this to trigger reveal which prevents the event triggering if user is scrolling. 12 | */ 13 | 14 | 15 | const DirectionReveal = function ({ 16 | selector: selector = '.direction-reveal', 17 | itemSelector: itemSelector = '.direction-reveal__card', 18 | animationName: animationName = 'swing', 19 | animationPostfixEnter: animationPostfixEnter = 'enter', 20 | animationPostfixLeave: animationPostfixLeave = 'leave', 21 | enableTouch: enableTouch = true, 22 | touchThreshold: touchThreshold = 250 23 | } = {}) { 24 | 25 | const containers = document.querySelectorAll(selector); 26 | let touchStart; 27 | 28 | 29 | // Utilities - https://hackernoon.com/rethinking-javascript-eliminate-the-switch-statement-for-better-code-5c81c044716d 30 | const addEventListenerMulti = function (element, events, fn) { 31 | events.forEach((e) => element.addEventListener(e, fn)); 32 | }; 33 | 34 | const switchCase = cases => defaultCase => key => key in cases ? cases[key] : defaultCase; 35 | 36 | const fireEvent = (item, eventName, eventDetail) => { 37 | const event = new CustomEvent(eventName, { 38 | bubbles: true, 39 | detail: eventDetail, 40 | }); 41 | 42 | item.dispatchEvent(event); 43 | }; 44 | 45 | 46 | // Get direction data based on element and pointer positions 47 | const getDirection = function (e, item) { 48 | // Width and height of current item 49 | let w = item.offsetWidth; 50 | let h = item.offsetHeight; 51 | let position = getPosition(item); 52 | 53 | // Calculate the x/y value of the pointer entering/exiting, relative to the center of the item. 54 | let x = (e.pageX - position.x - (w / 2)) * (w > h ? (h / w) : 1); 55 | let y = (e.pageY - position.y - (h / 2)) * (h > w ? (w / h) : 1); 56 | 57 | // Calculate the angle the pointer entered/exited and convert to clockwise format (top/right/bottom/left = 0/1/2/3). - https://stackoverflow.com/a/3647634 58 | let d = Math.round(Math.atan2(y, x) / 1.57079633 + 5) % 4; 59 | 60 | // console.table([x, y, w, h, e.pageX, e.pageY, item.offsetLeft, item.offsetTop, position.x, position.y]); 61 | 62 | return d; 63 | }; 64 | 65 | 66 | // Gets an elements position - https://www.kirupa.com/html5/get_element_position_using_javascript.htm 67 | const getPosition = function (el) { 68 | let xPos = 0; 69 | let yPos = 0; 70 | 71 | while (el) { 72 | xPos += (el.offsetLeft + el.clientLeft); 73 | yPos += (el.offsetTop + el.clientTop); 74 | 75 | el = el.offsetParent; 76 | } 77 | return { 78 | x: xPos, 79 | y: yPos 80 | }; 81 | }; 82 | 83 | 84 | const translateDirection = switchCase({ 85 | 0: 'top', 86 | 1: 'right', 87 | 2: 'bottom', 88 | 3: 'left' 89 | })('top'); 90 | 91 | 92 | // Updates direction and toggles classes 93 | const updateDirection = function (e, action) { 94 | let currentItem = e.currentTarget; 95 | let direction = getDirection(e, currentItem); 96 | let directionString = translateDirection(direction); 97 | 98 | // Remove current animation classes and adds current action/direction. 99 | let currentCssClasses = currentItem.className.split(' '); 100 | let filteredCssClasses = currentCssClasses.filter((cssClass) => (!cssClass.startsWith(animationName))).join(' '); 101 | currentItem.className = filteredCssClasses; 102 | currentItem.classList.add(`${animationName}--${action}-${directionString}`); 103 | 104 | let eventDetail = { action: action, direction: directionString }; 105 | fireEvent(currentItem, 'directionChange', eventDetail); 106 | }; 107 | 108 | 109 | const bindEvents = function (containerItem) { 110 | const items = containerItem.querySelectorAll(itemSelector); 111 | 112 | items.forEach((item) => { 113 | 114 | addEventListenerMulti(item, ['mouseenter', 'focus'], (e) => { 115 | updateDirection(e, animationPostfixEnter); 116 | }); 117 | 118 | addEventListenerMulti(item, ['mouseleave', 'blur'], (e) => { 119 | updateDirection(e, animationPostfixLeave); 120 | }); 121 | 122 | 123 | if (enableTouch) { 124 | 125 | item.addEventListener('touchstart', (e) => { 126 | touchStart = +new Date; 127 | }, { passive: true }); 128 | 129 | item.addEventListener('touchend', (e) => { 130 | let touchTime = +new Date - touchStart; 131 | 132 | if (touchTime < touchThreshold && !item.className.includes(`${animationName}--${animationPostfixEnter}`)) { 133 | e.preventDefault(); 134 | 135 | resetVisible(e, items, updateDirection(e, animationPostfixEnter)); 136 | } 137 | }); 138 | 139 | } 140 | 141 | }); 142 | }; 143 | 144 | 145 | const resetVisible = function (e, items, callback) { 146 | 147 | items.forEach((item) => { 148 | let currentCssClasses = item.className; 149 | 150 | if(currentCssClasses.includes(`${animationName}--${animationPostfixEnter}`) && item !== e.currentTarget) { 151 | item.className = currentCssClasses.replace(`${animationName}--${animationPostfixEnter}`, `${animationName}--${animationPostfixLeave}`); 152 | } 153 | }); 154 | 155 | callback; 156 | }; 157 | 158 | 159 | const init = function () { 160 | 161 | if (containers.length) { 162 | containers.forEach((containerItem) => { 163 | bindEvents(containerItem); 164 | }); 165 | } 166 | else { 167 | return; 168 | } 169 | 170 | }; 171 | 172 | 173 | // Self init 174 | init(); 175 | 176 | 177 | // Reveal API 178 | return { 179 | init 180 | }; 181 | }; 182 | 183 | export default DirectionReveal; 184 | -------------------------------------------------------------------------------- /src/scripts/main.js: -------------------------------------------------------------------------------- 1 | import DirectionReveal from './direction-reveal.js'; 2 | 3 | // Swing (Default) 4 | const directionRevealSwing = DirectionReveal({ 5 | selector: '.direction-reveal--demo-swing' 6 | }); 7 | 8 | 9 | // Rotate (all options) 10 | const directionRevealRotate = DirectionReveal({ 11 | selector: '.direction-reveal--demo-rotate', 12 | itemSelector: '.direction-reveal__card', 13 | animationName: 'rotate', 14 | animationPostfixEnter: 'enter', 15 | animationPostfixLeave: 'leave', 16 | enableTouch: true, 17 | touchThreshold: 250 18 | }); 19 | 20 | 21 | // Flip 22 | const directionRevealFlip = DirectionReveal({ 23 | selector: '.direction-reveal--demo-flip', 24 | animationName: 'flip' 25 | }); 26 | 27 | 28 | // Slide 29 | const directionRevealSlide = DirectionReveal({ 30 | selector: '.direction-reveal--demo-slide', 31 | animationName: 'slide' 32 | }); 33 | 34 | 35 | // Slide & push 36 | const directionRevealSlidePush = DirectionReveal({ 37 | selector: '.direction-reveal--demo-slide-push', 38 | animationName: 'slide' 39 | }); 40 | 41 | 42 | // Roll out 43 | const directionRevealRollOut = DirectionReveal({ 44 | selector: '.direction-reveal--demo-roll-out', 45 | animationName: 'roll-out' 46 | }); 47 | 48 | 49 | 50 | // Add a listener to an item to monitor direction changes 51 | 52 | // document.querySelector('.direction-reveal--demo-swing .direction-reveal__card:first-child').addEventListener('directionChange', (event) => { 53 | // console.log(`Action: ${event.detail.action} Direction: ${event.detail.direction}`); 54 | // }); 55 | 56 | // let eventTargets = document.querySelectorAll('.direction-reveal--demo-swing .direction-reveal__card'); 57 | // eventTargets.forEach((item) => { 58 | // item.addEventListener('directionChange', (event) => { 59 | // console.log(`Action: ${event.detail.action} Direction: ${event.detail.direction}`); 60 | // console.log(item); 61 | // }); 62 | // }); 63 | -------------------------------------------------------------------------------- /src/styles/direction-reveal.css: -------------------------------------------------------------------------------- 1 | .direction-reveal__card { 2 | display: inline-block; 3 | position: relative; 4 | overflow: hidden; 5 | } 6 | 7 | .direction-reveal__img { 8 | display: block; 9 | max-width: 100%; 10 | height: auto; 11 | } 12 | 13 | .direction-reveal__overlay { 14 | position: absolute; 15 | top: 0; 16 | left: 0; 17 | width: 100%; 18 | height: 100%; 19 | padding: 16px; 20 | color: #fff; 21 | overflow: hidden; 22 | background-color: rgba(0, 0, 0, 0.6); 23 | } 24 | 25 | .direction-reveal__anim--enter, .direction-reveal__anim--leave { 26 | -webkit-animation-duration: 0.6s; 27 | animation-duration: 0.6s; 28 | -webkit-animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 29 | animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 30 | -webkit-animation-fill-mode: forwards; 31 | animation-fill-mode: forwards; 32 | } 33 | 34 | .direction-reveal__anim--enter { 35 | transform: translate3d(0, -100%, 0); 36 | } 37 | 38 | .direction-reveal__title { 39 | margin-top: 0; 40 | } 41 | 42 | .direction-reveal__text { 43 | margin-bottom: 0; 44 | } 45 | 46 | .direction-reveal--3-grid { 47 | display: flex; 48 | flex-wrap: wrap; 49 | margin-right: -8px; 50 | margin-left: -8px; 51 | } 52 | 53 | .direction-reveal--3-grid .direction-reveal__card { 54 | border: 8px solid transparent; 55 | } 56 | 57 | @media (min-width: 576px) { 58 | .direction-reveal--3-grid .direction-reveal__card { 59 | flex: 0 0 33.333333%; 60 | max-width: 33.333333%; 61 | } 62 | } 63 | 64 | .direction-reveal--3-grid-cssgrid { 65 | display: grid; 66 | margin-right: -8px; 67 | margin-left: -8px; 68 | } 69 | 70 | @media (min-width: 576px) { 71 | .direction-reveal--3-grid-cssgrid { 72 | grid-template-columns: repeat(3, 1fr); 73 | } 74 | } 75 | 76 | .direction-reveal--3-grid-cssgrid .direction-reveal__card { 77 | border: 8px solid transparent; 78 | } 79 | 80 | .direction-reveal--grid-bootstrap .direction-reveal__card { 81 | margin-top: 15px; 82 | margin-bottom: 15px; 83 | } 84 | 85 | .swing--enter-top .direction-reveal__anim--enter { 86 | -webkit-animation-name: swing--enter-top; 87 | animation-name: swing--enter-top; 88 | } 89 | 90 | .swing--enter-top .direction-reveal__anim--leave { 91 | -webkit-animation-name: swing--leave-bottom; 92 | animation-name: swing--leave-bottom; 93 | } 94 | 95 | .swing--enter-bottom .direction-reveal__anim--enter { 96 | -webkit-animation-name: swing--enter-bottom; 97 | animation-name: swing--enter-bottom; 98 | } 99 | 100 | .swing--enter-bottom .direction-reveal__anim--leave { 101 | -webkit-animation-name: swing--leave-top; 102 | animation-name: swing--leave-top; 103 | } 104 | 105 | .swing--enter-left .direction-reveal__anim--enter { 106 | -webkit-animation-name: swing--enter-left; 107 | animation-name: swing--enter-left; 108 | } 109 | 110 | .swing--enter-left .direction-reveal__anim--leave { 111 | -webkit-animation-name: swing--leave-right; 112 | animation-name: swing--leave-right; 113 | } 114 | 115 | .swing--enter-right .direction-reveal__anim--enter { 116 | -webkit-animation-name: swing--enter-right; 117 | animation-name: swing--enter-right; 118 | } 119 | 120 | .swing--enter-right .direction-reveal__anim--leave { 121 | -webkit-animation-name: swing--leave-left; 122 | animation-name: swing--leave-left; 123 | } 124 | 125 | .swing--leave-top .direction-reveal__anim--enter { 126 | -webkit-animation-name: swing--leave-top; 127 | animation-name: swing--leave-top; 128 | } 129 | 130 | .swing--leave-top .direction-reveal__anim--leave { 131 | -webkit-animation-name: swing--enter-bottom; 132 | animation-name: swing--enter-bottom; 133 | } 134 | 135 | .swing--leave-bottom .direction-reveal__anim--enter { 136 | -webkit-animation-name: swing--leave-bottom; 137 | animation-name: swing--leave-bottom; 138 | } 139 | 140 | .swing--leave-bottom .direction-reveal__anim--leave { 141 | -webkit-animation-name: swing--enter-top; 142 | animation-name: swing--enter-top; 143 | } 144 | 145 | .swing--leave-left .direction-reveal__anim--enter { 146 | -webkit-animation-name: swing--leave-left; 147 | animation-name: swing--leave-left; 148 | } 149 | 150 | .swing--leave-left .direction-reveal__anim--leave { 151 | -webkit-animation-name: swing--enter-right; 152 | animation-name: swing--enter-right; 153 | } 154 | 155 | .swing--leave-right .direction-reveal__anim--enter { 156 | -webkit-animation-name: swing--leave-right; 157 | animation-name: swing--leave-right; 158 | } 159 | 160 | .swing--leave-right .direction-reveal__anim--leave { 161 | -webkit-animation-name: swing--enter-left; 162 | animation-name: swing--enter-left; 163 | } 164 | 165 | .direction-reveal [class*='swing--'] { 166 | perspective: 400px; 167 | } 168 | 169 | .direction-reveal [class*='swing--'] .direction-reveal__anim--enter, .direction-reveal [class*='swing--'] .direction-reveal__anim--leave { 170 | transform: rotate3d(0, 0, 0, 0); 171 | -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 172 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 173 | } 174 | 175 | .swing--enter-top, .swing--leave-top { 176 | perspective-origin: center top; 177 | } 178 | 179 | .swing--enter-top .direction-reveal__anim--enter, .swing--enter-top .direction-reveal__anim--leave, .swing--leave-top .direction-reveal__anim--enter, .swing--leave-top .direction-reveal__anim--leave { 180 | transform-origin: center top; 181 | } 182 | 183 | .swing--enter-bottom, .swing--leave-bottom { 184 | perspective-origin: center bottom; 185 | } 186 | 187 | .swing--enter-bottom .direction-reveal__anim--enter, .swing--enter-bottom .direction-reveal__anim--leave, .swing--leave-bottom .direction-reveal__anim--enter, .swing--leave-bottom .direction-reveal__anim--leave { 188 | transform-origin: center bottom; 189 | } 190 | 191 | .swing--enter-left, .swing--leave-left { 192 | perspective-origin: left center; 193 | } 194 | 195 | .swing--enter-left .direction-reveal__anim--enter, .swing--enter-left .direction-reveal__anim--leave, .swing--leave-left .direction-reveal__anim--enter, .swing--leave-left .direction-reveal__anim--leave { 196 | transform-origin: left center; 197 | } 198 | 199 | .swing--enter-right, .swing--leave-right { 200 | perspective-origin: right center; 201 | } 202 | 203 | .swing--enter-right .direction-reveal__anim--enter, .swing--enter-right .direction-reveal__anim--leave, .swing--leave-right .direction-reveal__anim--enter, .swing--leave-right .direction-reveal__anim--leave { 204 | transform-origin: right center; 205 | } 206 | 207 | @-webkit-keyframes swing--enter-top { 208 | 0% { 209 | transform: rotate3d(-1, 0, 0, 90deg); 210 | } 211 | } 212 | 213 | @keyframes swing--enter-top { 214 | 0% { 215 | transform: rotate3d(-1, 0, 0, 90deg); 216 | } 217 | } 218 | 219 | @-webkit-keyframes swing--leave-top { 220 | 100% { 221 | transform: rotate3d(-1, 0, 0, 90deg); 222 | } 223 | } 224 | 225 | @keyframes swing--leave-top { 226 | 100% { 227 | transform: rotate3d(-1, 0, 0, 90deg); 228 | } 229 | } 230 | 231 | @-webkit-keyframes swing--enter-bottom { 232 | 0% { 233 | transform: rotate3d(1, 0, 0, 90deg); 234 | } 235 | } 236 | 237 | @keyframes swing--enter-bottom { 238 | 0% { 239 | transform: rotate3d(1, 0, 0, 90deg); 240 | } 241 | } 242 | 243 | @-webkit-keyframes swing--leave-bottom { 244 | 100% { 245 | transform: rotate3d(1, 0, 0, 90deg); 246 | } 247 | } 248 | 249 | @keyframes swing--leave-bottom { 250 | 100% { 251 | transform: rotate3d(1, 0, 0, 90deg); 252 | } 253 | } 254 | 255 | @-webkit-keyframes swing--enter-left { 256 | 0% { 257 | transform: rotate3d(0, 1, 0, 90deg); 258 | } 259 | } 260 | 261 | @keyframes swing--enter-left { 262 | 0% { 263 | transform: rotate3d(0, 1, 0, 90deg); 264 | } 265 | } 266 | 267 | @-webkit-keyframes swing--leave-left { 268 | 100% { 269 | transform: rotate3d(0, 1, 0, 90deg); 270 | } 271 | } 272 | 273 | @keyframes swing--leave-left { 274 | 100% { 275 | transform: rotate3d(0, 1, 0, 90deg); 276 | } 277 | } 278 | 279 | @-webkit-keyframes swing--enter-right { 280 | 0% { 281 | transform: rotate3d(0, -1, 0, 90deg); 282 | } 283 | } 284 | 285 | @keyframes swing--enter-right { 286 | 0% { 287 | transform: rotate3d(0, -1, 0, 90deg); 288 | } 289 | } 290 | 291 | @-webkit-keyframes swing--leave-right { 292 | 100% { 293 | transform: rotate3d(0, -1, 0, 90deg); 294 | } 295 | } 296 | 297 | @keyframes swing--leave-right { 298 | 100% { 299 | transform: rotate3d(0, -1, 0, 90deg); 300 | } 301 | } 302 | 303 | .slide--enter-top .direction-reveal__anim--enter { 304 | -webkit-animation-name: slide--enter-top; 305 | animation-name: slide--enter-top; 306 | } 307 | 308 | .slide--enter-top .direction-reveal__anim--leave { 309 | -webkit-animation-name: slide--leave-bottom; 310 | animation-name: slide--leave-bottom; 311 | } 312 | 313 | .slide--enter-bottom .direction-reveal__anim--enter { 314 | -webkit-animation-name: slide--enter-bottom; 315 | animation-name: slide--enter-bottom; 316 | } 317 | 318 | .slide--enter-bottom .direction-reveal__anim--leave { 319 | -webkit-animation-name: slide--leave-top; 320 | animation-name: slide--leave-top; 321 | } 322 | 323 | .slide--enter-left .direction-reveal__anim--enter { 324 | -webkit-animation-name: slide--enter-left; 325 | animation-name: slide--enter-left; 326 | } 327 | 328 | .slide--enter-left .direction-reveal__anim--leave { 329 | -webkit-animation-name: slide--leave-right; 330 | animation-name: slide--leave-right; 331 | } 332 | 333 | .slide--enter-right .direction-reveal__anim--enter { 334 | -webkit-animation-name: slide--enter-right; 335 | animation-name: slide--enter-right; 336 | } 337 | 338 | .slide--enter-right .direction-reveal__anim--leave { 339 | -webkit-animation-name: slide--leave-left; 340 | animation-name: slide--leave-left; 341 | } 342 | 343 | .slide--leave-top .direction-reveal__anim--enter { 344 | -webkit-animation-name: slide--leave-top; 345 | animation-name: slide--leave-top; 346 | } 347 | 348 | .slide--leave-top .direction-reveal__anim--leave { 349 | -webkit-animation-name: slide--enter-bottom; 350 | animation-name: slide--enter-bottom; 351 | } 352 | 353 | .slide--leave-bottom .direction-reveal__anim--enter { 354 | -webkit-animation-name: slide--leave-bottom; 355 | animation-name: slide--leave-bottom; 356 | } 357 | 358 | .slide--leave-bottom .direction-reveal__anim--leave { 359 | -webkit-animation-name: slide--enter-top; 360 | animation-name: slide--enter-top; 361 | } 362 | 363 | .slide--leave-left .direction-reveal__anim--enter { 364 | -webkit-animation-name: slide--leave-left; 365 | animation-name: slide--leave-left; 366 | } 367 | 368 | .slide--leave-left .direction-reveal__anim--leave { 369 | -webkit-animation-name: slide--enter-right; 370 | animation-name: slide--enter-right; 371 | } 372 | 373 | .slide--leave-right .direction-reveal__anim--enter { 374 | -webkit-animation-name: slide--leave-right; 375 | animation-name: slide--leave-right; 376 | } 377 | 378 | .slide--leave-right .direction-reveal__anim--leave { 379 | -webkit-animation-name: slide--enter-left; 380 | animation-name: slide--enter-left; 381 | } 382 | 383 | .direction-reveal [class*='slide--'] .direction-reveal__anim--enter, .direction-reveal [class*='slide--'] .direction-reveal__anim--leave { 384 | transform: translate3d(0, 0, 0); 385 | -webkit-animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 386 | animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 387 | } 388 | 389 | @-webkit-keyframes slide--enter-top { 390 | 0% { 391 | transform: translate3d(0, -100%, 0); 392 | } 393 | } 394 | 395 | @keyframes slide--enter-top { 396 | 0% { 397 | transform: translate3d(0, -100%, 0); 398 | } 399 | } 400 | 401 | @-webkit-keyframes slide--leave-top { 402 | 100% { 403 | transform: translate3d(0, -100%, 0); 404 | } 405 | } 406 | 407 | @keyframes slide--leave-top { 408 | 100% { 409 | transform: translate3d(0, -100%, 0); 410 | } 411 | } 412 | 413 | @-webkit-keyframes slide--enter-bottom { 414 | 0% { 415 | transform: translate3d(0, 100%, 0); 416 | } 417 | } 418 | 419 | @keyframes slide--enter-bottom { 420 | 0% { 421 | transform: translate3d(0, 100%, 0); 422 | } 423 | } 424 | 425 | @-webkit-keyframes slide--leave-bottom { 426 | 100% { 427 | transform: translate3d(0, 100%, 0); 428 | } 429 | } 430 | 431 | @keyframes slide--leave-bottom { 432 | 100% { 433 | transform: translate3d(0, 100%, 0); 434 | } 435 | } 436 | 437 | @-webkit-keyframes slide--enter-left { 438 | 0% { 439 | transform: translate3d(-100%, 0, 0); 440 | } 441 | } 442 | 443 | @keyframes slide--enter-left { 444 | 0% { 445 | transform: translate3d(-100%, 0, 0); 446 | } 447 | } 448 | 449 | @-webkit-keyframes slide--leave-left { 450 | 100% { 451 | transform: translate3d(-100%, 0, 0); 452 | } 453 | } 454 | 455 | @keyframes slide--leave-left { 456 | 100% { 457 | transform: translate3d(-100%, 0, 0); 458 | } 459 | } 460 | 461 | @-webkit-keyframes slide--enter-right { 462 | 0% { 463 | transform: translate3d(100%, 0, 0); 464 | } 465 | } 466 | 467 | @keyframes slide--enter-right { 468 | 0% { 469 | transform: translate3d(100%, 0, 0); 470 | } 471 | } 472 | 473 | @-webkit-keyframes slide--leave-right { 474 | 100% { 475 | transform: translate3d(100%, 0, 0); 476 | } 477 | } 478 | 479 | @keyframes slide--leave-right { 480 | 100% { 481 | transform: translate3d(100%, 0, 0); 482 | } 483 | } 484 | 485 | .roll-out--enter-top .direction-reveal__anim--enter { 486 | -webkit-animation-name: roll-out--enter-top; 487 | animation-name: roll-out--enter-top; 488 | } 489 | 490 | .roll-out--enter-top .direction-reveal__anim--leave { 491 | -webkit-animation-name: roll-out--leave-bottom; 492 | animation-name: roll-out--leave-bottom; 493 | } 494 | 495 | .roll-out--enter-bottom .direction-reveal__anim--enter { 496 | -webkit-animation-name: roll-out--enter-bottom; 497 | animation-name: roll-out--enter-bottom; 498 | } 499 | 500 | .roll-out--enter-bottom .direction-reveal__anim--leave { 501 | -webkit-animation-name: roll-out--leave-top; 502 | animation-name: roll-out--leave-top; 503 | } 504 | 505 | .roll-out--enter-left .direction-reveal__anim--enter { 506 | -webkit-animation-name: roll-out--enter-left; 507 | animation-name: roll-out--enter-left; 508 | } 509 | 510 | .roll-out--enter-left .direction-reveal__anim--leave { 511 | -webkit-animation-name: roll-out--leave-right; 512 | animation-name: roll-out--leave-right; 513 | } 514 | 515 | .roll-out--enter-right .direction-reveal__anim--enter { 516 | -webkit-animation-name: roll-out--enter-right; 517 | animation-name: roll-out--enter-right; 518 | } 519 | 520 | .roll-out--enter-right .direction-reveal__anim--leave { 521 | -webkit-animation-name: roll-out--leave-left; 522 | animation-name: roll-out--leave-left; 523 | } 524 | 525 | .roll-out--leave-top .direction-reveal__anim--enter { 526 | -webkit-animation-name: roll-out--leave-top; 527 | animation-name: roll-out--leave-top; 528 | } 529 | 530 | .roll-out--leave-top .direction-reveal__anim--leave { 531 | -webkit-animation-name: roll-out--enter-bottom; 532 | animation-name: roll-out--enter-bottom; 533 | } 534 | 535 | .roll-out--leave-bottom .direction-reveal__anim--enter { 536 | -webkit-animation-name: roll-out--leave-bottom; 537 | animation-name: roll-out--leave-bottom; 538 | } 539 | 540 | .roll-out--leave-bottom .direction-reveal__anim--leave { 541 | -webkit-animation-name: roll-out--enter-top; 542 | animation-name: roll-out--enter-top; 543 | } 544 | 545 | .roll-out--leave-left .direction-reveal__anim--enter { 546 | -webkit-animation-name: roll-out--leave-left; 547 | animation-name: roll-out--leave-left; 548 | } 549 | 550 | .roll-out--leave-left .direction-reveal__anim--leave { 551 | -webkit-animation-name: roll-out--enter-right; 552 | animation-name: roll-out--enter-right; 553 | } 554 | 555 | .roll-out--leave-right .direction-reveal__anim--enter { 556 | -webkit-animation-name: roll-out--leave-right; 557 | animation-name: roll-out--leave-right; 558 | } 559 | 560 | .roll-out--leave-right .direction-reveal__anim--leave { 561 | -webkit-animation-name: roll-out--enter-left; 562 | animation-name: roll-out--enter-left; 563 | } 564 | 565 | .direction-reveal [class*='roll-out--'] .direction-reveal__anim--enter, .direction-reveal [class*='roll-out--'] .direction-reveal__anim--leave { 566 | transform: translate3d(0, 0, 0); 567 | -webkit-clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); 568 | clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); 569 | -webkit-animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 570 | animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 571 | } 572 | 573 | @-webkit-keyframes roll-out--enter-top { 574 | 0% { 575 | -webkit-clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 576 | clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 577 | } 578 | } 579 | 580 | @keyframes roll-out--enter-top { 581 | 0% { 582 | -webkit-clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 583 | clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 584 | } 585 | } 586 | 587 | @-webkit-keyframes roll-out--leave-top { 588 | 100% { 589 | -webkit-clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 590 | clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 591 | } 592 | } 593 | 594 | @keyframes roll-out--leave-top { 595 | 100% { 596 | -webkit-clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 597 | clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 598 | } 599 | } 600 | 601 | @-webkit-keyframes roll-out--enter-bottom { 602 | 0% { 603 | -webkit-clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 604 | clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 605 | } 606 | } 607 | 608 | @keyframes roll-out--enter-bottom { 609 | 0% { 610 | -webkit-clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 611 | clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 612 | } 613 | } 614 | 615 | @-webkit-keyframes roll-out--leave-bottom { 616 | 100% { 617 | -webkit-clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 618 | clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 619 | } 620 | } 621 | 622 | @keyframes roll-out--leave-bottom { 623 | 100% { 624 | -webkit-clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 625 | clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 626 | } 627 | } 628 | 629 | @-webkit-keyframes roll-out--enter-left { 630 | 0% { 631 | -webkit-clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 632 | clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 633 | } 634 | } 635 | 636 | @keyframes roll-out--enter-left { 637 | 0% { 638 | -webkit-clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 639 | clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 640 | } 641 | } 642 | 643 | @-webkit-keyframes roll-out--leave-left { 644 | 100% { 645 | -webkit-clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 646 | clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 647 | } 648 | } 649 | 650 | @keyframes roll-out--leave-left { 651 | 100% { 652 | -webkit-clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 653 | clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 654 | } 655 | } 656 | 657 | @-webkit-keyframes roll-out--enter-right { 658 | 0% { 659 | -webkit-clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 660 | clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 661 | } 662 | } 663 | 664 | @keyframes roll-out--enter-right { 665 | 0% { 666 | -webkit-clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 667 | clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 668 | } 669 | } 670 | 671 | @-webkit-keyframes roll-out--leave-right { 672 | 100% { 673 | -webkit-clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 674 | clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 675 | } 676 | } 677 | 678 | @keyframes roll-out--leave-right { 679 | 100% { 680 | -webkit-clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 681 | clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 682 | } 683 | } 684 | 685 | .rotate--enter-top .direction-reveal__anim--enter { 686 | -webkit-animation-name: rotate--enter-top; 687 | animation-name: rotate--enter-top; 688 | } 689 | 690 | .rotate--enter-top .direction-reveal__anim--leave { 691 | -webkit-animation-name: rotate--leave-bottom; 692 | animation-name: rotate--leave-bottom; 693 | } 694 | 695 | .rotate--enter-bottom .direction-reveal__anim--enter { 696 | -webkit-animation-name: rotate--enter-bottom; 697 | animation-name: rotate--enter-bottom; 698 | } 699 | 700 | .rotate--enter-bottom .direction-reveal__anim--leave { 701 | -webkit-animation-name: rotate--leave-top; 702 | animation-name: rotate--leave-top; 703 | } 704 | 705 | .rotate--enter-left .direction-reveal__anim--enter { 706 | -webkit-animation-name: rotate--enter-left; 707 | animation-name: rotate--enter-left; 708 | } 709 | 710 | .rotate--enter-left .direction-reveal__anim--leave { 711 | -webkit-animation-name: rotate--leave-right; 712 | animation-name: rotate--leave-right; 713 | } 714 | 715 | .rotate--enter-right .direction-reveal__anim--enter { 716 | -webkit-animation-name: rotate--enter-right; 717 | animation-name: rotate--enter-right; 718 | } 719 | 720 | .rotate--enter-right .direction-reveal__anim--leave { 721 | -webkit-animation-name: rotate--leave-left; 722 | animation-name: rotate--leave-left; 723 | } 724 | 725 | .rotate--leave-top .direction-reveal__anim--enter { 726 | -webkit-animation-name: rotate--leave-top; 727 | animation-name: rotate--leave-top; 728 | } 729 | 730 | .rotate--leave-top .direction-reveal__anim--leave { 731 | -webkit-animation-name: rotate--enter-bottom; 732 | animation-name: rotate--enter-bottom; 733 | } 734 | 735 | .rotate--leave-bottom .direction-reveal__anim--enter { 736 | -webkit-animation-name: rotate--leave-bottom; 737 | animation-name: rotate--leave-bottom; 738 | } 739 | 740 | .rotate--leave-bottom .direction-reveal__anim--leave { 741 | -webkit-animation-name: rotate--enter-top; 742 | animation-name: rotate--enter-top; 743 | } 744 | 745 | .rotate--leave-left .direction-reveal__anim--enter { 746 | -webkit-animation-name: rotate--leave-left; 747 | animation-name: rotate--leave-left; 748 | } 749 | 750 | .rotate--leave-left .direction-reveal__anim--leave { 751 | -webkit-animation-name: rotate--enter-right; 752 | animation-name: rotate--enter-right; 753 | } 754 | 755 | .rotate--leave-right .direction-reveal__anim--enter { 756 | -webkit-animation-name: rotate--leave-right; 757 | animation-name: rotate--leave-right; 758 | } 759 | 760 | .rotate--leave-right .direction-reveal__anim--leave { 761 | -webkit-animation-name: rotate--enter-left; 762 | animation-name: rotate--enter-left; 763 | } 764 | 765 | .direction-reveal [class*='rotate--'] .direction-reveal__anim--enter, .direction-reveal [class*='rotate--'] .direction-reveal__anim--leave { 766 | transform: rotate(0); 767 | -webkit-animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 768 | animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94); 769 | } 770 | 771 | .rotate--enter-top .direction-reveal__anim--enter, .rotate--enter-top .direction-reveal__anim--leave, .rotate--leave-top .direction-reveal__anim--enter, .rotate--leave-top .direction-reveal__anim--leave { 772 | transform-origin: left top; 773 | } 774 | 775 | .rotate--enter-bottom .direction-reveal__anim--enter, .rotate--enter-bottom .direction-reveal__anim--leave, .rotate--leave-bottom .direction-reveal__anim--enter, .rotate--leave-bottom .direction-reveal__anim--leave { 776 | transform-origin: left bottom; 777 | } 778 | 779 | .rotate--enter-left .direction-reveal__anim--enter, .rotate--enter-left .direction-reveal__anim--leave, .rotate--leave-left .direction-reveal__anim--enter, .rotate--leave-left .direction-reveal__anim--leave { 780 | transform-origin: left top; 781 | } 782 | 783 | .rotate--enter-right .direction-reveal__anim--enter, .rotate--enter-right .direction-reveal__anim--leave, .rotate--leave-right .direction-reveal__anim--enter, .rotate--leave-right .direction-reveal__anim--leave { 784 | transform-origin: right top; 785 | } 786 | 787 | @-webkit-keyframes rotate--enter-top { 788 | 0% { 789 | transform: rotate(-90deg); 790 | } 791 | } 792 | 793 | @keyframes rotate--enter-top { 794 | 0% { 795 | transform: rotate(-90deg); 796 | } 797 | } 798 | 799 | @-webkit-keyframes rotate--leave-top { 800 | 100% { 801 | transform: rotate(-90deg); 802 | } 803 | } 804 | 805 | @keyframes rotate--leave-top { 806 | 100% { 807 | transform: rotate(-90deg); 808 | } 809 | } 810 | 811 | @-webkit-keyframes rotate--enter-bottom { 812 | 0% { 813 | transform: rotate(90deg); 814 | } 815 | } 816 | 817 | @keyframes rotate--enter-bottom { 818 | 0% { 819 | transform: rotate(90deg); 820 | } 821 | } 822 | 823 | @-webkit-keyframes rotate--leave-bottom { 824 | 100% { 825 | transform: rotate(90deg); 826 | } 827 | } 828 | 829 | @keyframes rotate--leave-bottom { 830 | 100% { 831 | transform: rotate(90deg); 832 | } 833 | } 834 | 835 | @-webkit-keyframes rotate--enter-left { 836 | 0% { 837 | transform: rotate(90deg); 838 | } 839 | } 840 | 841 | @keyframes rotate--enter-left { 842 | 0% { 843 | transform: rotate(90deg); 844 | } 845 | } 846 | 847 | @-webkit-keyframes rotate--leave-left { 848 | 100% { 849 | transform: rotate(90deg); 850 | } 851 | } 852 | 853 | @keyframes rotate--leave-left { 854 | 100% { 855 | transform: rotate(90deg); 856 | } 857 | } 858 | 859 | @-webkit-keyframes rotate--enter-right { 860 | 0% { 861 | transform: rotate(-90deg); 862 | } 863 | } 864 | 865 | @keyframes rotate--enter-right { 866 | 0% { 867 | transform: rotate(-90deg); 868 | } 869 | } 870 | 871 | @-webkit-keyframes rotate--leave-right { 872 | 100% { 873 | transform: rotate(-90deg); 874 | } 875 | } 876 | 877 | @keyframes rotate--leave-right { 878 | 100% { 879 | transform: rotate(-90deg); 880 | } 881 | } 882 | 883 | .flip--enter-top .direction-reveal__anim--enter { 884 | -webkit-animation-name: flip--enter-top; 885 | animation-name: flip--enter-top; 886 | } 887 | 888 | .flip--enter-top .direction-reveal__anim--leave { 889 | -webkit-animation-name: flip--leave-bottom; 890 | animation-name: flip--leave-bottom; 891 | } 892 | 893 | .flip--enter-bottom .direction-reveal__anim--enter { 894 | -webkit-animation-name: flip--enter-bottom; 895 | animation-name: flip--enter-bottom; 896 | } 897 | 898 | .flip--enter-bottom .direction-reveal__anim--leave { 899 | -webkit-animation-name: flip--leave-top; 900 | animation-name: flip--leave-top; 901 | } 902 | 903 | .flip--enter-left .direction-reveal__anim--enter { 904 | -webkit-animation-name: flip--enter-left; 905 | animation-name: flip--enter-left; 906 | } 907 | 908 | .flip--enter-left .direction-reveal__anim--leave { 909 | -webkit-animation-name: flip--leave-right; 910 | animation-name: flip--leave-right; 911 | } 912 | 913 | .flip--enter-right .direction-reveal__anim--enter { 914 | -webkit-animation-name: flip--enter-right; 915 | animation-name: flip--enter-right; 916 | } 917 | 918 | .flip--enter-right .direction-reveal__anim--leave { 919 | -webkit-animation-name: flip--leave-left; 920 | animation-name: flip--leave-left; 921 | } 922 | 923 | .flip--leave-top .direction-reveal__anim--enter { 924 | -webkit-animation-name: flip--leave-top; 925 | animation-name: flip--leave-top; 926 | } 927 | 928 | .flip--leave-top .direction-reveal__anim--leave { 929 | -webkit-animation-name: flip--enter-bottom; 930 | animation-name: flip--enter-bottom; 931 | } 932 | 933 | .flip--leave-bottom .direction-reveal__anim--enter { 934 | -webkit-animation-name: flip--leave-bottom; 935 | animation-name: flip--leave-bottom; 936 | } 937 | 938 | .flip--leave-bottom .direction-reveal__anim--leave { 939 | -webkit-animation-name: flip--enter-top; 940 | animation-name: flip--enter-top; 941 | } 942 | 943 | .flip--leave-left .direction-reveal__anim--enter { 944 | -webkit-animation-name: flip--leave-left; 945 | animation-name: flip--leave-left; 946 | } 947 | 948 | .flip--leave-left .direction-reveal__anim--leave { 949 | -webkit-animation-name: flip--enter-right; 950 | animation-name: flip--enter-right; 951 | } 952 | 953 | .flip--leave-right .direction-reveal__anim--enter { 954 | -webkit-animation-name: flip--leave-right; 955 | animation-name: flip--leave-right; 956 | } 957 | 958 | .flip--leave-right .direction-reveal__anim--leave { 959 | -webkit-animation-name: flip--enter-left; 960 | animation-name: flip--enter-left; 961 | } 962 | 963 | .direction-reveal [class*='flip--'] { 964 | perspective: 800px; 965 | overflow: visible; 966 | } 967 | 968 | .direction-reveal [class*='flip--'] .direction-reveal__anim--enter, .direction-reveal [class*='flip--'] .direction-reveal__anim--leave { 969 | transform: rotateX(0) rotateY(0); 970 | -webkit-animation-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955); 971 | animation-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955); 972 | -webkit-animation-duration: 1.2s; 973 | animation-duration: 1.2s; 974 | -webkit-backface-visibility: hidden; 975 | backface-visibility: hidden; 976 | z-index: 1; 977 | } 978 | 979 | @-webkit-keyframes flip--enter-top { 980 | 0% { 981 | transform: rotateX(180deg) rotateY(0); 982 | } 983 | } 984 | 985 | @keyframes flip--enter-top { 986 | 0% { 987 | transform: rotateX(180deg) rotateY(0); 988 | } 989 | } 990 | 991 | @-webkit-keyframes flip--leave-top { 992 | 100% { 993 | transform: rotateX(180deg) rotateY(0); 994 | } 995 | } 996 | 997 | @keyframes flip--leave-top { 998 | 100% { 999 | transform: rotateX(180deg) rotateY(0); 1000 | } 1001 | } 1002 | 1003 | @-webkit-keyframes flip--enter-bottom { 1004 | 0% { 1005 | transform: rotateX(-180deg) rotateY(0); 1006 | } 1007 | } 1008 | 1009 | @keyframes flip--enter-bottom { 1010 | 0% { 1011 | transform: rotateX(-180deg) rotateY(0); 1012 | } 1013 | } 1014 | 1015 | @-webkit-keyframes flip--leave-bottom { 1016 | 100% { 1017 | transform: rotateX(-180deg) rotateY(0); 1018 | } 1019 | } 1020 | 1021 | @keyframes flip--leave-bottom { 1022 | 100% { 1023 | transform: rotateX(-180deg) rotateY(0); 1024 | } 1025 | } 1026 | 1027 | @-webkit-keyframes flip--enter-left { 1028 | 0% { 1029 | transform: rotateX(0) rotateY(-180deg); 1030 | } 1031 | } 1032 | 1033 | @keyframes flip--enter-left { 1034 | 0% { 1035 | transform: rotateX(0) rotateY(-180deg); 1036 | } 1037 | } 1038 | 1039 | @-webkit-keyframes flip--leave-left { 1040 | 100% { 1041 | transform: rotateX(0) rotateY(-180deg); 1042 | } 1043 | } 1044 | 1045 | @keyframes flip--leave-left { 1046 | 100% { 1047 | transform: rotateX(0) rotateY(-180deg); 1048 | } 1049 | } 1050 | 1051 | @-webkit-keyframes flip--enter-right { 1052 | 0% { 1053 | transform: rotateX(0) rotateY(180deg); 1054 | } 1055 | } 1056 | 1057 | @keyframes flip--enter-right { 1058 | 0% { 1059 | transform: rotateX(0) rotateY(180deg); 1060 | } 1061 | } 1062 | 1063 | @-webkit-keyframes flip--leave-right { 1064 | 100% { 1065 | transform: rotateX(0) rotateY(180deg); 1066 | } 1067 | } 1068 | 1069 | @keyframes flip--leave-right { 1070 | 100% { 1071 | transform: rotateX(0) rotateY(180deg); 1072 | } 1073 | } 1074 | -------------------------------------------------------------------------------- /src/styles/direction-reveal.scss: -------------------------------------------------------------------------------- 1 | @import 'direction-reveal/direction-reveal-variables'; 2 | @import 'direction-reveal/direction-reveal'; 3 | @import 'direction-reveal/direction-reveal-animations'; 4 | -------------------------------------------------------------------------------- /src/styles/direction-reveal/_direction-reveal-animations.scss: -------------------------------------------------------------------------------- 1 | // Generates classes to assign animations 2 | @mixin animation-classes($animation-name) { 3 | 4 | $directions-list: $anim-postfix-enter $anim-postfix-leave; 5 | $positions-list: top bottom left right; 6 | 7 | $directions-opposite-list: $anim-postfix-leave $anim-postfix-enter; 8 | $positions-opposite-list: bottom top right left; 9 | 10 | @each $direction in $directions-list { 11 | @each $position in $positions-list { 12 | 13 | $class-name: #{$animation-name}--#{$direction}-#{$position}; 14 | 15 | .#{$class-name} { 16 | .direction-reveal__anim--#{$anim-postfix-enter} { 17 | animation-name: #{$class-name}; 18 | } 19 | } 20 | 21 | $direction-opposite: nth($directions-opposite-list, index($directions-list, $direction)); 22 | $position-opposite: nth($positions-opposite-list, index($positions-list, $position)); 23 | $class-name-opposite: #{$animation-name}--#{$direction-opposite}-#{$position-opposite}; 24 | 25 | .#{$class-name} { 26 | .direction-reveal__anim--#{$anim-postfix-leave} { 27 | animation-name: #{$class-name-opposite}; 28 | } 29 | } 30 | 31 | } 32 | } 33 | 34 | } 35 | 36 | 37 | 38 | // Swing animation 39 | 40 | @include animation-classes('swing'); 41 | 42 | // Default visible state after animation - used as start or end points for the keyframe animations 43 | .direction-reveal [class*='swing--'] { 44 | perspective: 400px; 45 | 46 | .direction-reveal__anim--#{$anim-postfix-enter}, .direction-reveal__anim--#{$anim-postfix-leave} { 47 | transform: rotate3d(0, 0, 0, 0); 48 | animation-timing-function: $anim-easing-easeOutCubic; 49 | } 50 | } 51 | 52 | .swing--#{$anim-postfix-enter}-top, .swing--#{$anim-postfix-leave}-top { 53 | perspective-origin: center top; 54 | 55 | .direction-reveal__anim--#{$anim-postfix-enter}, .direction-reveal__anim--#{$anim-postfix-leave} { 56 | transform-origin: center top; 57 | } 58 | } 59 | 60 | .swing--#{$anim-postfix-enter}-bottom, .swing--#{$anim-postfix-leave}-bottom { 61 | perspective-origin: center bottom; 62 | 63 | .direction-reveal__anim--#{$anim-postfix-enter}, .direction-reveal__anim--#{$anim-postfix-leave} { 64 | transform-origin: center bottom; 65 | } 66 | } 67 | 68 | .swing--#{$anim-postfix-enter}-left, .swing--#{$anim-postfix-leave}-left { 69 | perspective-origin: left center; 70 | 71 | .direction-reveal__anim--#{$anim-postfix-enter}, .direction-reveal__anim--#{$anim-postfix-leave} { 72 | transform-origin: left center; 73 | } 74 | } 75 | 76 | .swing--#{$anim-postfix-enter}-right, .swing--#{$anim-postfix-leave}-right { 77 | perspective-origin: right center; 78 | 79 | .direction-reveal__anim--#{$anim-postfix-enter}, .direction-reveal__anim--#{$anim-postfix-leave} { 80 | transform-origin: right center; 81 | } 82 | } 83 | 84 | 85 | @keyframes swing--#{$anim-postfix-enter}-top { 86 | 0% { 87 | transform: rotate3d(-1, 0, 0, 90deg); 88 | } 89 | } 90 | 91 | @keyframes swing--#{$anim-postfix-leave}-top { 92 | 100% { 93 | transform: rotate3d(-1, 0, 0, 90deg); 94 | } 95 | } 96 | 97 | @keyframes swing--#{$anim-postfix-enter}-bottom { 98 | 0% { 99 | transform: rotate3d(1, 0, 0, 90deg); 100 | } 101 | } 102 | 103 | @keyframes swing--#{$anim-postfix-leave}-bottom { 104 | 100% { 105 | transform: rotate3d(1, 0, 0, 90deg); 106 | } 107 | } 108 | 109 | @keyframes swing--#{$anim-postfix-enter}-left { 110 | 0% { 111 | transform: rotate3d(0, 1, 0, 90deg); 112 | } 113 | } 114 | 115 | @keyframes swing--#{$anim-postfix-leave}-left { 116 | 100% { 117 | transform: rotate3d(0, 1, 0, 90deg); 118 | } 119 | } 120 | 121 | @keyframes swing--#{$anim-postfix-enter}-right { 122 | 0% { 123 | transform: rotate3d(0, -1, 0, 90deg); 124 | } 125 | } 126 | 127 | @keyframes swing--#{$anim-postfix-leave}-right { 128 | 100% { 129 | transform: rotate3d(0, -1, 0, 90deg); 130 | } 131 | } 132 | 133 | 134 | 135 | // Slide animation 136 | 137 | @include animation-classes('slide'); 138 | 139 | .direction-reveal [class*='slide--'] { 140 | 141 | .direction-reveal__anim--#{$anim-postfix-enter}, .direction-reveal__anim--#{$anim-postfix-leave} { 142 | transform: translate3d(0, 0, 0); 143 | animation-timing-function: $anim-easing-easeOutQuad; 144 | } 145 | } 146 | 147 | @keyframes slide--#{$anim-postfix-enter}-top { 148 | 0% { 149 | transform: translate3d(0, -100%, 0); 150 | } 151 | } 152 | 153 | @keyframes slide--#{$anim-postfix-leave}-top { 154 | 100% { 155 | transform: translate3d(0, -100%, 0); 156 | } 157 | } 158 | 159 | @keyframes slide--#{$anim-postfix-enter}-bottom { 160 | 0% { 161 | transform: translate3d(0, 100%, 0); 162 | } 163 | } 164 | 165 | @keyframes slide--#{$anim-postfix-leave}-bottom { 166 | 100% { 167 | transform: translate3d(0, 100%, 0); 168 | } 169 | } 170 | 171 | @keyframes slide--#{$anim-postfix-enter}-left { 172 | 0% { 173 | transform: translate3d(-100%, 0, 0); 174 | } 175 | } 176 | 177 | @keyframes slide--#{$anim-postfix-leave}-left { 178 | 100% { 179 | transform: translate3d(-100%, 0, 0); 180 | } 181 | } 182 | 183 | 184 | @keyframes slide--#{$anim-postfix-enter}-right { 185 | 0% { 186 | transform: translate3d(100%, 0, 0); 187 | } 188 | } 189 | 190 | @keyframes slide--#{$anim-postfix-leave}-right { 191 | 100% { 192 | transform: translate3d(100%, 0, 0); 193 | } 194 | } 195 | 196 | 197 | 198 | // Slide from corner 199 | 200 | @include animation-classes('roll-out'); 201 | 202 | .direction-reveal [class*='roll-out--'] { 203 | 204 | .direction-reveal__anim--#{$anim-postfix-enter}, .direction-reveal__anim--#{$anim-postfix-leave} { 205 | transform: translate3d(0, 0, 0); 206 | clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); 207 | animation-timing-function: $anim-easing-easeOutQuad; 208 | } 209 | } 210 | 211 | @keyframes roll-out--#{$anim-postfix-enter}-top { 212 | 0% { 213 | clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 214 | } 215 | } 216 | 217 | @keyframes roll-out--#{$anim-postfix-leave}-top { 218 | 100% { 219 | clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); 220 | } 221 | } 222 | 223 | @keyframes roll-out--#{$anim-postfix-enter}-bottom { 224 | 0% { 225 | clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 226 | } 227 | } 228 | 229 | @keyframes roll-out--#{$anim-postfix-leave}-bottom { 230 | 100% { 231 | clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0 100%); 232 | } 233 | } 234 | 235 | @keyframes roll-out--#{$anim-postfix-enter}-left { 236 | 0% { 237 | clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 238 | } 239 | } 240 | 241 | @keyframes roll-out--#{$anim-postfix-leave}-left { 242 | 100% { 243 | clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); 244 | } 245 | } 246 | 247 | 248 | @keyframes roll-out--#{$anim-postfix-enter}-right { 249 | 0% { 250 | clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 251 | } 252 | } 253 | 254 | @keyframes roll-out--#{$anim-postfix-leave}-right { 255 | 100% { 256 | clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); 257 | } 258 | } 259 | 260 | 261 | 262 | // Rotate animation 263 | 264 | @include animation-classes('rotate'); 265 | 266 | .direction-reveal [class*='rotate--'] { 267 | .direction-reveal__anim--#{$anim-postfix-enter}, .direction-reveal__anim--#{$anim-postfix-leave} { 268 | transform: rotate(0); 269 | animation-timing-function: $anim-easing-easeOutQuad; 270 | } 271 | } 272 | 273 | .rotate--#{$anim-postfix-enter}-top, .rotate--#{$anim-postfix-leave}-top { 274 | .direction-reveal__anim--#{$anim-postfix-enter}, .direction-reveal__anim--#{$anim-postfix-leave} { 275 | transform-origin: left top; 276 | } 277 | } 278 | 279 | .rotate--#{$anim-postfix-enter}-bottom, .rotate--#{$anim-postfix-leave}-bottom { 280 | .direction-reveal__anim--#{$anim-postfix-enter}, .direction-reveal__anim--#{$anim-postfix-leave} { 281 | transform-origin: left bottom; 282 | } 283 | } 284 | 285 | .rotate--#{$anim-postfix-enter}-left, .rotate--#{$anim-postfix-leave}-left { 286 | .direction-reveal__anim--#{$anim-postfix-enter}, .direction-reveal__anim--#{$anim-postfix-leave} { 287 | transform-origin: left top; 288 | } 289 | } 290 | 291 | .rotate--#{$anim-postfix-enter}-right, .rotate--#{$anim-postfix-leave}-right { 292 | .direction-reveal__anim--#{$anim-postfix-enter}, .direction-reveal__anim--#{$anim-postfix-leave} { 293 | transform-origin: right top; 294 | } 295 | } 296 | 297 | @keyframes rotate--#{$anim-postfix-enter}-top { 298 | 0% { 299 | transform: rotate(-90deg); 300 | } 301 | } 302 | 303 | @keyframes rotate--#{$anim-postfix-leave}-top { 304 | 100% { 305 | transform: rotate(-90deg); 306 | } 307 | } 308 | 309 | @keyframes rotate--#{$anim-postfix-enter}-bottom { 310 | 0% { 311 | transform: rotate(90deg); 312 | } 313 | } 314 | 315 | @keyframes rotate--#{$anim-postfix-leave}-bottom { 316 | 100% { 317 | transform: rotate(90deg); 318 | } 319 | } 320 | 321 | @keyframes rotate--#{$anim-postfix-enter}-left { 322 | 0% { 323 | transform: rotate(90deg); 324 | } 325 | } 326 | 327 | @keyframes rotate--#{$anim-postfix-leave}-left { 328 | 100% { 329 | transform: rotate(90deg); 330 | } 331 | } 332 | 333 | @keyframes rotate--#{$anim-postfix-enter}-right { 334 | 0% { 335 | transform: rotate(-90deg); 336 | } 337 | } 338 | 339 | @keyframes rotate--#{$anim-postfix-leave}-right { 340 | 100% { 341 | transform: rotate(-90deg); 342 | } 343 | } 344 | 345 | 346 | 347 | // Flip 348 | 349 | @include animation-classes('flip'); 350 | 351 | .direction-reveal [class*='flip--'] { 352 | perspective: 800px; 353 | overflow: visible; 354 | 355 | .direction-reveal__anim--#{$anim-postfix-enter}, .direction-reveal__anim--#{$anim-postfix-leave} { 356 | transform: rotateX(0) rotateY(0); // Order must be the same as the animations 357 | animation-timing-function: $anim-easing-easeInOutQuad; 358 | animation-duration: $anim-duration*2; 359 | backface-visibility: hidden; 360 | z-index: 1; 361 | } 362 | } 363 | 364 | @keyframes flip--#{$anim-postfix-enter}-top { 365 | 0% { 366 | transform: rotateX(180deg) rotateY(0); 367 | } 368 | } 369 | 370 | @keyframes flip--#{$anim-postfix-leave}-top { 371 | 100% { 372 | transform: rotateX(180deg) rotateY(0); 373 | } 374 | } 375 | 376 | @keyframes flip--#{$anim-postfix-enter}-bottom { 377 | 0% { 378 | transform: rotateX(-180deg) rotateY(0); 379 | } 380 | } 381 | 382 | @keyframes flip--#{$anim-postfix-leave}-bottom { 383 | 100% { 384 | transform: rotateX(-180deg) rotateY(0); 385 | } 386 | } 387 | 388 | @keyframes flip--#{$anim-postfix-enter}-left { 389 | 0% { 390 | transform: rotateX(0) rotateY(-180deg); 391 | } 392 | } 393 | 394 | @keyframes flip--#{$anim-postfix-leave}-left { 395 | 100% { 396 | transform: rotateX(0) rotateY(-180deg); 397 | } 398 | } 399 | 400 | @keyframes flip--#{$anim-postfix-enter}-right { 401 | 0% { 402 | transform: rotateX(0) rotateY(180deg); 403 | } 404 | } 405 | 406 | @keyframes flip--#{$anim-postfix-leave}-right { 407 | 100% { 408 | transform: rotateX(0) rotateY(180deg); 409 | } 410 | } 411 | -------------------------------------------------------------------------------- /src/styles/direction-reveal/_direction-reveal-variables.scss: -------------------------------------------------------------------------------- 1 | $anim-easing-easeOutQuad: cubic-bezier(0.250, 0.460, 0.450, 0.940); 2 | $anim-easing-easeOutCubic: cubic-bezier(0.215, 0.610, 0.355, 1.000); 3 | $anim-easing-easeInOutQuad: cubic-bezier(0.455, 0.030, 0.515, 0.955); 4 | $anim-duration: .6s; 5 | $anim-postfix-enter: 'enter'; 6 | $anim-postfix-leave: 'leave'; 7 | 8 | $overlay-background: rgba(0,0,0,.6); 9 | 10 | $grid-spacing: 8px; 11 | -------------------------------------------------------------------------------- /src/styles/direction-reveal/_direction-reveal.scss: -------------------------------------------------------------------------------- 1 | // ----- Component ----- 2 | .direction-reveal {} 3 | 4 | .direction-reveal__card { 5 | display: inline-block; 6 | position: relative; 7 | overflow: hidden; 8 | } 9 | 10 | .direction-reveal__img { 11 | display: block; 12 | max-width: 100%; 13 | height: auto; 14 | } 15 | 16 | .direction-reveal__overlay { 17 | position: absolute; 18 | top: 0; 19 | left: 0; 20 | width: 100%; 21 | height: 100%; 22 | padding: 16px; 23 | color: #fff; 24 | overflow: hidden; 25 | background-color: $overlay-background; 26 | } 27 | 28 | // .direction-reveal__anim--enter is used to animate in hidden content, the default animation used on the overlay 29 | // .direction-reveal__anim--leave is used to animate out visible content 30 | .direction-reveal__anim--#{$anim-postfix-enter}, .direction-reveal__anim--#{$anim-postfix-leave} { 31 | animation-duration: $anim-duration; 32 | animation-timing-function: $anim-easing-easeOutQuad; 33 | animation-fill-mode: forwards; 34 | } 35 | 36 | .direction-reveal__anim--#{$anim-postfix-enter} { 37 | transform: translate3d(0, -100%, 0); // Initial position before any animation i.e. hidden by default 38 | } 39 | 40 | .direction-reveal__title { 41 | margin-top: 0; 42 | } 43 | 44 | .direction-reveal__text { 45 | margin-bottom: 0; 46 | } 47 | 48 | 49 | 50 | // ----- Layouts ----- 51 | 52 | // 3x3 grid layout - flexbox 53 | .direction-reveal--3-grid { 54 | display: flex; 55 | flex-wrap: wrap; 56 | margin-right: -$grid-spacing; 57 | margin-left: -$grid-spacing; 58 | 59 | .direction-reveal__card { 60 | border: $grid-spacing solid transparent; // Transparent border used for spacing as this makes direction detection more accurate as there is more area to detect. 61 | @media (min-width: 576px) { 62 | flex: 0 0 33.333333%; 63 | max-width: 33.333333%; 64 | } 65 | } 66 | } 67 | 68 | // 3x3 grid layout using CSS grid 69 | .direction-reveal--3-grid-cssgrid { 70 | display: grid; 71 | margin-right: -$grid-spacing; 72 | margin-left: -$grid-spacing; 73 | 74 | @media (min-width: 576px) { 75 | grid-template-columns: repeat(3, 1fr); 76 | } 77 | 78 | .direction-reveal__card { 79 | border: $grid-spacing solid transparent; 80 | } 81 | } 82 | 83 | // Bootstrap grid 84 | .direction-reveal--grid-bootstrap { 85 | .direction-reveal__card { 86 | margin-top: 15px; 87 | margin-bottom: 15px; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/styles/site.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | /* Document 3 | ========================================================================== */ 4 | /** 5 | * 1. Correct the line height in all browsers. 6 | * 2. Prevent adjustments of font size after orientation changes in iOS. 7 | */ 8 | html { 9 | line-height: 1.15; 10 | /* 1 */ 11 | -webkit-text-size-adjust: 100%; 12 | /* 2 */ 13 | } 14 | 15 | /* Sections 16 | ========================================================================== */ 17 | /** 18 | * Remove the margin in all browsers. 19 | */ 20 | body { 21 | margin: 0; 22 | } 23 | 24 | /** 25 | * Render the `main` element consistently in IE. 26 | */ 27 | main { 28 | display: block; 29 | } 30 | 31 | /** 32 | * Correct the font size and margin on `h1` elements within `section` and 33 | * `article` contexts in Chrome, Firefox, and Safari. 34 | */ 35 | h1 { 36 | font-size: 2em; 37 | margin: 0.67em 0; 38 | } 39 | 40 | /* Grouping content 41 | ========================================================================== */ 42 | /** 43 | * 1. Add the correct box sizing in Firefox. 44 | * 2. Show the overflow in Edge and IE. 45 | */ 46 | hr { 47 | box-sizing: content-box; 48 | /* 1 */ 49 | height: 0; 50 | /* 1 */ 51 | overflow: visible; 52 | /* 2 */ 53 | } 54 | 55 | /** 56 | * 1. Correct the inheritance and scaling of font size in all browsers. 57 | * 2. Correct the odd `em` font sizing in all browsers. 58 | */ 59 | pre { 60 | font-family: monospace, monospace; 61 | /* 1 */ 62 | font-size: 1em; 63 | /* 2 */ 64 | } 65 | 66 | /* Text-level semantics 67 | ========================================================================== */ 68 | /** 69 | * Remove the gray background on active links in IE 10. 70 | */ 71 | a { 72 | background-color: transparent; 73 | } 74 | 75 | /** 76 | * 1. Remove the bottom border in Chrome 57- 77 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 78 | */ 79 | abbr[title] { 80 | border-bottom: none; 81 | /* 1 */ 82 | text-decoration: underline; 83 | /* 2 */ 84 | -webkit-text-decoration: underline dotted; 85 | text-decoration: underline dotted; 86 | /* 2 */ 87 | } 88 | 89 | /** 90 | * Add the correct font weight in Chrome, Edge, and Safari. 91 | */ 92 | b, 93 | strong { 94 | font-weight: bolder; 95 | } 96 | 97 | /** 98 | * 1. Correct the inheritance and scaling of font size in all browsers. 99 | * 2. Correct the odd `em` font sizing in all browsers. 100 | */ 101 | code, 102 | kbd, 103 | samp { 104 | font-family: monospace, monospace; 105 | /* 1 */ 106 | font-size: 1em; 107 | /* 2 */ 108 | } 109 | 110 | /** 111 | * Add the correct font size in all browsers. 112 | */ 113 | small { 114 | font-size: 80%; 115 | } 116 | 117 | /** 118 | * Prevent `sub` and `sup` elements from affecting the line height in 119 | * all browsers. 120 | */ 121 | sub, 122 | sup { 123 | font-size: 75%; 124 | line-height: 0; 125 | position: relative; 126 | vertical-align: baseline; 127 | } 128 | 129 | sub { 130 | bottom: -0.25em; 131 | } 132 | 133 | sup { 134 | top: -0.5em; 135 | } 136 | 137 | /* Embedded content 138 | ========================================================================== */ 139 | /** 140 | * Remove the border on images inside links in IE 10. 141 | */ 142 | img { 143 | border-style: none; 144 | } 145 | 146 | /* Forms 147 | ========================================================================== */ 148 | /** 149 | * 1. Change the font styles in all browsers. 150 | * 2. Remove the margin in Firefox and Safari. 151 | */ 152 | button, 153 | input, 154 | optgroup, 155 | select, 156 | textarea { 157 | font-family: inherit; 158 | /* 1 */ 159 | font-size: 100%; 160 | /* 1 */ 161 | line-height: 1.15; 162 | /* 1 */ 163 | margin: 0; 164 | /* 2 */ 165 | } 166 | 167 | /** 168 | * Show the overflow in IE. 169 | * 1. Show the overflow in Edge. 170 | */ 171 | button, 172 | input { 173 | /* 1 */ 174 | overflow: visible; 175 | } 176 | 177 | /** 178 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 179 | * 1. Remove the inheritance of text transform in Firefox. 180 | */ 181 | button, 182 | select { 183 | /* 1 */ 184 | text-transform: none; 185 | } 186 | 187 | /** 188 | * Correct the inability to style clickable types in iOS and Safari. 189 | */ 190 | button, 191 | [type="button"], 192 | [type="reset"], 193 | [type="submit"] { 194 | -webkit-appearance: button; 195 | } 196 | 197 | /** 198 | * Remove the inner border and padding in Firefox. 199 | */ 200 | button::-moz-focus-inner, 201 | [type="button"]::-moz-focus-inner, 202 | [type="reset"]::-moz-focus-inner, 203 | [type="submit"]::-moz-focus-inner { 204 | border-style: none; 205 | padding: 0; 206 | } 207 | 208 | /** 209 | * Restore the focus styles unset by the previous rule. 210 | */ 211 | button:-moz-focusring, 212 | [type="button"]:-moz-focusring, 213 | [type="reset"]:-moz-focusring, 214 | [type="submit"]:-moz-focusring { 215 | outline: 1px dotted ButtonText; 216 | } 217 | 218 | /** 219 | * Correct the padding in Firefox. 220 | */ 221 | fieldset { 222 | padding: 0.35em 0.75em 0.625em; 223 | } 224 | 225 | /** 226 | * 1. Correct the text wrapping in Edge and IE. 227 | * 2. Correct the color inheritance from `fieldset` elements in IE. 228 | * 3. Remove the padding so developers are not caught out when they zero out 229 | * `fieldset` elements in all browsers. 230 | */ 231 | legend { 232 | box-sizing: border-box; 233 | /* 1 */ 234 | color: inherit; 235 | /* 2 */ 236 | display: table; 237 | /* 1 */ 238 | max-width: 100%; 239 | /* 1 */ 240 | padding: 0; 241 | /* 3 */ 242 | white-space: normal; 243 | /* 1 */ 244 | } 245 | 246 | /** 247 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 248 | */ 249 | progress { 250 | vertical-align: baseline; 251 | } 252 | 253 | /** 254 | * Remove the default vertical scrollbar in IE 10+. 255 | */ 256 | textarea { 257 | overflow: auto; 258 | } 259 | 260 | /** 261 | * 1. Add the correct box sizing in IE 10. 262 | * 2. Remove the padding in IE 10. 263 | */ 264 | [type="checkbox"], 265 | [type="radio"] { 266 | box-sizing: border-box; 267 | /* 1 */ 268 | padding: 0; 269 | /* 2 */ 270 | } 271 | 272 | /** 273 | * Correct the cursor style of increment and decrement buttons in Chrome. 274 | */ 275 | [type="number"]::-webkit-inner-spin-button, 276 | [type="number"]::-webkit-outer-spin-button { 277 | height: auto; 278 | } 279 | 280 | /** 281 | * 1. Correct the odd appearance in Chrome and Safari. 282 | * 2. Correct the outline style in Safari. 283 | */ 284 | [type="search"] { 285 | -webkit-appearance: textfield; 286 | /* 1 */ 287 | outline-offset: -2px; 288 | /* 2 */ 289 | } 290 | 291 | /** 292 | * Remove the inner padding in Chrome and Safari on macOS. 293 | */ 294 | [type="search"]::-webkit-search-decoration { 295 | -webkit-appearance: none; 296 | } 297 | 298 | /** 299 | * 1. Correct the inability to style clickable types in iOS and Safari. 300 | * 2. Change font properties to `inherit` in Safari. 301 | */ 302 | ::-webkit-file-upload-button { 303 | -webkit-appearance: button; 304 | /* 1 */ 305 | font: inherit; 306 | /* 2 */ 307 | } 308 | 309 | /* Interactive 310 | ========================================================================== */ 311 | /* 312 | * Add the correct display in Edge, IE 10+, and Firefox. 313 | */ 314 | details { 315 | display: block; 316 | } 317 | 318 | /* 319 | * Add the correct display in all browsers. 320 | */ 321 | summary { 322 | display: list-item; 323 | } 324 | 325 | /* Misc 326 | ========================================================================== */ 327 | /** 328 | * Add the correct display in IE 10+. 329 | */ 330 | template { 331 | display: none; 332 | } 333 | 334 | /** 335 | * Add the correct display in IE 10. 336 | */ 337 | [hidden] { 338 | display: none; 339 | } 340 | 341 | *, 342 | *:before, 343 | *:after { 344 | box-sizing: border-box; 345 | } 346 | 347 | body { 348 | font-family: "Open Sans", -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; 349 | font-size: 1rem; 350 | font-weight: 400; 351 | line-height: 1.5; 352 | color: #333; 353 | background-color: #fff; 354 | } 355 | 356 | a { 357 | color: #1976D2; 358 | text-decoration: none; 359 | transition: color 0.3s; 360 | } 361 | 362 | a:hover, a:focus { 363 | color: #2196F3; 364 | } 365 | 366 | h1, h2, h3, h4, h5, h6 { 367 | line-height: 1.2; 368 | font-weight: 300; 369 | } 370 | 371 | h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { 372 | font-size: 60%; 373 | } 374 | 375 | h1, h2 { 376 | margin: 0 0 1.5rem 0; 377 | } 378 | 379 | h3 { 380 | margin: 0 0 1rem 0; 381 | } 382 | 383 | h4, h5, h6 { 384 | margin: 0 0 .5rem 0; 385 | font-weight: 400; 386 | } 387 | 388 | h1 { 389 | font-size: 2.5rem; 390 | } 391 | 392 | h2 { 393 | font-size: 2rem; 394 | } 395 | 396 | h3 { 397 | font-size: 1.75rem; 398 | } 399 | 400 | h4 { 401 | font-size: 1.5rem; 402 | } 403 | 404 | h5 { 405 | font-size: 1.25rem; 406 | } 407 | 408 | h6 { 409 | font-size: 1rem; 410 | } 411 | 412 | p { 413 | margin: 0 0 1rem 0; 414 | } 415 | 416 | strong, th { 417 | font-weight: 800; 418 | } 419 | 420 | pre, code { 421 | font-family: 'Cutive Mono', monospace; 422 | background-color: #f3f3f3; 423 | } 424 | 425 | pre { 426 | display: block; 427 | padding: .75rem 1rem; 428 | margin: 0 0 2rem 0; 429 | overflow: auto; 430 | font-size: 1rem; 431 | word-break: break-all; 432 | word-wrap: break-word; 433 | border: 1px solid #bbb; 434 | border-radius: 4px; 435 | } 436 | 437 | .table-wrapper { 438 | display: block; 439 | width: 100%; 440 | overflow-x: auto; 441 | } 442 | 443 | .table { 444 | width: 100%; 445 | margin: 0 0 1.5rem 0; 446 | border-collapse: collapse; 447 | } 448 | 449 | .table th, .table td { 450 | padding: .25rem .5rem; 451 | text-align: left; 452 | vertical-align: top; 453 | border: 1px solid #ddd; 454 | } 455 | 456 | .img-fluid { 457 | max-width: 100%; 458 | height: auto; 459 | } 460 | 461 | .fullwidth .container { 462 | width: 100%; 463 | padding-top: 2rem; 464 | padding-bottom: 2rem; 465 | } 466 | 467 | .fullwidth--sm .container { 468 | padding-top: 1rem; 469 | padding-bottom: 1rem; 470 | } 471 | 472 | .container { 473 | width: 100%; 474 | max-width: 1140px; 475 | margin: 0 auto; 476 | padding-left: 1rem; 477 | padding-right: 1rem; 478 | } 479 | 480 | .subsection { 481 | margin: 0 0 2.5rem 0; 482 | } 483 | 484 | .separator { 485 | border-bottom: 1px solid #ddd; 486 | } 487 | 488 | .header { 489 | position: -webkit-sticky; 490 | position: sticky; 491 | top: 0; 492 | z-index: 100; 493 | width: 100%; 494 | padding: .5rem 0; 495 | color: #fff; 496 | background-color: #1976D2; 497 | box-shadow: 0 0 6px 6px rgba(0, 0, 0, 0.1); 498 | } 499 | 500 | @media (min-width: 768px) { 501 | .header { 502 | padding: 1rem 0; 503 | } 504 | } 505 | 506 | .header .container { 507 | display: flex; 508 | } 509 | 510 | @media (max-width: 575px) { 511 | .header .container { 512 | flex-flow: column; 513 | } 514 | } 515 | 516 | @media (min-width: 576px) { 517 | .header .container { 518 | align-items: center; 519 | } 520 | } 521 | 522 | .logo-text { 523 | margin: 0; 524 | } 525 | 526 | @media (max-width: 767px) { 527 | .logo-text { 528 | font-size: 1.75rem; 529 | } 530 | } 531 | 532 | .header-links .btn-demo { 533 | margin: .5rem 0; 534 | } 535 | 536 | .header-links .btn-demo:last-child { 537 | margin-left: .5rem; 538 | } 539 | 540 | @media (min-width: 576px) { 541 | .header-links { 542 | margin-left: auto; 543 | } 544 | } 545 | 546 | .footer { 547 | text-align: center; 548 | } 549 | 550 | .btn-demo { 551 | display: inline-block; 552 | text-align: center; 553 | vertical-align: middle; 554 | cursor: pointer; 555 | border: 1px solid #bbb; 556 | padding: 4px 12px 6px 12px; 557 | font-size: 18px; 558 | background-color: #fff; 559 | border-radius: 4px; 560 | transition: background-color .3s, color .3s; 561 | } 562 | 563 | .btn-demo:hover, .btn-demo:focus, .btn-demo:active { 564 | background-color: #f3f3f3; 565 | } 566 | 567 | .btn-demo--white { 568 | color: #fff; 569 | border-color: #fff; 570 | background-color: transparent; 571 | } 572 | 573 | .m-0 { 574 | margin: 0; 575 | } 576 | 577 | .row { 578 | display: flex; 579 | flex-wrap: wrap; 580 | margin-right: -15px; 581 | margin-left: -15px; 582 | } 583 | 584 | .col-sm-4 { 585 | position: relative; 586 | width: 100%; 587 | padding-left: 15px; 588 | padding-right: 15px; 589 | min-height: 1px; 590 | } 591 | 592 | @media (min-width: 576px) { 593 | .col-sm-4 { 594 | flex: 0 0 33.333333%; 595 | max-width: 33.333333%; 596 | } 597 | } 598 | -------------------------------------------------------------------------------- /src/styles/site.scss: -------------------------------------------------------------------------------- 1 | @import "site/normalize.scss"; 2 | @import "site/site.scss"; 3 | -------------------------------------------------------------------------------- /src/styles/site/_normalize.scss: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; /* 1 */ 13 | -webkit-text-size-adjust: 100%; /* 2 */ 14 | } 15 | 16 | /* Sections 17 | ========================================================================== */ 18 | 19 | /** 20 | * Remove the margin in all browsers. 21 | */ 22 | 23 | body { 24 | margin: 0; 25 | } 26 | 27 | /** 28 | * Render the `main` element consistently in IE. 29 | */ 30 | 31 | main { 32 | display: block; 33 | } 34 | 35 | /** 36 | * Correct the font size and margin on `h1` elements within `section` and 37 | * `article` contexts in Chrome, Firefox, and Safari. 38 | */ 39 | 40 | h1 { 41 | font-size: 2em; 42 | margin: 0.67em 0; 43 | } 44 | 45 | /* Grouping content 46 | ========================================================================== */ 47 | 48 | /** 49 | * 1. Add the correct box sizing in Firefox. 50 | * 2. Show the overflow in Edge and IE. 51 | */ 52 | 53 | hr { 54 | box-sizing: content-box; /* 1 */ 55 | height: 0; /* 1 */ 56 | overflow: visible; /* 2 */ 57 | } 58 | 59 | /** 60 | * 1. Correct the inheritance and scaling of font size in all browsers. 61 | * 2. Correct the odd `em` font sizing in all browsers. 62 | */ 63 | 64 | pre { 65 | font-family: monospace, monospace; /* 1 */ 66 | font-size: 1em; /* 2 */ 67 | } 68 | 69 | /* Text-level semantics 70 | ========================================================================== */ 71 | 72 | /** 73 | * Remove the gray background on active links in IE 10. 74 | */ 75 | 76 | a { 77 | background-color: transparent; 78 | } 79 | 80 | /** 81 | * 1. Remove the bottom border in Chrome 57- 82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 83 | */ 84 | 85 | abbr[title] { 86 | border-bottom: none; /* 1 */ 87 | text-decoration: underline; /* 2 */ 88 | text-decoration: underline dotted; /* 2 */ 89 | } 90 | 91 | /** 92 | * Add the correct font weight in Chrome, Edge, and Safari. 93 | */ 94 | 95 | b, 96 | strong { 97 | font-weight: bolder; 98 | } 99 | 100 | /** 101 | * 1. Correct the inheritance and scaling of font size in all browsers. 102 | * 2. Correct the odd `em` font sizing in all browsers. 103 | */ 104 | 105 | code, 106 | kbd, 107 | samp { 108 | font-family: monospace, monospace; /* 1 */ 109 | font-size: 1em; /* 2 */ 110 | } 111 | 112 | /** 113 | * Add the correct font size in all browsers. 114 | */ 115 | 116 | small { 117 | font-size: 80%; 118 | } 119 | 120 | /** 121 | * Prevent `sub` and `sup` elements from affecting the line height in 122 | * all browsers. 123 | */ 124 | 125 | sub, 126 | sup { 127 | font-size: 75%; 128 | line-height: 0; 129 | position: relative; 130 | vertical-align: baseline; 131 | } 132 | 133 | sub { 134 | bottom: -0.25em; 135 | } 136 | 137 | sup { 138 | top: -0.5em; 139 | } 140 | 141 | /* Embedded content 142 | ========================================================================== */ 143 | 144 | /** 145 | * Remove the border on images inside links in IE 10. 146 | */ 147 | 148 | img { 149 | border-style: none; 150 | } 151 | 152 | /* Forms 153 | ========================================================================== */ 154 | 155 | /** 156 | * 1. Change the font styles in all browsers. 157 | * 2. Remove the margin in Firefox and Safari. 158 | */ 159 | 160 | button, 161 | input, 162 | optgroup, 163 | select, 164 | textarea { 165 | font-family: inherit; /* 1 */ 166 | font-size: 100%; /* 1 */ 167 | line-height: 1.15; /* 1 */ 168 | margin: 0; /* 2 */ 169 | } 170 | 171 | /** 172 | * Show the overflow in IE. 173 | * 1. Show the overflow in Edge. 174 | */ 175 | 176 | button, 177 | input { /* 1 */ 178 | overflow: visible; 179 | } 180 | 181 | /** 182 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 183 | * 1. Remove the inheritance of text transform in Firefox. 184 | */ 185 | 186 | button, 187 | select { /* 1 */ 188 | text-transform: none; 189 | } 190 | 191 | /** 192 | * Correct the inability to style clickable types in iOS and Safari. 193 | */ 194 | 195 | button, 196 | [type="button"], 197 | [type="reset"], 198 | [type="submit"] { 199 | -webkit-appearance: button; 200 | } 201 | 202 | /** 203 | * Remove the inner border and padding in Firefox. 204 | */ 205 | 206 | button::-moz-focus-inner, 207 | [type="button"]::-moz-focus-inner, 208 | [type="reset"]::-moz-focus-inner, 209 | [type="submit"]::-moz-focus-inner { 210 | border-style: none; 211 | padding: 0; 212 | } 213 | 214 | /** 215 | * Restore the focus styles unset by the previous rule. 216 | */ 217 | 218 | button:-moz-focusring, 219 | [type="button"]:-moz-focusring, 220 | [type="reset"]:-moz-focusring, 221 | [type="submit"]:-moz-focusring { 222 | outline: 1px dotted ButtonText; 223 | } 224 | 225 | /** 226 | * Correct the padding in Firefox. 227 | */ 228 | 229 | fieldset { 230 | padding: 0.35em 0.75em 0.625em; 231 | } 232 | 233 | /** 234 | * 1. Correct the text wrapping in Edge and IE. 235 | * 2. Correct the color inheritance from `fieldset` elements in IE. 236 | * 3. Remove the padding so developers are not caught out when they zero out 237 | * `fieldset` elements in all browsers. 238 | */ 239 | 240 | legend { 241 | box-sizing: border-box; /* 1 */ 242 | color: inherit; /* 2 */ 243 | display: table; /* 1 */ 244 | max-width: 100%; /* 1 */ 245 | padding: 0; /* 3 */ 246 | white-space: normal; /* 1 */ 247 | } 248 | 249 | /** 250 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 251 | */ 252 | 253 | progress { 254 | vertical-align: baseline; 255 | } 256 | 257 | /** 258 | * Remove the default vertical scrollbar in IE 10+. 259 | */ 260 | 261 | textarea { 262 | overflow: auto; 263 | } 264 | 265 | /** 266 | * 1. Add the correct box sizing in IE 10. 267 | * 2. Remove the padding in IE 10. 268 | */ 269 | 270 | [type="checkbox"], 271 | [type="radio"] { 272 | box-sizing: border-box; /* 1 */ 273 | padding: 0; /* 2 */ 274 | } 275 | 276 | /** 277 | * Correct the cursor style of increment and decrement buttons in Chrome. 278 | */ 279 | 280 | [type="number"]::-webkit-inner-spin-button, 281 | [type="number"]::-webkit-outer-spin-button { 282 | height: auto; 283 | } 284 | 285 | /** 286 | * 1. Correct the odd appearance in Chrome and Safari. 287 | * 2. Correct the outline style in Safari. 288 | */ 289 | 290 | [type="search"] { 291 | -webkit-appearance: textfield; /* 1 */ 292 | outline-offset: -2px; /* 2 */ 293 | } 294 | 295 | /** 296 | * Remove the inner padding in Chrome and Safari on macOS. 297 | */ 298 | 299 | [type="search"]::-webkit-search-decoration { 300 | -webkit-appearance: none; 301 | } 302 | 303 | /** 304 | * 1. Correct the inability to style clickable types in iOS and Safari. 305 | * 2. Change font properties to `inherit` in Safari. 306 | */ 307 | 308 | ::-webkit-file-upload-button { 309 | -webkit-appearance: button; /* 1 */ 310 | font: inherit; /* 2 */ 311 | } 312 | 313 | /* Interactive 314 | ========================================================================== */ 315 | 316 | /* 317 | * Add the correct display in Edge, IE 10+, and Firefox. 318 | */ 319 | 320 | details { 321 | display: block; 322 | } 323 | 324 | /* 325 | * Add the correct display in all browsers. 326 | */ 327 | 328 | summary { 329 | display: list-item; 330 | } 331 | 332 | /* Misc 333 | ========================================================================== */ 334 | 335 | /** 336 | * Add the correct display in IE 10+. 337 | */ 338 | 339 | template { 340 | display: none; 341 | } 342 | 343 | /** 344 | * Add the correct display in IE 10. 345 | */ 346 | 347 | [hidden] { 348 | display: none; 349 | } 350 | -------------------------------------------------------------------------------- /src/styles/site/_site.scss: -------------------------------------------------------------------------------- 1 | // ----- Base styles ----- 2 | 3 | *, 4 | *:before, 5 | *:after { 6 | box-sizing: border-box; 7 | } 8 | 9 | body { 10 | font-family: "Open Sans", -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";; 11 | font-size: 1rem; 12 | font-weight: 400; 13 | line-height: 1.5; 14 | color: #333; 15 | background-color: #fff; 16 | } 17 | 18 | 19 | a { 20 | color: #1976D2; 21 | text-decoration: none; 22 | transition: color 0.3s; 23 | 24 | &:hover, &:focus { 25 | color: #2196F3; 26 | } 27 | } 28 | 29 | 30 | h1, h2, h3, h4, h5, h6 { 31 | line-height: 1.2; 32 | font-weight: 300; 33 | 34 | small { 35 | font-size: 60%; 36 | } 37 | } 38 | 39 | h1, h2 { 40 | margin: 0 0 1.5rem 0; 41 | } 42 | 43 | h3 { 44 | margin: 0 0 1rem 0; 45 | } 46 | 47 | h4, h5, h6 { 48 | margin: 0 0 .5rem 0; 49 | font-weight: 400; 50 | } 51 | 52 | h1 { font-size: 2.5rem; } 53 | h2 { font-size: 2rem; } 54 | h3 { font-size: 1.75rem; } 55 | h4 { font-size: 1.5rem; } 56 | h5 { font-size: 1.25rem; } 57 | h6 { font-size: 1rem; } 58 | 59 | p { 60 | margin: 0 0 1rem 0; 61 | } 62 | 63 | strong, th { 64 | font-weight: 800; 65 | } 66 | 67 | pre, code { 68 | font-family: 'Cutive Mono', monospace; 69 | background-color: #f3f3f3; 70 | } 71 | 72 | pre { 73 | display: block; 74 | padding: .75rem 1rem; 75 | margin: 0 0 2rem 0; 76 | overflow: auto; 77 | font-size: 1rem; 78 | word-break: break-all; 79 | word-wrap: break-word; 80 | border: 1px solid #bbb; 81 | border-radius: 4px; 82 | } 83 | 84 | 85 | .table-wrapper { 86 | display: block; 87 | width: 100%; 88 | overflow-x: auto; 89 | } 90 | 91 | .table { 92 | width: 100%; 93 | margin: 0 0 1.5rem 0; 94 | border-collapse: collapse; 95 | 96 | th, td { 97 | padding: .25rem .5rem; 98 | text-align: left; 99 | vertical-align: top; 100 | border: 1px solid #ddd; 101 | } 102 | } 103 | 104 | 105 | .img-fluid { 106 | max-width: 100%; 107 | height: auto; 108 | } 109 | 110 | 111 | 112 | // ----- Layout ----- 113 | .fullwidth { 114 | .container { 115 | width: 100%; 116 | padding-top: 2rem; 117 | padding-bottom: 2rem; 118 | } 119 | } 120 | 121 | .fullwidth--sm { 122 | .container { 123 | padding-top: 1rem; 124 | padding-bottom: 1rem; 125 | } 126 | } 127 | 128 | .container { 129 | width: 100%; 130 | max-width: 1140px; 131 | margin: 0 auto; 132 | padding-left: 1rem; 133 | padding-right: 1rem; 134 | } 135 | 136 | .subsection { 137 | margin: 0 0 2.5rem 0; 138 | } 139 | 140 | 141 | .separator { 142 | border-bottom: 1px solid #ddd; 143 | } 144 | 145 | 146 | 147 | // ----- Header and footer ----- 148 | .header { 149 | position: sticky; 150 | top: 0; 151 | z-index: 100; 152 | width: 100%; 153 | padding: .5rem 0; 154 | color: #fff; 155 | background-color: #1976D2; 156 | box-shadow: 0 0 6px 6px rgba(0,0,0,0.1); 157 | 158 | @media (min-width: 768px) { 159 | padding: 1rem 0; 160 | } 161 | 162 | .container { 163 | display: flex; 164 | 165 | @media (max-width: 575px) { 166 | flex-flow: column; 167 | } 168 | 169 | @media (min-width: 576px) { 170 | align-items: center; 171 | } 172 | } 173 | 174 | } 175 | 176 | .logo-text { 177 | margin: 0; 178 | 179 | @media (max-width: 767px) { 180 | font-size: 1.75rem; 181 | } 182 | } 183 | 184 | .header-links { 185 | 186 | .btn-demo { 187 | margin: .5rem 0; 188 | 189 | &:last-child { 190 | margin-left: .5rem; 191 | } 192 | } 193 | 194 | @media (min-width: 576px) { 195 | margin-left: auto; 196 | } 197 | } 198 | 199 | 200 | .footer { 201 | text-align: center; 202 | } 203 | 204 | 205 | 206 | // ----- Buttons ----- 207 | .btn-demo { 208 | display: inline-block; 209 | text-align: center; 210 | vertical-align: middle; 211 | cursor: pointer; 212 | border: 1px solid #bbb; 213 | padding: 4px 12px 6px 12px; 214 | font-size: 18px; 215 | background-color: #fff; 216 | border-radius: 4px; 217 | transition: background-color .3s, color .3s; 218 | 219 | &:hover, &:focus, &:active { 220 | background-color: #f3f3f3; 221 | } 222 | } 223 | 224 | .btn-demo--white { 225 | color: #fff; 226 | border-color: #fff; 227 | background-color: transparent; 228 | } 229 | 230 | 231 | 232 | // ----- Utilities ----- 233 | .m-0 { 234 | margin: 0; 235 | } 236 | 237 | 238 | // Bootstrap grid example 239 | .row { 240 | display: flex; 241 | flex-wrap: wrap; 242 | margin-right: -15px; 243 | margin-left: -15px; 244 | } 245 | 246 | .col-sm-4 { 247 | position: relative; 248 | width: 100%; 249 | padding-left: 15px; 250 | padding-right: 15px; 251 | min-height: 1px; 252 | } 253 | 254 | @media (min-width: 576px) { 255 | .col-sm-4 { 256 | flex: 0 0 33.333333%; 257 | max-width: 33.333333%; 258 | } 259 | } 260 | --------------------------------------------------------------------------------