From 8fee10ccd2414ec2c8e2457de691bd10ca0d1aef Mon Sep 17 00:00:00 2001 From: Rick Dullaart Date: Fri, 18 Nov 2022 21:40:36 +0100 Subject: [PATCH] Initial commit --- CONTRIBUTING.md | 47 +++++++++++ README.md | 21 +++++ bin/demo.css | 145 ++++++++++++++++++++++++++++++++++ bin/mburger.js | 80 +++++++++++++++++++ bin/webcomponent.css | 1 + composer.json | 16 ++++ dist/mburger.css | 10 +++ dist/mburger.js | 80 +++++++++++++++++++ gulpfile.js | 72 +++++++++++++++++ index.html | 157 +++++++++++++++++++++++++++++++++++++ package.json | 34 ++++++++ src/scss/_base.scss | 85 ++++++++++++++++++++ src/scss/_fx.collapse.scss | 49 ++++++++++++ src/scss/_fx.spin.scss | 36 +++++++++ src/scss/_fx.squeeze.scss | 33 ++++++++ src/scss/_fx.tornado.scss | 39 +++++++++ src/scss/_variables.scss | 49 ++++++++++++ src/scss/mburger.scss | 18 +++++ src/scss/webcomponent.scss | 24 ++++++ src/ts/mburger.ts | 98 +++++++++++++++++++++++ tsconfig.json | 8 ++ 21 files changed, 1102 insertions(+) create mode 100644 CONTRIBUTING.md create mode 100644 README.md create mode 100644 bin/demo.css create mode 100644 bin/mburger.js create mode 100644 bin/webcomponent.css create mode 100644 composer.json create mode 100644 dist/mburger.css create mode 100644 dist/mburger.js create mode 100644 gulpfile.js create mode 100644 index.html create mode 100644 package.json create mode 100644 src/scss/_base.scss create mode 100644 src/scss/_fx.collapse.scss create mode 100644 src/scss/_fx.spin.scss create mode 100644 src/scss/_fx.squeeze.scss create mode 100644 src/scss/_fx.tornado.scss create mode 100644 src/scss/_variables.scss create mode 100644 src/scss/mburger.scss create mode 100644 src/scss/webcomponent.scss create mode 100644 src/ts/mburger.ts create mode 100644 tsconfig.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..dfeec2c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,47 @@ +# Contributing to this project + +Please take a moment to review this document in order to make the contribution +process easy and effective for everyone involved. + + +## Using the issue tracker + +The issue tracker is the preferred channel for [bug reports](#bugs) and +[features requests](#features), but please respect the following restrictions: + +* Please **do not** use the issue tracker for personal support requests. + +* Please keep the discussion **on topic** and respect the opinions of others. + + + +## Bug reports + +A bug is a _demonstrable problem_ that is caused by the code in the repository. +Good bug reports are extremely helpful - thank you! + +Guidelines for bug reports: + +1. **Use the GitHub issue search** — check if the issue has already been + reported. + +2. **Check if the issue has been fixed** — try to reproduce it using the + latest branch in the repository. + +3. **Isolate the problem** — create a [reduced test + case](http://css-tricks.com/reduced-test-cases/) and a live example. + +A good bug report shouldn't leave others needing to chase you up for more +information. Please try to be as detailed as possible in your report. What is +your environment? What steps will reproduce the issue? What browser(s) and OS +experience the problem? What would you expect to be the outcome? All these +details will help people to fix any potential bugs. + + + +## Feature requests + +Feature requests are welcome. But take a moment to find out whether your idea +fits with the scope and aims of the project. It's up to *you* to make a strong +case to convince the project's developers of the merits of this feature. Please +provide as much detail and context as possible. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a2562dc --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +mburger CSS +================ + +A small collection of CSS animated hamburgers. All set up to work out of the box with the mmenu.js plugin.
+[Examples](https://www.mmenujs.com/mburger) + +CSS animated hamburgers + +### Customize the hamburger +By default, the hamburger adopts to its environment pretty good, +the bars scale to fit and inherit their color for the parent element.
+ +Need help? Have a look at [the documentation](http://mmenujs.com/mburger) for examples and documentation. + +### Licence +The mburger CSS is licensed under the [CC-BY-4.0 license](http://creativecommons.org/licenses/by/4.0/). + +### Development +This project uses [Gulp(4)](http://gulpjs.com/) to transpile and minify SCSS to CSS. +If you are unfamiliar with Gulp, check [this tutorial](https://travismaynard.com/writing/getting-started-with-gulp) on how to get started.
+Run `gulp watch` in the command-line to put a watch on the files and run all scripts immediately after saving your changes. diff --git a/bin/demo.css b/bin/demo.css new file mode 100644 index 0000000..c5454e4 --- /dev/null +++ b/bin/demo.css @@ -0,0 +1,145 @@ +html, +body { + padding: 0; + margin: 0; + height: 100%; +} +body { + position: relative; + background-color: #3ea7e1; + -webkit-text-size-adjust: none; + font-family: Arial, Helvetica, Verdana; + font-size: 20px; + line-height: 30px; + color: #fff; +} +h1 { + margin: 0 0 100px; + text-shadow: 8px 10px 1px rgba(0,0,0,.1); + text-transform: lowercase; + font-family: 'Pacifico', Arial, sans-serif; + font-weight: normal; + font-size: 150px; + line-height: 1; + letter-spacing: -6px; +} + +pre { + font-size: 16px; + line-height: 24px; + width: 100%; + overflow: auto; + margin: 75px 0; +} + +button.reset { + appearance: none; + padding: 0; + margin: 0; + box-sizing: content-box; + border: 0; + background: none; + outline: none; + color: inherit; + font-size: inherit; + text-align: left; + cursor: pointer; +} + +@media (max-width: 500px) { + h1 { + font-size: 105px; + letter-spacing: -2px; + } +} + +a, +a:hover { + color: #fff; + text-decoration: underline; +} + +#page { + max-width: 600px; + min-width: 300px; + width: 90%; + padding: 100px 10px; + margin: auto; +} + +.xmpls { + display: flex; + flex-wrap: wrap; + justify-content: center; + margin: 75px -10px; +} + +.xmpl { + position: relative; + box-sizing: border-box; + width: calc( 50% - 20px ); + padding-top: calc( 50% - 20px ); + margin: 10px; + background: rgba(255, 255, 255, 0.05); + box-shadow: 6px 8px 1px rgba(0,0,0,.1); + transition: none 0.2s ease; + transition-property: background-color, box-shadow, transform; + text-align: center; + cursor: pointer; +} +.xmpl:hover { + background: rgba(255, 255, 255, 0.15); + box-shadow: 4px 6px 0 rgba(0,0,0,.15); + transform: translateY(2px); +} + +@media (min-width: 1280px) { + .xmpls { + margin-left: -320px; + margin-right: -320px; + } + .xmpl { + width: calc( 25% - 20px ); + padding-top: calc( 25% - 20px ); + } +} +.xmpl > button, +.xmpl > m-burger { + --mb-animate-timeout: 0s; + + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} +.xmpl > span { + position: absolute; + bottom: 10%; + left: 0; + right: 0; +} + +.xmpl .custom-button-1 { + padding: 5px 25px 5px 15px; + background-color: rgba( 255, 255, 255, 0.7 ); + color: #3ea7e1; + border-radius: 30px; + white-space: nowrap; +} +.xmpl .custom-button-1 .mburger { + --mb-animate-timeout: 0s; + --mb-button-size: 40px; + --mb-bar-height: 3px; + --mb-bar-spacing: 6px; +} +.xmpl .custom-button-2 { + --mb-animate-timeout: 0s; + --mb-button-size: 80px; + --mb-bar-width: 0.5; + --mb-bar-height: 2px; + --mb-bar-spacing: 12px; + + border-radius: 40px; + background-color: rgba( 255, 255, 255, 0.7 ); + color: #3ea7e1; +} diff --git a/bin/mburger.js b/bin/mburger.js new file mode 100644 index 0000000..13ecb23 --- /dev/null +++ b/bin/mburger.js @@ -0,0 +1,80 @@ +/* + * mburger webcomponent v1.3.3 + * mmenujs.com/mburger + * + * Copyright (c) Fred Heusschen + * www.frebsite.nl + * + * License: CC-BY-4.0 + * http://creativecommons.org/licenses/by/4.0/ + */ +export const mBurger = document.createElement('template'); +mBurger.innerHTML = ` + + + + + `; +customElements.define('m-burger', class extends HTMLElement { + constructor() { + super(); + /** The menu node. */ + this.menuNode = null; + /** API for the menu. */ + this.menuApi = null; + // Attach shadow DOM + var content = mBurger.content.cloneNode(true); + this.attachShadow({ mode: 'open' }).appendChild(content); + } + static get observedAttributes() { + return ['menu']; + } + attributeChangedCallback(name, oldValue, newValue) { + if (name == 'menu') { + // Initiate the new menu. + this.initMenu(newValue); + } + } + connectedCallback() { + // Open the menu when clicking the hamburger. + this.addEventListener('click', evnt => { + // If there is no API for a menu available (the menu isn't yet initiated), + // try to initiate the menu. + if (!this.menuApi) { + this.initMenu(); + } + // If there is an API for a menu available, + // open the menu. + if (this.menuApi && this.menuApi.open) { + this.menuApi.open(); + } + }); + } + /** + * Set the menu node and API. + * @param {string} [id] The ID-attribute for the menu node. + */ + initMenu(id) { + this.menuNode = null; + this.menuApi = null; + if (!id) { + id = this.getAttribute('menu'); + } + if (id) { + this.menuNode = document.getElementById(id); + } + if (this.menuNode) { + this.menuApi = + this.menuNode['mmApi'] || this.menuNode['mmenu'] || null; + } + // Change the hamburger state when opening and closing the menu. + if (this.menuApi) { + this.menuApi.bind('open:after', () => { + this.setAttribute('state', 'cross'); + }); + this.menuApi.bind('close:after', () => { + this.removeAttribute('state'); + }); + } + } +}); diff --git a/bin/webcomponent.css b/bin/webcomponent.css new file mode 100644 index 0000000..6f0259d --- /dev/null +++ b/bin/webcomponent.css @@ -0,0 +1 @@ +:host{--mb-button-size:60px;--mb-bar-width:0.6;--mb-bar-height:4px;--mb-bar-spacing:10px;--mb-animate-timeout:0s}:host{background:0 0;border:none;border-radius:0;color:inherit;display:inline-block;position:relative;box-sizing:border-box;height:var(--mb-button-size);padding:0 0 0 var(--mb-button-size);margin:0;line-height:var(--mb-button-size);vertical-align:middle;appearance:none;outline:0;cursor:pointer}:host b{display:block;position:absolute;left:calc(var(--mb-button-size) * ((1 - var(--mb-bar-width))/ 2));width:calc(var(--mb-button-size) * var(--mb-bar-width));height:var(--mb-bar-height);border-radius:calc(var(--mb-bar-height)/ 2);background:currentColor;color:inherit;opacity:1}:host b:nth-of-type(1){bottom:calc(50% + var(--mb-bar-spacing));transition:bottom .2s ease,transform .2s ease,width .2s ease}:host b:nth-of-type(2){top:calc(50% - (var(--mb-bar-height)/ 2));transition:opacity .2s ease}:host b:nth-of-type(3){top:calc(50% + var(--mb-bar-spacing));transition:top .2s ease,transform .2s ease,width .2s ease}:host([state=cross]) b:nth-of-type(1){bottom:calc(50% - (var(--mb-bar-height)/ 2));transform:rotate(45deg)}:host([state=cross]) b:nth-of-type(2){opacity:0}:host([state=cross]) b:nth-of-type(3){top:calc(50% - (var(--mb-bar-height)/ 2));transform:rotate(-45deg)}:host([fx=collapse]) b:nth-of-type(1){transition:bottom .2s ease,margin .2s ease,transform .2s ease;transition-delay:.2s,0s,0s}:host([fx=collapse]) b:nth-of-type(2){transition:top .2s ease,opacity 0s ease;transition-delay:.3s,.3s}:host([fx=collapse]) b:nth-of-type(3){transition:top .2s ease,transform .2s ease}:host([state=cross][fx=collapse]) b:nth-of-type(1){bottom:calc(50% - var(--mb-bar-spacing) - var(--mb-bar-height));margin-bottom:calc(var(--mb-bar-spacing) + (var(--mb-bar-height)/ 2));transform:rotate(45deg);transition-delay:calc(var(--mb-animate-timeout) + .1s),calc(var(--mb-animate-timeout) + .3s),calc(var(--mb-animate-timeout) + .3s)}:host([state=cross][fx=collapse]) b:nth-of-type(2){top:calc(50% + var(--mb-bar-spacing));opacity:0;transition-delay:calc(var(--mb-animate-timeout) + 0s),calc(var(--mb-animate-timeout) + .2s)}:host([state=cross][fx=collapse]) b:nth-of-type(3){top:calc(50% - (var(--mb-bar-height)/ 2));transform:rotate(-45deg);transition-delay:calc(var(--mb-animate-timeout) + .3s),calc(var(--mb-animate-timeout) + .3s)}:host([fx=spin]) b:nth-of-type(1){transition-delay:.2s,0s}:host([fx=spin]) b:nth-of-type(2){transition-duration:0s;transition-delay:.2s}:host([fx=spin]) b:nth-of-type(3){transition-delay:.2s,0s}:host([state=cross][fx=spin]) b:nth-of-type(1){transform:rotate(135deg);transition-delay:calc(var(--mb-animate-timeout) + 0s),calc(var(--mb-animate-timeout) + .2s)}:host([state=cross][fx=spin]) b:nth-of-type(2){transition-delay:calc(var(--mb-animate-timeout) + 0s)}:host([state=cross][fx=spin]) b:nth-of-type(3){transform:rotate(225deg);transition-delay:calc(var(--mb-animate-timeout) + 0s),calc(var(--mb-animate-timeout) + .2s)}:host([fx=squeeze]) b:nth-of-type(1){transition-delay:.1s,0s}:host([fx=squeeze]) b:nth-of-type(2){transition-delay:.1s}:host([fx=squeeze]) b:nth-of-type(3){transition-delay:.1s,0s}:host([state=cross][fx=squeeze]) b:nth-of-type(1){transition-delay:calc(var(--mb-animate-timeout) + 0s),calc(var(--mb-animate-timeout) + .1s)}:host([state=cross][fx=squeeze]) b:nth-of-type(2){transition-delay:calc(var(--mb-animate-timeout) + 0s)}:host([state=cross][fx=squeeze]) b:nth-of-type(3){transition-delay:calc(var(--mb-animate-timeout) + 0s),calc(var(--mb-animate-timeout) + .1s)}:host([fx=tornado]) b:nth-of-type(1){transition:bottom .2s ease,transform .2s ease;transition-delay:.2s}:host([fx=tornado]) b:nth-of-type(2){transition:opacity 0s ease,transform .2s ease;transition-delay:.1s,.1s}:host([fx=tornado]) b:nth-of-type(3){transition:top .2s ease,transform .2s ease;transition-delay:0s}:host([state=cross][fx=tornado]) b:nth-of-type(1){transform:rotate(-135deg);transition-delay:calc(var(--mb-animate-timeout) + 0s)}:host([state=cross][fx=tornado]) b:nth-of-type(2){opacity:0;transform:rotate(-135deg);transition-delay:calc(var(--mb-animate-timeout) + .4s),calc(var(--mb-animate-timeout) + .1s)}:host([state=cross][fx=tornado]) b:nth-of-type(3){transform:rotate(-225deg);transition-delay:calc(var(--mb-animate-timeout) + .2s)} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..3910c7f --- /dev/null +++ b/composer.json @@ -0,0 +1,16 @@ +{ + "name": "mburger-css", + "version": "1.3.3", + "authors": "Fred Heusschen ", + "license": "CC-BY-4.0", + "description": "A small collection of CSS animated hamburgers. All set up to work out of the box with the mmenu.js plugin.", + "keywords": [ + "hamburger", + "icon", + "CSS", + "animation", + "animated", + "menu", + "navigation" + ] +} diff --git a/dist/mburger.css b/dist/mburger.css new file mode 100644 index 0000000..e95e6d2 --- /dev/null +++ b/dist/mburger.css @@ -0,0 +1,10 @@ +/*! + * mburger CSS v1.3.3 + * mmenujs.com/mburger + * + * Copyright (c) Fred Heusschen + * www.frebsite.nl + * + * License: CC-BY-4.0 + * http://creativecommons.org/licenses/by/4.0/ + */:root{--mb-button-size:60px;--mb-bar-width:0.6;--mb-bar-height:4px;--mb-bar-spacing:10px;--mb-animate-timeout:0.4s}.mburger{background:0 0;border:none;border-radius:0;color:inherit;display:inline-block;position:relative;box-sizing:border-box;height:var(--mb-button-size);padding:0 0 0 var(--mb-button-size);margin:0;line-height:var(--mb-button-size);vertical-align:middle;appearance:none;outline:0;cursor:pointer}.mburger b{display:block;position:absolute;left:calc(var(--mb-button-size) * ((1 - var(--mb-bar-width))/ 2));width:calc(var(--mb-button-size) * var(--mb-bar-width));height:var(--mb-bar-height);border-radius:calc(var(--mb-bar-height)/ 2);background:currentColor;color:inherit;opacity:1}.mburger b:nth-of-type(1){bottom:calc(50% + var(--mb-bar-spacing));transition:bottom .2s ease,transform .2s ease,width .2s ease}.mburger b:nth-of-type(2){top:calc(50% - (var(--mb-bar-height)/ 2));transition:opacity .2s ease}.mburger b:nth-of-type(3){top:calc(50% + var(--mb-bar-spacing));transition:top .2s ease,transform .2s ease,width .2s ease}.mm-wrapper_opened .mburger b:nth-of-type(1){bottom:calc(50% - (var(--mb-bar-height)/ 2));transform:rotate(45deg)}.mm-wrapper_opened .mburger b:nth-of-type(2){opacity:0}.mm-wrapper_opened .mburger b:nth-of-type(3){top:calc(50% - (var(--mb-bar-height)/ 2));transform:rotate(-45deg)}.mburger--collapse b:nth-of-type(1){transition:bottom .2s ease,margin .2s ease,transform .2s ease;transition-delay:.2s,0s,0s}.mburger--collapse b:nth-of-type(2){transition:top .2s ease,opacity 0s ease;transition-delay:.3s,.3s}.mburger--collapse b:nth-of-type(3){transition:top .2s ease,transform .2s ease}.mm-wrapper_opened .mburger--collapse b:nth-of-type(1){bottom:calc(50% - var(--mb-bar-spacing) - var(--mb-bar-height));margin-bottom:calc(var(--mb-bar-spacing) + (var(--mb-bar-height)/ 2));transform:rotate(45deg);transition-delay:calc(var(--mb-animate-timeout) + .1s),calc(var(--mb-animate-timeout) + .3s),calc(var(--mb-animate-timeout) + .3s)}.mm-wrapper_opened .mburger--collapse b:nth-of-type(2){top:calc(50% + var(--mb-bar-spacing));opacity:0;transition-delay:calc(var(--mb-animate-timeout) + 0s),calc(var(--mb-animate-timeout) + .2s)}.mm-wrapper_opened .mburger--collapse b:nth-of-type(3){top:calc(50% - (var(--mb-bar-height)/ 2));transform:rotate(-45deg);transition-delay:calc(var(--mb-animate-timeout) + .3s),calc(var(--mb-animate-timeout) + .3s)}.mburger--spin b:nth-of-type(1){transition-delay:.2s,0s}.mburger--spin b:nth-of-type(2){transition-duration:0s;transition-delay:.2s}.mburger--spin b:nth-of-type(3){transition-delay:.2s,0s}.mm-wrapper_opened .mburger--spin b:nth-of-type(1){transform:rotate(135deg);transition-delay:calc(var(--mb-animate-timeout) + 0s),calc(var(--mb-animate-timeout) + .2s)}.mm-wrapper_opened .mburger--spin b:nth-of-type(2){transition-delay:calc(var(--mb-animate-timeout) + 0s)}.mm-wrapper_opened .mburger--spin b:nth-of-type(3){transform:rotate(225deg);transition-delay:calc(var(--mb-animate-timeout) + 0s),calc(var(--mb-animate-timeout) + .2s)}.mburger--squeeze b:nth-of-type(1){transition-delay:.1s,0s}.mburger--squeeze b:nth-of-type(2){transition-delay:.1s}.mburger--squeeze b:nth-of-type(3){transition-delay:.1s,0s}.mm-wrapper_opened .mburger--squeeze b:nth-of-type(1){transition-delay:calc(var(--mb-animate-timeout) + 0s),calc(var(--mb-animate-timeout) + .1s)}.mm-wrapper_opened .mburger--squeeze b:nth-of-type(2){transition-delay:calc(var(--mb-animate-timeout) + 0s)}.mm-wrapper_opened .mburger--squeeze b:nth-of-type(3){transition-delay:calc(var(--mb-animate-timeout) + 0s),calc(var(--mb-animate-timeout) + .1s)}.mburger--tornado b:nth-of-type(1){transition:bottom .2s ease,transform .2s ease;transition-delay:.2s}.mburger--tornado b:nth-of-type(2){transition:opacity 0s ease,transform .2s ease;transition-delay:.1s,.1s}.mburger--tornado b:nth-of-type(3){transition:top .2s ease,transform .2s ease;transition-delay:0s}.mm-wrapper_opened .mburger--tornado b:nth-of-type(1){transform:rotate(-135deg);transition-delay:calc(var(--mb-animate-timeout) + 0s)}.mm-wrapper_opened .mburger--tornado b:nth-of-type(2){opacity:0;transform:rotate(-135deg);transition-delay:calc(var(--mb-animate-timeout) + .4s),calc(var(--mb-animate-timeout) + .1s)}.mm-wrapper_opened .mburger--tornado b:nth-of-type(3){transform:rotate(-225deg);transition-delay:calc(var(--mb-animate-timeout) + .2s)} \ No newline at end of file diff --git a/dist/mburger.js b/dist/mburger.js new file mode 100644 index 0000000..9e4e9dc --- /dev/null +++ b/dist/mburger.js @@ -0,0 +1,80 @@ +/* + * mburger webcomponent v1.3.3 + * mmenujs.com/mburger + * + * Copyright (c) Fred Heusschen + * www.frebsite.nl + * + * License: CC-BY-4.0 + * http://creativecommons.org/licenses/by/4.0/ + */ +export const mBurger = document.createElement('template'); +mBurger.innerHTML = ` + + + + + `; +customElements.define('m-burger', class extends HTMLElement { + constructor() { + super(); + /** The menu node. */ + this.menuNode = null; + /** API for the menu. */ + this.menuApi = null; + // Attach shadow DOM + var content = mBurger.content.cloneNode(true); + this.attachShadow({ mode: 'open' }).appendChild(content); + } + static get observedAttributes() { + return ['menu']; + } + attributeChangedCallback(name, oldValue, newValue) { + if (name == 'menu') { + // Initiate the new menu. + this.initMenu(newValue); + } + } + connectedCallback() { + // Open the menu when clicking the hamburger. + this.addEventListener('click', evnt => { + // If there is no API for a menu available (the menu isn't yet initiated), + // try to initiate the menu. + if (!this.menuApi) { + this.initMenu(); + } + // If there is an API for a menu available, + // open the menu. + if (this.menuApi && this.menuApi.open) { + this.menuApi.open(); + } + }); + } + /** + * Set the menu node and API. + * @param {string} [id] The ID-attribute for the menu node. + */ + initMenu(id) { + this.menuNode = null; + this.menuApi = null; + if (!id) { + id = this.getAttribute('menu'); + } + if (id) { + this.menuNode = document.getElementById(id); + } + if (this.menuNode) { + this.menuApi = + this.menuNode['mmApi'] || this.menuNode['mmenu'] || null; + } + // Change the hamburger state when opening and closing the menu. + if (this.menuApi) { + this.menuApi.bind('open:after', () => { + this.setAttribute('state', 'cross'); + }); + this.menuApi.bind('close:after', () => { + this.removeAttribute('state'); + }); + } + } +}); diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..2057ce6 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,72 @@ +/* + Tasks: + + $ gulp : Runs all tasks. + $ gulp watch : Starts a watch on all tasks. + $ gulp css : Runs "css" task. + $ gulp webcomponent : Runs "webcomponent" tasks. +*/ + +const { src, dest, watch, parallel, series } = require('gulp'); + +const sass = require('gulp-sass'); +const cleancss = require('gulp-clean-css'); +// const concat = require('gulp-concat'); +const typescript = require('gulp-typescript'); +const replace = require('gulp-replace'); +const fs = require('fs'); + +const inputDir = 'src'; +const outputDir = 'dist'; +const binDir = 'bin'; + +const css = cb => { + return src(inputDir + '/scss/mburger.scss') + .pipe(sass().on('error', sass.logError)) + .pipe(cleancss()) + .pipe(dest(outputDir)); +}; + +const webcomponentJs = cb => { + return src([ + inputDir + '/ts/*.d.ts', // Include all typings. + inputDir + '/ts/*.ts' // Include the needed ts files. + ]) + .pipe( + typescript({ + target: 'es6', + module: 'es6' + }) + ) + .pipe(dest(binDir)); +}; + +const webcomponentCss = cb => { + return src(inputDir + '/scss/webcomponent.scss') + .pipe(sass().on('error', sass.logError)) + .pipe(cleancss()) + .pipe(dest(binDir)); +}; + +const webcomponentConcat = cb => { + var styles = fs.readFileSync(binDir + '/webcomponent.css'); + return src(binDir + '/mburger.js') + .pipe(replace('[__STYLES__]', styles)) + .pipe(dest(outputDir)); +}; + +const webcomponent = series( + parallel(webcomponentCss, webcomponentJs), + webcomponentConcat +); + +const watchTask = cb => { + watch(inputDir + '/scss/*.scss', parallel(css, webcomponent)); + watch(inputDir + '/ts/*.ts', webcomponent); + cb(); +}; + +exports.default = parallel(css, webcomponent); +exports.watch = watchTask; +exports.css = css; +exports.webcomponent = webcomponent; diff --git a/index.html b/index.html new file mode 100644 index 0000000..76466d1 --- /dev/null +++ b/index.html @@ -0,0 +1,157 @@ + + + + + + + + + mburger, CSS animated hamburgers! + + + + + + + + +
+ +

mburger

+

A small collection of CSS animated hamburgers. + All set up to work out of the box with the mmenu.js plugin. + Click a hamburger to see the animation. + More info here.

+ +
+ +
+ + collapse +
+
+ + spin +
+
+ + squeeze +
+
+ + tornado +
+ +
+ +
+<html>
+   <head>
+      <style type="text/css" rel="stylesheet" href="dist/mburger.css">
+   </heady>
+   <body>
+      <button class="mburger mburger--spin" href="#my-menu">
+         <b</b>
+         <b</b>
+         <b</b>
+      </button>
+   </body>
+</html>
+
+ +

Customize the hamburger

+

By default, the hamburger adopts to its environment pretty good, + the bars scale to fit and inherit their color for the parent element.

+

The hamburger is pretty easy to customize too, + just override some of the CSS values and variables.

+ +
+
+ +
+ +
+ +
+ +
+ +

For more examples and the full documentation, please visit: mmenujs.com/mburger.

+ +
+ +

Native webcomponent

+

Note that -at the time of writing (early 2019)- the native webcomponent is only (fully) supported in Chrome.

+ +
+
+ + collapse +
+ +
+ + spin +
+
+ +
+<html>
+   <head>
+      <script type="module" src="dist/mburger.js"></script>
+   </heady>
+   <body>
+      <m-burger fx="spin" menu="my-menu"></m-burger>
+   </body>
+</html>
+
+ +
+ + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..bbc3ad3 --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "name": "mburger-css", + "version": "1.3.3", + "main": "dist/mburger.css", + "module": "dist/mburger.js", + "author": "Fred Heusschen ", + "license": "CC-BY-4.0", + "repository": { + "type": "git", + "url": "https://github.com/FrDH/mburger-css.git" + }, + "description": "A small collection of CSS animated hamburgers. All set up to work out of the box with the mmenu.js plugin.", + "keywords": [ + "hamburger", + "icon", + "CSS", + "animation", + "animated", + "menu", + "navigation" + ], + "scripts": { + "build": "gulp default" + }, + "devDependencies": { + "fs": "0.0.1-security", + "gulp": "^4.0.0", + "gulp-clean-css": "^4.0.0", + "gulp-replace": "^1.0.0", + "gulp-sass": "^4.0.2", + "gulp-typescript": "^5.0.1", + "typescript": "^3.4.3" + } +} diff --git a/src/scss/_base.scss b/src/scss/_base.scss new file mode 100644 index 0000000..ab95c3e --- /dev/null +++ b/src/scss/_base.scss @@ -0,0 +1,85 @@ +#{$mb_root} { + /** Size for the button. */ + --mb-button-size: 60px; + + /** Width for the bars, relative to the button. */ + --mb-bar-width: 0.6; + + /** Height for the bars.*/ + --mb-bar-height: 4px; + + /** Distance between bars (approximately). */ + --mb-bar-spacing: 10px; + + /** Timeout before starting the animation, ensures the animation starts after the menu is fully opened. */ + --mb-animate-timeout: #{$mb_animate_timeout}; +} + +#{$mb_module} { + // Overridable values + background: transparent; + border: none; + border-radius: 0; + color: inherit; + + // Button + display: inline-block; + position: relative; + box-sizing: border-box; + height: var(--mb-button-size); + padding: 0 0 0 var(--mb-button-size); + margin: 0; + line-height: var(--mb-button-size); + vertical-align: middle; + appearance: none; + outline: none; + cursor: pointer; + + // Hamburger + b { + display: block; + position: absolute; + left: calc(var(--mb-button-size) * ((1 - var(--mb-bar-width)) / 2)); + width: calc(var(--mb-button-size) * var(--mb-bar-width)); + height: var(--mb-bar-height); + border-radius: calc(var(--mb-bar-height) / 2); + background: currentColor; + color: inherit; + opacity: 1; + + // Bar 1 + &:nth-of-type(1) { + bottom: calc(50% + var(--mb-bar-spacing)); + transition: bottom 0.2s ease, transform 0.2s ease, width 0.2s ease; + } + + // Bar 2 + &:nth-of-type(2) { + top: calc(50% - (var(--mb-bar-height) / 2)); + transition: opacity 0.2s ease; + } + + // Bar 3 + &:nth-of-type(3) { + top: calc(50% + var(--mb-bar-spacing)); + transition: top 0.2s ease, transform 0.2s ease, width 0.2s ease; + } + } +} + +// Cross +#{$mb_module_cross} { + b { + &:nth-of-type(1) { + bottom: calc(50% - (var(--mb-bar-height) / 2)); + transform: rotate(45deg); + } + &:nth-of-type(2) { + opacity: 0; + } + &:nth-of-type(3) { + top: calc(50% - (var(--mb-bar-height) / 2)); + transform: rotate(-45deg); + } + } +} diff --git a/src/scss/_fx.collapse.scss b/src/scss/_fx.collapse.scss new file mode 100644 index 0000000..7d03aef --- /dev/null +++ b/src/scss/_fx.collapse.scss @@ -0,0 +1,49 @@ +@if ($mb_fx_collapse) { + // Hamburger + #{$mb_module_collapse} { + b { + &:nth-of-type(1) { + transition: bottom 0.2s ease, margin 0.2s ease, + transform 0.2s ease; + transition-delay: 0.2s, 0s, 0s; + } + &:nth-of-type(2) { + transition: top 0.2s ease, opacity 0s ease; + transition-delay: 0.3s, 0.3s; + } + &:nth-of-type(3) { + transition: top 0.2s ease, transform 0.2s ease; + } + } + } + + // Cross + #{$mb_module_collapse_cross} { + b { + &:nth-of-type(1) { + bottom: calc( + 50% - var(--mb-bar-spacing) - var(--mb-bar-height) + ); + margin-bottom: calc( + var(--mb-bar-spacing) + (var(--mb-bar-height) / 2) + ); + transform: rotate(45deg); + transition-delay: calc(var(--mb-animate-timeout) + 0.1s), + calc(var(--mb-animate-timeout) + 0.3s), + calc(var(--mb-animate-timeout) + 0.3s); + } + &:nth-of-type(2) { + top: calc(50% + var(--mb-bar-spacing)); + opacity: 0; + transition-delay: calc(var(--mb-animate-timeout) + 0s), + calc(var(--mb-animate-timeout) + 0.2s); + } + &:nth-of-type(3) { + top: calc(50% - (var(--mb-bar-height) / 2)); + transform: rotate(-45deg); + transition-delay: calc(var(--mb-animate-timeout) + 0.3s), + calc(var(--mb-animate-timeout) + 0.3s); + } + } + } +} diff --git a/src/scss/_fx.spin.scss b/src/scss/_fx.spin.scss new file mode 100644 index 0000000..241905e --- /dev/null +++ b/src/scss/_fx.spin.scss @@ -0,0 +1,36 @@ +@if ($mb_fx_spin) { + // Hamburger + #{$mb_module_spin} { + b { + &:nth-of-type(1) { + transition-delay: 0.2s, 0s; + } + &:nth-of-type(2) { + transition-duration: 0s; + transition-delay: 0.2s; + } + &:nth-of-type(3) { + transition-delay: 0.2s, 0s; + } + } + } + + // Cross + #{$mb_module_spin_cross} { + b { + &:nth-of-type(1) { + transform: rotate(135deg); + transition-delay: calc(var(--mb-animate-timeout) + 0s), + calc(var(--mb-animate-timeout) + 0.2s); + } + &:nth-of-type(2) { + transition-delay: calc(var(--mb-animate-timeout) + 0s); + } + &:nth-of-type(3) { + transform: rotate(225deg); + transition-delay: calc(var(--mb-animate-timeout) + 0s), + calc(var(--mb-animate-timeout) + 0.2s); + } + } + } +} diff --git a/src/scss/_fx.squeeze.scss b/src/scss/_fx.squeeze.scss new file mode 100644 index 0000000..f6da98f --- /dev/null +++ b/src/scss/_fx.squeeze.scss @@ -0,0 +1,33 @@ +@if ($mb_fx_squeeze) { + // Hamburger + #{$mb_module_squeeze} { + b { + &:nth-of-type(1) { + transition-delay: 0.1s, 0s; + } + &:nth-of-type(2) { + transition-delay: 0.1s; + } + &:nth-of-type(3) { + transition-delay: 0.1s, 0s; + } + } + } + + // Cross + #{$mb_module_squeeze_cross} { + b { + &:nth-of-type(1) { + transition-delay: calc(var(--mb-animate-timeout) + 0s), + calc(var(--mb-animate-timeout) + 0.1s); + } + &:nth-of-type(2) { + transition-delay: calc(var(--mb-animate-timeout) + 0s); + } + &:nth-of-type(3) { + transition-delay: calc(var(--mb-animate-timeout) + 0s), + calc(var(--mb-animate-timeout) + 0.1s); + } + } + } +} diff --git a/src/scss/_fx.tornado.scss b/src/scss/_fx.tornado.scss new file mode 100644 index 0000000..1d00f81 --- /dev/null +++ b/src/scss/_fx.tornado.scss @@ -0,0 +1,39 @@ +@if ($mb_fx_tornado) { + // Hamburger + #{$mb_module_tornado} { + b { + &:nth-of-type(1) { + transition: bottom 0.2s ease, transform 0.2s ease; + transition-delay: 0.2s; + } + &:nth-of-type(2) { + transition: opacity 0s ease, transform 0.2s ease; + transition-delay: 0.1s, 0.1s; + } + &:nth-of-type(3) { + transition: top 0.2s ease, transform 0.2s ease; + transition-delay: 0s; + } + } + } + + // Cross + #{$mb_module_tornado_cross} { + b { + &:nth-of-type(1) { + transform: rotate(-135deg); + transition-delay: calc(var(--mb-animate-timeout) + 0s); + } + &:nth-of-type(2) { + opacity: 0; + transform: rotate(-135deg); + transition-delay: calc(var(--mb-animate-timeout) + 0.4s), + calc(var(--mb-animate-timeout) + 0.1s); + } + &:nth-of-type(3) { + transform: rotate(-225deg); + transition-delay: calc(var(--mb-animate-timeout) + 0.2s); + } + } + } +} diff --git a/src/scss/_variables.scss b/src/scss/_variables.scss new file mode 100644 index 0000000..a6faf40 --- /dev/null +++ b/src/scss/_variables.scss @@ -0,0 +1,49 @@ +/** Selector for the root */ +$mb_root: ':root' !default; + +/** Selector for the button. */ +$mb_module: '.mburger' !default; + +$__opened: '.mm-wrapper_opened'; + +/** Selector for the button with the "collapse" effect. */ +$mb_module_collapse: '#{$mb_module}--collapse' !default; + +/** Selector for the button with the "spin" effect. */ +$mb_module_spin: '#{$mb_module}--spin' !default; + +/** Selector for the button with the "squeeze" effect. */ +$mb_module_squeeze: '#{$mb_module}--squeeze' !default; + +/** Selector for the button with the "tornado" effect. */ +$mb_module_tornado: '#{$mb_module}--tornado' !default; + +/** Selector for the button when the menu is opened. */ +$mb_module_cross: '#{$__opened} #{$mb_module}' !default; + +/** Selector for the button with the "collapse" effect when the menu is opened. */ +$mb_module_collapse_cross: '#{$__opened} #{$mb_module_collapse}' !default; + +/** Selector for the button with the "spin" effect when the menu is opened. */ +$mb_module_spin_cross: '#{$__opened} #{$mb_module_spin}' !default; + +/** Selector for the button with the "squeeze" effect when the menu is opened. */ +$mb_module_squeeze_cross: '#{$__opened} #{$mb_module_squeeze}' !default; + +/** Selector for the button with the "tornado" effect when the menu is opened. */ +$mb_module_tornado_cross: '#{$__opened} #{$mb_module_tornado}' !default; + +/** Timeout before starting the animation, ensures the animation starts after the menu is fully opened. */ +$mb_animate_timeout: 0.4s !default; + +/** Whether or not to include the CSS for the "collapse" animation. */ +$mb_fx_collapse: true !default; + +/** Whether or not to include the CSS for the "spin" animation. */ +$mb_fx_spin: true !default; + +/** Whether or not to include the CSS for the "squeeze" animation. */ +$mb_fx_squeeze: true !default; + +/** Whether or not to include the CSS for the "tornado" animation. */ +$mb_fx_tornado: true !default; diff --git a/src/scss/mburger.scss b/src/scss/mburger.scss new file mode 100644 index 0000000..c4f5d6f --- /dev/null +++ b/src/scss/mburger.scss @@ -0,0 +1,18 @@ +/*! + * mburger CSS v1.3.3 + * mmenujs.com/mburger + * + * Copyright (c) Fred Heusschen + * www.frebsite.nl + * + * License: CC-BY-4.0 + * http://creativecommons.org/licenses/by/4.0/ + */ + +@import 'variables'; +@import 'base'; + +@import 'fx.collapse'; +@import 'fx.spin'; +@import 'fx.squeeze'; +@import 'fx.tornado'; diff --git a/src/scss/webcomponent.scss b/src/scss/webcomponent.scss new file mode 100644 index 0000000..226bf43 --- /dev/null +++ b/src/scss/webcomponent.scss @@ -0,0 +1,24 @@ +// Selectors for the button. +$mb_root: ':host'; +$mb_module: ':host'; +$mb_module_collapse: ':host( [fx="collapse"] )'; +$mb_module_spin: ':host( [fx="spin"] )'; +$mb_module_squeeze: ':host( [fx="squeeze"] )'; +$mb_module_tornado: ':host( [fx="tornado"] )'; + +// Selector for the button when the menu is opened. +$mb_module_cross: ':host( [state="cross"] )'; +$mb_module_collapse_cross: ':host( [state="cross"][fx="collapse"] )'; +$mb_module_spin_cross: ':host( [state="cross"][fx="spin"] )'; +$mb_module_squeeze_cross: ':host( [state="cross"][fx="squeeze"] )'; +$mb_module_tornado_cross: ':host( [state="cross"][fx="tornado"] )'; + +$mb_animate_timeout: 0s; + +@import 'variables'; +@import 'base'; + +@import 'fx.collapse'; +@import 'fx.spin'; +@import 'fx.squeeze'; +@import 'fx.tornado'; diff --git a/src/ts/mburger.ts b/src/ts/mburger.ts new file mode 100644 index 0000000..d2f0756 --- /dev/null +++ b/src/ts/mburger.ts @@ -0,0 +1,98 @@ +/* + * mburger webcomponent v1.3.3 + * mmenujs.com/mburger + * + * Copyright (c) Fred Heusschen + * www.frebsite.nl + * + * License: CC-BY-4.0 + * http://creativecommons.org/licenses/by/4.0/ + */ + +export const mBurger = document.createElement('template'); +mBurger.innerHTML = ` + + + + + `; + +customElements.define( + 'm-burger', + class extends HTMLElement { + /** The menu node. */ + menuNode: HTMLElement = null; + + /** API for the menu. */ + menuApi: { + bind: Function; + open: Function; + } = null; + + constructor() { + super(); + + // Attach shadow DOM + var content = mBurger.content.cloneNode(true); + this.attachShadow({ mode: 'open' }).appendChild(content); + } + + static get observedAttributes() { + return ['menu']; + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name == 'menu') { + // Initiate the new menu. + this.initMenu(newValue); + } + } + + connectedCallback() { + // Open the menu when clicking the hamburger. + this.addEventListener('click', evnt => { + // If there is no API for a menu available (the menu isn't yet initiated), + // try to initiate the menu. + if (!this.menuApi) { + this.initMenu(); + } + + // If there is an API for a menu available, + // open the menu. + if (this.menuApi && this.menuApi.open) { + this.menuApi.open(); + } + }); + } + + /** + * Set the menu node and API. + * @param {string} [id] The ID-attribute for the menu node. + */ + initMenu(id?: string) { + this.menuNode = null; + this.menuApi = null; + + if (!id) { + id = this.getAttribute('menu'); + } + if (id) { + this.menuNode = document.getElementById(id); + } + if (this.menuNode) { + this.menuApi = + this.menuNode['mmApi'] || this.menuNode['mmenu'] || null; + } + + // Change the hamburger state when opening and closing the menu. + if (this.menuApi) { + this.menuApi.bind('open:after', () => { + this.setAttribute('state', 'cross'); + }); + this.menuApi.bind('close:after', () => { + this.removeAttribute('state'); + }); + } + } + } +); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..cc44c7a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,8 @@ +{ + "include": [ + "src/**/*" + ], + "compilerOptions": { + "target": "es6" + } +}