I would like some opinions on this Mobile Menu plugin I wrote for work. We develop custom WordPress themes, nothing too advanced or anything. I would like if anyone could just take a look and provide any pointers on what I could make better. This is my first actual plugin, so I am open to any critiques.
The functionality is simple, you can define any HTML element to act as a trigger to open a panel that holds the menu. The panel can either fade in, or slide into the viewport from any direction. Menus with submenus reveal their submenus with an accordion type toggle. I only coded the needed CSS to make it work, since the theming of it should be left to the developer to make it fit their project.
A demo is here
HTML (this example uses a button, but anything can be a trigger):
<button class="cm-hamburger">
<span></span>
<span></span>
<span></span>
</button>
<nav id="panel" class="cm-panel">
<ul>
<li>
<a href="#">Link</a>
<ul>
<li><a href="#">Sub Link</a></li>
<li><a href="#">Sub Link</a></li>
<li><a href="#">Sub Link</a></li>
</ul>
</li>
<li><a href="#">Link</a></li>
<li>
<a href="#">Link</a>
<ul>
<li><a href="#">Sub Link</a></li>
<li><a href="#">Sub Link</a></li>
<li><a href="#">Sub Link</a></li>
</ul>
</li>
<li><a href="#">Link</a></li>
</ul>
</nav>
Calling with jQuery:
$( function() {
$( '#panel' ).clearmenu( {
hasSubMenu: true,
} );
} );
CSS (Sass):
// Colors
// -----------------------------------------------------------------------------
$cm_color-white: rgb(255, 255, 255) !default;
$cm_color-black: rgb(0, 0, 0) !default;
$cm_color-panel: rgb(239, 239, 239) !default;
// Panel Settings
// -----------------------------------------------------------------------------
$cm_panel-width: 100% !default;
$cm_panel-height: 100% !default;
$cm_panel-position: absolute !default;
$cm_panel-top: 0 !default;
$cm_panel-speed: 0.25s !default;
$cm_panel-transition: ease-in-out !default;
/* -------------------------------------------------------------------------------------------------
# Custom Normalize
------------------------------------------------------------------------------------------------- */
menu,
nav {
display: block;
}
a {
background-color: transparent;
-webkit-text-decoration-skip: objects;
&:active,
&:hover {
outline-width: 0;
}
}
button {
font: inherit;
margin: 0;
overflow: visible;
text-transform: none;
-webkit-appearance: button;
&::-moz-focus-inner {
border-style: none;
padding: 0;
}
&:-moz-focusring {
outline: 1px dotted ButtonText;
}
}
/* -------------------------------------------------------------------------------------------------
# Panel
------------------------------------------------------------------------------------------------- */
.cm-panel {
position: $cm_panel-position;
top: $cm_panel-top;
left: 0;
z-index: 99;
width: $cm_panel-width;
max-width: 100%;
height: $cm_panel-height;
max-height: 100%;
background: $cm_color-panel;
opacity: 1;
visibility: hidden;
transition: all $cm_panel-speed $cm_panel-transition;
-webkit-transform: translateZ(0);
-webkit-backface-visibility: hidden;
// Reveal from the top
&-top {
transform: translateY(-100%);
}
// Reveal from the bottom
&-bottom {
transform: translateY(100%);
top: unset;
bottom: 0;
}
// Reveal from the left
&-left {
transform: translateX(-100%);
}
// Reveal from the right
&-right {
transform: translateX(100%);
left: unset;
right: 0;
}
// Fade in reveal
&-fade {
opacity: 0;
}
// For when the panel is visible
&-open {
visibility: visible;
transform: translateY(0);
opacity: 1;
}
}
/* -------------------------------------------------------------------------------------------------
# Menu(s)
------------------------------------------------------------------------------------------------- */
.cm-panel {
> ul {
margin: 0 auto;
padding: 0;
position: relative;
width: 90%;
display: block;
list-style: none;
li {
position: relative;
> ul {
display: none;
margin: 0;
padding: 0;
position: relative;
a {
position: relative;
font-size: 1rem;
padding: 0.5em 2em;
}
}
}
a {
position: relative;
display: block;
padding: 0.25em 0;
overflow: hidden;
text-decoration: none;
}
}
}
/* -------------------------------------------------------------------------------------------------
# Hamburger
------------------------------------------------------------------------------------------------- */
.cm-hamburger {
position: relative;
border: none;
width: 35px;
height: 25px;
transform: rotate(0deg);
transition: all $cm_panel-speed $cm_panel-transition;
cursor: pointer;
background: $cm_color-white;
span {
display: block;
position: absolute;
height: 5px;
width: 100%;
background: $cm_color-black;
border-radius: 9px;
opacity: 1;
left: 0;
transform: rotate(0deg);
transition: all $cm_panel-speed $cm_panel-transition;
&:nth-child(1) {
top: 0;
transform-origin: left center;
}
&:nth-child(2) {
top: 10px;
transform-origin: left center;
}
&:nth-child(3) {
top: 20px;
transform-origin: left center;
}
}
&-open {
span:nth-child(1) {
transform: rotate(45deg);
top: -3px;
left: 5px;
}
span:nth-child(2) {
width: 0%;
opacity: 0;
}
span:nth-child(3) {
transform: rotate(-45deg);
top: 22px;
left: 5px;
}
}
}
/* -------------------------------------------------------------------------------------------------
# Icons
------------------------------------------------------------------------------------------------- */
.icon {
display: inline-block;
position: absolute;
top: 0;
right: 0;
font-style: normal;
color: $cm_color-black;
text-align: left;
text-indent: -9999px;
direction: ltr;
&::before,
&::after {
content: "";
pointer-events: none;
}
&-caret {
width: 20%;
height: 20px;
margin: 3px 10px;
&::before,
&::after {
width: 7px;
height: 1px;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
right: 2px;
box-shadow: inset 0 0 0 32px;
transform-origin: right;
}
&::before {
transform: rotate(45deg);
}
&::after {
transform: rotate(-45deg);
}
}
&-close {
width: 30px;
height: 30px;
margin: 2px;
transform: rotate(45deg);
&::before,
&::after {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
box-shadow: inset 0 0 0 32px;
}
&::before {
width: 20px;
height: 2px;
}
&::after {
height: 20px;
width: 2px;
}
}
}
JS (compiled from CoffeeScript):
'use strict';
(function($) {
$.fn.clearmenu = function(options) {
var animationSpeed, classes, revealMenu, setUpClearMenu, settings, toggleSubMenu;
classes = void 0;
settings = void 0;
setUpClearMenu = void 0;
animationSpeed = void 0;
toggleSubMenu = void 0;
revealMenu = void 0;
/**
* Establish the default CSS classes to use.
*/
classes = {
subMenu: 'cm-submenu',
top: 'cm-panel-top',
bottom: 'cm-panel-bottom',
left: 'cm-panel-left',
right: 'cm-panel-right',
fade: 'cm-panel-fade',
visible: 'cm-panel-open'
};
/**
* Establish the default plug-in settings.
*/
settings = $.extend({
panel: this,
close: true,
trigger: '.cm-hamburger',
reveal: 'fade',
hasSubMenu: true,
subMenu: '.cm-submenu',
wordpress: false
}, options);
setUpClearMenu = function() {
/**
* Determine how to reveal the target panel.
*/
switch (settings.reveal) {
case 'bottom':
$(settings.panel).addClass(classes.bottom);
break;
case 'left':
$(settings.panel).addClass(classes.left);
break;
case 'right':
$(settings.panel).addClass(classes.right);
break;
case 'fade':
$(settings.panel).addClass(classes.fade);
break;
default:
$(settings.panel).addClass(classes.top);
break;
}
/**
* Establish functionality if sub-menus are active.
*/
if (settings.hasSubMenu) {
animationSpeed = 200;
if (!settings.wordpress) {
toggleSubMenu = function() {
$(this).parent().find('ul').toggleClass('cm-submenu-open').slideToggle(animationSpeed);
};
$(settings.panel).find('> ul ul').addClass(classes.subMenu);
$(settings.subMenu).parent().append('<i class="icon icon-caret"></i>');
$(settings.subMenu).next().on('click.clearmenu', toggleSubMenu);
}
if (settings.wordpress) {
$(settings.panel).find('.menu-item-has-children').append('<i class="icon icon-caret"></i>');
$('.menu-item-has-children').on('click.clearmenu', 'i', function() {
$(this).prev('.sub-menu').slideToggle(animationSpeed);
});
}
}
/**
* Set up the close button for the panel.
*/
if (settings.close) {
$(settings.panel).prepend('<i class="icon icon-close"></i>');
$('.icon-close').on('click', function() {
$(settings.panel).removeClass(classes.visible);
if (settings.trigger == '.cm-hamburger') {
$(settings.trigger).removeClass('cm-hamburger-open');
}
});
}
};
/**
* Function to reveal the target panel that contains the menu.
*/
revealMenu = function() {
if (settings.trigger == '.cm-hamburger') {
$(this).toggleClass('cm-hamburger-open');
}
$(settings.panel).toggleClass(classes.visible);
};
$(settings.trigger).on('click.clearmenu', revealMenu);
setUpClearMenu();
};
})(jQuery);
I am not trying to make this a solution to fit every need, and I probably won't try to release it to a plugin repo or anything like that. It is really just for projects, both personal and at work.