diff --git a/resources/assets/js/components/back-top-top.js b/resources/assets/js/components/back-top-top.js
new file mode 100644
index 000000000..5fa9b3436
--- /dev/null
+++ b/resources/assets/js/components/back-top-top.js
@@ -0,0 +1,53 @@
+
+class BackToTop {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.targetElem = document.getElementById('header');
+        this.showing = false;
+        this.breakPoint = 1200;
+        this.elem.addEventListener('click', this.scrollToTop.bind(this));
+        window.addEventListener('scroll', this.onPageScroll.bind(this));
+    }
+
+    onPageScroll() {
+        let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
+        if (!this.showing && scrollTopPos > this.breakPoint) {
+            this.elem.style.display = 'block';
+            this.showing = true;
+            setTimeout(() => {
+                this.elem.style.opacity = 0.4;
+            }, 1);
+        } else if (this.showing && scrollTopPos < this.breakPoint) {
+            this.elem.style.opacity = 0;
+            this.showing = false;
+            setTimeout(() => {
+                this.elem.style.display = 'none';
+            }, 500);
+        }
+    }
+
+    scrollToTop() {
+        let targetTop = this.targetElem.getBoundingClientRect().top;
+        let scrollElem = document.documentElement.scrollTop ? document.documentElement : document.body;
+        let duration = 300;
+        let start = Date.now();
+        let scrollStart = this.targetElem.getBoundingClientRect().top;
+
+        function setPos() {
+            let percentComplete = (1-((Date.now() - start) / duration));
+            let target = Math.abs(percentComplete * scrollStart);
+            if (percentComplete > 0) {
+                scrollElem.scrollTop = target;
+                requestAnimationFrame(setPos.bind(this));
+            } else {
+                scrollElem.scrollTop = targetTop;
+            }
+        }
+
+        requestAnimationFrame(setPos.bind(this));
+    }
+
+}
+
+module.exports = BackToTop;
\ No newline at end of file
diff --git a/resources/assets/js/components/chapter-toggle.js b/resources/assets/js/components/chapter-toggle.js
new file mode 100644
index 000000000..ad373a668
--- /dev/null
+++ b/resources/assets/js/components/chapter-toggle.js
@@ -0,0 +1,67 @@
+
+class ChapterToggle {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.isOpen = elem.classList.contains('open');
+        elem.addEventListener('click', this.click.bind(this));
+    }
+
+    open() {
+        let list = this.elem.parentNode.querySelector('.inset-list');
+
+        this.elem.classList.add('open');
+        list.style.display = 'block';
+        list.style.height = '';
+        let height = list.getBoundingClientRect().height;
+        list.style.height = '0px';
+        list.style.overflow = 'hidden';
+        list.style.transition = 'height ease-in-out 240ms';
+
+        let transitionEndBound = onTransitionEnd.bind(this);
+        function onTransitionEnd() {
+            list.style.overflow = '';
+            list.style.height = '';
+            list.style.transition = '';
+            list.removeEventListener('transitionend', transitionEndBound);
+        }
+
+        setTimeout(() => {
+            list.style.height = `${height}px`;
+            list.addEventListener('transitionend', transitionEndBound)
+        }, 1);
+    }
+
+    close() {
+        let list = this.elem.parentNode.querySelector('.inset-list');
+
+        this.elem.classList.remove('open');
+        list.style.display =  'block';
+        list.style.height = list.getBoundingClientRect().height + 'px';
+        list.style.overflow = 'hidden';
+        list.style.transition = 'height ease-in-out 240ms';
+
+        let transitionEndBound = onTransitionEnd.bind(this);
+        function onTransitionEnd() {
+            list.style.overflow = '';
+            list.style.height = '';
+            list.style.transition = '';
+            list.style.display =  'none';
+            list.removeEventListener('transitionend', transitionEndBound);
+        }
+
+        setTimeout(() => {
+            list.style.height = `0px`;
+            list.addEventListener('transitionend', transitionEndBound)
+        }, 1);
+    }
+
+    click(event) {
+        event.preventDefault();
+        this.isOpen ?  this.close() : this.open();
+        this.isOpen = !this.isOpen;
+    }
+
+}
+
+module.exports = ChapterToggle;
\ No newline at end of file
diff --git a/resources/assets/js/components/expand-toggle.js b/resources/assets/js/components/expand-toggle.js
new file mode 100644
index 000000000..61d9f54b7
--- /dev/null
+++ b/resources/assets/js/components/expand-toggle.js
@@ -0,0 +1,65 @@
+
+class ExpandToggle {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.isOpen = false;
+        this.selector = elem.getAttribute('expand-toggle');
+        elem.addEventListener('click', this.click.bind(this));
+    }
+
+    open(elemToToggle) {
+        elemToToggle.style.display = 'block';
+        elemToToggle.style.height = '';
+        let height = elemToToggle.getBoundingClientRect().height;
+        elemToToggle.style.height = '0px';
+        elemToToggle.style.overflow = 'hidden';
+        elemToToggle.style.transition = 'height ease-in-out 240ms';
+
+        let transitionEndBound = onTransitionEnd.bind(this);
+        function onTransitionEnd() {
+            elemToToggle.style.overflow = '';
+            elemToToggle.style.height = '';
+            elemToToggle.style.transition = '';
+            elemToToggle.removeEventListener('transitionend', transitionEndBound);
+        }
+
+        setTimeout(() => {
+            elemToToggle.style.height = `${height}px`;
+            elemToToggle.addEventListener('transitionend', transitionEndBound)
+        }, 1);
+    }
+
+    close(elemToToggle) {
+        elemToToggle.style.display =  'block';
+        elemToToggle.style.height = elemToToggle.getBoundingClientRect().height + 'px';
+        elemToToggle.style.overflow = 'hidden';
+        elemToToggle.style.transition = 'all ease-in-out 240ms';
+
+        let transitionEndBound = onTransitionEnd.bind(this);
+        function onTransitionEnd() {
+            elemToToggle.style.overflow = '';
+            elemToToggle.style.height = '';
+            elemToToggle.style.transition = '';
+            elemToToggle.style.display =  'none';
+            elemToToggle.removeEventListener('transitionend', transitionEndBound);
+        }
+
+        setTimeout(() => {
+            elemToToggle.style.height = `0px`;
+            elemToToggle.addEventListener('transitionend', transitionEndBound)
+        }, 1);
+    }
+
+    click(event) {
+        event.preventDefault();
+        let matchingElems = document.querySelectorAll(this.selector);
+        for (let i = 0, len = matchingElems.length; i < len; i++) {
+            this.isOpen ?  this.close(matchingElems[i]) : this.open(matchingElems[i]);
+        }
+        this.isOpen = !this.isOpen;
+    }
+
+}
+
+module.exports = ExpandToggle;
\ No newline at end of file
diff --git a/resources/assets/js/components/index.js b/resources/assets/js/components/index.js
index 983b25d8f..c38e20aa2 100644
--- a/resources/assets/js/components/index.js
+++ b/resources/assets/js/components/index.js
@@ -1,6 +1,11 @@
 
 let componentMapping = {
     'dropdown': require('./dropdown'),
+    'overlay': require('./overlay'),
+    'back-to-top': require('./back-top-top'),
+    'notification': require('./notification'),
+    'chapter-toggle': require('./chapter-toggle'),
+    'expand-toggle': require('./expand-toggle'),
 };
 
 window.components = {};
