Skip to content

Instantly share code, notes, and snippets.

@3kynox
Created April 3, 2019 09:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 3kynox/7d9abef3b50f274c3cc62add57623f0c to your computer and use it in GitHub Desktop.
Save 3kynox/7d9abef3b50f274c3cc62add57623f0c to your computer and use it in GitHub Desktop.
Holy Grail Dashboard
<body>
<header class="ui compact inverted menu">
<!-- BEGIN logo & title -->
<div id="logo" class="borderless item">
<i class="cubes icon"></i>
</div>
<div id="title" class="item hidden-sm-down">Holy Grail Dashboard</div>
<!-- END logo & title -->
<nav class="menu">
<div onClick="toggleActions()" class="link item">
<i class="sidebar icon"></i>
</div>
<!-- BEGIN top right menu -->
<div class="right menu">
<div class="ui simple dropdown item top right">
<span id="name" class="text">Robert Claypool</span>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item">
<!-- BEGIN card -->
<div class="ui card">
<div class="content">
<img class="ui tiny image" src="https://semantic-ui.com/images/avatar2/small/matthew.png">
</div>
<div class="extra content">
<div class="ui left floated compact button">
<i class="user icon"></i> User Profile
</div>
<div class="ui right floated compact button">
<i class="sign out icon"></i> Log Out
</div>
</div>
</div>
<!-- END card -->
</div>
</div>
</div>
<div onClick="toggleSettings()" class="link item">
<i class="settings icon"></i>
</div>
</div>
<!-- END top right menu -->
</nav>
</header>
<div class="wrapper">
<!-- BEGIN actions-sidebar -->
<!--
Action sidebar defaults to "open" on large enough screens.
JS removes this class when it is closed.
-->
<aside id="actions-sidebar" class="open">
<div id="actions-sidebar-menu" class="ui fluid vertical menu">
<div class="header item">Page Actions</div>
<div class="item">
<div class="ui transparent icon input">
<input type="text" placeholder="Search">
<i class="search icon"></i>
</div>
</div>
<a class="item">
Action ABC
<div class="ui blue label">1</div>
</a>
<a class="item">
Action DEF
<div class="ui blue label">1</div>
</a>
<a class="item">
Action GHI
<div class="ui label">1</div>
</a>
<a class="item">
Action JKL
<div class="ui label">1</div>
</a>
<a class="item">
Action MNO
<div class="ui green label">2</div>
</a>
<a class="item">
Action PQR
<div class="ui blue label">4</div>
</a>
<a class="item">
Action STU
<div class="ui label">11</div>
</a>
<a class="item">
Action VWX
<div class="ui label">2</div>
</a>
<a class="item">
Action YZ!
<div class="ui blue label">1</div>
</a>
</div>
</aside>
<!-- END actions-sidebar -->
<!-- BEGIN settings-sidebar -->
<!--
Settings sidebar defaults to "closed".
JS removes this class when it is opened.
-->
<aside id="settings-sidebar" class="closed">
<div id="settings-sidebar-menu" class="ui fluid vertical menu">
<div class="header item">App Settings</div>
<a class="item">
<div class="ui checkbox">
<input type="checkbox">
<label>Setting ABC</label>
</div>
</a>
<a class="item">
<div class="ui checkbox">
<input type="checkbox">
<label>Setting XYZ</label>
</div>
</a>
</div>
</aside>
<!-- END settings-sidebar -->
<!--
We place <main> after the sidebars so that sibling
CSS selectors like '#settings-sidebar + main' will work.
-->
<main>
<section class="ui segment">
<h1 class="ui header">Main Content</h1>
<p>This page uses <em>Flexbox</em> for a complete, standards-based solution to the <a href="https://en.wikipedia.org/wiki/Holy_Grail_(web_design)">Holy Grail Layout</a>, see <a href="https://philipwalton.github.io/solved-by-flexbox/demos/holy-grail/">https://philipwalton.github.io/solved-by-flexbox/demos/holy-grail/</a>.
</p>
<p>Styling and UI elements are provided by <a href="https://semantic-ui.com/">Semantic UI</a>.
</p>
</section>
</main>
</div>
<footer class="ui inverted vertical segment">
<div id="footer-message">This is the footer!</div>
</footer>
</body>
const VISIBILITY = Object.freeze({
closed: 'closed',
open: 'open',
});
function toggleActions() {
$('#actions-sidebar').toggleClass(VISIBILITY.closed);
$('#actions-sidebar').toggleClass(VISIBILITY.open);
$('#title').toggleClass('hidden');
}
function toggleSettings() {
$('#settings-sidebar').toggleClass(VISIBILITY.closed);
$('#settings-sidebar').toggleClass(VISIBILITY.open);
}
(function() {
if (window.matchMedia) {
// Keep this in sync: 768px should be the breakpoint we also use in CSS.
const mediaQueryString = '(max-width: 768px)';
// Make the sizebars auto-close when the viewport is <= 768px.
// Restore sizebars' previous state when the viewport is > 768px.
function handleViewportWidth(mql) {
if (mql.media !== mediaQueryString) {
throw new Error('Unexpected media query list.');
}
const autoClose = mql.matches;
if (autoClose) {
// Save their current state.
this.actionsV = $('#actions-sidebar').hasClass(VISIBILITY.open) ?
VISIBILITY.open : VISIBILITY.closed;
this.settingsV = $('#settings-sidebar').hasClass(VISIBILITY.open) ?
VISIBILITY.open : VISIBILITY.closed;
// Now close them.
$('#actions-sidebar').removeClass(VISIBILITY.open);
$('#actions-sidebar').addClass(VISIBILITY.closed);
$('#settings-sidebar').removeClass(VISIBILITY.open);
$('#settings-sidebar').addClass(VISIBILITY.closed);
// Add overlay rules which stack the sidebars on top of main content.
// Stacking is better for small screens.
$('#actions-sidebar').addClass('overlay');
$('#settings-sidebar').addClass('overlay');
} else {
// Is there a saved state? Restore it.
if (this.actionsV === VISIBILITY.closed) {
$('#actions-sidebar').addClass(VISIBILITY.closed);
$('#actions-sidebar').removeClass(VISIBILITY.open);
} else {
$('#actions-sidebar').removeClass(VISIBILITY.closed);
$('#actions-sidebar').addClass(VISIBILITY.open);
}
if (this.settingsV === VISIBILITY.closed) {
$('#settings-sidebar').addClass(VISIBILITY.closed);
$('#settings-sidebar').removeClass(VISIBILITY.open);
} else {
$('#settings-sidebar').removeClass(VISIBILITY.closed);
$('#settings-sidebar').addClass(VISIBILITY.open);
}
// Always remove the overlay/stacking rules.
$('#actions-sidebar').removeClass('overlay');
$('#settings-sidebar').removeClass('overlay');
}
}
function initSidebars() {
const mql = window.matchMedia(mediaQueryString);
handleViewportWidth(mql);
// Listen for changes on the document.
mql.addListener(handleViewportWidth);
}
// Initialize the sidebars on load.
window.addEventListener('DOMContentLoaded', initSidebars, false);
}
})();
<script src="https://cdn.jsdelivr.net/semantic-ui/2.2.6/semantic.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
body {
/*
Good flexbox resources:
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
https://scotch.io/tutorials/a-visual-guide-to-css3-flexbox-properties
*/
display: flex;
/*
We use "column" for the header, wrapper, and footer.
We use "row" for children of the wrapper: sidebar, main, sidebar
*/
flex-direction: column;
min-height: 100vh;
background-color: steelblue;
}
#logo {
width: 4rem;
}
#title {
width: 14rem;
}
/*
#title.hidden is specific enough to override the display
value of Semantic's .item class which #title also uses.
*/
#title.hidden {
/*
.hidden is not present on page load.
It's added/removed by button click events.
*/
display: none;
}
nav {
width: 100%;
}
div.wrapper {
/* The default flex-direction of "row" is what we want. */
display: flex;
/*
The following "flex" directive applies to this wrapper
in relation to its parent (the body)...
1: Our wrapper should fill remaining available vertical space of the body.
0: Don't allow it to shrink less than its content; a scrollbar should appear if needed.
auto: The default value. It seems to work well.
*/
flex: 1 0 auto;
}
main {
flex: 1 1 auto;
/*
Our order is:
1 --> actions sidebar (aside)
2 --> main content
3 --> settings sidebar (aside)
*/
order: 2;
padding: 1rem;
}
#actions-sidebar {
/* Put this on the left */
order: 1;
}
#settings-sidebar {
/* Put this on the right */
order: 3;
}
#settings-sidebar,
#actions-sidebar {
/*
18rem is the width of our columns.
They are fixed; don't grow or shrink them.
*/
flex: 0 0 18rem;
/*
Set the overflow hidden to compensate for
an ugly margin/padding 5000px hack.
http://stackoverflow.com/a/8451485/23566
*/
overflow: hidden;
}
#settings-sidebar.closed,
#actions-sidebar.closed {
display: none;
}
/*
Add negative margin to push main under the open sidebars.
This works since main is given a lower z-index.
*/
#settings-sidebar.overlay.open ~ main {
margin-right: -18rem;
z-index: 1;
}
#actions-sidebar.overlay.open ~ main {
margin-left: -18rem;
z-index: 1;
}
#settings-sidebar.overlay,
#actions-sidebar.overlay {
z-index: 2;
}
#settings-sidebar-menu,
#actions-sidebar-menu {
/*
This margin/padding hack is for browsers like
Safari which do not properly extend the flexbox
item height of our sidebars using `height: 100%;`
http://stackoverflow.com/a/8451485/23566
*/
margin-bottom: -5000px;
/* Any large number will do */
padding-bottom: 5000px;
}
#footer-message {
padding-left: 1rem;
padding-right: 1rem;
}
/*
It's recommended to prefix media queries with "only" if you want to hide
their styles from old browsers. http://stackoverflow.com/a/8595600/23566
*/
@media only screen and (max-width: 768px) {
/*
hidden-sm-down is a "responsive visibility utility"
copied in from Bootstrap 4. I don't see any
similar functionality provided by Semantic UI.
*/
.hidden-sm-down {
display: none !important;
}
main {
padding: 0.5rem;
}
}
<link href="https://cdn.jsdelivr.net/semantic-ui/2.2.6/semantic.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment