├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── app ├── assets │ └── img │ │ ├── activitiy-icon.svg │ │ ├── add.svg │ │ ├── announcement-white.svg │ │ ├── announcement.svg │ │ ├── carrot.svg │ │ ├── catalog-menu.svg │ │ ├── catalog.svg │ │ ├── chat.svg │ │ ├── configure.svg │ │ ├── content.png │ │ ├── download.svg │ │ ├── edited.svg │ │ ├── folder.svg │ │ ├── gears-menu.svg │ │ ├── hamburger.svg │ │ ├── loader-animated.svg │ │ ├── loader.svg │ │ ├── motion-components │ │ ├── motion-drawer.png │ │ ├── motion-dropdown.png │ │ ├── motion-horizontal.png │ │ ├── motion-popout.png │ │ ├── motion-search.png │ │ └── motion-vertical.png │ │ ├── people.svg │ │ ├── popout.svg │ │ ├── repository.svg │ │ ├── save.svg │ │ ├── search-white.svg │ │ ├── search.svg │ │ ├── services.svg │ │ ├── settings-gear.svg │ │ ├── shared.svg │ │ ├── single-doc.svg │ │ ├── small-directory.svg │ │ ├── squares.svg │ │ ├── upload.svg │ │ ├── user.svg │ │ └── x.svg └── src │ ├── drawer │ ├── drawer.js │ ├── index.html │ └── main-drawer.scss │ ├── drop-down │ ├── drop-down.js │ ├── index.html │ └── main-dropdown.scss │ ├── index.html │ ├── lib │ ├── scripts │ │ └── utils │ │ │ ├── add-multiple-listeners.js │ │ │ ├── components │ │ │ ├── components.js │ │ │ ├── menu.js │ │ │ ├── scrollable.js │ │ │ └── search-data-object.js │ │ │ ├── enumerator.js │ │ │ ├── is-descendant.js │ │ │ ├── keys.js │ │ │ ├── select.js │ │ │ ├── swap-class.js │ │ │ ├── timeout.js │ │ │ └── utils.js │ └── styles │ │ ├── main.scss │ │ ├── pages │ │ └── index.scss │ │ └── partials │ │ ├── common.scss │ │ ├── mixins.scss │ │ └── variables.scss │ ├── pop-up │ ├── index.html │ ├── main-popup.scss │ └── pop-up.js │ ├── search │ ├── index.html │ ├── main-search.scss │ └── search.js │ ├── side-nav │ ├── index.html │ ├── main-sidenav.scss │ └── side-nav.js │ └── top-nav │ ├── index.html │ ├── main-topnav.scss │ └── top-nav.js ├── gulpfile.js ├── package.json └── scss-lint.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Deployed apps should consider commenting this line out: 24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 25 | node_modules 26 | .DS_Store 27 | .vagrant 28 | Vagrantfile 29 | Dockerfile 30 | npm-debug.log 31 | 32 | app/dist 33 | 34 | ### Vim ### 35 | [._]*.s[a-w][a-z] 36 | [._]s[a-w][a-z] 37 | *.un~ 38 | Session.vim 39 | .netrwhist 40 | *~ 41 | 42 | 43 | ### Sass ### 44 | .sass-cache 45 | *.css.map 46 | 47 | 48 | ### SublimeText ### 49 | # cache files for sublime text 50 | *.tmlanguage.cache 51 | *.tmPreferences.cache 52 | *.stTheme.cache 53 | 54 | # workspace files are user-specific 55 | *.sublime-workspace 56 | 57 | # project files should be checked into the repository, unless a significant 58 | # proportion of contributors will probably not be using SublimeText 59 | # *.sublime-project 60 | 61 | # sftp configuration file 62 | sftp-config.json 63 | 64 | 65 | ### OSX ### 66 | .DS_Store 67 | .AppleDouble 68 | .LSOverride 69 | 70 | # Icon must end with two \r 71 | Icon 72 | 73 | 74 | # Thumbnails 75 | ._* 76 | 77 | # Files that might appear on external disk 78 | .Spotlight-V100 79 | .Trashes 80 | 81 | # Directories potentially created on remote AFP share 82 | .AppleDB 83 | .AppleDesktop 84 | Network Trash Folder 85 | Temporary Items 86 | .apdisk 87 | 88 | # Eclipse files 89 | .project 90 | .settings 91 | 92 | # Brackets files 93 | .brackets.json 94 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "expr" : true, 3 | "bitwise" : true, 4 | "browser" : true, 5 | "camelcase" : true, 6 | "curly" : true, 7 | "eqeqeq" : true, 8 | "es3" : false, 9 | "forin" : false, 10 | "immed" : true, 11 | "indent" : 2, 12 | "latedef" : true, 13 | "maxparams" : 5, 14 | "maxdepth" : 3, 15 | "maxstatements" : false, 16 | "maxlen" : 120, 17 | "newcap" : true, 18 | "noarg" : true, 19 | "node" : true, 20 | "noempty" : true, 21 | "nonew" : true, 22 | "plusplus" : true, 23 | "quotmark" : "single", 24 | "undef" : true, 25 | "unused" : true, 26 | "strict" : false, 27 | "trailing" : true 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 IBM 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IBM Web Animation 2 | - [Code License][code-license] 3 | - [Getting Started][getting-started] 4 | - [Requirements][reqs] 5 | - [Installation][install] 6 | - [Find your way around][navigate] 7 | - [Example][example] 8 | - [Library][lib] 9 | - [Use in your own project][use] 10 | 11 | As you pull the code from this repository, get inspired by the IBM Design Language animation guidelines. Remember: thoughtfully applied animation should be straightforward, limited to the most important interactions on the screen and helping users in delightful ways as they interact. 12 | 13 | — [IBM Design Language: Animation][animation] 14 | 15 | This repository serves as a **codebase for developers** who want to use, prototype with, or get inspired by the machine motion style from the IBM Design Language. It contains **six unique examples**, each demonstrating the movement of a single component. The **source code and full working example** for each component are available. 16 | 17 | ## Code License 18 | [Apache License 2.0][apache] 19 | 20 | ## Getting Started 21 | 22 | ### Requirements 23 | - A modern browser 24 | - [Node.js][node] (to build the examples) 25 | - Ruby gem scss-lint 26 | 27 | ### Installation 28 | 29 | **Prerequisites** 30 | ```sh 31 | npm install -g gulp 32 | gem install scss-lint 33 | ``` 34 | 35 | **Build and run** 36 | ```sh 37 | git clone https://github.com/IBM-Design/web-animations.git 38 | npm install && npm start 39 | ``` 40 | ## Find your way around 41 | Source files for each example can be found in `app -> src -> `. Each component contains a `.js`, `.scss`, and `.html` file, which is the complete module for that given example. 42 | 43 | ### Example 44 | 45 | **Source (this is where you see the code)** 46 | ``` 47 | app/ 48 | |__ src/ 49 | |__ drawer/ 50 | |__ drawer.js 51 | |__ index.html 52 | |__ main-drawer.scss 53 | ``` 54 | 55 | `npm start` is an alias for just running `gulp` in the command line. [Gulp][gulp] will simply compile the `.scss` file into `.css`, run some linters, and move the three source files into the `dist` folder: 56 | 57 | **Distribution (this is where you see the working example)** 58 | ``` 59 | app/ 60 | |__ dist/ 61 | |__ drawer/ 62 | |__ drawer.js 63 | |__ index.html 64 | |__ drawer.css 65 | ``` 66 | A web server will open in your default browser, with a base URL of `app/dist`: 67 | ```javascript 68 | browserSync.init({ 69 | server: { 70 | baseDir: 'app/dist' // makes the distribution folder the base directory 71 | } 72 | // ... 73 | ``` 74 | 75 | # Library 76 | 77 | Helper functions and global CSS stylesheets are used throughout this project to prevent you from repeating things like `document.getElementById` and basic stylings. If you open up a component's `.html` file, it will contain something like `.` 78 | 79 | ``` 80 | app/ 81 | |__ src/ 82 | |__ lib/ 83 | ``` 84 | 85 | One of the main helper functions, found at `app -> src -> lib -> utils -> select.js`, provides a wrapper function for `Element.querySelectorAll`. 86 | 87 | Additionally, several of the components use scripts from `app -> src -> lib -> components`. For example, `scrollable.js` (found in that directory) is a module that creates a custom scrollbar for `drop-down`. 88 | 89 | # Using examples 90 | 91 | Simply copy and paste the `.html`, `.css`, and `.js` files from the built component inside `dist`, and add your own markup. All components are independent of each other. Make sure to follow the requirement set forth by our license. 92 | 93 | [code-license]: #code-license 94 | [getting-started]: #getting-started 95 | [reqs]: #requirements 96 | [install]: #installation 97 | [navigate]: #find-your-way-around 98 | [example]: #example 99 | [lib]: #library 100 | [use]: #use-in-your-own-project 101 | [animation]: http://www.ibm.com/design/language/framework/animation 102 | [apache]: http://www.apache.org/licenses/LICENSE-2.0 103 | [node]: http://www.nodejs.org 104 | [gulp]: http://gulpjs.com/ 105 | -------------------------------------------------------------------------------- /app/assets/img/activitiy-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/assets/img/add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/assets/img/announcement-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/assets/img/announcement.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /app/assets/img/carrot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/assets/img/catalog-menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/assets/img/catalog.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /app/assets/img/chat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/assets/img/configure.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/assets/img/content.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/web-animations/767de2f96484d8ffb5203f218bc2c3616b5cefd6/app/assets/img/content.png -------------------------------------------------------------------------------- /app/assets/img/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/assets/img/edited.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/assets/img/folder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /app/assets/img/gears-menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/assets/img/hamburger.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/assets/img/loader-animated.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /app/assets/img/loader.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /app/assets/img/motion-components/motion-drawer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/web-animations/767de2f96484d8ffb5203f218bc2c3616b5cefd6/app/assets/img/motion-components/motion-drawer.png -------------------------------------------------------------------------------- /app/assets/img/motion-components/motion-dropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/web-animations/767de2f96484d8ffb5203f218bc2c3616b5cefd6/app/assets/img/motion-components/motion-dropdown.png -------------------------------------------------------------------------------- /app/assets/img/motion-components/motion-horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/web-animations/767de2f96484d8ffb5203f218bc2c3616b5cefd6/app/assets/img/motion-components/motion-horizontal.png -------------------------------------------------------------------------------- /app/assets/img/motion-components/motion-popout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/web-animations/767de2f96484d8ffb5203f218bc2c3616b5cefd6/app/assets/img/motion-components/motion-popout.png -------------------------------------------------------------------------------- /app/assets/img/motion-components/motion-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/web-animations/767de2f96484d8ffb5203f218bc2c3616b5cefd6/app/assets/img/motion-components/motion-search.png -------------------------------------------------------------------------------- /app/assets/img/motion-components/motion-vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/web-animations/767de2f96484d8ffb5203f218bc2c3616b5cefd6/app/assets/img/motion-components/motion-vertical.png -------------------------------------------------------------------------------- /app/assets/img/people.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/assets/img/popout.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/assets/img/repository.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/assets/img/save.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/assets/img/search-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/assets/img/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/assets/img/services.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/assets/img/settings-gear.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/assets/img/shared.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 10 | 12 | 13 | -------------------------------------------------------------------------------- /app/assets/img/single-doc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/assets/img/small-directory.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 10 | 11 | 13 | 14 | 15 | 17 | 18 | 19 | 21 | 22 | 23 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/assets/img/squares.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/assets/img/upload.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/assets/img/user.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 9 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/assets/img/x.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/drawer/drawer.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var utils = utils || {}, 10 | components = components || {}; 11 | 12 | (function(win) { 13 | document.addEventListener('DOMContentLoaded', function() { 14 | // utilities 15 | var $ = utils.$, 16 | addMultipleListeners = utils.addMultipleListeners; 17 | 18 | // elements 19 | var $drawerToggle = $('.nav-drawer-toggler')[0], 20 | $closeDrawer = $('.close-drawer')[0], 21 | $drawer = $('.drawer')[0]; 22 | 23 | function open(e) { 24 | if(e.type === 'mouseup' || utils.keys.enter(e)) { 25 | $drawer.classList.remove('close'); 26 | $drawer.classList.add('open'); 27 | $drawer.focus(); 28 | } 29 | } 30 | 31 | function close(e) { 32 | // This check is necessary 33 | if($drawer.classList.contains('open')) { 34 | if(e.type === 'mouseup' || utils.keys.esc(e)) { 35 | $drawer.classList.remove('open'); 36 | $drawer.classList.add('close'); 37 | $drawerToggle.focus(); 38 | } 39 | } 40 | } 41 | 42 | var drawer = { 43 | open: open, 44 | close: close 45 | }; 46 | 47 | addMultipleListeners($drawerToggle, ['mouseup', 'keydown'], drawer.open.bind(this)); 48 | 49 | $closeDrawer.addEventListener('mouseup', drawer.close); 50 | win.addEventListener('keyup', drawer.close); 51 | }); 52 | })(this); 53 | -------------------------------------------------------------------------------- /app/src/drawer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IBM DL Motion Examples 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