diff --git a/resources/assets/js/components/notification.js b/resources/assets/js/components/notification.js
new file mode 100644
index 000000000..4b809c935
--- /dev/null
+++ b/resources/assets/js/components/notification.js
@@ -0,0 +1,40 @@
+
+class Notification {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.type = elem.getAttribute('notification');
+        this.textElem = elem.querySelector('span');
+        this.autohide = this.elem.hasAttribute('data-autohide');
+        window.Events.listen(this.type, text => {
+            console.log('show', text);
+            this.show(text);
+        });
+        elem.addEventListener('click', this.hide.bind(this));
+        if (elem.hasAttribute('data-show')) this.show(this.textElem.textContent);
+    }
+
+    show(textToShow = '') {
+        this.textElem.textContent = textToShow;
+        this.elem.style.display = 'block';
+        setTimeout(() => {
+            this.elem.classList.add('showing');
+        }, 1);
+
+        if (this.autohide) setTimeout(this.hide.bind(this), 2000);
+    }
+
+    hide() {
+        this.elem.classList.remove('showing');
+
+        function transitionEnd() {
+            this.elem.style.display = 'none';
+            this.elem.removeEventListener('transitionend', transitionEnd);
+        }
+
+        this.elem.addEventListener('transitionend', transitionEnd.bind(this));
+    }
+
+}
+
+module.exports = Notification;
\ No newline at end of file
diff --git a/resources/assets/js/components/overlay.js b/resources/assets/js/components/overlay.js
new file mode 100644
index 000000000..6984928bd
--- /dev/null
+++ b/resources/assets/js/components/overlay.js
@@ -0,0 +1,39 @@
+
+class Overlay {
+
+    constructor(elem) {
+        this.container = elem;
+        elem.addEventListener('click', event => {
+             if (event.target === elem) return this.hide();
+        });
+        let closeButtons = elem.querySelectorAll('.overlay-close');
+        for (let i=0; i < closeButtons.length; i++) {
+            closeButtons[i].addEventListener('click', this.hide.bind(this));
+        }
+    }
+
+    toggle(show = true) {
+        let start = Date.now();
+        let duration = 240;
+
+        function setOpacity() {
+            let elapsedTime = (Date.now() - start);
+            let targetOpacity = show ? (elapsedTime / duration) : 1-(elapsedTime / duration);
+            this.container.style.opacity = targetOpacity;
+            if (elapsedTime > duration) {
+                this.container.style.display = show ? 'display' : 'none';
+                this.container.style.opacity = '';
+            } else {
+                requestAnimationFrame(setOpacity.bind(this));
+            }
+        }
+
+        requestAnimationFrame(setOpacity.bind(this));
+    }
+
+    hide() { this.toggle(false); }
+    show() { this.toggle(true); }
+
+}
+
+module.exports = Overlay;
\ No newline at end of file
diff --git a/resources/assets/js/directives.js b/resources/assets/js/directives.js
index cd9cc0283..522dcacae 100644
--- a/resources/assets/js/directives.js
+++ b/resources/assets/js/directives.js
@@ -713,6 +713,7 @@ module.exports = function (ngApp, events) {
                 function hide() {
                     element.fadeOut(240);
                 }
+                scope.hide = hide;
 
                 // Listen to confirmation of entity selections (doubleclick)
                 events.listen('entity-select-confirm', entity => {
diff --git a/resources/assets/js/global.js b/resources/assets/js/global.js
index 979310777..3879a4d4f 100644
--- a/resources/assets/js/global.js
+++ b/resources/assets/js/global.js
@@ -19,12 +19,8 @@ let axiosInstance = axios.create({
     }
 });
 window.$http = axiosInstance;
-
 Vue.prototype.$http = axiosInstance;
 
-require("./vues/vues");
-require("./components");
-
 
 // AngularJS - Create application and load components
 const angular = require("angular");
@@ -67,6 +63,9 @@ class EventManager {
 window.Events = new EventManager();
 Vue.prototype.$events = window.Events;
 
+require("./vues/vues");
+require("./components");
+
 // Load in angular specific items
 const Services = require('./services');
 const Directives = require('./directives');
@@ -93,83 +92,11 @@ jQuery.expr[":"].contains = $.expr.createPseudo(function (arg) {
     };
 });
 
-// Global jQuery Elements
-let notifications = $('.notification');
-let successNotification = notifications.filter('.pos');
-let errorNotification = notifications.filter('.neg');
-let warningNotification = notifications.filter('.warning');
-// Notification Events
-window.Events.listen('success', function (text) {
-    successNotification.hide();
-    successNotification.find('span').text(text);
-    setTimeout(() => {
-        successNotification.show();
-    }, 1);
-});
-window.Events.listen('warning', function (text) {
-    warningNotification.find('span').text(text);
-    warningNotification.show();
-});
-window.Events.listen('error', function (text) {
-    errorNotification.find('span').text(text);
-    errorNotification.show();
-});
-
-// Notification hiding
-notifications.click(function () {
-    $(this).fadeOut(100);
-});
-
-// Chapter page list toggles
-$('.chapter-toggle').click(function (e) {
-    e.preventDefault();
-    $(this).toggleClass('open');
-    $(this).closest('.chapter').find('.inset-list').slideToggle(180);
-});
-
-// Back to top button
-$('#back-to-top').click(function() {
-     $('#header').smoothScrollTo();
-});
-let scrollTopShowing = false;
-let scrollTop = document.getElementById('back-to-top');
-let scrollTopBreakpoint = 1200;
-window.addEventListener('scroll', function() {
-    let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
-    if (!scrollTopShowing && scrollTopPos > scrollTopBreakpoint) {
-        scrollTop.style.display = 'block';
-        scrollTopShowing = true;
-        setTimeout(() => {
-            scrollTop.style.opacity = 0.4;
-        }, 1);
-    } else if (scrollTopShowing && scrollTopPos < scrollTopBreakpoint) {
-        scrollTop.style.opacity = 0;
-        scrollTopShowing = false;
-        setTimeout(() => {
-            scrollTop.style.display = 'none';
-        }, 500);
-    }
-});
-
-// Common jQuery actions
-$('[data-action="expand-entity-list-details"]').click(function() {
-    $('.entity-list.compact').find('p').not('.empty-text').slideToggle(240);
-});
-
-// Popup close
-$('.popup-close').click(function() {
-    $(this).closest('.overlay').fadeOut(240);
-});
-$('.overlay').click(function(event) {
-    if (!$(event.target).hasClass('overlay')) return;
-    $(this).fadeOut(240);
-});
-
 // Detect IE for css
 if(navigator.userAgent.indexOf('MSIE')!==-1
     || navigator.appVersion.indexOf('Trident/') > 0
     || navigator.userAgent.indexOf('Safari') !== -1){
-    $('body').addClass('flexbox-support');
+    document.body.classList.add('flexbox-support');
 }
 
 // Page specific items
diff --git a/resources/assets/sass/_animations.scss b/resources/assets/sass/_animations.scss
index 467399a66..015a23ab1 100644
--- a/resources/assets/sass/_animations.scss
+++ b/resources/assets/sass/_animations.scss
@@ -36,41 +36,12 @@
   }
 }
 
-.anim.notification {
-  transform: translate3d(580px, 0, 0);
-  animation-name: notification;
-  animation-duration: 3s;
-  animation-timing-function: ease-in-out;
-  animation-fill-mode: forwards;
-  &.stopped {
-    animation-name: notificationStopped;
-  }
-}
-
-@keyframes notification {
-  0% {
-    transform: translate3d(580px, 0, 0);
-  }
-  10% {
-    transform: translate3d(0, 0, 0);
-  }
-  90% {
-    transform: translate3d(0, 0, 0);
-  }
-  100% {
-    transform: translate3d(580px, 0, 0);
-  }
-}
-@keyframes notificationStopped {
-  0% {
-    transform: translate3d(580px, 0, 0);
-  }
-  10% {
-    transform: translate3d(0, 0, 0);
-  }
-  100% {
-    transform: translate3d(0, 0, 0);
-  }
+.anim.menuIn {
+  transform-origin: 100% 0%;
+  animation-name: menuIn;
+  animation-duration: 120ms;
+  animation-delay: 0s;
+  animation-timing-function: cubic-bezier(.62, .28, .23, .99);
 }
 
 @keyframes menuIn {
@@ -85,14 +56,6 @@
   }
 }
 
-.anim.menuIn {
-  transform-origin: 100% 0%;
-  animation-name: menuIn;
-  animation-duration: 120ms;
-  animation-delay: 0s;
-  animation-timing-function: cubic-bezier(.62, .28, .23, .99);
-}
-
 @keyframes loadingBob {
   0% {
     transform: translate3d(0, 0, 0);
diff --git a/resources/assets/sass/_components.scss b/resources/assets/sass/_components.scss
index 12babae73..8092caa07 100644
--- a/resources/assets/sass/_components.scss
+++ b/resources/assets/sass/_components.scss
@@ -1,4 +1,65 @@
-.overlay {
+// System wide notifications
+[notification] {
+  position: fixed;
+  top: 0;
+  right: 0;
+  margin: $-xl*2 $-xl;
+  padding: $-l $-xl;
+  background-color: #EEE;
+  border-radius: 3px;
+  box-shadow: $bs-med;
+  z-index: 999999;
+  display: block;
+  cursor: pointer;
+  max-width: 480px;
+  transition: transform ease-in-out 360ms;
+  transform: translate3d(580px, 0, 0);
+  i, span {
+    display: table-cell;
+  }
+  i {
+    font-size: 2em;
+    padding-right: $-l;
+  }
+  span {
+    vertical-align: middle;
+  }
+  &.pos {
+    background-color: $positive;
+    color: #EEE;
+  }
+  &.neg {
+    background-color: $negative;
+    color: #EEE;
+  }
+  &.warning {
+    background-color: $secondary;
+    color: #EEE;
+  }
+  &.showing {
+    transform: translate3d(0, 0, 0);
+  }
+}
+
+[chapter-toggle] {
+  cursor: pointer;
+  margin: 0;
+  transition: all ease-in-out 180ms;
+  user-select: none;
+  i.zmdi-caret-right {
+    transition: all ease-in-out 180ms;
+    transform: rotate(0deg);
+    transform-origin: 25% 50%;
+  }
+  &.open {
+    //margin-bottom: 0;
+  }
+  &.open i.zmdi-caret-right {
+    transform: rotate(90deg);
+  }
+}
+
+[overlay] {
   background-color: rgba(0, 0, 0, 0.333);
   position: fixed;
   z-index: 95536;
diff --git a/resources/assets/sass/_lists.scss b/resources/assets/sass/_lists.scss
index 051268926..d08ccc9bb 100644
--- a/resources/assets/sass/_lists.scss
+++ b/resources/assets/sass/_lists.scss
@@ -9,7 +9,6 @@
   .inset-list {
     display: none;
     overflow: hidden;
-    margin-bottom: $-l;
   }
   h5 {
     display: block;
@@ -22,6 +21,9 @@
       border-left-color: $color-page-draft;
     }
   }
+  .entity-list-item {
+    margin-bottom: $-m;
+  }
   hr {
     margin-top: 0;
   }
@@ -51,23 +53,6 @@
     margin-right: $-s;
   }
 }
-.chapter-toggle {
-  cursor: pointer;
-  margin: 0 0 $-l 0;
-  transition: all ease-in-out 180ms;
-  user-select: none;
-  i.zmdi-caret-right {
-    transition: all ease-in-out 180ms;
-    transform: rotate(0deg);
-    transform-origin: 25% 50%;
-  }
-  &.open {
-    margin-bottom: 0;
-  }
-  &.open i.zmdi-caret-right {
-    transform: rotate(90deg);
-  }
-}
 
 .sidebar-page-nav {
   $nav-indent: $-s;
@@ -171,7 +156,7 @@
       background-color: rgba($color-chapter, 0.12);
     }
   }
