├── .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 `