IBM DL Motion Examples

14 | 23 |
24 | 25 |
26 |
27 |
28 |
29 | 30 |
31 |

Alerts

32 | 33 | 34 | 35 |
36 |
37 |
    38 |
  • 39 |
  • 40 |
  • 41 |
42 |
43 |
44 | 82 |
83 |
84 |
85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /app/src/drawer/main-drawer.scss: -------------------------------------------------------------------------------- 1 | // Licensed Materials - Property of IBM 2 | // © Copyright IBM Corporation 2015. All Rights Reserved. 3 | // This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 4 | 5 | @import '../lib/styles/partials/variables'; 6 | @import '../lib/styles/partials/mixins'; 7 | @import '../lib/styles/partials/common'; 8 | @import '../top-nav/main-topnav'; 9 | 10 | .drawer { 11 | bottom: 0; 12 | box-shadow: none; 13 | overflow: hidden; 14 | padding: 0; 15 | position: fixed; 16 | right: 0; 17 | top: 0; 18 | transform: translateX(400px); 19 | transition: transform $cubic-bezier; 20 | width: 400px; 21 | z-index: 99999; 22 | 23 | &:active, 24 | &:focus { 25 | outline: none; 26 | transition: transform $cubic-bezier, box-shadow 800ms cubic-bezier(0, 0, 0, .5) 300ms; 27 | } 28 | 29 | a { 30 | color: $blue; 31 | } 32 | } 33 | 34 | .drawer-container { 35 | background: $super-light-gray; 36 | height: 100%; 37 | position: absolute; 38 | width: 400px; 39 | z-index: 9999; 40 | } 41 | 42 | .close { 43 | transition-delay: .2s; 44 | } 45 | 46 | .open { 47 | transform: translateX(0); 48 | 49 | .activity-list { 50 | li { 51 | opacity: 1; 52 | transform: translateX(0); 53 | } 54 | } 55 | 56 | .drawer-nav img { 57 | opacity: 1; 58 | transform: translate(0, 0); 59 | } 60 | } 61 | 62 | .drawer-title { 63 | background: $active; 64 | height: 80px; 65 | line-height: 80px; 66 | position: relative; 67 | user-select: none; 68 | 69 | .title-icon { 70 | float: left; 71 | text-align: center; 72 | width: 80px; 73 | 74 | img { 75 | margin-bottom: -5px; 76 | width: 30px; 77 | } 78 | } 79 | 80 | h3 { 81 | color: $white; 82 | font-size: 1.5rem; 83 | font-weight: normal; 84 | line-height: 80px; 85 | margin: 0; 86 | } 87 | 88 | .close-drawer { 89 | position: absolute; 90 | right: 0; 91 | text-align: center; 92 | top: 0; 93 | width: 60px; 94 | 95 | img { 96 | width: 15px; 97 | } 98 | } 99 | } 100 | 101 | 102 | .tab img { 103 | transition-delay: .5s; 104 | } 105 | 106 | .drawer-nav { 107 | user-select: none; 108 | 109 | ul { 110 | line-height: 8.25rem; 111 | list-style-type: none; 112 | margin: 0 auto; 113 | padding: 0; 114 | width: 60%; 115 | word-spacing: -5px; 116 | 117 | li { 118 | border-bottom: 0; 119 | display: inline-block; 120 | height: 95px; 121 | list-style: none; 122 | padding: 0; 123 | position: relative; 124 | text-align: center; 125 | width: 33%; 126 | } 127 | 128 | img { 129 | opacity: 0; 130 | transform: translate(0, -2rem); 131 | transition: all $cubic-bezier; 132 | transition-delay: .1s; 133 | width: 24px; 134 | } 135 | 136 | .active:after { 137 | background: $active; 138 | bottom: 0; 139 | content: ''; 140 | height: 4px; 141 | left: 16px; 142 | position: absolute; 143 | right: 16px; 144 | } 145 | } 146 | } 147 | 148 | .activity-list { 149 | list-style-type: none; 150 | margin: 0 auto; 151 | padding: 0; 152 | width: 60%; 153 | 154 | li { 155 | border-bottom: 1px solid $almost-light-gray; 156 | font-size: .9rem; 157 | opacity: 0; 158 | transform: translateX(5rem); 159 | transition: all $cubic-bezier; 160 | transition-delay: .1s; 161 | width: 100%; 162 | 163 | .activity-description { 164 | line-height: 1.5rem; 165 | } 166 | 167 | .activity-time { 168 | color: $medium-gray; 169 | display: block; 170 | font-size: .85rem; 171 | padding: 20px 0 12px; 172 | } 173 | } 174 | 175 | .table-header { 176 | border-bottom: 2px solid $active; 177 | color: $blue; 178 | height: 42px; 179 | line-height: 42px; 180 | opacity: 1; 181 | transform: translateX(0); 182 | } 183 | } 184 | 185 | .activity-icon { 186 | left: -80px; 187 | margin-top: -10px; 188 | position: absolute; 189 | text-align: center; 190 | top: 50%; 191 | width: 80px; 192 | 193 | img { 194 | height: 20px; 195 | } 196 | } 197 | 198 | @keyframes fade-in-out { 199 | 0% { 200 | opacity: 0; 201 | } 202 | 203 | 50% { 204 | opacity: 0; 205 | } 206 | 207 | 100% { 208 | opacity: 1; 209 | } 210 | } 211 | 212 | @keyframes close_bottom_bar { 213 | 0% { 214 | transform: rotate(-45deg); 215 | } 216 | 217 | 50% { 218 | transform: translateY(-6px); 219 | } 220 | 221 | 100% { 222 | transform: translate(0, 0); 223 | } 224 | } 225 | 226 | @keyframes close_top_bar { 227 | 0% { 228 | transform: rotate(45deg); 229 | } 230 | 231 | 50% { 232 | transform: translateY(6px); 233 | } 234 | 235 | 100% { 236 | transform: translate(0, 0); 237 | } 238 | } 239 | 240 | @keyframes active_bottom_bar { 241 | 0% { 242 | transform: translate(0, 0); 243 | } 244 | 245 | 50% { 246 | transform: translateY(-6px) translateX(0); 247 | } 248 | 249 | 100% { 250 | transform: rotate(-45deg); 251 | } 252 | } 253 | 254 | 255 | @keyframes active_top_bar { 256 | 0% { 257 | transform: translate(0, 0); 258 | } 259 | 260 | 50% { 261 | transform: translateY(6px) translateX(0); 262 | } 263 | 264 | 100% { 265 | transform: rotate(45deg); 266 | } 267 | } 268 | 269 | .svg-menu-toggle { 270 | cursor: pointer; 271 | display: inline-block; 272 | fill: $white; 273 | margin: 0 20px; 274 | pointer-events: all; 275 | 276 | .bar { 277 | opacity: 1; 278 | 279 | transform: translateY(0) translateX(0); 280 | transition: transform .4s ease-in-out, opacity .2s ease-in-out; 281 | } 282 | 283 | .bar:nth-of-type(1) { 284 | transform-origin: 2px 5px; 285 | } 286 | 287 | .bar:nth-of-type(3) { 288 | transform-origin: 2px 10px; 289 | } 290 | } 291 | 292 | .nav-drawer-toggler.active .svg-menu-toggle { 293 | .bar:nth-of-type(1) { 294 | animation: active_top_bar; 295 | animation-duration: .3s; 296 | animation-fill-mode: forwards; 297 | } 298 | 299 | .bar:nth-of-type(2) { 300 | opacity: 0; 301 | } 302 | 303 | .bar:nth-of-type(3) { 304 | animation: active_bottom_bar; 305 | animation-duration: .3s; 306 | animation-fill-mode: forwards; 307 | } 308 | } 309 | 310 | .nav-drawer-toggler.close .svg-menu-toggle { 311 | .bar:nth-of-type(1) { 312 | animation: close_top_bar; 313 | animation-duration: .3s; 314 | animation-fill-mode: forwards; 315 | } 316 | 317 | .bar:nth-of-type(2) { 318 | opacity: 1; 319 | } 320 | 321 | .bar:nth-of-type(3) { 322 | animation: close_bottom_bar; 323 | animation-duration: .3s; 324 | animation-fill-mode: forwards; 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /app/src/drop-down/drop-down.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var utils = utils || {}, 10 | components = components || {}; 11 | 12 | (function() { 13 | document.addEventListener('DOMContentLoaded', function() { 14 | // utilities 15 | var $ = utils.$, 16 | each = utils.enumerator.each, 17 | swapClass = utils.swapClass, 18 | timeout = utils.timeout, 19 | addMultipleListeners = utils.addMultipleListeners, 20 | clearScrollableTimeout, 21 | Scrollable = components.Scrollable; 22 | 23 | /** 24 | * Creates option elements to use for form processing (HTTP requests) 25 | */ 26 | function populateHiddenForm(_$option) { 27 | var $option = document.createElement('option'); 28 | 29 | $option.value = _$option.id; 30 | $option.innerHTML = _$option.innerHTML; 31 | 32 | this.elements.$hiddenSelect.appendChild($option); 33 | } 34 | 35 | /** 36 | * Opens and closes the dropdown when you interact with the $toggle area 37 | */ 38 | function toggle(e) { 39 | if(e.type === 'mouseup' || utils.keys.enter(e)) { 40 | if(this.elements.$list.classList.contains('active')) { 41 | 42 | this.menu.close.call(this); 43 | 44 | } else { 45 | 46 | this.menu.open.call(this); 47 | } 48 | } 49 | } 50 | 51 | /** 52 | * Selects and option and closes the dropdown 53 | */ 54 | function select(e, $li) { 55 | var selectedText, 56 | $previousSelectedElement; 57 | 58 | if(e.type === 'mouseup' || utils.keys.enter(e)) { 59 | selectedText = $($li, 'span')[0].innerHTML; 60 | $previousSelectedElement = $(this.elements.$el, '[aria-selected="true"]')[0]; 61 | 62 | if(typeof $previousSelectedElement !== 'undefined') { 63 | $previousSelectedElement.setAttribute('aria-selected', false); 64 | } 65 | 66 | $li.setAttribute('aria-selected', true); 67 | 68 | // select a form element for use with HTTP requests 69 | this.elements.$hiddenSelect.value = $li.id; 70 | 71 | swapClass(this.elements.$toggle, 'show-query').forClass('fade-query'); 72 | this.elements.$toggle.setAttribute('aria-activedescendant', $li.id); 73 | 74 | this.menu.close.call(this, function() { 75 | timeout(function(){ 76 | swapClass(this.elements.$toggle, 'fade-query').forClass('show-query'); 77 | 78 | this.elements.$selected.innerHTML = selectedText; 79 | }.bind(this), 300); 80 | }.bind(this)); 81 | } 82 | } 83 | 84 | function closeMenu(cb) { 85 | this.elements.$list.classList.remove('active'); 86 | this.elements.$toggle.classList.remove('open'); 87 | this.elements.$toggle.setAttribute('aria-expanded', false); 88 | 89 | each(this.elements.$options, function($li) { 90 | $li.setAttribute('tabindex', -1); 91 | }); 92 | 93 | this.scrollable.hide(); 94 | this.elements.$toggle.focus(); 95 | 96 | if(cb instanceof Function) { 97 | return cb(); 98 | } 99 | } 100 | 101 | function openMenu(cb) { 102 | var activeDescendant; 103 | 104 | if(typeof clearScrollableTimeout !== 'undefined') { 105 | clearScrollableTimeout.clear(); 106 | } 107 | 108 | this.elements.$toggle.classList.toggle('open'); 109 | this.elements.$list.classList.add('active'); 110 | 111 | clearScrollableTimeout = timeout(function() { 112 | /** 113 | * if-statement necessary to make sure scrollbar 114 | * doesn't show on fast open-close because we need 115 | * to make sure the $dropDownList is still active 116 | * when we decide to show the scrollbar 117 | */ 118 | if(this.elements.$list.classList.contains('active')) { 119 | each(this.elements.$options, function($li) { 120 | $li.setAttribute('tabindex', 0); 121 | }); 122 | 123 | this.elements.$toggle.setAttribute('aria-expanded', true); 124 | this.scrollable.show(); 125 | activeDescendant = this.elements.$toggle.getAttribute('aria-activedescendant'); 126 | 127 | if(activeDescendant !== '' && activeDescendant !== null && activeDescendant !== 'undefined') { 128 | $('#' + activeDescendant).focus(); 129 | } else { 130 | this.elements.$options[0].focus(); 131 | } 132 | } 133 | }.bind(this), 300); 134 | 135 | if(cb instanceof Function) { 136 | return cb(); 137 | } 138 | } 139 | 140 | var dropdown = { 141 | elements: { 142 | $el: $('#random-dropdown'), 143 | $toggle: $('#random-dropdown .dropdown-toggle')[0], 144 | $selected: $('#random-dropdown .dropdown-selected-option')[0], 145 | $list: $('#random-dropdown .dropdown-options > ul')[0], 146 | $options: $('#random-dropdown .dropdown-options > ul li'), 147 | $hiddenSelect: $('#random-dropdown form.hidden > select')[0] 148 | }, 149 | scrollable: new Scrollable(function() { 150 | var config = { 151 | $scroller: $('#scroller'), 152 | $container: $('#random-dropdown') 153 | }; 154 | 155 | return config; 156 | }), 157 | init: function() { 158 | // Make the $toggle tabbable 159 | this.elements.$toggle.setAttribute('tabindex', 0); 160 | 161 | // Any SPANs or IMGs inside $toggle should be aria-hidden 162 | each($(this.elements.$toggle, 'img'), function($el) { 163 | $el.setAttribute('aria-hidden', true); 164 | }); 165 | 166 | // We don't want to be able to tab to the UL element, just straight to the $toggle 167 | this.elements.$list.setAttribute('tabindex', -1); 168 | 169 | // Populat the hidden form so that we can use HTTP requests 170 | each(this.elements.$options, this.populateHiddenForm.bind(this)); 171 | 172 | /** 173 | ************************************************************ 174 | */ 175 | 176 | // toggle the menu on enter or click 177 | addMultipleListeners(this.elements.$toggle, ['mouseup', 'keyup'], this.menu.toggle.bind(this)); 178 | this.elements.$toggle.addEventListener('keyup', function(e) { 179 | if(utils.keys.down(e)) { 180 | if(this.elements.$list.classList.contains('active')) { 181 | this.elements.$options[0].focus(); 182 | } 183 | } 184 | }.bind(this)); 185 | 186 | each(this.elements.$options, function($li, i, $liNodeList) { 187 | // set some aria stuff so we don't forget it in the markup 188 | $li.setAttribute('role', 'option'); 189 | $li.setAttribute('aria-selected', false); 190 | 191 | // select an option on enter key or click 192 | addMultipleListeners($li, ['mouseup', 'keyup'], select.bind(this), false, [$li]); 193 | 194 | // be able to use the up and down arrow keys to select an option 195 | $li.addEventListener('keydown', function(e) { 196 | var $next, 197 | $previous; 198 | 199 | // Uncomment the next line to disable tab and shift+tab functionality when selecting an option 200 | // e.preventDefault(); 201 | 202 | // up key functionality 203 | if(utils.keys.up(e)) { 204 | $previous = $liNodeList[i - 1]; 205 | 206 | // go to either the previous option, or if you're already on 207 | // the first option, use the up arrow key to focus on the $toggle 208 | if(typeof $previous !== 'undefined') { 209 | $li.blur(); 210 | } else { 211 | $previous = this.elements.$toggle; 212 | } 213 | 214 | $previous.focus(); 215 | // down key functionality 216 | } else if (utils.keys.down(e)) { 217 | $next = $liNodeList[i + 1]; 218 | 219 | // we can't go past the last element in the list, or there will be an error 220 | if(typeof $next !== 'undefined') { 221 | $li.blur(); 222 | $next.focus(); 223 | } 224 | } else if (utils.keys.esc(e)) { 225 | return this.menu.close.call(this); 226 | } 227 | }.bind(this)); 228 | }.bind(this)); 229 | }, 230 | populateHiddenForm: populateHiddenForm, 231 | menu: { 232 | open: openMenu, 233 | close: closeMenu, 234 | toggle: toggle, 235 | select: select 236 | } 237 | }; 238 | 239 | dropdown.init(); 240 | }); 241 | }()); 242 | -------------------------------------------------------------------------------- /app/src/drop-down/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IBM DL Motion Examples 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 |
19 | 43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/drop-down/main-dropdown.scss: -------------------------------------------------------------------------------- 1 | // Licensed Materials - Property of IBM 2 | // © Copyright IBM Corporation 2015. All Rights Reserved. 3 | // This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 4 | 5 | @import '../lib/styles/partials/variables'; 6 | @import '../lib/styles/partials/mixins'; 7 | @import '../lib/styles/partials/common'; 8 | 9 | .dropdown { 10 | margin: 200px auto; 11 | max-width: 380px; 12 | outline: none; 13 | position: relative; 14 | user-select: none; 15 | 16 | * { 17 | @include box-sizing(border-box); 18 | cursor: default; 19 | outline: none; 20 | } 21 | 22 | form { 23 | &.hidden { 24 | display: none; 25 | } 26 | } 27 | 28 | } 29 | 30 | ul { 31 | background: $white; 32 | height: 0; 33 | list-style-type: none; 34 | margin: 0; 35 | overflow-y: scroll; 36 | padding: 0; 37 | transition: height .4s cubic-bezier(.5, .08, 0, 1); 38 | width: 450px; 39 | 40 | &.active { 41 | display: block; 42 | height: 18rem; 43 | } 44 | 45 | li { 46 | animation-fill-mode: forwards; 47 | background: $white; 48 | border: 0; 49 | border-bottom: 1px solid $light-gray; 50 | font-size: .9rem; 51 | font-weight: 200; 52 | height: 3rem; 53 | line-height: 3rem; 54 | margin: 0 1rem; 55 | transition: color 300ms; 56 | width: 350px; 57 | 58 | &:active, 59 | &:focus { 60 | color: $medium-gray; 61 | } 62 | } 63 | } 64 | 65 | .dropdown-toggle { 66 | background: $white; 67 | border-bottom: 1px solid $light-gray; 68 | display: block; 69 | font-size: .875rem; 70 | font-weight: 200; 71 | height: 3rem; 72 | line-height: 3rem; 73 | padding-left: 1rem; 74 | position: relative; 75 | width: 380px; 76 | 77 | &:active, 78 | &:focus { 79 | transition: box-shadow 500ms cubic-bezier(0, 0, 0, 1); 80 | } 81 | 82 | &.open .dropdown-caret { 83 | transform: rotate(-180deg); 84 | } 85 | 86 | .dropdown-selected-option { 87 | transition: all .1s linear; 88 | } 89 | 90 | &.show-query { 91 | opacity: 1; 92 | transform: translateX(0); 93 | } 94 | 95 | .dropdown-caret { 96 | height: .5rem; 97 | margin-top: -.1rem; 98 | position: absolute; 99 | right: 1.3rem; 100 | top: 50%; 101 | transition: all $cubic-bezier; 102 | } 103 | } 104 | 105 | .fade-query { 106 | .dropdown-selected-option { 107 | opacity: 0; 108 | transform: translateX(-.5rem); 109 | } 110 | } 111 | 112 | .dropdown-options { 113 | overflow: hidden; 114 | position: relative; 115 | } 116 | 117 | .scrollable { 118 | background: rgba(0, 0, 0, .2); 119 | border-radius: 10px; 120 | cursor: pointer; 121 | position: absolute; 122 | right: 3px; 123 | top: 0; 124 | transition: top .08s; 125 | width: 3px; 126 | z-index: 5; 127 | 128 | &.fade-out { 129 | // need !important declaration to override inline javascript styling for this element 130 | height: 0 !important; 131 | opacity: 0; 132 | transition: all .2s linear; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IBM DL Motion Examples 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |

IBM DL Motion Examples

15 |
16 | 17 |
18 | 68 |
69 | 70 | 71 | -------------------------------------------------------------------------------- /app/src/lib/scripts/utils/add-multiple-listeners.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var utils = utils || {}, 10 | Window = Window || {}; 11 | 12 | (function() { 13 | var addMultipleListeners = function(_element, _eventsArray, _handler, _useCapture, _handlerArgs) { 14 | var element, 15 | eventsArray, 16 | handler, 17 | handlerArgs = [], 18 | useCapture, 19 | errors = { 20 | element: 'First arguments must be an HTMLElement or Window object', 21 | eventsArray: 'Second argument must be an array of strings', 22 | handler: 'Third argument must be a function', 23 | useCapture: 'Fourth argument must be a boolean value', 24 | handlerArgs: 'Fifth argument must be an Array of arguments' 25 | }; 26 | 27 | if(_element instanceof HTMLElement || _element instanceof Window) { 28 | element = _element; 29 | } else { 30 | throw new TypeError(errors.element); 31 | } 32 | 33 | if(_eventsArray instanceof Array) { 34 | eventsArray = _eventsArray; 35 | } else { 36 | throw new TypeError(errors.eventsArray); 37 | } 38 | 39 | if(_handlerArgs instanceof Array) { 40 | handlerArgs = _handlerArgs; 41 | } else if(typeof _handlerArgs !== 'undefined') { 42 | throw new TypeError(errors.handlerArgs); 43 | } 44 | 45 | if(_handler instanceof Function) { 46 | handler = function(e) { 47 | _handler.apply(this, [e].concat(handlerArgs)); 48 | }; 49 | } else { 50 | throw new TypeError(errors.handler); 51 | } 52 | 53 | if(typeof _useCapture === 'boolean') { 54 | useCapture = _useCapture; 55 | } else if(typeof _useCapture !== 'undefined') { 56 | throw new TypeError(errors.useCapture); 57 | } 58 | 59 | eventsArray.forEach(function(event) { 60 | element.addEventListener(event, handler, useCapture); 61 | }); 62 | }; 63 | 64 | utils.addMultipleListeners = addMultipleListeners; 65 | }()); 66 | -------------------------------------------------------------------------------- /app/src/lib/scripts/utils/components/components.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | (function(global) { 10 | global.components = {}; 11 | })(this); 12 | -------------------------------------------------------------------------------- /app/src/lib/scripts/utils/components/menu.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var components = components || {}, 10 | utils = utils || {}; 11 | 12 | (function(win) { 13 | var $ = utils.$, 14 | each = utils.enumerator.each, 15 | filter = utils.enumerator.filter, 16 | timeout = utils.timeout, 17 | isDescendant = utils.isDescendant, 18 | activeBarClearTimeout; 19 | 20 | /* 21 | * Filters out $activeBar from the list 22 | */ 23 | function notActiveBar(li) { 24 | return !li.classList.contains('active-bar-horizontal') && !li.classList.contains('active-bar-vertical'); 25 | } 26 | 27 | /** 28 | * Hides the $activeBar so that it doesn't slide down on mouseleave 29 | */ 30 | function stopAnimations() { 31 | if(this.$activeBar.classList.contains('move')) { 32 | this.$activeBar.classList.remove('move'); 33 | } 34 | 35 | /** 36 | * The if-statement is critical, or else when hovering 37 | * over the navigation menu (but not an item), activeBarClearTimeout 38 | * is never defined 39 | */ 40 | if(typeof activeBarClearTimeout !== 'undefined') { 41 | activeBarClearTimeout.clear(); 42 | } 43 | } 44 | 45 | /** 46 | * Changes the X or Y position of the $activeBar 47 | */ 48 | function slide(i, $el) { 49 | /** 50 | * if-statement is for IE10 so that when hovering 51 | * over an element [data-active-bar="false"], and then 52 | * going back to one without that attribute, the bar 53 | * behaves correctly 54 | */ 55 | if($el.getAttribute('data-active-bar') !== 'false' && this.turnedOn) { 56 | // in all browsers, provides X movement 57 | this.$menu.className = this.direction + ' directional-nav pos-' + i; 58 | 59 | /** 60 | * The if-statement is critical, or else when moving quickly 61 | * from one item to another, the class 'move' may stay on 62 | * even when hovering off of $topNav 63 | */ 64 | if(this.$activeBar.classList.contains('move') === false) { 65 | activeBarClearTimeout = timeout(function() { 66 | this.$activeBar.classList.add('move'); 67 | }.bind(this), 200); 68 | // this delay is the same as the CSS transition speed 69 | } 70 | } 71 | } 72 | 73 | /** 74 | * Selects a link clicked (provides state through CSS class) 75 | * Fades in a new title 76 | */ 77 | function changePage(i, $el) { 78 | var $parentLi = $el.tagName === 'LI' ? $el : $el.parentNode, 79 | doesNotLinkToPage = $parentLi.getAttribute('data-page') !== 'false'; 80 | 81 | if(doesNotLinkToPage && this.turnedOn) { 82 | // set some of the aria attributes for accessibility purposes 83 | $(this.$menu, '[aria-selected="true"]')[0].setAttribute('aria-selected', false); 84 | $parentLi.setAttribute('aria-selected', true); 85 | 86 | // remove any existing .active classes 87 | $(this.$menu, '.active')[0].classList.remove('active'); 88 | 89 | // make the parent li be .active 90 | $parentLi.classList.add('active'); 91 | } 92 | } 93 | 94 | var Menu = function(_config) { 95 | var config = _config(); 96 | 97 | this.direction = config.direction || 'horizontal'; 98 | this.$menu = config.$menu; 99 | this.turnedOn = this.$menu.getAttribute('data-on') !== 'false'; 100 | this.$menuContainer = $(this.$menu, 'ul')[0]; 101 | this.$menuItems = filter($(this.$menu, 'li'), notActiveBar); 102 | this.$links = $(this.$menu, 'a'); 103 | this.$activeBar = $('.active-bar-'+this.direction)[0]; 104 | 105 | this.$activeBar.setAttribute('tabindex', -1); 106 | // event listeners 107 | 108 | /** 109 | * If anywhere outside the navigation menu is clicked, 110 | * we want to remove all transitions/animations. This is 111 | * important because when using tab and enter to select a 112 | * menu item, and then we click outside of the menu, and then 113 | * tab or click back into the menu, the $activeBar will slide over from 114 | * its last position (it shouldn't do that; it should 'reset' and then 115 | * animate up into view, but not slide from its last position). 116 | * 117 | * Also, document.activeElement will always be the body or whatever element 118 | * has a tabindex set on it, so we want to check if the current active element 119 | * (the one we click on) is a menu item, and if it IS NOT a menu item, then 120 | * we'll stop animations. 121 | */ 122 | win.addEventListener('mouseup', function() { 123 | if(isDescendant(this.$menu, document.activeElement) === false) { 124 | return this.stopAnimations(); 125 | } 126 | }.bind(this)); 127 | 128 | this.$menu.addEventListener('mouseleave', this.stopAnimations.bind(this)); 129 | 130 | /** 131 | * When transitioning from keyboard interaction to 132 | * mouse interaction, we have to blur any focus so that 133 | * the move transitions will work correctly. 134 | */ 135 | this.$menu.addEventListener('mouseenter', function() { 136 | each(this.$menuItems, function($li) { 137 | $li.blur(); 138 | }); 139 | }.bind(this)); 140 | 141 | each(this.$menuItems, function($li, i) { 142 | if(this.turnedOn || $li.hasAttribute('data-icon')) { 143 | $li.setAttribute('tabindex', 0); 144 | } 145 | // be able to provide x and y movement for the $activeBar 146 | $li.addEventListener('mouseenter', this.slide.bind(this, i, $li)); 147 | 148 | $li.addEventListener('keyup', function(e) { 149 | if(this.turnedOn) { 150 | // change page on enter key 151 | if(utils.keys.enter(e)) { 152 | return this.changePage(i, $li); 153 | } 154 | 155 | // slide the active bar on tab or shift+tab 156 | if(utils.keys.tab(e)) { 157 | return this.slide(i, $li); 158 | } 159 | } 160 | }.bind(this)); 161 | }.bind(this)); 162 | 163 | each(this.$links, function($a, i) { 164 | $a.setAttribute('tabindex', -1); 165 | $a.addEventListener('click', this.changePage.bind(this, i, $a)); 166 | }.bind(this)); 167 | 168 | }; 169 | 170 | Menu.prototype = { 171 | stopAnimations: stopAnimations, 172 | slide: slide, 173 | changePage: changePage 174 | }; 175 | 176 | components.Menu = Menu; 177 | })(this); 178 | -------------------------------------------------------------------------------- /app/src/lib/scripts/utils/components/scrollable.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var components = components || {}, 10 | utils = utils || {}; 11 | 12 | (function(global) { 13 | var $ = utils.$; 14 | 15 | function show() { 16 | var scrollerHeight = this.calculateScrollerHeight(); 17 | 18 | // if we need a scrollbar, then style it 19 | if (scrollerHeight / this.$container.offsetHeight < 1) { 20 | this.scroll(); 21 | this.$scroller.classList.remove('fade-out'); 22 | 23 | this.$scroller.style.height = scrollerHeight + 'px'; 24 | } 25 | } 26 | 27 | function hide() { 28 | this.$scroller.classList.add('fade-out'); 29 | } 30 | 31 | function calculateScrollerHeight() { 32 | // ratio of the portion of the list that's visible by the actual height, including hidden overflow 33 | var ratio = this.$list.offsetHeight / this.$list.scrollHeight; 34 | 35 | return ratio * this.$list.offsetHeight; 36 | } 37 | 38 | /** 39 | * Sets the top position of the scrollbar while scrolling with the scroll wheel on the mouse 40 | */ 41 | function scroll() { 42 | var scrollPercentage = this.$list.scrollTop / this.$list.scrollHeight, 43 | // 5px arbitrary offset so scroll bar doesn't 44 | // move too far beyond content wrapper bounding box 45 | topPosition = scrollPercentage * (this.$container.offsetHeight - 40); 46 | 47 | this.$scroller.style.top = topPosition + 'px'; 48 | } 49 | 50 | /** 51 | * Stores the initial positions of the content and scrollbar 52 | */ 53 | function startDrag(e) { 54 | this.position.start = e.pageY; 55 | this.position.content = this.$list.scrollTop; 56 | this.dragging = true; 57 | } 58 | 59 | function stopDrag() { 60 | this.dragging = false; 61 | } 62 | 63 | /** 64 | * Sets the top position of the scrollbar while dragging it (holding down mouse button and moving up and down) 65 | */ 66 | function dragScroll(e) { 67 | var mouseDifferential = e.pageY - this.position.start, 68 | scrollEquivalent = mouseDifferential * (this.$list.scrollHeight / this.$container.offsetHeight); 69 | 70 | if (this.dragging === true) { 71 | this.$list.scrollTop = this.position.content + scrollEquivalent; 72 | } 73 | } 74 | 75 | var Scrollable = function(_config) { 76 | var config = _config(); 77 | 78 | this.$container = config.$container; 79 | this.$list = $(this.$container, '.dropdown-options > ul')[0]; 80 | this.$scroller = config.$scroller; 81 | this.dragging = false; 82 | this.position = { 83 | content: 0, 84 | start: 0, 85 | stop: 0 86 | }; 87 | 88 | this.$scroller.setAttribute('aria-hidden', true); 89 | this.$scroller.setAttribute('tabindex', -1); 90 | this.$scroller.addEventListener('mousedown', this.drag.start.bind(this)); 91 | global.addEventListener('mouseup', this.drag.stop.bind(this)); 92 | global.addEventListener('mousemove', this.drag.scroll.bind(this)); 93 | 94 | this.$list.addEventListener('scroll', this.scroll.bind(this)); 95 | }; 96 | 97 | Scrollable.prototype = { 98 | show: show, 99 | hide: hide, 100 | calculateScrollerHeight: calculateScrollerHeight, 101 | scroll: scroll, 102 | drag: { 103 | start: startDrag, 104 | stop: stopDrag, 105 | scroll: dragScroll 106 | } 107 | }; 108 | 109 | components.Scrollable = Scrollable; 110 | })(this); 111 | -------------------------------------------------------------------------------- /app/src/lib/scripts/utils/components/search-data-object.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var utils = utils || {}, 10 | components = components || {}; 11 | 12 | (function() { 13 | var $ = utils.$; 14 | 15 | /** 16 | * Generates an HTMLElement for use in injecting into the DOM 17 | * @return {SearchDataObject} this (used for chaining methods) 18 | */ 19 | function build() { 20 | this.span.appendChild(document.createTextNode(this.text)); 21 | this.li.appendChild(this.span); 22 | 23 | return this; 24 | } 25 | 26 | /** 27 | * Selects element based on Element.prototype.querySelectorAll 28 | * @param {String} selector 29 | * @param {Element} parent 30 | * @return {NodeList} {Element} nodeList 31 | */ 32 | function placeElementIn(parent) { 33 | parent.appendChild(this.li); 34 | } 35 | 36 | /** 37 | * Shows or hides elements based on the search text 38 | * @param {String} exp 39 | */ 40 | function test(exp) { 41 | // hide if the search is blank 42 | if(exp.test('')) { 43 | this.setInactive(); 44 | return false; 45 | } 46 | 47 | // show the element if the search text matches the element text 48 | if(exp.test(this.text)) { 49 | this.setActive(); 50 | } else { 51 | this.setInactive(); 52 | } 53 | } 54 | 55 | function setActive() { 56 | this.li.classList.add('matched'); 57 | this.li.setAttribute('tabindex', 0); 58 | this.span.className = 'enter'; 59 | } 60 | 61 | function setInactive() { 62 | this.li.classList.remove('matched'); 63 | this.li.setAttribute('tabindex', -1); 64 | this.span.className = ''; 65 | } 66 | 67 | /** 68 | * Holds information about a search query and generates list items 69 | * to show or hide based on whether the content matches the query 70 | * 71 | * Usage: 72 | * new SearchDataObject(item).build().placeElementIn($resultsContainer); 73 | * 74 | * .build() can be used later to delay building the item 75 | * .placeElementIn(HTMLElement) can also be used later to delay placing the elements 76 | */ 77 | var SearchDataObject = function(text) { 78 | this.text = text; 79 | this.li = document.createElement('li'); 80 | this.span = document.createElement('span'); 81 | 82 | this.li.setAttribute('role', 'option'); 83 | this.li.setAttribute('tabindex', -1); 84 | this.li.classList.add('result'); 85 | 86 | this.li.addEventListener('keyup', function(e) { 87 | var $next, 88 | $previous; 89 | 90 | // Uncomment the next line to disable tab and shift+tab functionality when selecting an option 91 | // e.preventDefault(); 92 | 93 | // up key functionality 94 | if(utils.keys.up(e)) { 95 | $previous = this.previousSibling; 96 | 97 | // go to either the previous option, or if you're already on 98 | // the first option, use the up arrow key to focus on the $toggle 99 | if($previous.tagName === 'LI' && +$previous.getAttribute('tabindex') !== -1) { 100 | this.blur(); 101 | } else { 102 | $previous = $('.search-input')[0]; 103 | } 104 | 105 | if(+$previous.getAttribute('tabindex') !== -1) { 106 | $previous.focus(); 107 | } 108 | // down key functionality 109 | } else if (utils.keys.down(e)) { 110 | $next = this.nextSibling; 111 | 112 | // we can't go past the last element in the list, or there will be an error 113 | if($next !== null && $next.getAttribute('tabindex') !== -1) { 114 | this.blur(); 115 | $next.focus(); 116 | } 117 | } 118 | }); 119 | }; 120 | 121 | SearchDataObject.prototype = { 122 | build: build, 123 | placeElementIn: placeElementIn, 124 | test: test, 125 | setActive: setActive, 126 | setInactive: setInactive 127 | }; 128 | 129 | components.SearchDataObject = SearchDataObject; 130 | 131 | // REVIEW: why do I have this return here? 132 | return SearchDataObject; 133 | }()); 134 | -------------------------------------------------------------------------------- /app/src/lib/scripts/utils/enumerator.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var utils = utils || {}; 10 | 11 | (function() { 12 | /** 13 | * Distillation of [].forEach.call for iterating over NodeLists, etc. 14 | * @param {Array-like Object} array 15 | * @param {Function} fn 16 | */ 17 | function each(array, fn) { 18 | return [].forEach.call(array, fn); 19 | } 20 | 21 | function filter(array, fn) { 22 | return [].filter.call(array, fn); 23 | } 24 | 25 | var enumerator = { 26 | each: each, 27 | filter: filter 28 | }; 29 | 30 | // binds this helper function to the global 'utils' 31 | utils.enumerator = enumerator; 32 | }()); 33 | -------------------------------------------------------------------------------- /app/src/lib/scripts/utils/is-descendant.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var Window = Window || {}, 10 | utils = utils || {}; 11 | 12 | (function() { 13 | var isDescendant = function(parent, child) { 14 | var node; 15 | 16 | if(!validType(parent)) { 17 | throw new TypeError(err('Parent', parent)); 18 | } 19 | 20 | if(!validType(child)) { 21 | throw new TypeError(err('Child', child)); 22 | } 23 | 24 | node = child.parentNode; 25 | 26 | while (node !== null) { 27 | if (node === parent) { 28 | return true; 29 | } 30 | 31 | node = node.parentNode; 32 | } 33 | 34 | return false; 35 | }; 36 | 37 | utils.isDescendant = isDescendant; 38 | 39 | ///////////////////////////////////////////////////////////////////// 40 | 41 | function validType(element) { 42 | return element instanceof HTMLElement || element instanceof Window; 43 | } 44 | 45 | function err(stringName, element) { 46 | return stringName + ' must be an HTMLElement or Window. It is currently a(n) ' + typeof element; 47 | } 48 | }()); 49 | -------------------------------------------------------------------------------- /app/src/lib/scripts/utils/keys.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var utils = utils || {}; 10 | 11 | (function() { 12 | // checks if the tab key was pressed 13 | function tab(e) { 14 | var key = e.keyCode || e.which; 15 | 16 | return key === 9 || key === '9'; 17 | } 18 | 19 | // checks if the enter key was pressed 20 | function enter(e) { 21 | var key = e.keyCode || e.which; 22 | 23 | return key === 13 || key === '13'; 24 | } 25 | 26 | // checks if the escape key was pressed 27 | function esc(e) { 28 | var key = e.keyCode || e.which; 29 | 30 | return key === 27 || key === '27'; 31 | } 32 | 33 | // checks if the up key was pressed 34 | function up(e) { 35 | var key = e.keyCode || e.which; 36 | 37 | return key === 38 || key === '38'; 38 | } 39 | 40 | // checks if the down key was pressed 41 | function down(e) { 42 | var key = e.keyCode || e.which; 43 | 44 | return key === 40 || key === '40'; 45 | } 46 | 47 | var keys = { 48 | enter: enter, 49 | tab: tab, 50 | esc: esc, 51 | up: up, 52 | down: down 53 | }; 54 | 55 | utils.keys = keys; 56 | }()); 57 | -------------------------------------------------------------------------------- /app/src/lib/scripts/utils/select.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var utils = utils || {}; 10 | 11 | (function() { 12 | /** 13 | * Selects element based on Element.prototype.querySelectorAll 14 | * @param {String} selector 15 | * @param {Element} parent 16 | * @return {NodeList} {Element} nodeList 17 | */ 18 | var $ = function() { 19 | var len = arguments.length, 20 | isLoneId, 21 | selector, 22 | parent, 23 | nodeList; 24 | 25 | // checks if only a selector is passed in 26 | if(len === 1 && typeof arguments[0] === 'string') { 27 | selector = arguments[0].trim(); 28 | isLoneId = selector.indexOf('#') === 0 && selector.indexOf(' ') === -1; 29 | // use querySelector if a single ID string is passed in, e.g. '#some-id', but not '#some-id .some-class' 30 | // BUG: $('#some-id #some-other-id') will give a NodeList, but you shouldn't select that way anyway 31 | nodeList = isLoneId ? document.querySelector(selector) : document.querySelectorAll(selector); 32 | 33 | return nodeList; 34 | // checks if both a selector and parent are passed in 35 | } else if (len === 2 && typeof arguments[0] !== 'string') { 36 | selector = arguments[1]; 37 | parent = arguments[0]; 38 | // if parent is a NodeList, select the first element only 39 | parent = parent.length > 0 ? parent[0] : parent; 40 | nodeList = Element.prototype.querySelectorAll.call(parent,selector); 41 | 42 | if (selector.indexOf('#') === 0 ) { 43 | return nodeList[0]; 44 | } else { 45 | return nodeList; 46 | } 47 | } 48 | }; 49 | 50 | // binds this helper function to the global 'utils' 51 | utils.$ = $; 52 | }()); 53 | -------------------------------------------------------------------------------- /app/src/lib/scripts/utils/swap-class.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var utils = utils || []; 10 | 11 | (function() { 12 | /** 13 | * Flip-flops two classes 14 | * @param {HTMLElement} $el 15 | * @param {String} _class 16 | * @return {Object} 17 | * 18 | * Usage: 19 | * utils.swapClass(document.getElementById('some-element'), 'class1').forClass('class2'); 20 | */ 21 | var swapClass = function($el, _class) { 22 | $el.classList.remove(_class); 23 | 24 | // chain this method 25 | return { 26 | forClass: function(__class) { 27 | return forClass.call(this, $el, __class); 28 | } 29 | }; 30 | }; 31 | 32 | ///////////////////////////////////////////////////////////////////// 33 | 34 | function forClass($el, _class) { 35 | $el.classList.add(_class); 36 | } 37 | 38 | utils.swapClass = swapClass; 39 | 40 | }()); 41 | -------------------------------------------------------------------------------- /app/src/lib/scripts/utils/timeout.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var utils = utils || {}; 10 | 11 | (function() { 12 | // based on http://codereview.stackexchange.com/questions/47889/alternative-to-setinterval-and-settimeout 13 | var timeout = function(callback, delay) { 14 | var dateNow = Date.now, 15 | requestAnimation = window.requestAnimationFrame, 16 | start = dateNow(), 17 | stop, 18 | timeoutFn = function() { 19 | return dateNow() - start < delay ? stop || requestAnimation(timeoutFn) : callback(); 20 | }; 21 | 22 | requestAnimation(timeoutFn); 23 | 24 | return { 25 | clear: function(){ 26 | stop = 1; 27 | } 28 | }; 29 | }; 30 | 31 | utils.timeout = timeout; 32 | }()); 33 | -------------------------------------------------------------------------------- /app/src/lib/scripts/utils/utils.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | (function(global) { 10 | global.utils = {}; 11 | })(this); 12 | -------------------------------------------------------------------------------- /app/src/lib/styles/main.scss: -------------------------------------------------------------------------------- 1 | // Licensed Materials - Property of IBM 2 | // © Copyright IBM Corporation 2015. All Rights Reserved. 3 | // This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 4 | 5 | @import 'pages/index'; 6 | @import 'partials/variables'; 7 | @import 'partials/mixins'; 8 | @import 'partials/common'; 9 | -------------------------------------------------------------------------------- /app/src/lib/styles/pages/index.scss: -------------------------------------------------------------------------------- 1 | // Licensed Materials - Property of IBM 2 | // © Copyright IBM Corporation 2015. All Rights Reserved. 3 | // This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 4 | 5 | @import '../partials/variables'; 6 | @import '../partials/mixins'; 7 | @import '../partials/common'; 8 | 9 | .examples { 10 | $size: 750px; 11 | $color-change-time: 300ms; 12 | 13 | list-style-type: none; 14 | margin: 2rem; 15 | padding: 0; 16 | text-align: center; 17 | width: 800px; 18 | 19 | li { 20 | $grid-border: 1px solid $light-gray; 21 | background: $almost-white; 22 | border-left: $grid-border; 23 | border-top: $grid-border; 24 | float: left; 25 | height: $size / 3; 26 | margin: 0; 27 | padding: 0; 28 | position: relative; 29 | transition: background-color $color-change-time; 30 | width: $size / 3; 31 | 32 | &:hover { 33 | background-color: $white; 34 | } 35 | 36 | &:nth-child(3), 37 | &:last-child { 38 | border-right: $grid-border; 39 | } 40 | 41 | &:nth-last-child(3), 42 | &:nth-last-child(2), 43 | &:last-child { 44 | border-bottom: $grid-border; 45 | } 46 | 47 | a { 48 | display: block; 49 | height: 100%; 50 | left: 0; 51 | position: absolute; 52 | top: 0; 53 | transition: color $color-change-time; 54 | width: 100%; 55 | } 56 | 57 | a:hover { 58 | color: $blue; 59 | } 60 | 61 | img { 62 | left: 50%; 63 | max-height: 67%; 64 | max-width: 67%; 65 | position: absolute; 66 | top: 25%; 67 | transform: translate(-50%); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/lib/styles/partials/common.scss: -------------------------------------------------------------------------------- 1 | // Licensed Materials - Property of IBM 2 | // © Copyright IBM Corporation 2015. All Rights Reserved. 3 | // This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 4 | 5 | html { 6 | // http://stackoverflow.com/ 7 | // questions/9733011/ 8 | // safari-changing-font-weights-when-unrelated-animations-are-running 9 | -webkit-font-smoothing: antialiased; 10 | } 11 | 12 | body { 13 | font-family: "Helvetica Neue", Helvetica, sans-serif; 14 | margin: 0; 15 | min-width: 1024px; 16 | padding: 0; 17 | } 18 | 19 | a { 20 | color: $active; 21 | cursor: pointer; 22 | text-decoration: none; 23 | 24 | &:active { 25 | background: none; 26 | } 27 | 28 | &:active, 29 | &:focus { 30 | outline: none; 31 | } 32 | 33 | &:hover { 34 | color: $overlay-blue; 35 | } 36 | } 37 | 38 | h1 { 39 | color: $blue; 40 | font-weight: bold; 41 | line-height: 8.25rem; 42 | padding-left: 4px; 43 | 44 | a:active, 45 | a:focus { 46 | outline: 1px dotted $active; 47 | } 48 | } 49 | 50 | .title-container { 51 | height: 8.25rem; 52 | position: relative; 53 | width: 100%; 54 | 55 | .page-title { 56 | opacity: 0; 57 | position: absolute; 58 | transition: all 200ms; 59 | 60 | &.fade-in { 61 | opacity: 1; 62 | } 63 | 64 | .fade-in.fade-out { 65 | opacity: 0; 66 | } 67 | } 68 | } 69 | 70 | main { 71 | display: inline-block; 72 | left: 50%; 73 | position: relative; 74 | transform: translate(-50%); 75 | } 76 | 77 | ::-webkit-input-placeholder { 78 | color: $active; 79 | } 80 | 81 | .skip { 82 | left: -1000px; 83 | position: absolute; 84 | top: -1000px; 85 | 86 | &:focus { 87 | color: $white; 88 | left: 40%; 89 | top: 40%; 90 | transform: translate(-50%); 91 | z-index: 9999999; 92 | } 93 | } 94 | 95 | .example { 96 | border: 1px solid $light-gray; 97 | margin: 2rem auto; 98 | width: 75%; 99 | } 100 | 101 | header { 102 | background: $blue; 103 | position: relative; 104 | z-index: 99999; 105 | 106 | a { 107 | color: $white; 108 | 109 | &:hover { 110 | color: $white; 111 | } 112 | } 113 | 114 | h1 { 115 | display: inline-block; 116 | font-size: 1rem; 117 | font-weight: normal; 118 | line-height: 80px; 119 | margin: 0; 120 | padding: 0 0 0 1rem; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /app/src/lib/styles/partials/mixins.scss: -------------------------------------------------------------------------------- 1 | // Licensed Materials - Property of IBM 2 | // © Copyright IBM Corporation 2015. All Rights Reserved. 3 | // This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 4 | 5 | // Necessary for FireFox. 6 | // input[type="search"] needs -moz-box-sizing declared LAST for it to 7 | // recognize it. 8 | // Gulp's autoprefixer doesn't resolve this issue, because it'll set the 9 | // vendor prefixes FIRST. 10 | @mixin box-sizing($box-model) { 11 | box-sizing: $box-model; 12 | // -webkit-box-sizing: $box-model; // Safari <= 5 13 | // -moz-box-sizing: $box-model; // Firefox <= 19 14 | } 15 | -------------------------------------------------------------------------------- /app/src/lib/styles/partials/variables.scss: -------------------------------------------------------------------------------- 1 | // Licensed Materials - Property of IBM 2 | // © Copyright IBM Corporation 2015. All Rights Reserved. 3 | // This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 4 | 5 | $light-yellow: #fbffcf; 6 | $blue: #00b4a0; 7 | $overlay-blue:#004840; 8 | $gray: #eaebec; 9 | $text-gray: #858989; 10 | $medium-gray: #aeaeae; 11 | $almost-light-gray: #e0e0e0; 12 | $light-gray: #eee; 13 | $lighter-gray: #f4f4f4; 14 | $super-light-gray: #f9f9f9; 15 | $almost-white: #fafafa; 16 | $white: #fff; 17 | $active: #008571; 18 | 19 | $cubic-bezier: .3s cubic-bezier(.5, .8, 0, 1); 20 | -------------------------------------------------------------------------------- /app/src/pop-up/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IBM DL Motion Examples 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |

Title

16 |
17 |
18 |

Welcome to Product Title!

19 |

This is a pop up with useful information about this specific page or feature.

20 | 21 |
22 |
23 |
24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/pop-up/main-popup.scss: -------------------------------------------------------------------------------- 1 | // Licensed Materials - Property of IBM 2 | // © Copyright IBM Corporation 2015. All Rights Reserved. 3 | // This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 4 | 5 | @import '../lib/styles/partials/variables'; 6 | @import '../lib/styles/partials/mixins'; 7 | @import '../lib/styles/partials/common'; 8 | 9 | .popout { 10 | width: 800px; 11 | 12 | h1 { 13 | cursor: default; 14 | display: inline-block; 15 | font-size: 3rem; 16 | } 17 | } 18 | 19 | .information { 20 | background: $lighter-gray; 21 | padding: 1.5rem 2.5rem; 22 | 23 | &:before { 24 | border-bottom: 20px solid transparent; 25 | border-right: 20px solid $lighter-gray; 26 | border-top: 20px solid transparent; 27 | content: ''; 28 | display: block; 29 | height: 0; 30 | left: -15px; 31 | position: absolute; 32 | top: 40px; 33 | width: 15px; 34 | z-index: 9999; 35 | } 36 | 37 | h2 { 38 | color: $blue; 39 | font-size: 20px; 40 | font-weight: bold; 41 | margin: 0; 42 | width: 250px; 43 | } 44 | 45 | p { 46 | font-weight: light; 47 | line-height: 28px; 48 | margin: 1rem 0; 49 | width: 300px; 50 | } 51 | 52 | button { 53 | background: none; 54 | border: 2px solid $blue; 55 | color: $blue; 56 | font-size: 1rem; 57 | height: 34px; 58 | line-height: 30px; 59 | min-width: 120px; 60 | outline-width: 0; 61 | padding: 0 10px; 62 | transition: all 100ms ease-in-out; 63 | 64 | &:active, 65 | &:focus { 66 | border-color: $active; 67 | } 68 | 69 | &:hover { 70 | background: $blue; 71 | color: $white; 72 | } 73 | } 74 | } 75 | 76 | .information-container { 77 | display: inline-block; 78 | margin: 45px 0 0 15px; 79 | overflow: hidden; 80 | padding: 0 0 0 15px; 81 | position: absolute; 82 | transition: all 500ms ease-in-out; 83 | width: 400px; 84 | 85 | &.closed { 86 | padding: 0; 87 | width: 0; 88 | } 89 | } 90 | 91 | .information-container:active, 92 | .information-container:focus { 93 | outline: none; 94 | 95 | .information { 96 | background: $light-gray; 97 | transition: all 200ms; 98 | 99 | &:before { 100 | border-right-color: $light-gray; 101 | transition: all 200ms; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/pop-up/pop-up.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var utils = utils || {}; 10 | 11 | (function() { 12 | var $ = utils.$, 13 | addMultipleListeners = utils.addMultipleListeners, 14 | $popout = $('#random-info'), 15 | $title = $($popout, 'h1')[0], 16 | $infoContainer = $('.information-container')[0], 17 | $show = $('[data-show]')[0], 18 | $hide = $('[data-hide]')[0]; 19 | 20 | function show(e) { 21 | if(e.type === 'mouseup' || utils.keys.enter(e)) { 22 | $infoContainer.classList.remove('closed'); 23 | $infoContainer.focus(); 24 | $title.setAttribute('aria-expanded', true); 25 | tabbable(true); 26 | } 27 | } 28 | 29 | function hide(e) { 30 | if(e.type === 'mouseup' || utils.keys.esc(e) || utils.keys.enter(e)) { 31 | $infoContainer.classList.add('closed'); 32 | $title.setAttribute('aria-expanded', false); 33 | $title.focus(); 34 | 35 | tabbable(false); 36 | } 37 | } 38 | 39 | function tabbable(_bool) { 40 | var bool = _bool === true ? 0 : -1; 41 | 42 | $infoContainer.setAttribute('tabindex', bool); 43 | $($infoContainer, 'button')[0].setAttribute('tabindex', bool); 44 | } 45 | 46 | var popout = { 47 | show: show, 48 | hide: hide 49 | }; 50 | 51 | $title.setAttribute('tabindex', 0); 52 | tabbable(true); 53 | 54 | $infoContainer.addEventListener('keyup', popout.hide); 55 | addMultipleListeners($show, ['mouseup', 'keyup'], popout.show); 56 | addMultipleListeners($hide, ['mouseup', 'keyup'], popout.hide); 57 | }()); 58 | -------------------------------------------------------------------------------- /app/src/search/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IBM DL Motion Examples 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

IBM DL Motion Examples

14 | 42 |
43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/search/main-search.scss: -------------------------------------------------------------------------------- 1 | // Licensed Materials - Property of IBM 2 | // © Copyright IBM Corporation 2015. All Rights Reserved. 3 | // This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 4 | 5 | @import '../lib/styles/partials/variables'; 6 | @import '../lib/styles/partials/mixins'; 7 | @import '../lib/styles/partials/common'; 8 | @import '../top-nav/main-topnav'; 9 | 10 | $border: .7rem; 11 | $line-height: .875rem; 12 | 13 | .search-container { 14 | opacity: 0; 15 | position: absolute; 16 | right: 0; 17 | top: 0; 18 | transform: translateY(0); 19 | transition: transform $cubic-bezier; 20 | width: 350px; 21 | z-index: -1; 22 | 23 | &.open { 24 | opacity: 1; 25 | transform: translateY(80px); 26 | } 27 | } 28 | 29 | .search-loader { 30 | align-items: center; 31 | background: $super-light-gray; 32 | bottom: 0; 33 | display: flex; 34 | flex-flow: column wrap; 35 | justify-content: center; 36 | left: 0; 37 | opacity: 0; 38 | position: absolute; 39 | right: 0; 40 | top: 0; 41 | z-index: 99; 42 | 43 | &.showing { 44 | opacity: 1; 45 | } 46 | 47 | img { 48 | flex: 0 1 auto; 49 | height: 36px; 50 | margin-top: 48px; 51 | width: 36px; 52 | } 53 | } 54 | 55 | .results { 56 | background: $super-light-gray; 57 | margin: 0; 58 | padding: 0; 59 | 60 | span { 61 | display: inline-block; 62 | opacity: 0; 63 | transition: all .2s ease-in-out; 64 | } 65 | 66 | .enter { 67 | opacity: 1; 68 | transform: translateX(1.5rem); 69 | } 70 | } 71 | 72 | .result { 73 | font-size: .875rem; 74 | font-weight: 200; 75 | height: 0; 76 | opacity: 0; 77 | overflow: hidden; 78 | position: relative; 79 | transition: height $cubic-bezier; 80 | } 81 | 82 | .matched { 83 | background: $super-light-gray; 84 | float: none; 85 | height: 3rem; 86 | line-height: 3rem; 87 | opacity: 1; 88 | text-align: left; 89 | width: 350px; 90 | 91 | &:active, 92 | &:focus { 93 | background-color: $medium-gray; 94 | outline: none; 95 | transition: background-color 200ms; 96 | } 97 | } 98 | 99 | .glass { 100 | position: absolute; 101 | right: 1rem; 102 | top: .9rem; 103 | transition: $cubic-bezier; 104 | z-index: 999; 105 | 106 | img { 107 | height: 12px; 108 | width: 12px; 109 | } 110 | } 111 | 112 | form { 113 | overflow: hidden; 114 | position: relative; 115 | 116 | input { 117 | @include box-sizing(border-box); 118 | // this is important, so that we can override the default styles in Safari 119 | -webkit-appearance: none; 120 | background: $white; 121 | border: .7rem solid $active; 122 | display: block; 123 | font-family: inherit; 124 | font-size: .875rem; 125 | font-weight: 200; 126 | height: 55px; 127 | margin: 0; 128 | padding: 0 12px 0 8px; 129 | position: relative; 130 | transition: box-shadow .45s, border-color .45s ease-in-out, background-color 400ms; 131 | width: 100%; 132 | z-index: 999; 133 | 134 | &:focus, 135 | &:active { 136 | outline-width: 0; 137 | // used in conjunction with a timeout to briefly highlight the search input 138 | } 139 | } 140 | 141 | .flash { 142 | background-color: $light-yellow; 143 | transition-delay: 200ms; 144 | } 145 | 146 | &.searching { 147 | .glass { 148 | opacity: 0; 149 | transform: translateY(-2rem); 150 | } 151 | } 152 | } 153 | 154 | .number-of-results { 155 | left: -1000px; 156 | position: fixed; 157 | top: -1000px; 158 | } 159 | 160 | $elements: 100; 161 | $transition-speed:.25s; 162 | @for $i from 1 to $elements+1 { 163 | ul.results li:nth-child(#{$i}) { 164 | span { 165 | transition-delay: ($i*$transition-speed); 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /app/src/search/search.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var utils = utils || {}, 10 | components = components || {}; 11 | 12 | (function() { 13 | document.addEventListener('DOMContentLoaded', function() { 14 | // utilities 15 | var $ = utils.$, 16 | timeout = utils.timeout, 17 | SearchDataObject = components.SearchDataObject, 18 | addMultipleListeners = utils.addMultipleListeners, 19 | clearFlash, 20 | clearTimeout; 21 | 22 | // elements 23 | var $searchForm = $('#search'), 24 | $searchInput = $('.search-input')[0], 25 | $resultsContainer = $('.results')[0], 26 | data = ['Item 1','Item 2','Item 3','Item 4','Item 5'], 27 | dataObj = [], 28 | $searchContainer = $('.search-container')[0], 29 | $searchToggle = $('.search-toggler')[0], 30 | $searchLoader = $('.search-loader')[0]; 31 | 32 | function searchToggle(e) { 33 | var escapedWhileInsideResults = utils.keys.esc(e) && $searchContainer.classList.contains('open'); 34 | 35 | if((e.type === 'mouseup') || utils.keys.enter(e) || escapedWhileInsideResults) { 36 | $searchContainer.classList.toggle('open'); 37 | 38 | this.classList.toggle('active'); 39 | 40 | $searchInput.focus(); 41 | 42 | if($searchContainer.classList.contains('open') === false){ 43 | this.classList.remove('open'); 44 | $searchInput.value = ''; 45 | search(e); 46 | $('.directional-nav > ul')[0].focus(); 47 | } 48 | } 49 | } 50 | 51 | function keepOpen(e) { 52 | e.preventDefault(); 53 | e.stopPropagation(); 54 | 55 | return false; 56 | } 57 | 58 | function flash() { 59 | $searchInput.classList.add('flash'); 60 | clearFlash = timeout(function() { 61 | if(typeof clearFlash !== 'undefined') { 62 | clearFlash.clear(); 63 | } 64 | 65 | $searchInput.classList.remove('flash'); 66 | }, 1000); 67 | } 68 | 69 | // in IE10, refreshing page keeps old value and focus; we don't want that 70 | $searchInput.blur(); 71 | $searchInput.value = ''; 72 | 73 | // build each search object 74 | data.forEach(function(item) { 75 | var obj = new SearchDataObject(item); 76 | 77 | obj.build().placeElementIn($resultsContainer); 78 | 79 | dataObj.push(obj); 80 | }); 81 | 82 | // toggle the search box 83 | addMultipleListeners($searchToggle, ['mouseup', 'keyup'], searchToggle); 84 | $searchContainer.addEventListener('mouseup', keepOpen); 85 | $searchContainer.addEventListener('keyup', searchToggle); 86 | 87 | // search on keyup 88 | addMultipleListeners($searchInput, ['keyup', 'search', 'focus'], function(e) { 89 | var firstResult = $('.results li.matched')[0]; 90 | // call this (the specific key pressed is handled in the search function) 91 | search(e); 92 | 93 | // highlight the first search result 94 | // it will quietly fail if there are no results, because 95 | // the element will exist, but there will be no tabindex 96 | if(utils.keys.down(e) && typeof firstResult !== 'undefined') { 97 | if(+firstResult.getAttribute('tabindex') !== -1) { 98 | firstResult.focus(); 99 | } 100 | } 101 | 102 | if(e.type === 'focus') { 103 | flash(); 104 | } 105 | }); 106 | 107 | ///////////////////////////////////////////////////////////////////// 108 | 109 | function search(e) { 110 | var searchTerm, 111 | numberOfResults, 112 | query, 113 | $alert; 114 | 115 | if((!utils.keys.down(e) && !utils.keys.up(e)) || e.type === 'search') { 116 | 117 | searchTerm = new RegExp($searchInput.value, 'i'); 118 | 119 | if(typeof clearTimeout !== 'undefined') { 120 | clearTimeout.clear(); 121 | } 122 | 123 | $searchLoader.classList.add('showing'); 124 | 125 | if($searchInput.value.length > 0) { 126 | $searchForm.className = 'searching'; 127 | } else { 128 | $searchLoader.classList.remove('showing'); 129 | $searchForm.className = ''; 130 | } 131 | 132 | clearTimeout = timeout(function() { 133 | $searchLoader.classList.remove('showing'); 134 | }, 700); 135 | 136 | 137 | dataObj.forEach(function(o) { 138 | // this method sets the class of each item to show it or hide it 139 | o.test(searchTerm); 140 | }); 141 | 142 | if(e.type === 'keyup') { 143 | numberOfResults = $('#search li.matched').length; 144 | query = $searchInput.value; 145 | 146 | if(!!$('.number-of-results')[0]) { 147 | document.body.removeChild($('.number-of-results')[0]); 148 | } 149 | 150 | $alert = document.createElement('div'); 151 | $alert.className = 'number-of-results'; 152 | $alert.setAttribute('role', 'alert'); 153 | $alert.innerHTML = 'Search for ' + query + ' returned ' + numberOfResults + ' results'; 154 | 155 | document.body.appendChild($alert); 156 | } 157 | 158 | } 159 | } 160 | }); 161 | }()); 162 | -------------------------------------------------------------------------------- /app/src/side-nav/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IBM DL Motion Examples 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |

IBM DL Motion Examples

15 |
16 | 17 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/side-nav/main-sidenav.scss: -------------------------------------------------------------------------------- 1 | // Licensed Materials - Property of IBM 2 | // © Copyright IBM Corporation 2015. All Rights Reserved. 3 | // This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 4 | 5 | @import '../lib/styles/partials/variables'; 6 | @import '../lib/styles/partials/mixins'; 7 | @import '../lib/styles/partials/common'; 8 | 9 | $horizontal-menu-li-width:140px; 10 | $horizontal-menu-li-width-small: 100px; 11 | $vertical-menu-li-height: 50px; 12 | $vertical-menu-li-padding: 36px; 13 | $move-transition: cubic-bezier(.8, .005, .2, 1); 14 | 15 | .directional-nav { 16 | background: $blue; 17 | user-select: none; 18 | 19 | .active-bar-horizontal { 20 | bottom: 0; 21 | display: block; 22 | height: 0; 23 | left: 0; 24 | position: absolute; 25 | transition: height 200ms $move-transition; 26 | width: $horizontal-menu-li-width; 27 | z-index: 1; 28 | } 29 | 30 | .active-bar-vertical { 31 | background: $active; 32 | display: block; 33 | height: 86px; 34 | position: absolute; 35 | right: 0; 36 | top: 0; 37 | transition: width 200ms $move-transition; 38 | width: 0; 39 | } 40 | 41 | .active-bar-horizontal, 42 | .active-bar-vertical { 43 | &.move { 44 | transition: transform 500ms $move-transition; 45 | } 46 | } 47 | 48 | ul { 49 | list-style-type: none; 50 | padding: 0; 51 | } 52 | 53 | li:not(.result) { 54 | text-align: center; 55 | 56 | &:active, 57 | &:focus { 58 | outline: none; 59 | } 60 | } 61 | 62 | // #active-bar must be last element in list for this to work 63 | [data-active-bar="false"]:hover, 64 | [data-active-bar="false"]:active, 65 | [data-active-bar="false"]:focus { 66 | ~ .active-bar-horizontal, 67 | ~ .active-bar-vertical { 68 | display: none; 69 | } 70 | 71 | ~ .active-bar-horizontal { 72 | transform: translateX(0); 73 | } 74 | 75 | ~ .active-bar-vertical { 76 | transform: translateY(0); 77 | } 78 | } 79 | 80 | a { 81 | display: block; 82 | width: 100%; 83 | 84 | &:after { 85 | background: $active; 86 | bottom: 0; 87 | content: ''; 88 | position: absolute; 89 | right: 0; 90 | } 91 | 92 | span { 93 | color: $white; 94 | } 95 | } 96 | } 97 | 98 | .vertical.directional-nav { 99 | bottom: 0; 100 | left: 0; 101 | overflow: hidden; 102 | position: fixed; 103 | top: 0; 104 | width: 100px; 105 | z-index: 9999; 106 | 107 | &:not([data-on="false"]):hover { 108 | .active-bar-vertical { 109 | width: 6px; 110 | } 111 | } 112 | 113 | &:not([data-on="false"]) { 114 | li:focus ~ .active-bar-vertical, 115 | li:active ~ .active-bar-vertical { 116 | background: $active; 117 | width: 6px; 118 | } 119 | } 120 | 121 | // arbitrary large number larger than the total number 122 | // of menu items that would realistically fit in order 123 | // to avoid JS CSS injection 124 | $vertical-menu-items:10; 125 | @for $i from 0 to $vertical-menu-items { 126 | &.pos-#{$i} .active-bar-vertical { 127 | transform: translateY($i*($vertical-menu-li-height + $vertical-menu-li-padding)); 128 | } 129 | } 130 | 131 | ul { 132 | display: inline-block; 133 | margin: (116px + 80px) 0; 134 | position: relative; 135 | width: 100%; 136 | } 137 | 138 | .icon { 139 | padding: 5px 0 0; 140 | width: 50px; 141 | } 142 | 143 | .icon, 144 | span { 145 | position: relative; 146 | z-index: 99; 147 | } 148 | } 149 | 150 | .vertical li:not(.active-bar-vertical) { 151 | height: $vertical-menu-li-height + $vertical-menu-li-padding; 152 | width: 100%; 153 | 154 | &:focus, 155 | &:active { 156 | ~ .active-bar-vertical { 157 | background: $active; 158 | width: 6px; 159 | } 160 | } 161 | 162 | &.active a { 163 | &:after { 164 | width: 100%; 165 | } 166 | } 167 | 168 | a { 169 | height: 100%; 170 | position: relative; 171 | 172 | &:after { 173 | height: 100%; 174 | transition: width $cubic-bezier; 175 | width: 0; 176 | } 177 | } 178 | 179 | span { 180 | display: block; 181 | 182 | font-size: .9rem; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /app/src/side-nav/side-nav.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var utils = utils || {}, 10 | components = components || {}; 11 | 12 | (function() { 13 | document.addEventListener('DOMContentLoaded', function() { 14 | return new components.Menu(function() { 15 | var config = { 16 | direction: 'vertical', 17 | $menu: document.getElementById('side-nav') 18 | }; 19 | 20 | return config; 21 | }); 22 | }); 23 | }()); 24 | -------------------------------------------------------------------------------- /app/src/top-nav/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IBM DL Motion Examples 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

IBM DL Motion Examples

14 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/top-nav/main-topnav.scss: -------------------------------------------------------------------------------- 1 | // Licensed Materials - Property of IBM 2 | // © Copyright IBM Corporation 2015. All Rights Reserved. 3 | // This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 4 | 5 | @import '../lib/styles/partials/variables'; 6 | @import '../lib/styles/partials/mixins'; 7 | @import '../lib/styles/partials/common'; 8 | 9 | $horizontal-menu-li-width:140px; 10 | $horizontal-menu-li-width-small: 100px; 11 | $vertical-menu-li-height: 50px; 12 | $vertical-menu-li-padding: 36px; 13 | $move-transition: cubic-bezier(.8, .005, .2, 1); 14 | 15 | .directional-nav { 16 | background: $blue; 17 | user-select: none; 18 | 19 | .active-bar-horizontal { 20 | bottom: 0; 21 | display: block; 22 | height: 0; 23 | left: 0; 24 | position: absolute; 25 | transition: height 200ms $move-transition; 26 | width: $horizontal-menu-li-width; 27 | z-index: 1; 28 | } 29 | 30 | .active-bar-vertical { 31 | background: $active; 32 | display: block; 33 | height: 86px; 34 | position: absolute; 35 | right: 0; 36 | top: 0; 37 | transition: width 200ms $move-transition; 38 | width: 0; 39 | } 40 | 41 | .active-bar-horizontal, 42 | .active-bar-vertical { 43 | &.move { 44 | transition: transform 500ms $move-transition; 45 | } 46 | } 47 | 48 | ul { 49 | list-style-type: none; 50 | padding: 0; 51 | } 52 | 53 | li:not(.result) { 54 | text-align: center; 55 | 56 | &:active, 57 | &:focus { 58 | outline: none; 59 | } 60 | } 61 | 62 | // #active-bar must be last element in list for this to work 63 | [data-active-bar="false"]:hover, 64 | [data-active-bar="false"]:active, 65 | [data-active-bar="false"]:focus { 66 | ~ .active-bar-horizontal, 67 | ~ .active-bar-vertical { 68 | display: none; 69 | } 70 | 71 | ~ .active-bar-horizontal { 72 | transform: translateX(0); 73 | } 74 | 75 | ~ .active-bar-vertical { 76 | transform: translateY(0); 77 | } 78 | } 79 | 80 | a { 81 | display: block; 82 | width: 100%; 83 | 84 | &:after { 85 | background: $active; 86 | bottom: 0; 87 | content: ''; 88 | position: absolute; 89 | right: 0; 90 | } 91 | 92 | span { 93 | color: $white; 94 | } 95 | } 96 | } 97 | 98 | .horizontal.directional-nav { 99 | position: absolute; 100 | right: 0; 101 | top: 0; 102 | 103 | &:not([data-on="false"]):hover { 104 | .active-bar-horizontal { 105 | background: $active; 106 | height: 6px; 107 | } 108 | } 109 | 110 | &:not([data-on="false"]) { 111 | li:focus ~ .active-bar-horizontal, 112 | li:active ~ .active-bar-horizontal { 113 | background: $active; 114 | height: 6px; 115 | } 116 | } 117 | 118 | // arbitrary number to avoid JavaScript CSS injection 119 | $horizontal-menu-items:10; 120 | @for $i from 0 to $horizontal-menu-items { 121 | &.pos-#{$i} .active-bar-horizontal { 122 | display: block; 123 | transform: translateX($i*$horizontal-menu-li-width); 124 | } 125 | } 126 | 127 | ul { 128 | float: right; 129 | margin: 0; 130 | position: relative; 131 | width: 100%; 132 | } 133 | } 134 | 135 | 136 | .horizontal li:not(.active-bar-horizontal):not(.result) { 137 | float: left; 138 | position: relative; 139 | width: $horizontal-menu-li-width; 140 | 141 | &.active a { 142 | &:after { 143 | height: 100%; 144 | } 145 | } 146 | 147 | &[data-icon] { 148 | transition: background-color 500ms cubic-bezier(0, 0, 0, 1); 149 | width: $horizontal-menu-li-width-small; 150 | 151 | &:active, 152 | &:focus { 153 | background-color: $active; 154 | } 155 | } 156 | 157 | img { 158 | height: 1.75em; 159 | position: relative; 160 | top: 10%; 161 | z-index: 3; 162 | } 163 | 164 | a { 165 | height: 80px; 166 | line-height: 80px; 167 | 168 | &:after { 169 | height: 0; 170 | transition: height $cubic-bezier; 171 | width: 100%; 172 | z-index: 2; 173 | } 174 | } 175 | 176 | span { 177 | position: relative; 178 | z-index: 3; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /app/src/top-nav/top-nav.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed Materials - Property of IBM 3 | 4 | © Copyright IBM Corporation 2015. All Rights Reserved. 5 | 6 | This licensed material is licensed under the Apache 2.0 license. http://www.apache.org/licenses/LICENSE-2.0. 7 | */ 8 | 9 | var utils = utils || {}, 10 | components = components || {}; 11 | 12 | (function() { 13 | document.addEventListener('DOMContentLoaded', function() { 14 | return new components.Menu(function() { 15 | var config = { 16 | $menu: document.getElementById('top-nav') 17 | }; 18 | 19 | return config; 20 | }); 21 | }); 22 | }()); 23 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'), 4 | sass = require('gulp-sass'), 5 | concat = require('gulp-concat'), 6 | autoprefixer = require('gulp-autoprefixer'), 7 | browserSync = require('browser-sync'), 8 | rename = require('gulp-rename'), 9 | jshint = require('gulp-jshint'), 10 | stylish = require('jshint-stylish'), 11 | scssLint = require('gulp-scss-lint'), 12 | reload = browserSync.reload; 13 | 14 | var css = (function() { 15 | return { 16 | compile: function(config) { 17 | gulp.src('app/src/' + config.src + '.scss') 18 | .pipe(scssLint({ 19 | 'config': 'scss-lint.yml', 20 | })); 21 | 22 | gulp.src('app/src/' + config.src + '.scss') 23 | .pipe(sass()) 24 | .pipe(autoprefixer()) 25 | .pipe(rename(config.rename + '.css')) 26 | .pipe(gulp.dest('app/dist/' + config.dest)) 27 | .pipe(reload({stream: true})); 28 | 29 | return this; 30 | } 31 | }; 32 | }()); 33 | 34 | var js = (function() { 35 | return { 36 | compile: function(config) { 37 | gulp.src('app/src/' + config.src + '.js') 38 | .pipe(jshint()) 39 | .pipe(jshint.reporter('jshint-stylish')) 40 | .pipe(gulp.dest('app/dist/' + config.dest)); 41 | 42 | return this; 43 | } 44 | }; 45 | }()); 46 | 47 | gulp.task('sass', function() { 48 | return css 49 | .compile({ 50 | src: 'drawer/main-drawer', 51 | rename: 'drawer', 52 | dest: 'drawer' 53 | }) 54 | .compile({ 55 | src: 'drop-down/main-dropdown', 56 | rename: 'drop-down', 57 | dest: 'drop-down' 58 | }) 59 | .compile({ 60 | src: 'pop-up/main-popup', 61 | rename: 'pop-up', 62 | dest: 'pop-up' 63 | }) 64 | .compile({ 65 | src: 'search/main-search', 66 | rename: 'search', 67 | dest: 'search' 68 | }) 69 | .compile({ 70 | src: 'side-nav/main-sidenav', 71 | rename: 'side-nav', 72 | dest: 'side-nav' 73 | }) 74 | .compile({ 75 | src: 'top-nav/main-topnav', 76 | rename: 'top-nav', 77 | dest: 'top-nav' 78 | }) 79 | .compile({ 80 | src: 'lib/styles/main', 81 | rename: 'main', 82 | dest: 'lib' 83 | }); 84 | 85 | }); 86 | 87 | gulp.task('js', function() { 88 | gulp.src([ 89 | 'app/src/lib/scripts/utils/utils.js', 90 | 'app/src/lib/scripts/utils/*.js', 91 | 'app/src/lib/scripts/utils/components/components.js', 92 | 'app/src/lib/scripts/utils/components/*.js' 93 | ]) 94 | .pipe(jshint()) 95 | .pipe(jshint.reporter('jshint-stylish')) 96 | .pipe(concat('scripts.js')) 97 | .pipe(gulp.dest('app/dist/lib')); 98 | 99 | return js 100 | .compile({ 101 | src: 'drawer/drawer', 102 | dest: 'drawer' 103 | }) 104 | .compile({ 105 | src: 'drop-down/drop-down', 106 | dest: 'drop-down' 107 | }) 108 | .compile({ 109 | src: 'pop-up/pop-up', 110 | dest: 'pop-up' 111 | }) 112 | .compile({ 113 | src: 'search/search', 114 | dest: 'search' 115 | }) 116 | .compile({ 117 | src: 'side-nav/side-nav', 118 | dest: 'side-nav' 119 | }) 120 | .compile({ 121 | src: 'top-nav/top-nav', 122 | dest: 'top-nav' 123 | }); 124 | }); 125 | 126 | gulp.task('html', function() { 127 | gulp.src('app/src/index.html').pipe(gulp.dest('app/dist')); 128 | gulp.src('app/src/drawer/index.html').pipe(gulp.dest('app/dist/drawer')); 129 | gulp.src('app/src/drop-down/index.html').pipe(gulp.dest('app/dist/drop-down')); 130 | gulp.src('app/src/pop-up/index.html').pipe(gulp.dest('app/dist/pop-up')); 131 | gulp.src('app/src/search/index.html').pipe(gulp.dest('app/dist/search')); 132 | gulp.src('app/src/side-nav/index.html').pipe(gulp.dest('app/dist/side-nav')); 133 | gulp.src('app/src/top-nav/index.html').pipe(gulp.dest('app/dist/top-nav')); 134 | }); 135 | 136 | gulp.task('serve', ['sass','js'], function() { 137 | browserSync.init({ 138 | server: { 139 | baseDir: 'app/dist', 140 | routes: { 141 | '/assets': './app/assets' 142 | } 143 | } 144 | }); 145 | }); 146 | 147 | gulp.task('watch', function() { 148 | gulp.watch(['app/src/**/**/**/*.scss'], ['sass']); 149 | gulp.watch(['app/src/**/**/**/*.js'], ['js']); 150 | gulp.watch('app/**/**/*.html', ['html', reload]); 151 | }); 152 | 153 | gulp.task('default', ['serve', 'watch', 'html']); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "motion", 3 | "author": "IBM", 4 | "license": "Apache-2.0", 5 | "scripts": { 6 | "start": "gulp" 7 | }, 8 | "dependencies": { 9 | "browser-sync": "^2.7.1", 10 | "gulp": "^3.8.11", 11 | "gulp-autoprefixer": "^2.3.0", 12 | "gulp-concat": "^2.5.2", 13 | "gulp-jshint": "^1.11.0", 14 | "gulp-rename": "^1.2.2", 15 | "gulp-sass": "^2.0.1", 16 | "gulp-scss-lint": "^0.2.0", 17 | "jshint-stylish": "^2.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /scss-lint.yml: -------------------------------------------------------------------------------- 1 | # default config from https://github.com/causes/scss-lint/blob/master/config/default.yml 2 | linters: 3 | # documented at https://github.com/causes/scss-lint/blob/master/lib/scss_lint/linter/README.md 4 | BorderZero: 5 | enabled: true 6 | 7 | CapitalizationInSelector: 8 | enabled: true 9 | 10 | ColorKeyword: 11 | enabled: true 12 | 13 | Comment: 14 | enabled: false 15 | 16 | DebugStatement: 17 | enabled: true 18 | 19 | DeclarationOrder: 20 | enabled: true 21 | 22 | DuplicateProperty: 23 | enabled: true 24 | 25 | EmptyLineBetweenBlocks: 26 | enabled: true 27 | ignore_single_line_blocks: true 28 | 29 | EmptyRule: 30 | enabled: true 31 | 32 | FinalNewline: 33 | enabled: false 34 | present: false 35 | 36 | HexFormat: 37 | enabled: true 38 | 39 | IdWithExtraneousSelector: 40 | enabled: true 41 | 42 | Indentation: 43 | enabled: true 44 | width: 2 45 | 46 | LeadingZero: 47 | enabled: true 48 | style: exclude_zero # or 'include_zero' 49 | 50 | NameFormat: 51 | enabled: true 52 | convention: hyphenated_lowercase # or 'BEM', or a regex pattern 53 | 54 | SelectorFormat: 55 | enabled: true 56 | convention: hyphenated_lowercase # or 'BEM', or a regex pattern 57 | ignored_names: ['ng\:cloak', 'new_window'] 58 | 59 | PlaceholderInExtend: 60 | enabled: true 61 | 62 | PropertySortOrder: 63 | enabled: false 64 | 65 | PropertySpelling: 66 | enabled: true 67 | extra_properties: [] 68 | 69 | SelectorDepth: 70 | enabled: true 71 | max_depth: 5 72 | 73 | NestingDepth: 74 | enabled: true 75 | max_depth: 5 76 | 77 | Shorthand: 78 | enabled: true 79 | 80 | SingleLinePerSelector: 81 | enabled: false 82 | 83 | SpaceAfterComma: 84 | enabled: true 85 | 86 | SpaceAfterPropertyColon: 87 | enabled: true 88 | 89 | SpaceAfterPropertyName: 90 | enabled: true 91 | 92 | SpaceBeforeBrace: 93 | enabled: true 94 | allow_single_line_padding: false 95 | 96 | SpaceBetweenParens: 97 | enabled: true 98 | spaces: 0 99 | 100 | StringQuotes: 101 | enabled: true 102 | style: single_quotes # or double_quotes 103 | 104 | TrailingSemicolonAfterPropertyValue: 105 | enabled: true 106 | 107 | UnnecessaryMantissa: 108 | enabled: true 109 | 110 | UrlFormat: 111 | enabled: false 112 | 113 | UrlQuotes: 114 | enabled: true 115 | 116 | ZeroUnit: 117 | enabled: true 118 | 119 | Compass::*: 120 | enabled: false 121 | 122 | HexValidation: 123 | enabled: false 124 | 125 | VendorPrefixes: 126 | enabled: true 127 | 128 | QualifyingElement: 129 | enabled: true 130 | 131 | ImportantRule: 132 | enabled: false 133 | --------------------------------------------------------------------------------