Responsive Side Menu

A layout example with a side menu that hides on mobile.
This commit is contained in:
Tommy Miland 2019-03-12 17:01:33 +01:00
parent 5f1cd6b552
commit 6aab7b0f67
3 changed files with 464 additions and 104 deletions

248
assets/css/side-menu.css Normal file
View File

@ -0,0 +1,248 @@
body {
color: #777;
}
.pure-img-responsive {
max-width: 100%;
height: auto;
}
/*
Add transition to containers so they can push in and out.
*/
#layout,
#menu,
.menu-link {
-webkit-transition: all 0.2s ease-out;
-moz-transition: all 0.2s ease-out;
-ms-transition: all 0.2s ease-out;
-o-transition: all 0.2s ease-out;
transition: all 0.2s ease-out;
}
/*
This is the parent `<div>` that contains the menu and the content area.
*/
#layout {
position: relative;
left: 0;
padding-left: 0;
}
#layout.active #menu {
left: 160px;
width: 160px;
}
#layout.active .menu-link {
left: 160px;
}
/*
The content `<div>` is where all your content goes.
*/
.content {
margin: 0 auto;
padding: 0 2em;
max-width: 800px;
margin-bottom: 50px;
line-height: 1.6em;
}
.header {
margin: 0;
color: #333;
text-align: center;
padding: 2.5em 2em 0;
border-bottom: 1px solid #eee;
}
.header h1 {
margin: 0.2em 0;
font-size: 3em;
font-weight: 300;
}
.header h2 {
font-weight: 300;
color: #ccc;
padding: 0;
margin-top: 0;
}
.content-subhead {
margin: 50px 0 20px 0;
font-weight: 300;
color: #888;
}
/*
The `#menu` `<div>` is the parent `<div>` that contains the `.pure-menu` that
appears on the left side of the page.
*/
#menu {
margin-left: -160px; /* "#menu" width */
width: 160px;
position: fixed;
top: 0;
left: 0;
bottom: 0;
z-index: 1000; /* so the menu or its navicon stays above all content */
background: #191818;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
/*
All anchors inside the menu should be styled like this.
*/
#menu a {
color: #999;
border: none;
padding: 0.6em 0 0.6em 0.6em;
}
/*
Remove all background/borders, since we are applying them to #menu.
*/
#menu .pure-menu,
#menu .pure-menu ul {
border: none;
background: transparent;
}
/*
Add that light border to separate items into groups.
*/
#menu .pure-menu ul,
#menu .pure-menu .menu-item-divided {
border-top: 1px solid #333;
}
/*
Change color of the anchor links on hover/focus.
*/
#menu .pure-menu li a:hover,
#menu .pure-menu li a:focus {
background: #333;
}
/*
This styles the selected menu item `<li>`.
*/
#menu .pure-menu-selected,
#menu .pure-menu-heading {
background: #1f8dd6;
}
/*
This styles a link within a selected menu item `<li>`.
*/
#menu .pure-menu-selected a {
color: #fff;
}
/*
This styles the menu heading.
*/
#menu .pure-menu-heading {
font-size: 110%;
color: #fff;
margin: 0;
}
/* -- Dynamic Button For Responsive Menu -------------------------------------*/
/*
The button to open/close the Menu is custom-made and not part of Pure. Here's
how it works:
*/
/*
`.menu-link` represents the responsive menu toggle that shows/hides on
small screens.
*/
.menu-link {
position: fixed;
display: block; /* show this only on small screens */
top: 0;
left: 0; /* "#menu width" */
background: #000;
background: rgba(0,0,0,0.7);
font-size: 10px; /* change this value to increase/decrease button size */
z-index: 10;
width: 2em;
height: auto;
padding: 2.1em 1.6em;
}
.menu-link:hover,
.menu-link:focus {
background: #000;
}
.menu-link span {
position: relative;
display: block;
}
.menu-link span,
.menu-link span:before,
.menu-link span:after {
background-color: #fff;
width: 100%;
height: 0.2em;
}
.menu-link span:before,
.menu-link span:after {
position: absolute;
margin-top: -0.6em;
content: " ";
}
.menu-link span:after {
margin-top: 0.6em;
}
/* -- Responsive Styles (Media Queries) ------------------------------------- */
/*
Hides the menu at `48em`, but modify this based on your app's needs.
*/
@media (min-width: 48em) {
.header,
.content {
padding-left: 2em;
padding-right: 2em;
}
#layout {
padding-left: 160px; /* left col width "#menu" */
left: 0;
}
#menu {
left: 160px;
}
.menu-link {
position: fixed;
left: 160px;
display: none;
}
#layout.active .menu-link {
left: 160px;
}
}
@media (max-width: 48em) {
/* Only apply this when the window is small. Otherwise, the following
case results in extra padding on the left:
* Make the window small.
* Tap the menu to trigger the active state.
* Make the window large again.
*/
#layout.active {
position: relative;
left: 160px;
}
}

