Skip to content

Instantly share code, notes, and snippets.

@3kynox
Created April 3, 2019 09:39
Show Gist options
  • Select an option

  • Save 3kynox/7d9abef3b50f274c3cc62add57623f0c to your computer and use it in GitHub Desktop.

Select an option

Save 3kynox/7d9abef3b50f274c3cc62add57623f0c to your computer and use it in GitHub Desktop.

Revisions

  1. 3kynox created this gist Apr 3, 2019.
    7 changes: 7 additions & 0 deletions holy-grail-dashboard.markdown
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    Holy Grail Dashboard
    --------------------


    A [Pen](https://codepen.io/robert-claypool/pen/dOzYBa) by [Robert Claypool](https://codepen.io/robert-claypool) on [CodePen](https://codepen.io).

    [License](https://codepen.io/robert-claypool/pen/dOzYBa/license).
    139 changes: 139 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,139 @@
    <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>
    78 changes: 78 additions & 0 deletions script.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,78 @@
    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);
    }
    })();
    2 changes: 2 additions & 0 deletions scripts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,2 @@
    <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>
    149 changes: 149 additions & 0 deletions style.css
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,149 @@
    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;
    }
    }
    1 change: 1 addition & 0 deletions styles
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    <link href="https://cdn.jsdelivr.net/semantic-ui/2.2.6/semantic.min.css" rel="stylesheet" />