-  .chapter-toggle {
+  [chapter-toggle] {
     padding-left: $-s;
   }
   .list-item-chapter {
@@ -336,8 +321,10 @@ ul.pagination {
   h4, a {
     line-height: 1.2;
   }
-  p {
+  .entity-item-snippet {
     display: none;
+  }
+  p {
     font-size: $fs-m * 0.8;
     padding-top: $-xs;
     margin: 0;
diff --git a/resources/assets/sass/styles.scss b/resources/assets/sass/styles.scss
index 3b279b8bd..e40430bd8 100644
--- a/resources/assets/sass/styles.scss
+++ b/resources/assets/sass/styles.scss
@@ -66,44 +66,6 @@ body.dragging, body.dragging * {
   }
 }
 
-// System wide notifications
-.notification {
-  position: fixed;
-  top: 0;
-  right: 0;
-  margin: $-xl*2 $-xl;
-  padding: $-l $-xl;
-  background-color: #EEE;
-  border-radius: 3px;
-  box-shadow: $bs-med;
-  z-index: 999999;
-  display: block;
-  cursor: pointer;
-  max-width: 480px;
-  i, span {
-    display: table-cell;
-  }
-  i {
-    font-size: 2em;
-    padding-right: $-l;
-  }
-  span {
-    vertical-align: middle;
-  }
-  &.pos {
-    background-color: $positive;
-    color: #EEE;
-  }
-  &.neg {
-    background-color: $negative;
-    color: #EEE;
-  }
-  &.warning {
-    background-color: $secondary;
-    color: #EEE;
-  }
-}
-
 // Loading icon
 $loadingSize: 10px;
 .loading-container {
@@ -151,7 +113,7 @@ $loadingSize: 10px;
 
 // Back to top link
 $btt-size: 40px;
-#back-to-top {
+[back-to-top] {
   background-color: $primary;
   position: fixed;
   bottom: $-m;
diff --git a/resources/views/base.blade.php b/resources/views/base.blade.php
index 95a9d72b0..b1cacf08c 100644
--- a/resources/views/base.blade.php
+++ b/resources/views/base.blade.php
@@ -77,7 +77,7 @@
         @yield('content')
     </section>
 
-    <div id="back-to-top">
+    <div back-to-top>
         <div class="inner">
             <i class="zmdi zmdi-chevron-up"></i> <span>{{ trans('common.back_to_top') }}</span>
         </div>
diff --git a/resources/views/books/list-item.blade.php b/resources/views/books/list-item.blade.php
index 605841f7f..92d0f9e2d 100644
--- a/resources/views/books/list-item.blade.php
+++ b/resources/views/books/list-item.blade.php
@@ -1,8 +1,10 @@
 <div class="book entity-list-item"  data-entity-type="book" data-entity-id="{{$book->id}}">
     <h4 class="text-book"><a class="text-book entity-list-item-link" href="{{$book->getUrl()}}"><i class="zmdi zmdi-book"></i><span class="entity-list-item-name">{{$book->name}}</span></a></h4>
-    @if(isset($book->searchSnippet))
-        <p class="text-muted">{!! $book->searchSnippet !!}</p>
-    @else
-        <p class="text-muted">{{ $book->getExcerpt() }}</p>
-    @endif
+    <div class="entity-item-snippet">
+        @if(isset($book->searchSnippet))
+            <p class="text-muted">{!! $book->searchSnippet !!}</p>
+        @else
+            <p class="text-muted">{{ $book->getExcerpt() }}</p>
+        @endif
+    </div>
 </div>
\ No newline at end of file
diff --git a/resources/views/chapters/list-item.blade.php b/resources/views/chapters/list-item.blade.php
index 1ae20b301..1572f0d9b 100644
--- a/resources/views/chapters/list-item.blade.php
+++ b/resources/views/chapters/list-item.blade.php
@@ -10,14 +10,18 @@
             <i class="zmdi zmdi-collection-bookmark"></i><span class="entity-list-item-name">{{ $chapter->name }}</span>
         </a>
     </h4>
-    @if(isset($chapter->searchSnippet))
-        <p class="text-muted">{!! $chapter->searchSnippet !!}</p>
-    @else
-        <p class="text-muted">{{ $chapter->getExcerpt() }}</p>
-    @endif
+
+    <div class="entity-item-snippet">
+        @if(isset($chapter->searchSnippet))
+            <p class="text-muted">{!! $chapter->searchSnippet !!}</p>
+        @else
+            <p class="text-muted">{{ $chapter->getExcerpt() }}</p>
+        @endif
+    </div>
+
 
     @if(!isset($hidePages) && count($chapter->pages) > 0)
-        <p class="text-muted chapter-toggle"><i class="zmdi zmdi-caret-right"></i> <i class="zmdi zmdi-file-text"></i> <span>{{ trans('entities.x_pages', ['count' => $chapter->pages->count()]) }}</span></p>
+        <p chapter-toggle class="text-muted"><i class="zmdi zmdi-caret-right"></i> <i class="zmdi zmdi-file-text"></i> <span>{{ trans('entities.x_pages', ['count' => $chapter->pages->count()]) }}</span></p>
         <div class="inset-list">
             @foreach($chapter->pages as $page)
                 <h5 class="@if($page->draft) draft @endif"><a href="{{ $page->getUrl() }}" class="text-page @if($page->draft) draft @endif"><i class="zmdi zmdi-file-text"></i>{{$page->name}}</a></h5>
diff --git a/resources/views/components/code-editor.blade.php b/resources/views/components/code-editor.blade.php
index 5a385ef49..5788bd7f7 100644
--- a/resources/views/components/code-editor.blade.php
+++ b/resources/views/components/code-editor.blade.php
@@ -1,10 +1,10 @@
 <div id="code-editor">
-    <div class="overlay" ref="overlay" v-cloak @click="hide()">
+    <div overlay ref="overlay" v-cloak @click="hide()">
         <div class="popup-body" @click.stop>
 
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('components.code_editor') }}</div>
-                <button class="popup-close neg corner-button button" @click="hide()">x</button>
+                <button class="overlay-close neg corner-button button" @click="hide()">x</button>
             </div>
 
             <div class="padded">
diff --git a/resources/views/components/entity-selector-popup.blade.php b/resources/views/components/entity-selector-popup.blade.php
index 1c4d1fadb..39d25bfa6 100644
--- a/resources/views/components/entity-selector-popup.blade.php
+++ b/resources/views/components/entity-selector-popup.blade.php
@@ -1,9 +1,9 @@
 <div id="entity-selector-wrap">
-    <div class="overlay" entity-link-selector>
+    <div overlay entity-link-selector>
         <div class="popup-body small flex-child">
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('entities.entity_select') }}</div>
-                <button type="button" class="corner-button neg button popup-close">x</button>
+                <button type="button" class="corner-button neg button overlay-close">x</button>
             </div>
             @include('components.entity-selector', ['name' => 'entity-selector'])
             <div class="popup-footer">
diff --git a/resources/views/components/image-manager.blade.php b/resources/views/components/image-manager.blade.php
index 39f3bcd3c..05bf09799 100644
--- a/resources/views/components/image-manager.blade.php
+++ b/resources/views/components/image-manager.blade.php
@@ -1,10 +1,10 @@
 <div id="image-manager" image-type="{{ $imageType }}" ng-controller="ImageManagerController" uploaded-to="{{ $uploaded_to or 0 }}">
-    <div class="overlay" ng-cloak ng-click="hide()">
+    <div overlay ng-cloak ng-click="hide()">
         <div class="popup-body" ng-click="$event.stopPropagation()">
 
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('components.image_select') }}</div>
-                <button class="popup-close neg corner-button button">x</button>
+                <button class="overlay-close neg corner-button button">x</button>
             </div>
 
             <div class="flex-fill image-manager-body">
diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php
index 49cd2a75a..87c84ec1e 100644
--- a/resources/views/home.blade.php
+++ b/resources/views/home.blade.php
@@ -7,7 +7,7 @@
             <div class="row">
                 <div class="col-sm-6 faded">
                     <div class="action-buttons text-left">
-                        <a data-action="expand-entity-list-details" class="text-primary text-button"><i class="zmdi zmdi-wrap-text"></i>{{ trans('common.toggle_details') }}</a>
+                        <a expand-toggle=".entity-list.compact .entity-item-snippet" class="text-primary text-button"><i class="zmdi zmdi-wrap-text"></i>{{ trans('common.toggle_details') }}</a>
                     </div>
                 </div>
             </div>
diff --git a/resources/views/pages/list-item.blade.php b/resources/views/pages/list-item.blade.php
index f440a52f6..593c2fc3c 100644
--- a/resources/views/pages/list-item.blade.php
+++ b/resources/views/pages/list-item.blade.php
@@ -15,11 +15,13 @@
         <a href="{{ $page->getUrl() }}" class="text-page entity-list-item-link"><i class="zmdi zmdi-file-text"></i><span class="entity-list-item-name">{{ $page->name }}</span></a>
     </h4>
 
-    @if(isset($page->searchSnippet))
-        <p class="text-muted">{!! $page->searchSnippet !!}</p>
-    @else
-        <p class="text-muted">{{ $page->getExcerpt() }}</p>
-    @endif
+    <div class="entity-item-snippet">
+        @if(isset($page->searchSnippet))
+            <p class="text-muted">{!! $page->searchSnippet !!}</p>
+        @else
+            <p class="text-muted">{{ $page->getExcerpt() }}</p>
+        @endif
+    </div>
 
     @if(isset($style) && $style === 'detailed')
         <div class="row meta text-muted text-small">
diff --git a/resources/views/pages/sidebar-tree-list.blade.php b/resources/views/pages/sidebar-tree-list.blade.php
index 0a10987d6..90737740c 100644
--- a/resources/views/pages/sidebar-tree-list.blade.php
+++ b/resources/views/pages/sidebar-tree-list.blade.php
@@ -51,7 +51,7 @@
                 </a>
 
                 @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
-                    <p class="text-muted chapter-toggle @if($bookChild->matchesOrContains($current)) open @endif">
+                    <p chapter-toggle class="text-muted @if($bookChild->matchesOrContains($current)) open @endif">
                         <i class="zmdi zmdi-caret-right"></i> <i class="zmdi zmdi-file-text"></i> <span>{{ trans('entities.x_pages', ['count' => $bookChild->pages->count()]) }}</span>
                     </p>
                     <ul class="menu sub-menu inset-list @if($bookChild->matchesOrContains($current)) open @endif">
diff --git a/resources/views/partials/custom-styles.blade.php b/resources/views/partials/custom-styles.blade.php
index 62bcc881f..c13051df4 100644
--- a/resources/views/partials/custom-styles.blade.php
+++ b/resources/views/partials/custom-styles.blade.php
@@ -1,5 +1,5 @@
 <style id="custom-styles" data-color="{{ setting('app-color') }}" data-color-light="{{ setting('app-color-light') }}">
