Parcourir la source

feat: improve mobile-first menu

- On mobile, position toggle on left and logo in centre (adopt standard)
- Show Search, Day/Night, and Language icons in mobile menu bar
- Add option to align menu items to left, centre, or right
- Add option to remove logo image/text from menu
- Complete navbar SCSS redesign, making use of Bootstrap vars
- Add automatic menu dropdowns on mouseover (for desktop)

Also,
- Apply live day/night update on system prefs change (no need for refresh)
- Add Mermaid security level on init - a req. for latest Mermaid version

BREAKING CHANGES:
menu_align_right option removed from params.toml
main_menu option added to params.toml

Close #1418
George Cushen il y a 5 ans
Parent
commit
766c559e50

+ 23 - 7
assets/js/academic.js

@@ -14,11 +14,7 @@
   // Dynamically get responsive navigation bar height for offsetting Scrollspy.
   function getNavBarHeight() {
     let $navbar = $('#navbar-main');
-    let $navbar_collapse_show = $('#navbar-main .navbar-collapse.collapse.show');
-    let navbar_offset = $navbar.innerHeight();
-    if ($navbar_collapse_show.length){
-      navbar_offset -= $navbar_collapse_show.innerHeight();
-    }
+    let navbar_offset = $navbar.outerHeight();
     console.debug('Navbar height: ' + navbar_offset);
     return navbar_offset;
   }
@@ -437,7 +433,7 @@
         codeHlDark.disabled = false;
       }
       if (diagramEnabled) {
-        mermaid.initialize({ theme: 'dark' });
+        mermaid.initialize({ theme: 'dark', securityLevel: 'loose' });
       }
       $('.js-dark-toggle i').removeClass('fa-moon').addClass('fa-sun');
     } else {
@@ -447,7 +443,7 @@
         codeHlDark.disabled = true;
       }
       if (diagramEnabled) {
-        mermaid.initialize({ theme: 'default' });
+        mermaid.initialize({ theme: 'default', securityLevel: 'loose' });
       }
       $('.js-dark-toggle i').removeClass('fa-sun').addClass('fa-moon');
     }
@@ -457,6 +453,14 @@
       e.preventDefault();
       toggleDarkMode(codeHlEnabled, codeHlLight, codeHlDark, diagramEnabled);
     });
+
+    // Live update of day/night mode on system preferences update (no refresh required).
+    const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
+    darkModeMediaQuery.addListener((e) => {
+      const darkModeOn = e.matches;
+      console.log(`Dark mode is ${darkModeOn ? '🌒 on' : '☀️ off'}.`);
+      toggleDarkMode(codeHlEnabled, codeHlLight, codeHlDark, diagramEnabled);
+    });
   });
 
   /* ---------------------------------------------------------------------------
@@ -610,4 +614,16 @@
   // Normalize Bootstrap carousel slide heights.
   $(window).on('load resize orientationchange', normalizeCarouselSlideHeights);
 
+  // Automatic main menu dropdowns on mouse over.
+  $('body').on('mouseenter mouseleave', '.dropdown', function (e) {
+    var dropdown = $(e.target).closest('.dropdown');
+    var menu = $('.dropdown-menu', dropdown);
+    dropdown.addClass('show');
+    menu.addClass('show');
+    setTimeout(function () {
+      dropdown[dropdown.is(':hover') ? 'addClass' : 'removeClass']('show');
+      menu[dropdown.is(':hover') ? 'addClass' : 'removeClass']('show');
+    }, 300);
+  });
+
 })(jQuery);

+ 0 - 5
assets/scss/academic/_dark.scss

@@ -113,11 +113,6 @@ body.dark,
   color: #fff;
 }
 
-.dark .navbar-light {
-  box-shadow: 0 0.125rem 0.25rem 0 rgba(255, 255, 255, .11);
-  border-color: #070707;
-}
-
 .dark select {
   background: rgb(40, 42, 54);
   color: rgb(248, 248, 242);

+ 131 - 154
assets/scss/academic/_nav.scss

@@ -3,196 +3,173 @@
  **************************************************/
 
 .navbar {
-  min-height: 70px !important;
-}
-
-.navbar-light {
-  background: $sta-menu-primary !important;
-  box-shadow: 0 0.125rem 0.25rem 0 rgba(0,0,0,.11)
-}
-
-.navbar-light .navbar-toggler {
-  border-color: transparent;
-}
-
-.navbar-toggler {
-  color: $sta-menu-text !important;
-}
-
-.navbar-light .navbar-toggler:focus,
-.navbar-light .navbar-toggler:hover {
-  background-color: transparent;
-}
-
-.dropdown-menu,
-nav#navbar-main li.nav-item {
+  height: 70px;
+  background: $sta-menu-primary;
+  box-shadow: 0 0.125rem 0.25rem 0 rgba(0,0,0,.11);
   font-size: #{$sta-font-size-small}px;
