├── .editorconfig ├── .gitignore ├── Gruntfile.js ├── LICENSE.txt ├── README.md ├── bower.json ├── dist ├── ml-stack-nav-theme.css ├── ml-stack-nav-theme.css.map ├── ml-stack-nav-theme.min.css ├── ml-stack-nav-theme.min.css.map ├── ml-stack-nav.css ├── ml-stack-nav.css.map ├── ml-stack-nav.js ├── ml-stack-nav.js.map ├── ml-stack-nav.min.css ├── ml-stack-nav.min.css.map ├── ml-stack-nav.min.js └── ml-stack-nav.min.js.map ├── index.html ├── package-lock.json ├── package.json └── src ├── _variables.scss ├── ml-stack-nav-theme.scss ├── ml-stack-nav.js └── ml-stack-nav.scss /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.scss] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [{*.json,*.yml}] 16 | indent_style = space 17 | indent_size = 2 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # https://git-scm.com/docs/gitignore 2 | # https://help.github.com/articles/ignoring-files/ 3 | .sass-cache 4 | .ruby-version 5 | .idea 6 | .vscode 7 | bower_components 8 | node_modules 9 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | var pkg = grunt.file.readJSON('package.json'), 4 | globalConfig = { 5 | pkg: pkg, 6 | getBanner: function (raw) { 7 | var prefix = raw ? '' : '/*!\n', 8 | newLinePrefix = raw ? '' : ' * ', 9 | suffix = raw ? '' : '\n */'; 10 | 11 | return prefix + 12 | newLinePrefix + pkg.name + ' - v' + pkg.version + '\n' + 13 | newLinePrefix + pkg.description + '\n' + 14 | newLinePrefix + pkg.homepage + '\n' + 15 | newLinePrefix + '\n' + 16 | newLinePrefix + 'Author: ' + pkg.author + '\n' + 17 | newLinePrefix + 'License: ' + pkg.license + '\n' + 18 | newLinePrefix + '\n' + 19 | newLinePrefix + '@preserve' + 20 | suffix; 21 | } 22 | }; 23 | 24 | grunt.initConfig({ 25 | pkg: pkg, 26 | jshint: { 27 | files: ['Gruntfile.js', 'src/**/*.js'], 28 | options: { 29 | globals: { 30 | jQuery: true 31 | } 32 | } 33 | }, 34 | concat: { 35 | options: { 36 | banner: globalConfig.getBanner() + "\n", 37 | sourceMap: true 38 | }, 39 | dist: { 40 | src: ['src/<%= pkg.name %>.js'], 41 | dest: 'dist/<%= pkg.name %>.js' 42 | } 43 | }, 44 | uglify: { 45 | options: { 46 | banner: globalConfig.getBanner(), 47 | sourceMap: true 48 | }, 49 | dist: { 50 | src: 'src/<%= pkg.name %>.js', 51 | dest: 'dist/<%= pkg.name %>.min.js' 52 | } 53 | }, 54 | sass: { 55 | dist: { 56 | options: { 57 | style: 'expanded' 58 | }, 59 | files: [{ 60 | expand: true, 61 | cwd: 'src', 62 | src: ['**/*.scss'], 63 | dest: 'dist', 64 | ext: '.css' 65 | }] 66 | } 67 | }, 68 | cssmin: { 69 | options: { 70 | sourceMap: true 71 | }, 72 | dist: { 73 | files: [ 74 | { 75 | expand: true, 76 | cwd: 'dist', 77 | src: ['**/*.css', '!**/*.min.css'], 78 | dest: 'dist', 79 | ext: '.min.css' 80 | } 81 | ] 82 | } 83 | }, 84 | postcss: { 85 | options: { 86 | map: true, 87 | processors: [ 88 | require('postcss-banner')({ 89 | banner: globalConfig.getBanner(true), 90 | important: true 91 | }), 92 | require('autoprefixer')({ 93 | remove: false 94 | }) 95 | ] 96 | }, 97 | dist: { 98 | src: 'dist/*.css' 99 | } 100 | }, 101 | browserSync: { 102 | bsFiles: { 103 | src: [ 104 | 'dist/**/*.css', 105 | 'dist/**/*.js', 106 | 'index.html' 107 | ] 108 | }, 109 | options: { 110 | watchTask: true, 111 | server: { 112 | baseDir: "./" 113 | } 114 | } 115 | }, 116 | watch: { 117 | js: { 118 | files: ['<%= jshint.files %>'], 119 | tasks: ['jshint', 'concat', 'uglify'] 120 | }, 121 | sass: { 122 | files: ['src/**/*.scss'], 123 | tasks: ['sass', 'postcss', 'cssmin'] 124 | } 125 | } 126 | }); 127 | 128 | grunt.loadNpmTasks('grunt-contrib-jshint'); 129 | grunt.loadNpmTasks('grunt-contrib-concat'); 130 | grunt.loadNpmTasks('grunt-contrib-uglify'); 131 | grunt.loadNpmTasks('grunt-contrib-sass'); 132 | grunt.loadNpmTasks('grunt-contrib-cssmin'); 133 | grunt.loadNpmTasks('grunt-postcss'); 134 | grunt.loadNpmTasks('grunt-browser-sync'); 135 | grunt.loadNpmTasks('grunt-contrib-watch'); 136 | 137 | grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'sass', 'postcss', 'cssmin', 'browserSync', 'watch']); 138 | 139 | }; 140 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Damian Wajer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multi-level stack navigation (jQuery mlStackNav) 2 | > Customizable, accessible, easy-to-use multi-level stack navigation menu. 3 | 4 | The purpose behind this project is to provide a fully functional, responsive and usable navigation with minimal styling, which can also be used as a good starting point to implement your own custom design. 5 | 6 | ## Demo 7 | 8 | https://damianwajer.github.io/ml-stack-nav/ 9 | 10 | ## Requirements 11 | 12 | - [jQuery](https://github.com/jquery/jquery) >= 1.7 13 | 14 | ## Browser support 15 | 16 | - All modern browsers such as Chrome, Firefox, Safari, Edge (last 2 versions) and Internet Explorer 10+ 17 | - Stock browser on Android 4.0+ and Safari on iOS 7+ 18 | - Internet Explorer 9 is also supported but without transition animations and with some additional CSS (I recommend placing IE 9 rules within conditional comment in `` section of the HTML file or in some separate `.css` file with other IE 9 fixes): 19 | 20 | ```html 21 | 32 | ``` 33 | 34 | ## Download 35 | 36 | - via [bower](https://bower.io): `bower install ml-stack-nav` 37 | - via [npm](https://www.npmjs.com/package/ml-stack-nav): `npm install ml-stack-nav` 38 | - from the [releases page](https://github.com/damianwajer/ml-stack-nav/releases) 39 | 40 | ## Usage 41 | 42 | To get started just add required styles: 43 | 44 | ```html 45 | 46 | ``` 47 | 48 | Include jQuery: 49 | 50 | ```html 51 | 52 | ``` 53 | 54 | …and the plugin JS file: 55 | 56 | ```html 57 | 58 | ``` 59 | 60 | Navigation HTML markup should be placed at the top-level position directly under the `body` element in the document to avoid the stacking context issues. 61 | 62 | ```html 63 | 64 | 65 | 66 | ``` 67 | 68 | Please see `index.html` file for demo and code example. 69 | 70 | Finally, initiate the plugin: 71 | 72 | ```javascript 73 | $(".js-ml-stack-nav").mlStackNav(); 74 | ``` 75 | 76 | The `dist/ml-stack-nav.js` and `dist/ml-stack-nav.css` files contain all what is necessary for the menu to work properly and they should not be edited directly. Unlike them, `dist/ml-stack-nav-theme.css` theme file provides example styles for the navigation and can be edited, extended or completely replaced with your own styles (the minified files `.min.css` and `.min.js` are also available in `dist` directory and you can use them on production instead of regular ones). 77 | 78 | ## Advanced usage 79 | 80 | The goal behind the mlStackNav is to provide a good starting point to implement navigation with custom design, so advanced usage is highly recommended. In order to take full advantage of the mlStackNav, please keep in mind its most important features: 81 | 82 | - separation of the appearance and the functionality 83 | - CSS class names according to BEM methodology 84 | - state rules inspired by SMACSS (Scalable and Modular Architecture for CSS) 85 | - `js-` prefixed classes to decouple JavaScript classes from CSS ones 86 | 87 | ### Multiple instances 88 | 89 | Multi-level stack navigation also supports multiple instances of the menu. 90 | 91 | You can use it to implement as many navigation menus as you want by simply initializing different menus with separate toggle buttons. For example: 92 | 93 | ```javascript 94 | $(".js-ml-stack-nav").mlStackNav({ 95 | navToggleSelector: ".js-ml-stack-nav-toggle" 96 | }); 97 | 98 | $(".js-ml-stack-nav-2").mlStackNav({ 99 | navToggleSelector: ".js-ml-stack-nav-2-toggle" 100 | }); 101 | ``` 102 | 103 | or by providing jQuery selector inside `data-nav-toggle` attribute: 104 | 105 | ```html 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | ``` 114 | 115 | ### Navigation with JavaScript disabled 116 | 117 | mlStackNav can still works in its basic form even when JavaScript is disabled. You just need to make sure that: 118 | 119 | - `no-js` class is present on the top-level element on your page (e.g. on `html` element: `` 120 | - `no-js` is removed before mlStackNav initialization, e.g. `$("html").removeClass("no-js")` 121 | - a link with the anchor matching the navigation `id` is used instead of a button (e.g. `` and `