-    header, #back-to-top, .primary-background {
+    header, [back-to-top], .primary-background {
         background-color: {{ setting('app-color') }} !important;
     }
     .faded-small, .primary-background-light {
diff --git a/resources/views/partials/notifications.blade.php b/resources/views/partials/notifications.blade.php
index c079080db..215aee3ed 100644
--- a/resources/views/partials/notifications.blade.php
+++ b/resources/views/partials/notifications.blade.php
@@ -1,12 +1,12 @@
 
-<div class="notification anim pos" @if(!session()->has('success')) style="display:none;" @endif>
+<div notification="success" data-autohide class="pos" @if(session()->has('success')) data-show @endif>
     <i class="zmdi zmdi-check-circle"></i> <span>{!! nl2br(htmlentities(session()->get('success'))) !!}</span>
 </div>
 
-<div class="notification anim warning stopped" @if(!session()->has('warning')) style="display:none;" @endif>
+<div notification="warning" class="warning" @if(session()->has('warning')) data-show @endif>
     <i class="zmdi zmdi-info"></i> <span>{!! nl2br(htmlentities(session()->get('warning'))) !!}</span>
 </div>
 
-<div class="notification anim neg stopped" @if(!session()->has('error')) style="display:none;" @endif>
+<div notification="error" class="neg" @if(session()->has('error')) data-show @endif>
     <i class="zmdi zmdi-alert-circle"></i> <span>{!! nl2br(htmlentities(session()->get('error'))) !!}</span>
 </div>