-}
+  position: fixed;
+  top: 0;
+  right: 0;
+  left: 0;
+  z-index: 1030;
+
+  @include media-breakpoint-down(md) {
+    height: 50px;
+
+    .navbar-nav-scroll {
+      width: 100%;
+      overflow: hidden;
+
+      .navbar-nav {
+        overflow-x: auto;
+        white-space: nowrap;
+        -webkit-overflow-scrolling: touch;
+      }
+    }
+  }
 
-.navbar-light .navbar-nav>.nav-item>.nav-link,
-.navbar-light .navbar-nav>.nav-item>.nav-link:focus,
-.navbar-light .navbar-nav>.nav-item>.nav-link:hover {
-  white-space: nowrap;
-  -webkit-transition: 0.2s ease;
-  transition: 0.2s ease;
-  color: $sta-menu-text;
-}
+  .navbar-nav {
+    display: flex;
 
-.navbar-light .navbar-nav>.nav-item>.nav-link:focus {
-  color: $sta-menu-text;
-  background-color: transparent;
-}
+    .nav-link {
+      color: rgba($sta-menu-text, .85);
 
-.navbar-light .navbar-nav>.nav-item>.nav-link:hover {
-  color: $sta-menu-text-active;
-  background-color: transparent;
-}
+      &.active,
+      &:hover,
+      &:focus {
+        color: $sta-menu-text;
+      }
 
-.navbar-light .navbar-nav>li.nav-item>a.active,
-.navbar-light .navbar-nav>li.nav-item>a.active:focus,
-.navbar-light .navbar-nav>li.nav-item>a.active:hover {
-  color: $sta-menu-text-active;
-  font-weight: 700;
-  background-color: transparent !important; /* Override Bootstrap. */
-}
+      &.active {
+        font-weight: bold !important;
+      }
+    }
+  }
 
-.navbar-brand,
-.navbar-nav li.nav-item a.nav-link {
-  height: inherit;
-  line-height: 50px;
-  padding-top: 10px;
-  padding-bottom: 10px;
-}
+  .dropdown-menu {
+    font-size: .875rem;
+  }
 
-.navbar-brand img {
-  max-height: 50px;
-}
+  .dropdown-item.active {
+    font-weight: 500;
+    color: #2b2b2b;
+    background-color: transparent;
+    background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23292b2c' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E");
+    background-repeat: no-repeat;
+    background-position: .4rem .6rem;
+    background-size: .75rem .75rem;
+  }
 
-.navbar-light .navbar-toggler .icon-bar {
-  background-color: $sta-menu-text !important;
 }
 
-.dropdown-menu {
-  background-color: $sta-menu-primary !important;
+.dark .navbar {
+  box-shadow: 0 0.125rem 0.25rem 0 rgba(255, 255, 255, .11);
 }
 
-.dropdown-menu > li > a {
-  display: block;
-  padding: 3px 20px;
-  clear: both;
-  font-weight: 400;
-  line-height: 1.42857143;
-  color: $sta-menu-text;
-  white-space: nowrap;
+// Remove Bootstrap's border from Toggle button.
+.navbar-toggler {
+  border: 0 !important;
 }
-
-.dropdown-menu>li>a:focus,
-.dropdown-menu>li>a:hover {
-  color: $sta-menu-text-active;
-  text-decoration: none;
-  background-color: $sta-menu-primary;
+.navbar-toggler:focus,
+.navbar-toggler:active {
+  outline: none !important;
+  box-shadow: none !important;
 }
 
-.dropdown-menu > .active,
-.dropdown-menu > .active:focus,
-.dropdown-menu > .active:hover {
-  color: $sta-menu-primary;
-  text-decoration: none;
-  background-color: $sta-menu-text-active;
-  outline: 0;
+@include media-breakpoint-down(md) {  /* Match breakpoint for i18n dropdown in navbar.html. */
+  .i18n-dropdown .nav-link::after {
+    content: none;
+  }
+  .i18n-dropdown .dropdown-menu {
+    /* Use style from uncollapsable dropdowns to prevent dropdown going off page. */
+    position: absolute;
+    /* Below style from .dropdown-menu-right to prevent dropdown going off page. */
+    right: 0;
+    left: auto;
+  }
 }
 
-.navbar-light .navbar-nav>.open>a,
-.navbar-light .navbar-nav>.open>a:focus,
-.navbar-light .navbar-nav>.open>a:hover,
-.navbar-light .navbar-nav>.open>a:visited {
-  color: $sta-menu-text !important;
-  background-color: $sta-menu-primary !important;
+.navbar .collapse {
+  overflow-x: auto;
 }
 
-.navbar-light .navbar-brand {
+.navbar-brand {
+  padding-top: 0;
+  padding-bottom: 0;
   font-weight: bold;
-  font-size: 1.2em;
-  color: $sta-menu-title;
 }
 
-.navbar-light .navbar-brand:focus,
-.navbar-light .navbar-brand:hover {
-  color: $sta-menu-title;
-  background-color: transparent;
+.navbar-brand img {
+  max-height: 60px;
+  max-width: fit-content;  /* Otherwise logo disappears at some responsive sizes. */
 }
 
-@media screen and (max-width: 1200px) {
-  .navbar {
-    min-height: 50px !important;
-  }
-
-  .navbar-brand,
-  .navbar-nav li.nav-item a.nav-link {
-    height: inherit;
-    line-height: 40px;
-    padding-top: 5px;
-    padding-bottom: 5px;
-  }
+#navbar-main .main-menu-item ul li .nav-link {
+  color: $sta-menu-text;
+}
 
+@include media-breakpoint-down(md) {
   .navbar-brand img {
     max-height: 40px;
   }
-
+  .navbar-brand {
+    max-width: 100px;
+    margin: 0 auto;
+    position: absolute;
+    left: 0;
+    right: 0;
+    display: inline-block;
+  }
   .navbar-toggler {
-    display: block;
+    border-color: transparent;  // Remove Bootstrap's border from Toggle button.
   }
-
-  .fixed-top {
-    top: 0;
-    border-width: 0 0 1px;
+  #navbar-main .main-menu-item {
+    text-align: left !important;
+    padding-left: 0;
   }
-
-  .navbar-nav > li.nav-item > a.nav-link {
-    padding-top: 10px;
-    padding-bottom: 10px;
-    line-height: normal;
+  .navbar-collapse {
+    z-index: 9999 !important;
+    position: absolute;
+    left: 0;
+    top: 50px;
+    width: 100%;
+    background-color: $sta-menu-primary;
+    text-align: center !important;
   }
-
-  .dropdown-menu > li > a {
-    display: block;
-    padding: 3px 20px;
-    clear: both;
-    font-weight: 400;
-    line-height: 1.42857143;
-    color: $sta-menu-text;
-    white-space: nowrap;
+  #navbar-main .main-menu-item .nav-item {
+    padding: 10px 15px !important;
   }
-
-  .navbar-light .navbar-nav .open .dropdown-menu {
-    position: static;
-    width: auto;
-    margin-top: 0;
-    background-color: transparent;
-    border: 0;
-    box-shadow: none;
+  #navbar-main .main-menu-item .nav-item .nav-link {
+    padding: 5px 15px !important;
   }
+}
 
-  .navbar-light .navbar-nav .open .dropdown-menu > li > a {
-    padding: 5px 15px 5px 25px;
-    line-height: 20px;
-    color: $sta-menu-text;
-  }
+ul.nav-icons {
+  list-style-type: none;
+  font-size: 18px;
+  padding: 0.5rem 0 0.5rem 0;
+  margin: 0;
+}
 
-  .navbar-light .navbar-nav .open .dropdown-menu > li > a:focus,
-  .navbar-light .navbar-nav .open .dropdown-menu > li > a:hover {
-    color: inherit;
-    background-color: transparent;
-  }
+ul.nav-icons li {
+  display: inline;
+  padding-right: 1rem;
+}
 
-  .navbar-light .navbar-nav .open .dropdown-menu >.nav-item> .active,
-  .navbar-light .navbar-nav .open .dropdown-menu >.nav-item> .active:focus,
-  .navbar-light .navbar-nav .open .dropdown-menu >.nav-item> .active:hover {
-    color: $sta-menu-text-active;
-    background-color: transparent;
-  }
+ul.nav-icons li:last-of-type {
+  padding-right: 0;
+}
 
-  .collapse.in {
-    display: block !important;
-  }
+ul.nav-icons li.nav-item a.nav-link {
+  padding: 0;
+}
+
+.dropdown-menu {
+  background-color: $sta-menu-primary !important;
+  color: $sta-menu-text !important;
+}
+
+.dropdown-item {
+  background-color: $sta-menu-primary !important;
+  color: $sta-menu-text !important;
 }

+ 10 - 0
assets/scss/bootstrap_variables.scss

@@ -11,3 +11,13 @@ $container-max-widths: (
   lg: 960px,
   xl: 1200px
 );
+
+$navbar-toggler-padding-x: 0;
+$navbar-toggler-font-size: 18px;
+$navbar-brand-font-size: 1.2rem;
+
+$navbar-light-color: $sta-menu-text;
+$navbar-light-active-color: $sta-menu-text-active;
+$navbar-light-brand-color: $sta-menu-title;
+$navbar-light-brand-hover-color: $navbar-light-active-color;
+$navbar-light-toggler-border-color: transparent;

+ 2 - 2
exampleSite/config/_default/params.toml

@@ -147,8 +147,8 @@ list_delimiter = ", "
 # Get user avatars from Gravatar.com? (true/false)
 gravatar = false
 
-# Align the main menu to the right of the page? (true/false)
-menu_align_right = false
+# Main menu alignment (l = left, c = center, r = right) and logo options.
+main_menu = {align = "l", show_logo = true}
 
 # Show estimated reading time for posts? (true/false)
 reading_time = true

+ 49 - 42
layouts/partials/navbar.html

@@ -1,28 +1,32 @@
 {{ $current_page := . }}
-<nav class="navbar navbar-light fixed-top navbar-expand-lg py-0 compensate-for-scrollbar" id="navbar-main">
+<nav class="navbar navbar-expand-lg navbar-light compensate-for-scrollbar" id="navbar-main">
   <div class="container">
 
     <!-- Brand and toggle get grouped for better mobile display -->
+    {{ $show_logo := site.Params.main_menu.show_logo | default true }}
+    {{if $show_logo}}
       <a class="navbar-brand" href="{{ "/" | relLangURL }}">
         {{- if site.Params.logo -}}
-        <img src="{{ printf "/img/%s" site.Params.logo | relURL }}" alt="{{ site.Title }}">
+          <img src="{{ printf "/img/%s" site.Params.logo | relURL }}" alt="{{ site.Title }}">
         {{- else -}}
-        {{- site.Title -}}
+          {{- site.Title -}}
         {{- end -}}
       </a>
-      {{ if or site.Menus.main .IsTranslated }}
-      <button type="button" class="navbar-toggler" data-toggle="collapse"
-              data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="{{ i18n "toggle_navigation" }}">
-        <span><i class="fas fa-bars"></i></span>
-      </button>
-      {{ end }}
+    {{end}}
+
+    {{ if site.Menus.main }}
+    <button type="button" class="navbar-toggler" data-toggle="collapse"
+            data-target="#navbar-content" aria-controls="navbar" aria-expanded="false" aria-label="{{ i18n "toggle_navigation" }}">
+    <span><i class="fas fa-bars"></i></span>
+    </button>
+    {{ end }}
 
     <!-- Collect the nav links, forms, and other content for toggling -->
-    <div class="collapse navbar-collapse" id="navbar">
+    {{ $align_menu := site.Params.main_menu.align | default "l" }}
+    <div class="navbar-collapse main-menu-item collapse {{ if eq $align_menu "c" }}justify-content-center{{ else if eq $align_menu "r" }}justify-content-end{{else}}justify-content-start{{ end }}" id="navbar-content">
 
       <!-- Left Nav Bar -->
-      {{ $align_right := site.Params.menu_align_right | default true }}
-      <ul class="navbar-nav {{ if $align_right }}ml-auto{{ else }}mr-auto{{ end }}">
+      <ul class="navbar-nav d-md-inline-flex">
         {{ range site.Menus.main }}
 
         {{ if .HasChildren }}
@@ -75,9 +79,9 @@
         {{ end }}
         {{ end }}
 
-      {{ if not $align_right | and (.IsTranslated | or site.Menus.main_right | or site.Params.search.engine | or site.Params.day_night) }}
+      {{ if site.Menus.main_right | and (eq $align_menu "l") }}
       </ul>
-      <ul class="navbar-nav ml-auto">
+      <ul class="navbar-nav ml-md-auto">
       {{ end }}
 
         {{ range site.Menus.main_right }}
@@ -97,38 +101,41 @@
         </li>
 
         {{ end }}
+      </ul>
+    </div><!-- /.navbar-collapse -->
 
-        {{ if site.Params.search.engine }}
-        <li class="nav-item">
-          <a class="nav-link js-search" href="#"><i class="fas fa-search" aria-hidden="true"></i></a>
-        </li>
-        {{ end }}
+    <ul class="nav-icons navbar-nav flex-row ml-auto d-flex pl-md-2">
+      {{ if site.Params.search.engine }}
+      <li class="nav-item">
+        <a class="nav-link js-search" href="#"><i class="fas fa-search" aria-hidden="true"></i></a>
+      </li>
+      {{ end }}
 
-        {{ if .IsTranslated }}
-        <li class="nav-item dropdown">
-          <a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true">
-            <i class="fas fa-globe mr-1" aria-hidden="true"></i><span>{{ index site.Data.i18n.languages .Lang }}</span>
-          </a>
-          <ul class="dropdown-menu">
-            {{ range .Translations }}
-            <li class="dropdown-item my-0 py-0 mx-0 px-0">
-              <a href="{{ .Permalink }}"{{ if $.IsHome }} data-target="{{ .URL }}"{{ end }}>
-                <span>{{ index site.Data.i18n.languages .Lang }}</span>
-              </a>
-            </li>
-            {{ end }}
-          </ul>
-        </li>
-        {{ end }}
+      {{ if site.Params.day_night }}
+      <li class="nav-item">
+        <a class="nav-link js-dark-toggle" href="#"><i class="fas fa-moon" aria-hidden="true"></i></a>
+      </li>
+      {{ end }}
 
-        {{ if site.Params.day_night }}
-        <li class="nav-item">
-          <a class="nav-link js-dark-toggle" href="#"><i class="fas fa-moon" aria-hidden="true"></i></a>
-        </li>
-        {{ end }}
+      {{ if .IsTranslated }}
+      <li class="nav-item dropdown i18n-dropdown">
+        <a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true">
+          <i class="fas fa-globe mr-1" aria-hidden="true"></i><span class="d-none d-lg-inline">{{ index site.Data.i18n.languages .Lang }}</span>
+        </a>
+        <div class="dropdown-menu">
+          <div class="dropdown-item active font-weight-bold">
+            <span>{{ index site.Data.i18n.languages .Lang }}</span>
+          </div>
+          {{ range .Translations }}
+          <a class="dropdown-item" href="{{ .RelPermalink }}"{{ if $.IsHome }} data-target="{{ .RelPermalink }}"{{ end }}>
+            <span>{{ index site.Data.i18n.languages .Lang }}</span>
+          </a>
+          {{ end }}
+        </div>
+      </li>
+      {{ end }}
 
-      </ul>
+    </ul>
 
-    </div><!-- /.navbar-collapse -->
   </div><!-- /.container -->
 </nav>