46
assets/js/ui.js Normal file
View File

@ -0,0 +1,46 @@
(function (window, document) {
var layout = document.getElementById('layout'),
menu = document.getElementById('menu'),
menuLink = document.getElementById('menuLink'),
content = document.getElementById('main');
function toggleClass(element, className) {
var classes = element.className.split(/\s+/),
length = classes.length,
i = 0;
for(; i < length; i++) {
if (classes[i] === className) {
classes.splice(i, 1);
break;
}
}
// The className is not found
if (length === classes.length) {
classes.push(className);
}
element.className = classes.join(' ');
}
function toggleAll(e) {
var active = 'active';
e.preventDefault();
toggleClass(layout, active);
toggleClass(menu, active);
toggleClass(menuLink, active);
}
menuLink.onclick = function (e) {
toggleAll(e);
};
content.onclick = function(e) {
if (menu.className.indexOf('active') !== -1) {
toggleAll(e);
}
};
}(this, this.document));

View File

@ -14,127 +14,193 @@
<meta name="msapplication-TileColor" content="#575757">
<meta name="theme-color" content="#575757">
<link title="Invidious" type="application/opensearchdescription+xml" rel="search" href="/opensearch.xml">
<link rel="stylesheet" href="/css/side-menu.css">
<link rel="stylesheet" href="/css/pure-min.css">
<link rel="stylesheet" href="/css/grids-responsive-min.css">
<link rel="stylesheet" href="/css/ionicons.min.css">
<link rel="stylesheet" href="/css/default.css">
<% if env.get?("preferences").try &.as(Preferences).dark_mode %>
<% if env.get("preferences").as(Preferences).dark_mode %>
<link rel="stylesheet" href="/css/darktheme.css">
<% else %>
<link rel="stylesheet" href="/css/lighttheme.css">
<% end %>
</head>
<% locale = LOCALES[env.get("locale").as(String)]? %>
<% locale = LOCALES[env.get("preferences").as(Preferences).locale]? %>
<body>
<div class="pure-g">
<div class="pure-u-1 pure-u-md-2-24"></div>
<div class="pure-u-1 pure-u-md-20-24">
<div class="pure-g navbar h-box">
<div class="pure-u-1 pure-u-md-4-24">
<a href="/" class="index-link pure-menu-heading">Invidious</a>
</div>
<div class="pure-u-1 pure-u-md-12-24 searchbar">
<form class="pure-form" action="/search" method="get">
<fieldset>
<input type="search" style="width:100%;" name="q" placeholder="<%= translate(locale, "search") %>" value="<%= env.get?("search").try {|x| HTML.escape(x.as(String)) } || env.params.query["q"]?.try {|x| HTML.escape(x)} %>">
</fieldset>
</form>
</div>
<div class="pure-u-1 pure-u-md-8-24 user-field">
<% if env.get? "user" %>
<div class="pure-u-1-4">
<a href="/toggle_theme?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
<% preferences = env.get("user").as(User).preferences %>
<% if preferences.dark_mode %>
<i class="icon ion-ios-sunny"></i>
<% else %>
<i class="icon ion-ios-moon"></i>
<% end %>
</a>
</div>
<div class="pure-u-1-4">
<a title="<%= translate(locale, "Subscriptions") %>" href="/feed/subscriptions" class="pure-menu-heading">
<% notification_count = env.get("user").as(User).notifications.size %>
<% if notification_count > 0 %>
<%= notification_count %> <i class="icon ion-ios-notifications"></i>
<% else %>
<i class="icon ion-ios-notifications-outline"></i>
<% end %>
</a>
</div>
<div class="pure-u-1-4">
<a title="<%= translate(locale, "Preferences") %>" href="/preferences?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
<i class="icon ion-ios-cog"></i>
</a>
</div>
<div class="pure-u-1-4">
<a href="/signout?referer=<%= env.get?("current_page") %>&token=<%= env.get?("token") %>&challenge=<%= env.get?("challenge") %>" class="pure-menu-heading">
<%= translate(locale, "Sign out") %>
</a>
</div>
<% else %>
<div class="pure-u-1-3">
<a href="/toggle_theme?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
<% if env.get?("preferences").try &.as(Preferences).dark_mode %>
<i class="icon ion-ios-sunny"></i>
<% else %>
<i class="icon ion-ios-moon"></i>
<% end %>
</a>
</div>
<div class="pure-u-1-3">
<a title="<%= translate(locale, "Preferences") %>" href="/preferences?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
<i class="icon ion-ios-cog"></i>
</a>
</div>
<% if config.login_enabled %>
<div class="pure-u-1-3">
<a href="/login?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
<%= translate(locale, "Login") %>
</a>
</div>
<% end %>
<% end %>
</div>
</div>
<%= content %>
<div class="footer">
<div class="pure-g">
<div class="pure-u-1 pure-u-md-1-3">
<a href="https://github.com/omarroth/invidious">
<%= translate(locale, "Released under the AGPLv3 by Omar Roth.") %>
<div id="layout">
<!-- Menu toggle -->
<a href="#menu" id="menuLink" class="menu-link">
<!-- Hamburger icon -->
<span></span>
</a>
<div id="menu">
<div class="pure-menu">
<a class="pure-menu-heading" href="/">
<i class="icon ion-ios-home"></i>
Home
</a>
<ul class="pure-menu-list">
<li class="pure-menu-item">
<a title="<%= translate(locale, " Subscriptions") %>" href="/feed/subscriptions" class="pure-menu-link">
<i class="icon ion-logo-rss"></i>
<%= translate(locale, "Subscriptions") %>
</a>
</div>
<div class="pure-u-1 pure-u-md-1-3">
<i class="icon ion-logo-bitcoin"></i>
<%= translate(locale, "BTC: ") %>356DpZyMXu6rYd55Yqzjs29n79kGKWcYrY</div>
<div class="pure-u-1 pure-u-md-1-3">
<i class="icon ion-logo-bitcoin"></i>
<%= translate(locale, "BCH: ") %>qq4ptclkzej5eza6a50et5ggc58hxsq5aylqut2npk</div>
<div class="pure-u-1 pure-u-md-1-3">
<i class="icon ion-logo-usd"></i>
<a href="https://liberapay.com/omarroth"><%= translate(locale, "Liberapay") %></a>
/
<a href="https://patreon.com/omarroth"><%= translate(locale, "Patreon") %></a>
</div>
<div class="pure-u-1 pure-u-md-1-3">
<i class="icon ion-logo-javascript"></i>
<a rel="jslicense" href="/licenses">
<%= translate(locale, "View JavaScript license information.") %>
</li>
<li class="pure-menu-item">
<a title="<%= translate(locale, " Trending") %>" href="/feed/trending" class="pure-menu-link">
<i class="icon ion-ios-flame"></i>
<%= translate(locale, "Trending") %>
</a>
</div>
<div class="pure-u-1 pure-u-md-1-3">
<i class="icon ion-logo-github"></i>
<%= translate(locale, "Current version: ") %> <%= CURRENT_VERSION %>-<%= CURRENT_COMMIT %>
<i class="icon ion-logo-github"></i>
<%= CURRENT_BRANCH %></div>
</div>
</li>
<li class="pure-menu-item">
<a title="<%= translate(locale, " History") %>" href="feed/history" class="pure-menu-link">
<i class="icon ion-ios-timer"></i>
<%= translate(locale, "History") %>
</a>
</li>
<li class="pure-menu-item menu-item-divided">
</li>
<li class="pure-menu-item">
<a title="<%= translate(locale, " Preferences") %>" href="/preferences?referer=
<%= env.get?("current_page") %>" class="pure-menu-link">
<i class="icon ion-ios-options"></i>
<%= translate(locale, "Preferences") %>
</a>
</li>
<li class="pure-menu-item menu-item-divided">
</li>
<li class="pure-menu-item"><a href="https://github.com/omarroth/invidious/wiki" class="pure-menu-link">
<i class="icon ion-ios-information-circle"></i>
<%= translate(locale, "Wiki") %>
</a>
</li>
<li class="pure-menu-item"><a href="https://github.com/omarroth/invidious/issues" class="pure-menu-link">
<i class="icon ion-ios-create"></i>
<%= translate(locale, "Send Feedback") %>
</a>
</li>
</ul>
</div>
</div>
<div class="pure-u-1 pure-u-md-2-24"></div>
<div class="pure-g">
<div class="pure-u-1 pure-u-md-2-24"></div>
<div class="pure-u-1 pure-u-md-20-24">
<div class="pure-g navbar h-box">
<div class="pure-u-1 pure-u-md-4-24">
<a href="/" class="index-link pure-menu-heading">Invidious</a>
</div>
<div class="pure-u-1 pure-u-md-12-24 searchbar">
<form class="pure-form" action="/search" method="get">
<fieldset>
<input type="search" style="width:100%;" name="q" placeholder="<%= translate(locale, " search") %>" value="
<%= env.get?("search").try {|x| HTML.escape(x.as(String)) } || env.params.query["q"]?.try {|x| HTML.escape(x)} %>">
</fieldset>
</form>
</div>
<div class="pure-u-1 pure-u-md-8-24 user-field">
<% if env.get? "user" %>
<div class="pure-u-1-4">
<a href="/toggle_theme?referer=<%= env.get?(" current_page") %>" class="pure-menu-heading">
<% preferences = env.get("user").as(User).preferences %>
<% if preferences.dark_mode %>
<i class="icon ion-ios-sunny"></i>
<% else %>
<i class="icon ion-ios-moon"></i>
<% end %>
</a>
</div>
<div class="pure-u-1-4">
<a title="<%= translate(locale, " Subscriptions") %>" href="/feed/subscriptions" class="pure-menu-heading">
<% notification_count = env.get("user").as(User).notifications.size %>
<% if notification_count > 0 %>
<%= notification_count %> <i class="icon ion-ios-notifications"></i>
<% else %>
<i class="icon ion-ios-notifications-outline"></i>
<% end %>
</a>
</div>
<div class="pure-u-1-4">
<a title="<%= translate(locale, " Preferences") %>" href="/preferences?referer=
<%= env.get?("current_page") %>" class="pure-menu-heading">
<i class="icon ion-ios-cog"></i>
</a>
</div>
<div class="pure-u-1-4">
<a href="/signout?referer=<%= env.get?(" current_page") %>&token=
<%= env.get?("token") %>&challenge=
<%= env.get?("challenge") %>" class="pure-menu-heading">
<%= translate(locale, "Sign out") %>
</a>
</div>
<% else %>
<div class="pure-u-1-3">
<a href="/toggle_theme?referer=<%= env.get?(" current_page") %>" class="pure-menu-heading">
<% if env.get?("preferences").try &.as(Preferences).dark_mode %>
<i class="icon ion-ios-sunny"></i>
<% else %>
<i class="icon ion-ios-moon"></i>
<% end %>
</a>
</div>
<div class="pure-u-1-3">
<a title="<%= translate(locale, " Preferences") %>" href="/preferences?referer=
<%= env.get?("current_page") %>" class="pure-menu-heading">
<i class="icon ion-ios-cog"></i>
</a>
</div>
<% if config.login_enabled %>
<div class="pure-u-1-3">
<a href="/login?referer=<%= env.get?(" current_page") %>" class="pure-menu-heading">
<%= translate(locale, "Login") %>
</a>
</div>
<% end %>
<% end %>
</div>
</div>
<%= content %>
<div class="footer">
<div class="pure-g">
<div class="pure-u-1 pure-u-md-1-3">
<a href="https://github.com/omarroth/invidious">
<%= translate(locale, "Released under the AGPLv3 by Omar Roth.") %>
</a>
</div>
<div class="pure-u-1 pure-u-md-1-3">
<i class="icon ion-logo-bitcoin"></i>
BTC: 356DpZyMXu6rYd55Yqzjs29n79kGKWcYrY</div>
<div class="pure-u-1 pure-u-md-1-3">
<i class="icon ion-logo-bitcoin"></i>
BCH: qq4ptclkzej5eza6a50et5ggc58hxsq5aylqut2npk</div>
<div class="pure-u-1 pure-u-md-1-3">
<i class="icon ion-logo-usd"></i>
<a href="https://liberapay.com/omarroth">Liberapay</a>
/
<a href="https://patreon.com/omarroth">Patreon</a>
</div>
<div class="pure-u-1 pure-u-md-1-3">
<i class="icon ion-logo-javascript"></i>
<a rel="jslicense" href="/licenses">
<%= translate(locale, "View JavaScript license information.") %>
</a>
</div>
<div class="pure-u-1 pure-u-md-1-3">
<i class="icon ion-logo-github"></i>
<%= translate(locale, "Current version: ") %>
<%= CURRENT_VERSION %>-
<%= CURRENT_COMMIT %>
<i class="icon ion-logo-github"></i>
<%= CURRENT_BRANCH %>
</div>
</div>
</div>
</div>
<div class="pure-u-1 pure-u-md-2-24"></div>
</div>
</div>
<script src="/js/ui.js"></script>
</body>
</html>