Header navigation dropdown accessibility (#11509)
* Improve keyboard a11y of header menu dropdown This commit also includes some cleanup of unnecessary functions that seemed to degrade performance. Closes https://github.com/forem/forem/issues/1154 * Add temporary focus style on navigation-button * Put menu items in a list * Adjust menu button based on VoiceOver testing * Refactor menu logic to be reusable/work on touch * Preserve admin link visibility * Focus on first item on menu open * Clean up some HTML and CSS * Apply suggestions from code review Co-authored-by: ludwiczakpawel <ludwiczakpawel@gmail.com> * Ensure menu hides on click outside * Rename toggle function and adjust formatting * Update nav button focus style * Clean up padding on header avatar focus style * Make button show focus state for keyboard only Using .focus-visible:focus targets keyboard focus and eliminates a flash of the blue border on click before focus is moved to the child item (which also has no focus style on mouse click) * Update app/views/layouts/_top_bar.html.erb Co-authored-by: ludwiczakpawel <ludwiczakpawel@gmail.com> * Add some defensive programming * Removed initializeTouchDevice from base.js.erb * navigation-butt ID is now member-menu-button * Moved all the logic from initializeTouchDevice.js into a pack file/utilities. * committing re-ordered schema after setup * add tests for initializeTouchDevice * remove some unneeded html setup * make sure menu doesn't close if user tabs back from sign out * Revert "committing re-ordered schema after setup" This reverts commit a41a1c861cca3b97d8a7b8a99268b8afaae9f028. * optimized code * small tweaks, only show outline when focused Co-authored-by: ludwiczakpawel <ludwiczakpawel@gmail.com> Co-authored-by: rhymes <rhymes@hey.com> Co-authored-by: Nick Taylor <nick@dev.to> Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
This commit is contained in:
parent
bb59e4b869
commit
f072730f0f
16 changed files with 263 additions and 93 deletions
|
|
@ -114,8 +114,9 @@ var instantClick
|
|||
|
||||
function changePage(title, body, newUrl, scrollY, pop) {
|
||||
var pageContentDiv = document.getElementById("page-content");
|
||||
if (document.getElementById("navigation-butt")) {
|
||||
document.getElementById("navigation-butt").classList.remove('showing')
|
||||
var memberMenuButton = document.getElementById("member-menu-button")
|
||||
if (!memberMenuButton) {
|
||||
memberMenuButton.classList.remove('showing')
|
||||
}
|
||||
document.getElementsByTagName("BODY")[0].replaceChild(body, pageContentDiv)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
global initializeLocalStorageRender, initializeBodyData,
|
||||
initializeAllChatButtons, initializeAllTagEditButtons, initializeUserFollowButts,
|
||||
initializeBaseTracking, initializeTouchDevice, initializeCommentsPage,
|
||||
initializeBaseTracking, initializeCommentsPage,
|
||||
initializeArticleDate, initializeArticleReactions, initNotifications,
|
||||
initializeCommentDate, initializeCommentDropdown, initializeSettings,
|
||||
initializeCommentPreview,
|
||||
|
|
@ -40,7 +40,6 @@ function callInitializers() {
|
|||
|
||||
initializeBaseTracking();
|
||||
initializePaymentPointers();
|
||||
initializeTouchDevice();
|
||||
initializeCommentsPage();
|
||||
initializeArticleDate();
|
||||
initializeArticleReactions();
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
function getById(className) {
|
||||
return document.getElementById(className);
|
||||
}
|
||||
function getClassList(className) {
|
||||
return getById(className).classList;
|
||||
}
|
||||
|
||||
function blur(event, className) {
|
||||
setTimeout(() => {
|
||||
if (document.activeElement !== getById(className)) {
|
||||
getClassList('crayons-header__menu').remove('showing');
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
|
||||
function removeShowingMenu() {
|
||||
getClassList('crayons-header__menu').remove('showing');
|
||||
setTimeout(() => {
|
||||
getClassList('crayons-header__menu').remove('showing');
|
||||
}, 5);
|
||||
setTimeout(() => {
|
||||
getClassList('crayons-header__menu').remove('showing');
|
||||
}, 150);
|
||||
}
|
||||
|
||||
function toggleMenu() {
|
||||
getClassList('crayons-header__menu').toggle('showing');
|
||||
}
|
||||
|
||||
function initializeTouchDevice() {
|
||||
var isTouchDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|DEV-Native-ios/i.test(
|
||||
navigator.userAgent,
|
||||
);
|
||||
if (navigator.userAgent === 'DEV-Native-ios') {
|
||||
document.body.classList.add('dev-ios-native-body');
|
||||
}
|
||||
if (document.querySelector('.crayons-header__menu')) {
|
||||
setTimeout(() => {
|
||||
removeShowingMenu();
|
||||
if (isTouchDevice) {
|
||||
// Use a named function instead of anonymous so duplicate event handlers are discarded
|
||||
getById('navigation-butt').addEventListener('click', toggleMenu);
|
||||
} else {
|
||||
getClassList('crayons-header__menu').add('desktop');
|
||||
getById('navigation-butt').addEventListener('focus', (e) =>
|
||||
getClassList('crayons-header__menu').add('showing'),
|
||||
);
|
||||
getById('last-nav-link').addEventListener('blur', (e) =>
|
||||
blur(e, 'second-last-nav-link'),
|
||||
);
|
||||
getById('navigation-butt').addEventListener('blur', (e) =>
|
||||
blur(e, 'first-nav-link'),
|
||||
);
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
|
@ -71,6 +71,8 @@
|
|||
|
||||
// Dropdown under profile picture / hamburger icon
|
||||
.crayons-header__menu {
|
||||
--trigger-color: var(--header-icons-color);
|
||||
--trigger-border-color: transparent;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -81,11 +83,13 @@
|
|||
|
||||
&__trigger {
|
||||
all: unset;
|
||||
padding: var(--su-1);
|
||||
padding: calc(var(--su-1) / 2);
|
||||
margin: 0 var(--su-2);
|
||||
border-radius: 50%;
|
||||
color: var(--header-icons-color);
|
||||
color: var(--trigger-color);
|
||||
cursor: pointer;
|
||||
// hold space for interaction style
|
||||
border: 2px solid var(--trigger-border-color);
|
||||
}
|
||||
|
||||
&__dropdown {
|
||||
|
|
@ -103,17 +107,18 @@
|
|||
}
|
||||
|
||||
&.showing,
|
||||
&.desktop:hover,
|
||||
&.desktop:focus {
|
||||
&.desktop:hover {
|
||||
--trigger-border-color: var(--header-bg-hover);
|
||||
--trigger-color: var(--header-icons-color-hover);
|
||||
--dropdown-transform: translateY(0) rotateX(0);
|
||||
--dropdown-delay: 0;
|
||||
--dropdown-opacity: 1;
|
||||
--dropdown-visibility: visible;
|
||||
}
|
||||
|
||||
.crayons-header__menu__trigger {
|
||||
color: var(--header-icons-color-hover);
|
||||
background: var(--header-bg-hover);
|
||||
}
|
||||
.focus-visible:focus {
|
||||
--trigger-border-color: var(--header-button-focus-color);
|
||||
--trigger-color: var(--header-button-focus-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@
|
|||
// Header
|
||||
--header-bg: var(--base-inverted);
|
||||
--header-bg-hover: var(--base-10);
|
||||
--header-button-focus-color: var(--accent-brand);
|
||||
--header-bg-current: var(--base-20);
|
||||
--header-shadow: rgba(0, 0, 0, 0.1);
|
||||
--header-icons-color: var(--base-70);
|
||||
|
|
|
|||
|
|
@ -29,9 +29,6 @@
|
|||
font-size: 1em;
|
||||
resize: vertical;
|
||||
}
|
||||
.focus-visible {
|
||||
border-color: $black;
|
||||
}
|
||||
.tag-area-text {
|
||||
height: 5em;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@
|
|||
// Header
|
||||
--header-bg: var(--base-10);
|
||||
--header-bg-hover: var(--base-20);
|
||||
--header-button-focus-color: var(--accent-brand);
|
||||
--header-bg-current: var(--base-0);
|
||||
--header-shadow: rgba(0, 0, 0, 0.1);
|
||||
--header-icons-color: var(--base-70);
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@
|
|||
// Header
|
||||
--header-bg: var(--base-0);
|
||||
--header-bg-hover: var(--base-10);
|
||||
--header-button-focus-color: var(--accent-brand);
|
||||
--header-bg-current: var(--base-20);
|
||||
--header-shadow: rgba(0, 0, 0, 0.1);
|
||||
--header-icons-color: #0a0a0a;
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@
|
|||
--header-bg: var(--base-10);
|
||||
--header-bg-hover: var(--base-0);
|
||||
--header-bg-current: var(--base-20);
|
||||
--header-button-focus-color: var(--accent-brand);
|
||||
--header-shadow: rgba(0, 0, 0, 0.3);
|
||||
--header-icons-color: rgba(255, 255, 255, 0.8);
|
||||
--header-icons-color-hover: #fff;
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@
|
|||
--header-bg: var(--base-40);
|
||||
--header-bg-hover: var(--base-50);
|
||||
--header-bg-current: var(--base-50);
|
||||
--header-button-focus-color: var(--accent-brand);
|
||||
--header-shadow: rgba(0, 0, 0, 0.1);
|
||||
--header-icons-color: var(--base-10);
|
||||
--header-icons-color-hover: var(--base-0);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import {
|
|||
initializeMobileMenu,
|
||||
setCurrentPageIconLink,
|
||||
getInstantClick,
|
||||
initializeTouchDevice,
|
||||
} from '../topNavigation/utilities';
|
||||
|
||||
function getPageEntries() {
|
||||
|
|
@ -25,6 +26,9 @@ getInstantClick().then((spa) => {
|
|||
});
|
||||
|
||||
const { currentPage } = document.getElementById('page-content').dataset;
|
||||
const memberMenu = document.getElementById('crayons-header__menu');
|
||||
const menuNavButton = document.getElementById('member-menu-button');
|
||||
|
||||
setCurrentPageIconLink(currentPage, getPageEntries());
|
||||
initializeMobileMenu(menus, moreMenus);
|
||||
initializeTouchDevice(memberMenu, menuNavButton);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
// TODO: Once this is merged, PR up the removal of this and in other tests and move it to testSetup.js
|
||||
import '@testing-library/jest-dom';
|
||||
import { getByLabelText, getByText } from '@testing-library/dom';
|
||||
import {
|
||||
getByTestId,
|
||||
getByLabelText,
|
||||
getByText,
|
||||
fireEvent,
|
||||
waitFor,
|
||||
} from '@testing-library/dom';
|
||||
import {
|
||||
getInstantClick,
|
||||
initializeMobileMenu,
|
||||
setCurrentPageIconLink,
|
||||
initializeTouchDevice,
|
||||
} from '../utilities';
|
||||
|
||||
// TODO: ★★★ These tests should be promoted to E2E tests once we have that in place. ★★★
|
||||
|
|
@ -134,4 +141,107 @@ describe('top navigation utilitities', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('initializeTouchDevice', () => {
|
||||
const initialMenuHTML = `
|
||||
<div class="crayons-header__menu" id="crayons-header__menu" data-testid="menu-dropdown">
|
||||
<button type="button" class="crayons-header__menu__trigger" id="member-menu-button" aria-label="Navigation menu">
|
||||
<span class="crayons-avatar crayons-avatar--l"><img class="crayons-avatar__image" alt="" id="nav-profile-image" src=""></span>
|
||||
</button>
|
||||
<div class="crayons-dropdown left-2 right-2 s:right-4 s:left-auto p-0 crayons-header__menu__dropdown inline-block">
|
||||
<ul class="p-0" id="crayons-header__menu__dropdown__list">
|
||||
<li id="user-profile-link-placeholder" class="border-0 border-b-1 border-solid border-base-20 p-1 mb-1">
|
||||
<a id="first-nav-link" class="crayons-link crayons-link--block" href="">
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-1 py-1">
|
||||
<a href="/signout_confirm" class="crayons-link crayons-link--block" id="last-nav-link">Sign Out</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
it('toggles dropdown menu expanded state', async () => {
|
||||
document.body.innerHTML = initialMenuHTML;
|
||||
|
||||
const menuNavButton = getByLabelText(document.body, 'Navigation menu');
|
||||
const memberMenu = getByTestId(document.body, 'menu-dropdown');
|
||||
|
||||
initializeTouchDevice(memberMenu, menuNavButton);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(menuNavButton.getAttribute('aria-expanded')).toEqual('false'),
|
||||
);
|
||||
expect(menuNavButton.getAttribute('aria-expanded')).toEqual('false');
|
||||
expect(memberMenu).not.toHaveClass('showing');
|
||||
|
||||
fireEvent.click(menuNavButton);
|
||||
|
||||
expect(menuNavButton.getAttribute('aria-expanded')).toEqual('true');
|
||||
expect(memberMenu).toHaveClass('showing');
|
||||
|
||||
fireEvent.click(menuNavButton);
|
||||
|
||||
expect(menuNavButton.getAttribute('aria-expanded')).toEqual('false');
|
||||
expect(memberMenu).not.toHaveClass('showing');
|
||||
});
|
||||
|
||||
it('closes menu on Escape key', async () => {
|
||||
document.body.innerHTML = initialMenuHTML;
|
||||
|
||||
const menuNavButton = getByLabelText(document.body, 'Navigation menu');
|
||||
const memberMenu = getByTestId(document.body, 'menu-dropdown');
|
||||
|
||||
initializeTouchDevice(memberMenu, menuNavButton);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(menuNavButton.getAttribute('aria-expanded')).toEqual('false'),
|
||||
);
|
||||
|
||||
fireEvent.click(menuNavButton);
|
||||
|
||||
expect(menuNavButton.getAttribute('aria-expanded')).toEqual('true');
|
||||
expect(memberMenu).toHaveClass('showing');
|
||||
|
||||
fireEvent.keyUp(memberMenu, { key: 'Escape' });
|
||||
|
||||
await waitFor(() =>
|
||||
expect(menuNavButton.getAttribute('aria-expanded')).toEqual('false'),
|
||||
);
|
||||
|
||||
expect(memberMenu).not.toHaveClass('showing');
|
||||
});
|
||||
|
||||
it('closes the menu on click outside', async () => {
|
||||
document.body.innerHTML = `
|
||||
<div data-testid="container">
|
||||
${initialMenuHTML}
|
||||
</div>
|
||||
`;
|
||||
|
||||
const menuNavButton = getByLabelText(document.body, 'Navigation menu');
|
||||
const memberMenu = getByTestId(document.body, 'menu-dropdown');
|
||||
const outerWrapper = getByTestId(document.body, 'container');
|
||||
|
||||
initializeTouchDevice(memberMenu, menuNavButton);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(menuNavButton.getAttribute('aria-expanded')).toEqual('false'),
|
||||
);
|
||||
|
||||
fireEvent.click(menuNavButton);
|
||||
|
||||
expect(menuNavButton.getAttribute('aria-expanded')).toEqual('true');
|
||||
expect(memberMenu).toHaveClass('showing');
|
||||
|
||||
fireEvent.click(outerWrapper);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(menuNavButton.getAttribute('aria-expanded')).toEqual('false'),
|
||||
);
|
||||
|
||||
expect(memberMenu).not.toHaveClass('showing');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,3 +1,106 @@
|
|||
function closeHeaderMenu(memberMenu, menuNavButton) {
|
||||
if (menuNavButton) {
|
||||
menuNavButton.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
|
||||
if (memberMenu) {
|
||||
setTimeout(() => {
|
||||
memberMenu.classList.remove('showing');
|
||||
}, 5);
|
||||
}
|
||||
}
|
||||
|
||||
function blurHeaderMenu(memberMenu, menuNavButton, potentiallyActiveElement) {
|
||||
setTimeout(() => {
|
||||
if (document.activeElement !== potentiallyActiveElement) {
|
||||
closeHeaderMenu(memberMenu, menuNavButton);
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
|
||||
function toggleHeaderMenu(memberMenu, navigationButton) {
|
||||
if (!memberMenu || !navigationButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
let crayonsHeaderMenuClassList = memberMenu.classList;
|
||||
if (crayonsHeaderMenuClassList.contains('showing')) {
|
||||
crayonsHeaderMenuClassList.remove('showing');
|
||||
navigationButton.setAttribute('aria-expanded', 'false');
|
||||
} else {
|
||||
crayonsHeaderMenuClassList.add('showing');
|
||||
navigationButton.setAttribute('aria-expanded', 'true');
|
||||
|
||||
let firstNavLink = document.getElementById('first-nav-link');
|
||||
if (firstNavLink) {
|
||||
setTimeout(() => {
|
||||
// focus first item on open
|
||||
firstNavLink.focus();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isTouchDevice() {
|
||||
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|DEV-Native-ios/i.test(
|
||||
navigator.userAgent,
|
||||
);
|
||||
}
|
||||
|
||||
export function initializeTouchDevice(memberTopMenu, menuNavButton) {
|
||||
if (navigator.userAgent === 'DEV-Native-ios') {
|
||||
document.body.classList.add('dev-ios-native-body');
|
||||
}
|
||||
|
||||
if (memberTopMenu) {
|
||||
let crayonsHeaderMenuClassList = memberTopMenu.classList;
|
||||
|
||||
setTimeout(() => {
|
||||
closeHeaderMenu(memberTopMenu, menuNavButton);
|
||||
|
||||
if (isTouchDevice()) {
|
||||
// Use a named function instead of anonymous so duplicate event handlers are discarded
|
||||
menuNavButton.addEventListener('click', (_event) => {
|
||||
toggleHeaderMenu(memberTopMenu, menuNavButton);
|
||||
});
|
||||
} else {
|
||||
crayonsHeaderMenuClassList.add('desktop');
|
||||
menuNavButton.addEventListener('click', (_event) => {
|
||||
toggleHeaderMenu(memberTopMenu, menuNavButton);
|
||||
});
|
||||
memberTopMenu.addEventListener('keyup', (e) => {
|
||||
if (
|
||||
e.key === 'Escape' &&
|
||||
crayonsHeaderMenuClassList.contains('showing')
|
||||
) {
|
||||
crayonsHeaderMenuClassList.remove('showing');
|
||||
menuNavButton.focus();
|
||||
}
|
||||
});
|
||||
document
|
||||
.getElementById('last-nav-link')
|
||||
.addEventListener('blur', (_event) => {
|
||||
blurHeaderMenu(
|
||||
memberTopMenu,
|
||||
menuNavButton,
|
||||
document.getElementById('second-last-nav-link'),
|
||||
);
|
||||
});
|
||||
document.addEventListener('click', (_event) => {
|
||||
// if clicking outside of the menu, close it
|
||||
if (!memberTopMenu.contains(document.activeElement)) {
|
||||
blurHeaderMenu(
|
||||
memberTopMenu,
|
||||
menuNavButton,
|
||||
document.getElementById('first-nav-link'),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleBurgerMenu() {
|
||||
document.body.classList.toggle('hamburger-open');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,26 @@
|
|||
<div class="crayons-dropdown left-2 right-2 s:right-4 s:left-auto p-0 crayons-header__menu__dropdown inline-block">
|
||||
<div id="user-profile-link-placeholder" class="border-0 border-b-1 border-solid border-base-20 p-1">
|
||||
<a id="first-nav-link" class="crayons-link crayons-link--block">
|
||||
<div>
|
||||
<span class="fw-medium block">...</span>
|
||||
<small class="fs-s color-base-50"></small>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<ul class="p-0" id="crayons-header__menu__dropdown__list">
|
||||
<li id="user-profile-link-placeholder" class="border-0 border-b-1 border-solid border-base-20 p-1 mb-1">
|
||||
<a id="first-nav-link" class="crayons-link crayons-link--block">
|
||||
<div>
|
||||
<span class="fw-medium block">...</span>
|
||||
<small class="fs-s color-base-50"></small>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<div class="border-0 border-b-1 border-solid border-base-20 p-1">
|
||||
<a href="<%= admin_path %>" class="crayons-link crayons-link--block js-header-menu-admin-link hidden" data-no-instant>Admin</a>
|
||||
<a href="<%= dashboard_path %>" class="crayons-link crayons-link--block">Dashboard</a>
|
||||
<a href="<%= new_path %>" class="crayons-link crayons-link--block">Write a Post</a>
|
||||
<a href="<%= readinglist_path %>" class="crayons-link crayons-link--block">Reading list</a>
|
||||
<a href="<%= user_settings_path %>" class="crayons-link crayons-link--block">Settings</a>
|
||||
</div>
|
||||
<li class="px-1 js-header-menu-admin-link hidden">
|
||||
<a href="<%= admin_path %>" class="crayons-link crayons-link--block" data-no-instant>Admin</a>
|
||||
</li>
|
||||
<li class="px-1"><a href="<%= dashboard_path %>" class="crayons-link crayons-link--block">Dashboard</a></li>
|
||||
<li class="px-1"><a href="<%= new_path %>" class="crayons-link crayons-link--block">Write a Post</a></li>
|
||||
<li class="px-1"><a href="<%= readinglist_path %>" class="crayons-link crayons-link--block">Reading list</a></li>
|
||||
<li class="border-0 border-b-1 border-solid border-base-20 px-1 pb-1">
|
||||
<a href="<%= user_settings_path %>" class="crayons-link crayons-link--block" id="second-last-nav-link">Settings</a>
|
||||
</li>
|
||||
|
||||
<div class="p-1">
|
||||
<a href="<%= signout_confirm_path %>" class="crayons-link crayons-link--block" id="last-nav-link">Sign Out</a>
|
||||
</div>
|
||||
<li class="px-1 py-1">
|
||||
<a href="<%= signout_confirm_path %>" class="crayons-link crayons-link--block" id="last-nav-link">Sign Out</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
</a>
|
||||
|
||||
<div class="crayons-header__menu" id="crayons-header__menu">
|
||||
<button type="button" class="crayons-header__menu__trigger navigation-butt" id="navigation-butt" aria-label="Navigation menu">
|
||||
<button type="button" class="crayons-header__menu__trigger" id="member-menu-button" aria-label="Navigation menu">
|
||||
<span class="crayons-avatar crayons-avatar--l"><img class="crayons-avatar__image" alt="" id="nav-profile-image" /></span>
|
||||
</button>
|
||||
<%= render "layouts/nav_menu" %>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ that major sections of the websites are bootstrapped, for example:
|
|||
|
||||
```javascript
|
||||
initializeBaseTracking();
|
||||
initializeTouchDevice();
|
||||
initializeCommentsPage();
|
||||
initEditorResize();
|
||||
initLeaveEditorWarning();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue